From b7922d171d42960f2420cf6f77361d30bc6648d0 Mon Sep 17 00:00:00 2001 From: Nathan DeTar Date: Fri, 14 Jan 2022 22:33:16 -0800 Subject: [PATCH] Improved Cast Support (#143) * Added cast integration requirements. HTTPS and a supported browser are required for this integration. * Improved cast handeling. * Theme cast button. * Improved cast support. * Added proper cast button. * Moved cast button location * Moved button location * Reorder cast button * Fix typo * Revert cast setting description. * Match master branch * Added comments * Added id `cast-script` to cast-videos.js * Reworked event listener * Add cast button to home player * Check if active media on cast, pause browser video * Commented out console logging * Uncommented cast failed console log * Cast video at current playback position * use theme vars for cast color buttons * add cast variable to base ArchivistViewConfig class Co-authored-by: simon --- tubearchivist/home/templates/home/base.html | 11 +- .../home/templates/home/settings.html | 6 +- tubearchivist/home/templates/home/video.html | 5 +- tubearchivist/home/views.py | 1 + tubearchivist/static/cast-videos.js | 141 ++++++++---------- tubearchivist/static/css/style.css | 8 + tubearchivist/static/script.js | 8 + 7 files changed, 90 insertions(+), 90 deletions(-) diff --git a/tubearchivist/home/templates/home/base.html b/tubearchivist/home/templates/home/base.html index ab10b1e..4525dd2 100644 --- a/tubearchivist/home/templates/home/base.html +++ b/tubearchivist/home/templates/home/base.html @@ -29,15 +29,8 @@ {% endif %} {% if cast %} - - - + + {% endif %} diff --git a/tubearchivist/home/templates/home/settings.html b/tubearchivist/home/templates/home/settings.html index 291e862..0716d3e 100644 --- a/tubearchivist/home/templates/home/settings.html +++ b/tubearchivist/home/templates/home/settings.html @@ -107,8 +107,8 @@ {{ app_form.downloads_integrate_ryd }}
-

Untested! Current Cast integration: {{ config.application.enable_cast }}

- Enabling Cast will load an additional JS library from google.
+

Current Cast integration: {{ config.application.enable_cast }}

+ Enabling Cast will load an additional JS library from Google. HTTPS and a supported browser are required for this integration.
{{ app_form.application_enable_cast }}
@@ -293,4 +293,4 @@ {% endif %} -{% endblock content %} \ No newline at end of file +{% endblock content %} diff --git a/tubearchivist/home/templates/home/video.html b/tubearchivist/home/templates/home/video.html index 9ed944e..a220fa8 100644 --- a/tubearchivist/home/templates/home/video.html +++ b/tubearchivist/home/templates/home/video.html @@ -11,11 +11,10 @@
-

{{ video.title }}

{% if cast %} - - + {% endif %} +

{{ video.title }}

diff --git a/tubearchivist/home/views.py b/tubearchivist/home/views.py index 62b22e7..ca83db9 100644 --- a/tubearchivist/home/views.py +++ b/tubearchivist/home/views.py @@ -111,6 +111,7 @@ class ArchivistViewConfig(View): self.context = { "colors": self.default_conf["application"]["colors"], + "cast": self.default_conf["application"]["enable_cast"], "sort_by": self._get_sort_by(), "sort_order": self._get_sort_order(), "view_style": self._get_view_style(), diff --git a/tubearchivist/static/cast-videos.js b/tubearchivist/static/cast-videos.js index 1d6c9fd..c2b05ba 100644 --- a/tubearchivist/static/cast-videos.js +++ b/tubearchivist/static/cast-videos.js @@ -1,91 +1,82 @@ -var session = null; - function initializeCastApi() { - var applicationID = chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID; - var sessionRequest = new chrome.cast.SessionRequest(applicationID); - var apiConfig = new chrome.cast.ApiConfig(sessionRequest, - sessionListener, - receiverListener); - chrome.cast.initialize(apiConfig, onInitSuccess, onInitError); -}; + cast.framework.CastContext.getInstance().setOptions({ + receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID, // Use built in reciver app on cast device, see https://developers.google.com/cast/docs/styled_receiver if you want to be able to add a theme, splash screen or watermark. Has a $5 one time fee. + autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED + }); -function sessionListener(e) { - session = e; - console.log('New session'); - if (session.media.length != 0) { - console.log('Found ' + session.media.length + ' sessions.'); - } -} - -function receiverListener(e) { - if( e === 'available' ) { - console.log("Chromecast was found on the network."); - } - else { - console.log("There are no Chromecasts available."); + var player = new cast.framework.RemotePlayer(); + var playerController = new cast.framework.RemotePlayerController(player); + + // Add event listerner to check if a connection to a cast device is initiated + playerController.addEventListener( + cast.framework.RemotePlayerEventType.IS_CONNECTED_CHANGED, function() { + castConnectionChange(player) } + ); } -function onInitSuccess() { - console.log("Initialization succeeded"); + +function castConnectionChange(player) { + // If cast connection is initialized start cast + if (player.isConnected) { + // console.log("Cast Connected."); + castStart(); + } else if (!player.isConnected) { + // console.log("Cast Disconnected."); + } } -function onInitError() { - console.log("Initialization failed"); +function castStart() { + var castSession = cast.framework.CastContext.getInstance().getCurrentSession(); + + // Check if there is already media playing on the cast target to prevent recasting on page reload or switching to another video page + if (!castSession.getMediaSession()) { + contentId = document.getElementById("video-item").src; // Get video URL + contentTitle = document.getElementById('video-title').innerHTML; // Get video title + contentImage = document.getElementById("video-item").poster; // Get video thumbnail URL + contentType = 'video/mp4'; // Set content type, only videos right now so it is hard coded + contentCurrentTime = document.getElementById("video-item").currentTime; // Get video's current position + + mediaInfo = new chrome.cast.media.MediaInfo(contentId, contentType); // Create MediaInfo var that contains url and content type + // mediaInfo.streamType = chrome.cast.media.StreamType.BUFFERED; // Set type of stream, BUFFERED, LIVE, OTHER + mediaInfo.metadata = new chrome.cast.media.GenericMediaMetadata(); // Create metadata var and add it to MediaInfo + mediaInfo.metadata.title = contentTitle; // Set the video title + mediaInfo.metadata.images = [new chrome.cast.Image(contentImage)]; // Set the video thumbnail + + var request = new chrome.cast.media.LoadRequest(mediaInfo); // Create request with the previously set MediaInfo. + // request.queueData = new chrome.cast.media.QueueData(); // See https://developers.google.com/cast/docs/reference/web_sender/chrome.cast.media.QueueData for playlist support. + request.currentTime = shiftCurrentTime(contentCurrentTime); // Set video start position based on the browser video position + // request.autoplay = false; // Set content to auto play, true by default + castSession.loadMedia(request).then( + function() { + castSuccessful(); + }, + function() { + castFailed(errorCode); + } + ); // Send request to cast device + } } -function startCast() { - console.log("Starting cast..."); - chrome.cast.requestSession(onRequestSessionSuccess, onLaunchError); +function shiftCurrentTime(contentCurrentTime) { // Shift media back 3 seconds to prevent missing some of the content + if (contentCurrentTime > 5) { + return(contentCurrentTime - 3); + } else { + return(0); + } } -function onRequestSessionSuccess(e) { - console.log("Successfully created session: " + e.sessionId); - session = e; +function castSuccessful() { + // console.log('Cast Successful.'); + document.getElementById("video-item").pause(); // Pause browser video on successful cast } -function onLaunchError() { - console.log("Error connecting to the Chromecast."); +function castFailed(errorCode) { + console.log('Error code: ' + errorCode); } -function onRequestSessionSuccess(e) { - console.log("Successfully created session: " + e.sessionId); - session = e; - loadMedia(); -} - -function loadMedia() { - if (!session) { - console.log("No session."); - return; - } - - var videoSrc = document.getElementById("video-item").src; - var mediaInfo = new chrome.cast.media.MediaInfo(videoSrc); - mediaInfo.contentType = 'video/mp4'; - - var request = new chrome.cast.media.LoadRequest(mediaInfo); - request.autoplay = true; - - session.loadMedia(request, onLoadSuccess, onLoadError); -} - -function onLoadSuccess() { - console.log('Successfully loaded video.'); -} - -function onLoadError() { - console.log('Failed to load video.'); -} - -function stopCast() { - session.stop(onStopCastSuccess, onStopCastError); -} - -function onStopCastSuccess() { - console.log('Successfully stopped casting.'); -} - -function onStopCastError() { - console.log('Error stopping cast.'); +window['__onGCastApiAvailable'] = function(isAvailable) { + if (isAvailable) { + initializeCastApi(); + } } diff --git a/tubearchivist/static/css/style.css b/tubearchivist/static/css/style.css index d745cc3..8a779d2 100644 --- a/tubearchivist/static/css/style.css +++ b/tubearchivist/static/css/style.css @@ -267,6 +267,14 @@ button:hover { filter: var(--img-filter); } +#castbutton { + float: right; + width: 40px; + padding: 0 5px; + --disconnected-color: var(--accent-font-dark); + --connected-color: var(--accent-font-light); +} + /* top of page */ .title-bar { padding-top: 30px; diff --git a/tubearchivist/static/script.js b/tubearchivist/static/script.js index 2d5b465..a7c6b17 100644 --- a/tubearchivist/static/script.js +++ b/tubearchivist/static/script.js @@ -309,6 +309,7 @@ function createPlayer(button) { videoPlayer.setAttribute('width', '100%'); videoPlayer.setAttribute('playsinline', true); videoPlayer.setAttribute('poster', mediaThumb); + videoPlayer.setAttribute('id', 'video-item'); // Set ID to get URL for casting playerElement.appendChild(videoPlayer); // title bar var titleBar = document.createElement('div'); @@ -335,7 +336,14 @@ function createPlayer(button) { var videoTitleLink = document.createElement('a'); videoTitleLink.setAttribute('href', '/video/' + dataId + '/'); var videoTitle = document.createElement('h2'); + videoTitle.setAttribute('id', "video-title"); // Set ID to get title for casting videoTitle.innerText = mediaTitle; + var castScript = document.getElementById('cast-script'); // Get cast-script + if(typeof(castScript) != 'undefined' && castScript != null) { // Check if cast integration is enabled + var castButton = document.createElement("google-cast-launcher"); // Create cast button + castButton.setAttribute('id', "castbutton"); // Set ID to apply theme + titleBar.appendChild(castButton); // Add cast button to title + } videoTitleLink.appendChild(videoTitle); titleBar.appendChild(videoTitleLink); // add titlebar