Skip to content

Commit f2f9e71

Browse files
Add basic support for playlist controls
1 parent a92b95d commit f2f9e71

File tree

10 files changed

+262
-52
lines changed

10 files changed

+262
-52
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,17 @@ If you need to change the orientation of your Activity/Fragment, remember that b
389389
</application>
390390
```
391391

392+
### Playlist
393+
You can initialize the player to play playlists instead of videos. This can be done by setting `listType` to `playlist` and then providing the id of the playlist to `list`.
394+
395+
```kotlin
396+
val iFramePlayerOptions = IFramePlayerOptions.Builder()
397+
.controls(1)
398+
.listType("playlist")
399+
.list(PLAYLIST_ID)
400+
.build()
401+
```
402+
392403
### Release the YouTubePlayerView
393404
Remember to release the `YouTubePlayerView` when you're done using it, by calling `YouTubePlayerView.release()`.
394405

chromecast-receiver/js/YouTubePlayer.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,46 @@ function YouTubePlayer(communicationConstants, communicationChannel) {
137137
player.setPlaybackRate(playbackRate)
138138
}
139139

140+
function nextVideo() {
141+
player.nextVideo()
142+
}
143+
144+
function previousVideo() {
145+
player.previousVideo()
146+
}
147+
148+
function playVideoAt(index) {
149+
player.playVideoAt(index)
150+
}
151+
140152
function getActions() {
141153
return actions
142154
}
143155

144-
const actions = { seekTo, pauseVideo, playVideo, loadVideo, cueVideo, mute, unMute, setVolume, setPlaybackRate }
156+
function setLoop(loop) {
157+
player.setLoop(loop)
158+
}
159+
160+
function setShuffle(shuffle) {
161+
player.setShuffle(shuffle);
162+
}
163+
164+
const actions = {
165+
seekTo,
166+
pauseVideo,
167+
playVideo,
168+
loadVideo,
169+
cueVideo,
170+
mute,
171+
unMute,
172+
setVolume,
173+
setPlaybackRate,
174+
nextVideo,
175+
previousVideo,
176+
playVideoAt,
177+
setLoop,
178+
setShuffle
179+
}
145180

146181
return {
147182
initialize,

chromecast-receiver/js/io/SenderMessagesDispatcher.js

+35-11
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,52 @@ function SenderMessagesDispatcher(communicationConstants, callbacks) {
1010
function onMessage(message) {
1111
console.log(message.data)
1212

13-
if(message.data.command === INIT_COMMUNICATION_CONSTANTS)
13+
if(message.data.command === INIT_COMMUNICATION_CONSTANTS) {
1414
callbacks.onInitMessageReceived(message.data.communicationConstants)
15+
}
1516

16-
else if(message.data.command === communicationConstants.LOAD)
17+
else if(message.data.command === communicationConstants.LOAD) {
1718
callbacks.loadVideo(message.data.videoId, Number(message.data.startSeconds))
18-
else if(message.data.command === communicationConstants.CUE)
19+
}
20+
else if(message.data.command === communicationConstants.CUE) {
1921
callbacks.cueVideo(message.data.videoId, Number(message.data.startSeconds))
20-
else if(message.data.command === communicationConstants.PLAY)
22+
}
23+
else if(message.data.command === communicationConstants.PLAY) {
2124
callbacks.playVideo()
22-
else if(message.data.command === communicationConstants.PAUSE)
25+
}
26+
else if(message.data.command === communicationConstants.PAUSE) {
2327
callbacks.pauseVideo()
24-
25-
else if(message.data.command === communicationConstants.MUTE)
28+
}
29+
else if(message.data.command === communicationConstants.MUTE) {
2630
callbacks.mute()
27-
else if(message.data.command === communicationConstants.UNMUTE)
31+
}
32+
else if(message.data.command === communicationConstants.UNMUTE) {
2833
callbacks.unMute()
29-
else if(message.data.command === communicationConstants.SET_VOLUME)
34+
}
35+
else if(message.data.command === communicationConstants.SET_VOLUME) {
3036
callbacks.setVolume(Number(message.data.volumePercent))
31-
else if(message.data.command === communicationConstants.SEEK_TO)
37+
}
38+
else if(message.data.command === communicationConstants.SEEK_TO) {
3239
callbacks.seekTo(Number(message.data.time))
33-
else if(message.data.command === communicationConstants.SET_PLAYBACK_RATE)
40+
}
41+
else if(message.data.command === communicationConstants.SET_PLAYBACK_RATE) {
3442
callbacks.setPlaybackRate(Number(message.data.playbackRate))
43+
}
44+
else if(message.data.command === communicationConstants.NEXT_VIDEO) {
45+
callbacks.nextVideo()
46+
}
47+
else if(message.data.command === communicationConstants.PREVIOUS_VIDEO) {
48+
callbacks.previousVideo()
49+
}
50+
else if(message.data.command === communicationConstants.PLAY_VIDEO_AT) {
51+
callbacks.playVideoAt(Number(message.data.index))
52+
}
53+
else if(message.data.command === communicationConstants.SET_LOOP) {
54+
callbacks.setLoop(message.data.loop === "true")
55+
}
56+
else if(message.data.command === communicationConstants.SET_SHUFFLE) {
57+
callbacks.setShuffle(message.data.shuffle === "true")
58+
}
3559
}
3660

3761
return {

chromecast-sender/src/main/java/com/pierfrancescosoffritti/androidyoutubeplayer/chromecast/chromecastsender/ChromecastYouTubePlayer.kt

+43
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,49 @@ class ChromecastYouTubePlayer internal constructor(private val chromecastCommuni
7070
chromecastCommunicationChannel.sendMessage(message)
7171
}
7272

73+
override fun nextVideo() {
74+
val message = JSONUtils.buildFlatJson(
75+
"command" to ChromecastCommunicationConstants.PLAY_NEXT_VIDEO
76+
)
77+
78+
chromecastCommunicationChannel.sendMessage(message)
79+
}
80+
81+
override fun previousVideo() {
82+
val message = JSONUtils.buildFlatJson(
83+
"command" to ChromecastCommunicationConstants.PLAY_PREVIOUS_VIDEO
84+
)
85+
86+
chromecastCommunicationChannel.sendMessage(message)
87+
}
88+
89+
override fun playVideoAt(index: Int) {
90+
val message = JSONUtils.buildFlatJson(
91+
"command" to ChromecastCommunicationConstants.PLAY_VIDEO_AT,
92+
"index" to index.toString()
93+
)
94+
95+
chromecastCommunicationChannel.sendMessage(message)
96+
}
97+
98+
override fun setLoop(loop: Boolean) {
99+
val message = JSONUtils.buildFlatJson(
100+
"command" to ChromecastCommunicationConstants.SET_LOOP,
101+
"loop" to loop.toString()
102+
)
103+
104+
chromecastCommunicationChannel.sendMessage(message)
105+
}
106+
107+
override fun setShuffle(shuffle: Boolean) {
108+
val message = JSONUtils.buildFlatJson(
109+
"command" to ChromecastCommunicationConstants.SET_SHUFFLE,
110+
"shuffle" to shuffle.toString()
111+
)
112+
113+
chromecastCommunicationChannel.sendMessage(message)
114+
}
115+
73116
override fun mute() {
74117
val message = JSONUtils.buildFlatJson(
75118
"command" to ChromecastCommunicationConstants.MUTE

chromecast-sender/src/main/java/com/pierfrancescosoffritti/androidyoutubeplayer/chromecast/chromecastsender/io/youtube/ChromecastCommunicationConstants.kt

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ internal object ChromecastCommunicationConstants {
3030
const val MUTE = "MUTE"
3131
const val UNMUTE = "UNMUTE"
3232
const val SET_PLAYBACK_RATE = "SET_PLAYBACK_RATE"
33+
const val PLAY_NEXT_VIDEO = "PLAY_NEXT_VIDEO"
34+
const val PLAY_PREVIOUS_VIDEO = "PLAY_PREVIOUS_VIDEO"
35+
const val PLAY_VIDEO_AT = "PLAY_VIDEO_AT"
36+
const val SET_LOOP = "SET_LOOP"
37+
const val SET_SHUFFLE = "SET_SHUFFLE"
3338

3439
fun asJson() = JSONUtils.buildFlatJson(
3540
IFRAME_API_READY to IFRAME_API_READY,
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,65 @@
1-
package com.pierfrancescosoffritti.androidyoutubeplayer.core.sampleapp.examples.playlistExample;
1+
package com.pierfrancescosoffritti.androidyoutubeplayer.core.sampleapp.examples.playlistExample
22

3-
import android.content.res.Configuration;
4-
import android.os.Bundle;
3+
import android.os.Bundle
4+
import android.widget.Button
5+
import androidx.appcompat.app.AppCompatActivity
6+
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
7+
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
8+
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions
9+
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
10+
import com.pierfrancescosoffritti.aytplayersample.R
511

6-
import androidx.annotation.NonNull;
7-
import androidx.appcompat.app.AppCompatActivity;
12+
class PlaylistExampleActivity : AppCompatActivity() {
13+
private var youTubePlayerView: YouTubePlayerView? = null
14+
private var youTubePlayer: YouTubePlayer? = null
815

9-
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener;
10-
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions;
11-
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView;
12-
import com.pierfrancescosoffritti.aytplayersample.R;
16+
override fun onCreate(savedInstanceState: Bundle?) {
17+
super.onCreate(savedInstanceState)
18+
setContentView(R.layout.activity_playlist_example)
1319

14-
public class PlaylistExampleActivity extends AppCompatActivity {
20+
youTubePlayerView = findViewById<YouTubePlayerView>(R.id.youtube_player_view).apply {
21+
val iFramePlayerOptions = IFramePlayerOptions.Builder()
22+
.controls(1)
23+
.listType("playlist")
24+
.list(PLAYLIST_ID)
25+
.build()
1526

16-
private static final String PLAYLIST_ID = "PLEpEmEcrrKJUhZkyIAgQ17Oxyd3fx_y1j";
17-
private YouTubePlayerView youTubePlayerView;
18-
19-
@Override
20-
protected void onCreate(Bundle savedInstanceState) {
21-
super.onCreate(savedInstanceState);
22-
setContentView(R.layout.activity_playlist_example);
27+
lifecycle.addObserver(this)
28+
this.initialize(
29+
youtubePlayerListener,
30+
handleNetworkEvents = true,
31+
iFramePlayerOptions
32+
)
33+
}
2334

24-
youTubePlayerView = findViewById(R.id.youtube_player_view);
25-
getLifecycle().addObserver(youTubePlayerView);
35+
findViewById<Button>(R.id.next_video_button).setOnClickListener {
36+
youTubePlayer?.nextVideo()
37+
}
2638

27-
initYouTubePlayerView();
28-
}
39+
findViewById<Button>(R.id.previous_video_button).setOnClickListener {
40+
youTubePlayer?.previousVideo()
41+
}
2942

30-
private void initYouTubePlayerView() {
31-
IFramePlayerOptions iFramePlayerOptions = new IFramePlayerOptions.Builder()
32-
.controls(1)
33-
.listType("playlist")
34-
.list(PLAYLIST_ID)
35-
.build();
43+
findViewById<Button>(R.id.play_second_video_button).setOnClickListener {
44+
youTubePlayer?.playVideoAt(1)
45+
}
3646

37-
getLifecycle().addObserver(youTubePlayerView);
47+
findViewById<Button>(R.id.shuffle_button).setOnClickListener {
48+
youTubePlayer?.setShuffle(true)
49+
}
3850

39-
youTubePlayerView.initialize(new AbstractYouTubePlayerListener() {
40-
}, true, iFramePlayerOptions);
51+
findViewById<Button>(R.id.loop_button).setOnClickListener {
52+
youTubePlayer?.setLoop(true)
53+
}
4154
}
4255

43-
@Override
44-
public void onConfigurationChanged(@NonNull Configuration newConfig) {
45-
super.onConfigurationChanged(newConfig);
56+
companion object {
57+
private const val PLAYLIST_ID = "PLEpEmEcrrKJUhZkyIAgQ17Oxyd3fx_y1j"
58+
}
4659

47-
// Checks the orientation of the screen
48-
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
49-
youTubePlayerView.matchParent();
50-
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
51-
youTubePlayerView.wrapContent();
60+
private val youtubePlayerListener = object : AbstractYouTubePlayerListener() {
61+
override fun onReady(youTubePlayer: YouTubePlayer) {
62+
this@PlaylistExampleActivity.youTubePlayer = youTubePlayer
5263
}
5364
}
54-
}
65+
}

core-sample-app/src/main/res/layout/activity_playlist_example.xml

+44-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,55 @@
33
xmlns:android="http://schemas.android.com/apk/res/android"
44
xmlns:app="http://schemas.android.com/apk/res-auto"
55
android:layout_width="match_parent"
6-
android:layout_height="match_parent" >
6+
android:layout_height="match_parent"
7+
android:orientation="vertical">
78

89
<com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
910
android:id="@+id/youtube_player_view"
1011
android:layout_width="match_parent"
1112
android:layout_height="wrap_content"
1213
app:enableAutomaticInitialization="false" />
1314

15+
<androidx.appcompat.widget.LinearLayoutCompat
16+
android:layout_width="match_parent"
17+
android:layout_height="wrap_content"
18+
android:orientation="horizontal">
19+
20+
<Button
21+
android:id="@+id/previous_video_button"
22+
android:layout_width="wrap_content"
23+
android:layout_height="wrap_content"
24+
android:text="Previous video" />
25+
<Button
26+
android:id="@+id/next_video_button"
27+
android:layout_width="wrap_content"
28+
android:layout_height="wrap_content"
29+
android:text="Next video" />
30+
<Button
31+
android:id="@+id/play_second_video_button"
32+
android:layout_width="wrap_content"
33+
android:layout_height="wrap_content"
34+
android:text="Play Second Video" />
35+
36+
</androidx.appcompat.widget.LinearLayoutCompat>
37+
38+
<androidx.appcompat.widget.LinearLayoutCompat
39+
android:layout_width="match_parent"
40+
android:layout_height="wrap_content"
41+
android:orientation="horizontal">
42+
43+
<Button
44+
android:id="@+id/loop_button"
45+
android:layout_width="wrap_content"
46+
android:layout_height="wrap_content"
47+
android:text="Loop" />
48+
49+
<Button
50+
android:id="@+id/shuffle_button"
51+
android:layout_width="wrap_content"
52+
android:layout_height="wrap_content"
53+
android:text="Shuffle" />
54+
55+
</androidx.appcompat.widget.LinearLayoutCompat>
56+
1457
</LinearLayout>

core/src/main/java/com/pierfrancescosoffritti/androidyoutubeplayer/core/player/YouTubePlayer.kt

+13
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ interface YouTubePlayer {
2323
fun play()
2424
fun pause()
2525

26+
/** If the player is playing a playlist, play the next video. */
27+
fun nextVideo()
28+
/** If the player is playing a playlist, play the previous video. */
29+
fun previousVideo()
30+
/** If the player is playing a playlist, play the video at position [index]. */
31+
fun playVideoAt(index: Int)
32+
33+
/** If the player is playing a playlist, enable or disable looping of the playlist. */
34+
fun setLoop(loop: Boolean)
35+
36+
/** If the player is playing a playlist, enable or disable shuffling of the playlist. */
37+
fun setShuffle(shuffle: Boolean)
38+
2639
fun mute()
2740
fun unMute()
2841

core/src/main/java/com/pierfrancescosoffritti/androidyoutubeplayer/core/player/views/WebViewYouTubePlayer.kt

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ private class YouTubePlayerImpl(private val webView: WebView) : YouTubePlayer {
3333
override fun cueVideo(videoId: String, startSeconds: Float) = webView.invoke("cueVideo", videoId, startSeconds)
3434
override fun play() = webView.invoke("playVideo")
3535
override fun pause() = webView.invoke("pauseVideo")
36+
override fun nextVideo() = webView.invoke("nextVideo")
37+
override fun previousVideo() = webView.invoke("previousVideo")
38+
override fun playVideoAt(index: Int) = webView.invoke("playVideoAt", index)
39+
override fun setLoop(loop: Boolean) = webView.invoke("setLoop", loop)
40+
override fun setShuffle(shuffle: Boolean) = webView.invoke("setShuffle", shuffle)
3641
override fun mute() = webView.invoke("mute")
3742
override fun unMute() = webView.invoke("unMute")
3843
override fun setVolume(volumePercent: Int) {

0 commit comments

Comments
 (0)