Skip to content

Commit

Permalink
Implemented playlists
Browse files Browse the repository at this point in the history
  • Loading branch information
reactormonk committed Jun 22, 2023
1 parent a8d2924 commit d88497e
Show file tree
Hide file tree
Showing 16 changed files with 365 additions and 10 deletions.
4 changes: 4 additions & 0 deletions chromecast-receiver/js/io/SenderMessagesDispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ function SenderMessagesDispatcher(communicationConstants, callbacks) {
callbacks.loadVideo(message.data.videoId, Number(message.data.startSeconds))
else if(message.data.command === communicationConstants.CUE)
callbacks.cueVideo(message.data.videoId, Number(message.data.startSeconds))
else if(message.data.command === communicationConstants.LOAD_PLAYLIST)
callbacks.loadPlaylist(message.data.playlistId, message.data.playlistIndex, Number(message.data.startSeconds))
else if(message.data.command === communicationConstants.CUE_PLAYLIST)
callbacks.cuePlaylist(message.data.playlistId, message.data.playlistIndex, Number(message.data.startSeconds))
else if(message.data.command === communicationConstants.PLAY)
callbacks.playVideo()
else if(message.data.command === communicationConstants.PAUSE)
Expand Down
27 changes: 27 additions & 0 deletions chromecast-receiver/js/io/YouTubePlayerRemoteBridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,28 @@ function YouTubePlayerRemoteBridge(communicationConstants, communicationChannel)
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.VIDEO_ID, data))
}

function sendPlaylistId(playlist) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.PLAYLIST_ID, playlist))
}
function sendPlaylistIndex(playlist) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.PLAYLIST_INDEX, index))
}
function sendPlaylistType(playlistType) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.PLAYLIST_TYPE, playlistType))
}
function sendPlaylistLength(length) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.PLAYLIST_LENGTH, length))
}
function sendVideoList(videos) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.VIDEO_LIST, videos))
}
function sendLoopStatus(loop) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.LOOP_STATUS, loop))
}
function sendShuffleStatus(shuffle) {
communicationChannel.sendMessage(new YouTubeMessage(communicationConstants.SHUFFLE_STATUS, shuffle))
}

return {
sendYouTubeIframeAPIReady: sendYouTubeIframeAPIReady,
sendReady: sendReady,
Expand All @@ -58,6 +80,11 @@ function YouTubePlayerRemoteBridge(communicationConstants, communicationChannel)
sendVideoCurrentTime: sendVideoCurrentTime,
sendVideoDuration: sendVideoDuration,
sendVideoId: sendVideoId,
sendPlaylistIndex: sendPlaylistIndex,
sendPlaylistId: sendPlaylistId,
sendPlaylistType: sendPlaylistType,
sendPlaylistLength: sendPlaylistLength,
sendVideoList: sendVideoList,
}
}

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

override fun loadPlaylist(videos: List<String>, index: Int, startSeconds: Float) {
TODO("Not yet implemented")
}

override fun loadPlaylist(list: String, listType: String, index: Int, startSeconds: Float) {
TODO("Not yet implemented")
}

override fun cuePlaylist(videos: List<String>, index: Int, startSeconds: Float) {
TODO("Not yet implemented")
}

override fun cuePlaylist(list: String, listType: String, index: Int, startSeconds: Float) {
TODO("Not yet implemented")
}

override fun setLoop(loop: Boolean) {
TODO("Not yet implemented")
}

override fun setShuffle(shuffle: Boolean) {
TODO("Not yet implemented")
}

override fun nextVideo() {
TODO("Not yet implemented")
}

override fun previousVideo() {
TODO("Not yet implemented")
}

override fun playVideoAt(index: Int) {
TODO("Not yet implemented")
}

