Skip to content

Commit

Permalink
Add basic support for playlist controls
Browse files Browse the repository at this point in the history
  • Loading branch information
PierfrancescoSoffritti committed Aug 1, 2023
1 parent a92b95d commit f2f9e71
Show file tree
Hide file tree
Showing 10 changed files with 262 additions and 52 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,17 @@ If you need to change the orientation of your Activity/Fragment, remember that b
</application>
```

### Playlist
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`.

```kotlin
val iFramePlayerOptions = IFramePlayerOptions.Builder()
.controls(1)
.listType("playlist")
.list(PLAYLIST_ID)
.build()
```

### Release the YouTubePlayerView
Remember to release the `YouTubePlayerView` when you're done using it, by calling `YouTubePlayerView.release()`.

Expand Down
37 changes: 36 additions & 1 deletion chromecast-receiver/js/YouTubePlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,46 @@ function YouTubePlayer(communicationConstants, communicationChannel) {
player.setPlaybackRate(playbackRate)
}

function nextVideo() {
player.nextVideo()
}

function previousVideo() {
player.previousVideo()
}

function playVideoAt(index) {
player.playVideoAt(index)
}

function getActions() {
return actions
}

const actions = { seekTo, pauseVideo, playVideo, loadVideo, cueVideo, mute, unMute, setVolume, setPlaybackRate }
function setLoop(loop) {
player.setLoop(loop)
}

function setShuffle(shuffle) {
player.setShuffle(shuffle);
}

const actions = {
seekTo,
pauseVideo,
playVideo,
loadVideo,
cueVideo,
mute,
unMute,
setVolume,
setPlaybackRate,
nextVideo,
previousVideo,
playVideoAt,
setLoop,
setShuffle
}

return {
initialize,
Expand Down
46 changes: 35 additions & 11 deletions chromecast-receiver/js/io/SenderMessagesDispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,52 @@ function SenderMessagesDispatcher(communicationConstants, callbacks) {
function onMessage(message) {
console.log(message.data)

if(message.data.command === INIT_COMMUNICATION_CONSTANTS)
if(message.data.command === INIT_COMMUNICATION_CONSTANTS) {
callbacks.onInitMessageReceived(message.data.communicationConstants)
}

else if(message.data.command === communicationConstants.LOAD)
else if(message.data.command === communicationConstants.LOAD) {
callbacks.loadVideo(message.data.videoId, Number(message.data.startSeconds))
else if(message.data.command === communicationConstants.CUE)
}
else if(message.data.command === communicationConstants.CUE) {
callbacks.cueVideo(message.data.videoId, Number(message.data.startSeconds))
else if(message.data.command === communicationConstants.PLAY)
}
else if(message.data.command === communicationConstants.PLAY) {
callbacks.playVideo()
else if(message.data.command === communicationConstants.PAUSE)
}
else if(message.data.command === communicationConstants.PAUSE) {
callbacks.pauseVideo()

else if(message.data.command === communicationConstants.MUTE)
}
else if(message.data.command === communicationConstants.MUTE) {
callbacks.mute()
else if(message.data.command === communicationConstants.UNMUTE)
}
else if(message.data.command === communicationConstants.UNMUTE) {
callbacks.unMute()
else if(message.data.command === communicationConstants.SET_VOLUME)
}
else if(message.data.command === communicationConstants.SET_VOLUME) {
callbacks.setVolume(Number(message.data.volumePercent))
else if(message.data.command === communicationConstants.SEEK_TO)
}
else if(message.data.command === communicationConstants.SEEK_TO) {
callbacks.seekTo(Number(message.data.time))
else if(message.data.command === communicationConstants.SET_PLAYBACK_RATE)
}
else if(message.data.command === communicationConstants.SET_PLAYBACK_RATE) {
callbacks.setPlaybackRate(Number(message.data.playbackRate))
}
else if(message.data.command === communicationConstants.NEXT_VIDEO) {
callbacks.nextVideo()
}
else if(message.data.command === communicationConstants.PREVIOUS_VIDEO) {
callbacks.previousVideo()
}
else if(message.data.command === communicationConstants.PLAY_VIDEO_AT) {
callbacks.playVideoAt(Number(message.data.index))
}
else if(message.data.command === communicationConstants.SET_LOOP) {
callbacks.setLoop(message.data.loop === "true")
}
else if(message.data.command === communicationConstants.SET_SHUFFLE) {
callbacks.setShuffle(message.data.shuffle === "true")
}
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,49 @@ class ChromecastYouTubePlayer internal constructor(private val chromecastCommuni
chromecastCommunicationChannel.sendMessage(message)
}

override fun nextVideo() {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.PLAY_NEXT_VIDEO
)

chromecastCommunicationChannel.sendMessage(message)
}

override fun previousVideo() {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.PLAY_PREVIOUS_VIDEO
)

chromecastCommunicationChannel.sendMessage(message)
}

override fun playVideoAt(index: Int) {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.PLAY_VIDEO_AT,
"index" to index.toString()
)

chromecastCommunicationChannel.sendMessage(message)
}

override fun setLoop(loop: Boolean) {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.SET_LOOP,
"loop" to loop.toString()
)

chromecastCommunicationChannel.sendMessage(message)
}

override fun setShuffle(shuffle: Boolean) {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.SET_SHUFFLE,
"shuffle" to shuffle.toString()
)

chromecastCommunicationChannel.sendMessage(message)
}