override fun play() {
val message = JSONUtils.buildFlatJson(
"command" to ChromecastCommunicationConstants.PLAY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.pierfrancescosoffritti.androidyoutubeplayer.chromecast.chromecastsend
* Set of constants used for sender-receiver communication. The sender will send the constants to the receiver when a connection is initiated.
*/
internal object ChromecastCommunicationConstants {

// receiver to sender
const val INIT_COMMUNICATION_CONSTANTS = "INIT_COMMUNICATION_CONSTANTS"

Expand All @@ -19,10 +20,21 @@ internal object ChromecastCommunicationConstants {
const val VIDEO_CURRENT_TIME = "VIDEO_CURRENT_TIME"
const val VIDEO_DURATION = "VIDEO_DURATION"
const val VIDEO_ID = "VIDEO_ID"
const val PLAYLIST_ID = "PLAYLIST_ID"
const val PLAYLIST_INDEX = "PLAYLIST_INDEX"
const val PLAYLIST_TYPE = "PLAYLIST_TYPE"
const val PLAYLIST_LENGTH = "PLAYLIST_LENGTH"
const val VIDEO_LIST = "VIDEO_LIST"
const val LOOP_STATUS = "LOOP_STATUS"
const val SHUFFLE_STATUS = "SHUFFLE_STATUS"

// sender to receiver
const val LOAD = "LOAD"
const val LOAD_PLAYLIST = "LOAD_PLAYLIST"
const val LOAD_PLAYLIST_OBJ = "LOAD_PLAYLIST_OBJ"
const val CUE = "CUE"
const val CUE_PLAYLIST = "CUE_PLAYLIST"
const val CUE_PLAYLIST_OBJ = "CUE_PLAYLIST_OBJ"
const val PLAY = "PLAY"
const val PAUSE = "PAUSE"
const val SET_VOLUME = "SET_VOLUME"
Expand All @@ -42,9 +54,20 @@ internal object ChromecastCommunicationConstants {
VIDEO_CURRENT_TIME to VIDEO_CURRENT_TIME,
VIDEO_DURATION to VIDEO_DURATION,
VIDEO_ID to VIDEO_ID,
PLAYLIST_ID to PLAYLIST_ID,
PLAYLIST_INDEX to PLAYLIST_INDEX,
PLAYLIST_TYPE to PLAYLIST_TYPE,
PLAYLIST_LENGTH to PLAYLIST_LENGTH,
VIDEO_LIST to VIDEO_LIST,
SHUFFLE_STATUS to SHUFFLE_STATUS,
LOOP_STATUS to LOOP_STATUS,

LOAD to LOAD,
LOAD_PLAYLIST to LOAD_PLAYLIST,
LOAD_PLAYLIST_OBJ to LOAD_PLAYLIST_OBJ,
CUE to CUE,
CUE_PLAYLIST to CUE_PLAYLIST,
CUE_PLAYLIST_OBJ to CUE_PLAYLIST_OBJ,
PLAY to PLAY,
PAUSE to PAUSE,
SET_VOLUME to SET_VOLUME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ internal class ChromecastYouTubeMessageDispatcher(
ChromecastCommunicationConstants.VIDEO_CURRENT_TIME -> bridge.sendVideoCurrentTime(messageFromReceiver.data)
ChromecastCommunicationConstants.VIDEO_DURATION -> bridge.sendVideoDuration(messageFromReceiver.data)
ChromecastCommunicationConstants.VIDEO_ID -> bridge.sendVideoId(messageFromReceiver.data)
ChromecastCommunicationConstants.PLAYLIST_ID -> bridge.sendPlaylistId(messageFromReceiver.data)
ChromecastCommunicationConstants.PLAYLIST_TYPE -> bridge.sendPlaylistType(messageFromReceiver.data)
ChromecastCommunicationConstants.PLAYLIST_LENGTH -> bridge.sendPlaylistLength(messageFromReceiver.data)
ChromecastCommunicationConstants.VIDEO_LIST -> bridge.sendVideoList(messageFromReceiver.data)
ChromecastCommunicationConstants.PLAYLIST_INDEX -> bridge.sendPlaylistIndex(messageFromReceiver.data)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,23 @@ interface YouTubePlayer {
*/
fun cueVideo(videoId: String, startSeconds: Float)

fun loadPlaylist(videos: List<String>, index: Int, startSeconds: Float)
/**
* @param index Start index. if the index is out of range, youtube will only play the first video on the playlist, then stop.
*/
fun loadPlaylist(list: String, listType: String, index: Int, startSeconds: Float)

fun cuePlaylist(videos: List<String>, index: Int, startSeconds: Float)
/**
* @param index Start index. if the index is out of range, youtube will only play the first video on the playlist, then stop.
*/
fun cuePlaylist(list: String, listType: String, index: Int, startSeconds: Float)
fun setLoop(loop: Boolean)
fun setShuffle(shuffle: Boolean)
fun nextVideo()
fun previousVideo()
fun playVideoAt(index: Int)

fun play()
fun pause()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,62 @@ class YouTubePlayerBridge(private val youTubePlayerOwner: YouTubePlayerBridgeCal
youTubePlayerOwner.listeners.forEach { it.onVideoId(youTubePlayerOwner.getInstance(), videoId) }
}

@JavascriptInterface
fun sendPlaylistId(playlistId: String) = mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onPlaylistId(youTubePlayerOwner.getInstance(), playlistId) }
}
@JavascriptInterface
fun sendPlaylistType(playlistType: String) = mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onPlaylistType(youTubePlayerOwner.getInstance(), playlistType) }
}

@JavascriptInterface
fun sendPlaylistLength(data: String) {
val length = try {
data.toInt()
} catch (e: NumberFormatException) {
e.printStackTrace()
return
}
mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onPlaylistLength(youTubePlayerOwner.getInstance(), length) }
}
}

@JavascriptInterface
fun sendPlaylistIndex(playlistIndex: String) {
val index = try {
playlistIndex.toInt()
} catch (e: NumberFormatException) {
e.printStackTrace()
return
}
mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onPlaylistIndex(youTubePlayerOwner.getInstance(), index) }
}
}
@JavascriptInterface
fun sendVideoList(data: String) {
val list = data.split(",")
mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onVideoList(youTubePlayerOwner.getInstance(), list) }
}
}
@JavascriptInterface
fun sendLoopStatus(data: String) {
val loop = (data == "true")
mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onLoopStatus(youTubePlayerOwner.getInstance(), loop) }
}
}
@JavascriptInterface
fun sendShuffleStatus(data: String) {
val shuffle = (data == "true")
mainThreadHandler.post {
youTubePlayerOwner.listeners.forEach { it.onShuffleStatus(youTubePlayerOwner.getInstance(), shuffle) }
}
}