override fun mute() {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.MUTE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ internal object ChromecastCommunicationConstants {
const val MUTE = "MUTE"
const val UNMUTE = "UNMUTE"
const val SET_PLAYBACK_RATE = "SET_PLAYBACK_RATE"
const val PLAY_NEXT_VIDEO = "PLAY_NEXT_VIDEO"
const val PLAY_PREVIOUS_VIDEO = "PLAY_PREVIOUS_VIDEO"
const val PLAY_VIDEO_AT = "PLAY_VIDEO_AT"
const val SET_LOOP = "SET_LOOP"
const val SET_SHUFFLE = "SET_SHUFFLE"

fun asJson() = JSONUtils.buildFlatJson(
IFRAME_API_READY to IFRAME_API_READY,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,65 @@
package com.pierfrancescosoffritti.androidyoutubeplayer.core.sampleapp.examples.playlistExample;
package com.pierfrancescosoffritti.androidyoutubeplayer.core.sampleapp.examples.playlistExample

import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
import com.pierfrancescosoffritti.aytplayersample.R

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
class PlaylistExampleActivity : AppCompatActivity() {
private var youTubePlayerView: YouTubePlayerView? = null
private var youTubePlayer: YouTubePlayer? = null

import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions;
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView;
import com.pierfrancescosoffritti.aytplayersample.R;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_playlist_example)

public class PlaylistExampleActivity extends AppCompatActivity {
youTubePlayerView = findViewById<YouTubePlayerView>(R.id.youtube_player_view).apply {
val iFramePlayerOptions = IFramePlayerOptions.Builder()
.controls(1)
.listType("playlist")
.list(PLAYLIST_ID)
.build()

private static final String PLAYLIST_ID = "PLEpEmEcrrKJUhZkyIAgQ17Oxyd3fx_y1j";
private YouTubePlayerView youTubePlayerView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_playlist_example);
lifecycle.addObserver(this)
this.initialize(
youtubePlayerListener,
handleNetworkEvents = true,
iFramePlayerOptions
)
}

youTubePlayerView = findViewById(R.id.youtube_player_view);
getLifecycle().addObserver(youTubePlayerView);
findViewById<Button>(R.id.next_video_button).setOnClickListener {
youTubePlayer?.nextVideo()
}

initYouTubePlayerView();
}
findViewById<Button>(R.id.previous_video_button).setOnClickListener {
youTubePlayer?.previousVideo()
}

private void initYouTubePlayerView() {
IFramePlayerOptions iFramePlayerOptions = new IFramePlayerOptions.Builder()
.controls(1)
.listType("playlist")
.list(PLAYLIST_ID)
.build();
findViewById<Button>(R.id.play_second_video_button).setOnClickListener {
youTubePlayer?.playVideoAt(1)
}

getLifecycle().addObserver(youTubePlayerView);
findViewById<Button>(R.id.shuffle_button).setOnClickListener {
youTubePlayer?.setShuffle(true)
}

youTubePlayerView.initialize(new AbstractYouTubePlayerListener() {
}, true, iFramePlayerOptions);
findViewById<Button>(R.id.loop_button).setOnClickListener {
youTubePlayer?.setLoop(true)
}
}

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
companion object {
private const val PLAYLIST_ID = "PLEpEmEcrrKJUhZkyIAgQ17Oxyd3fx_y1j"
}

// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
youTubePlayerView.matchParent();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
youTubePlayerView.wrapContent();
private val youtubePlayerListener = object : AbstractYouTubePlayerListener() {
override fun onReady(youTubePlayer: YouTubePlayer) {
this@PlaylistExampleActivity.youTubePlayer = youTubePlayer
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,55 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
android:layout_height="match_parent"
android:orientation="vertical">

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

<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:id="@+id/previous_video_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Previous video" />
<Button
android:id="@+id/next_video_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Next video" />
<Button
android:id="@+id/play_second_video_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play Second Video" />

</androidx.appcompat.widget.LinearLayoutCompat>

<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:id="@+id/loop_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Loop" />

<Button
android:id="@+id/shuffle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Shuffle" />

</androidx.appcompat.widget.LinearLayoutCompat>

</LinearLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ interface YouTubePlayer {
fun play()
fun pause()

/** If the player is playing a playlist, play the next video. */
fun nextVideo()
/** If the player is playing a playlist, play the previous video. */
fun previousVideo()
/** If the player is playing a playlist, play the video at position [index]. */
fun playVideoAt(index: Int)

/** If the player is playing a playlist, enable or disable looping of the playlist. */
fun setLoop(loop: Boolean)

/** If the player is playing a playlist, enable or disable shuffling of the playlist. */
fun setShuffle(shuffle: Boolean)

fun mute()
fun unMute()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ private class YouTubePlayerImpl(private val webView: WebView) : YouTubePlayer {
override fun cueVideo(videoId: String, startSeconds: Float) = webView.invoke("cueVideo", videoId, startSeconds)
override fun play() = webView.invoke("playVideo")
override fun pause() = webView.invoke("pauseVideo")
override fun nextVideo() = webView.invoke("nextVideo")
override fun previousVideo() = webView.invoke("previousVideo")
override fun playVideoAt(index: Int) = webView.invoke("playVideoAt", index)
override fun setLoop(loop: Boolean) = webView.invoke("setLoop", loop)
override fun setShuffle(shuffle: Boolean) = webView.invoke("setShuffle", shuffle)
override fun mute() = webView.invoke("mute")
override fun unMute() = webView.invoke("unMute")
override fun setVolume(volumePercent: Int) {
Expand Down
Loading

0 comments on commit f2f9e71

Please sign in to comment.