private fun parsePlayerState(state: String): PlayerConstants.PlayerState {
return when {
state.equals(STATE_UNSTARTED, ignoreCase = true) -> PlayerConstants.PlayerState.UNSTARTED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ abstract class AbstractYouTubePlayerListener : YouTubePlayerListener {
override fun onVideoDuration(youTubePlayer: YouTubePlayer, duration: Float) {}
override fun onVideoLoadedFraction(youTubePlayer: YouTubePlayer, loadedFraction: Float) {}
override fun onVideoId(youTubePlayer: YouTubePlayer, videoId: String) {}
override fun onPlaylistId(youTubePlayer: YouTubePlayer, playlistId: String) {}
override fun onPlaylistType(instance: YouTubePlayer, playlistType: String) {}
override fun onPlaylistIndex(youTubePlayer: YouTubePlayer, index: Int) {}
override fun onPlaylistLength(instance: YouTubePlayer, length: Int) {}
override fun onVideoList(instance: YouTubePlayer, list: List<String>) {}
override fun onLoopStatus(instance: YouTubePlayer, loop: Boolean) {}
override fun onShuffleStatus(instance: YouTubePlayer, loop: Boolean) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,11 @@ interface YouTubePlayerListener {
fun onVideoId(youTubePlayer: YouTubePlayer, videoId: String)

fun onApiChange(youTubePlayer: YouTubePlayer)
fun onPlaylistIndex(youTubePlayer: YouTubePlayer, index: Int)
fun onPlaylistId(youTubePlayer: YouTubePlayer, playlistId: String)
fun onVideoList(instance: YouTubePlayer, list: List<String>)
fun onPlaylistType(instance: YouTubePlayer, playlistType: String)
fun onPlaylistLength(instance: YouTubePlayer, length: Int)
fun onLoopStatus(instance: YouTubePlayer, loop: Boolean)
fun onShuffleStatus(instance: YouTubePlayer, loop: Boolean)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@ internal class NetworkObserver(private val context: Context) {

/** Stop observing network changes and cleanup */
fun destroy() {
// Min API for `unregisterNetworkCallback` is L, but we use `registerDefaultNetworkCallback` only for N and above.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val callback = networkCallback ?: return
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.unregisterNetworkCallback(callback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,42 @@ internal class PlaybackResumer : AbstractYouTubePlayerListener() {
private var currentVideoId: String? = null
private var currentSecond: Float = 0f

private var currentPlaylistId: String? = null
private var currentPlaylistType: String? = null
private var currentPlaylistIndex: Int? = null
private var currentVideoList: List<String>? = null

fun resume(youTubePlayer: YouTubePlayer) {
val videoId = currentVideoId ?: return
if (isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.loadOrCueVideo(canLoad, videoId, currentSecond)
}
else if (!isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.cueVideo(videoId, currentSecond)
}
val videoId = currentVideoId
val playlistId = currentPlaylistId
val playlistType = currentPlaylistType
val playlistIndex = currentPlaylistIndex
val videoList = currentVideoList
if (videoId != null) {
if (isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.loadOrCueVideo(canLoad, videoId, currentSecond)
} else if (!isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.cueVideo(videoId, currentSecond)
}

error = null
} else if (playlistId != null && playlistType != null && playlistIndex != null) {
if (isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.loadPlaylist(playlistId, playlistType, playlistIndex, currentSecond)
} else if (!isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.cuePlaylist(playlistId, playlistType, playlistIndex, currentSecond)
}

error = null
error = null
} else if (videoList != null && playlistIndex != null) {
if (isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.loadPlaylist(videoList, playlistIndex, currentSecond)
} else if (!isPlaying && error == PlayerConstants.PlayerError.HTML_5_PLAYER) {
youTubePlayer.cuePlaylist(videoList, playlistIndex, currentSecond)
}

error = null
}
}

override fun onStateChange(youTubePlayer: YouTubePlayer, state: PlayerConstants.PlayerState) {
Expand All @@ -51,6 +77,22 @@ internal class PlaybackResumer : AbstractYouTubePlayerListener() {
currentVideoId = videoId
}

override fun onPlaylistId(youTubePlayer: YouTubePlayer, playlistId: String) {
currentPlaylistId = playlistId
}

override fun onPlaylistIndex(youTubePlayer: YouTubePlayer, index: Int) {
currentPlaylistIndex = index
}

override fun onVideoList(instance: YouTubePlayer, list: List<String>) {
currentVideoList = list
}

override fun onPlaylistType(instance: YouTubePlayer, playlistType: String) {
currentPlaylistType = playlistType
}

fun onLifecycleResume() {
canLoad = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ class YouTubePlayerTracker : AbstractYouTubePlayerListener() {
private set
var videoId: String? = null
private set
var playlistId: String? = null
private set
var playlistType: String? = null
private set
var playlistIndex: Int? = null
private set
var videoList: List<String>? = null
private set

override fun onStateChange(youTubePlayer: YouTubePlayer, state: PlayerConstants.PlayerState) {
this.state = state
Expand All @@ -36,4 +44,20 @@ class YouTubePlayerTracker : AbstractYouTubePlayerListener() {
override fun onVideoId(youTubePlayer: YouTubePlayer, videoId: String) {
this.videoId = videoId
}

override fun onPlaylistId(youTubePlayer: YouTubePlayer, playlistId: String) {
this.playlistId = playlistId
}

override fun onPlaylistIndex(youTubePlayer: YouTubePlayer, index: Int) {
playlistIndex = index
}

override fun onPlaylistType(instance: YouTubePlayer, playlistType: String) {
this.playlistType = playlistType
}

override fun onVideoList(instance: YouTubePlayer, list: List<String>) {
videoList = list
}
}
Loading

0 comments on commit d88497e

Please sign in to comment.