diff --git a/assets/css/default.css b/assets/css/default.css index 2cedcf0c..0a04ea67 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -158,6 +158,8 @@ body a.pure-button { } button.pure-button-primary, +summary.pure-button-primary, +#share-widget > summary, body a.pure-button-primary, .channel-owner:hover, .channel-owner:focus { @@ -534,13 +536,15 @@ span > select { background-color: #fff2; } -.light-theme a { +.light-theme a, +.light-theme summary { color: #335d7a; text-decoration: none; } /* All links that do not fit with the default color goes here */ .light-theme a:not([data-id]) > .icon, +.light-theme summary > .icon, .light-theme .pure-u-lg-1-5 > .h-box > a[href^="/watch?"], .light-theme .playlist-restricted > ol > li > a { color: #303030; @@ -573,13 +577,15 @@ span > select { background-color: #fff2; } - .no-theme a { + .no-theme a, + .no-theme summary { color: #335d7a; text-decoration: none; } /* All links that do not fit with the default color goes here */ .no-theme a:not([data-id]) > .icon, + .no-theme summary > .icon, .no-theme .pure-u-lg-1-5 > .h-box > a[href^="/watch?"], .no-theme .playlist-restricted > ol > li > a { color: #303030; @@ -625,7 +631,8 @@ span > select { color: #ddd; } -.dark-theme a { +.dark-theme a, +.dark-theme summary { color: #adadad; text-decoration: none; } @@ -661,8 +668,10 @@ body.dark-theme { @media (prefers-color-scheme: dark) { .no-theme a:hover, .no-theme a:active, - .no-theme a:focus { - color: rgb(0, 182, 240); + .no-theme summary:hover, + .no-theme a:focus, + .no-theme summary:focus { + color: rgb(0, 182, 240); } .no-theme .pure-button-primary:hover, @@ -679,7 +688,7 @@ body.dark-theme { color: #ddd; } - .no-theme a { + .no-theme a, .no-theme summary { color: #adadad; text-decoration: none; } @@ -816,3 +825,77 @@ h1, h2, h3, h4, h5, p, #download_widget { width: 100%; } + +#share-widget > summary { + display: block; + font-weight: bold; + text-align: center; + border-radius: 5px; + border:0; + padding: 10px; +} + +#share-widget > div { + display: flex; + flex-direction: column; + padding: 20px; + gap: 20px; +} + +#share-to-sites { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 0; + justify-content: space-between; +} + +#add-time-to-share-link-container { + display: flex; + gap: 10px; + align-items: center; + padding-left: 5px; + font-size: 16px; +} + +#add-time-to-share-link-container input { + width: 20px; + height: 20px; +} + +#add-time-to-share-link-container input:checked + label { + color: unset; +} + +#add-time-to-share-link-container input + label { + color: #adadad; +} + +#share-to-sites > li { + padding: 5px; +} + +#share-to-sites > li:has(>details[open]) { + flex-basis: 100%; + width: 100%; +} + +.share-site > a, .share-site > details > summary { + display: flex; + flex-direction: column; + align-items: center; +} + +#share-to-sites i { + font-size: 32px; +} + +#share-widget-embed-section > div { + font-family: monospace; + padding: 5px; + border: 1px solid #adadad; + border-radius: 10px; + margin: 20px 0px; + width: 100%; +} \ No newline at end of file diff --git a/assets/css/player.css b/assets/css/player.css index 9cb400ad..6659db73 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -115,12 +115,8 @@ ul.vjs-menu-content::-webkit-scrollbar { order: 5; } -.vjs-share-control { - order: 6; -} - .vjs-fullscreen-control { - order: 7; + order: 6; } .vjs-playback-rate > .vjs-menu { @@ -252,12 +248,6 @@ video.video-js { top: 1.5em; } -@media screen and (max-width: 700px) { - .video-js .vjs-share { - justify-content: unset; - } -} - @media screen and (max-width: 650px) { .vjs-modal-dialog-content { overflow-x: hidden; diff --git a/assets/js/player.js b/assets/js/player.js index 353a5296..2fcd970e 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -95,6 +95,109 @@ if (video_data.params.quality === 'dash') { }); } + +// Populated by DOMContentLoaded event +let shareWidgetDetailsBox +let shareToSiteContainer +let shareWidgetAddDurationButton + +/** + * Modifies share widget links to remove or add the current time to the invidious video URL + * + * @param {bool} setTime + */ +function modifySocialWidgetURLs(setTime = false) { + for (let shareToSite of shareToSiteContainer ) { + let socialLink = shareToSite.getElementsByTagName("a")[0]; + + if (!socialLink) { + continue; + } + + let shareUrl = URL.parse(socialLink.href); + let shareUrlParams = shareUrl.searchParams + let replaceAttr = socialLink.dataset.replaceAttr; + + let videoUrl = shareUrlParams.get(replaceAttr) + if (videoUrl.startsWith("/")) { + videoUrl = window.location.origin + videoUrl; + } + + let urlToReplace + + if (setTime) { + urlToReplace = addCurrentTimeToURL(videoUrl) + } else { + urlToReplace = URL.parse(videoUrl) + urlToReplace.searchParams.delete('t'); + urlToReplace = urlToReplace.toString() + } + + shareUrlParams.set(replaceAttr, urlToReplace); + socialLink.href = shareUrl.toString(); + } + + // Edit url for iframe + const iframeCodeElement = document.getElementById("share-widget-embed-code") + const iframeParsedDom = new DOMParser().parseFromString(iframeCodeElement.textContent, "text/html") + + const iframeElement = iframeParsedDom.getElementsByTagName("iframe")[0] + + let iframeSrc = iframeElement.src + + if (iframeSrc.startsWith("/")) { + iframeSrc = window.location.origin + iframeSrc; + } + + if (setTime) { + iframeSrc = addCurrentTimeToURL(iframeSrc) + } else { + iframeSrc = URL.parse(iframeSrc) + iframeSrc.searchParams.delete('t'); + iframeSrc = iframeSrc.toString() + } + + iframeElement.src = iframeSrc + iframeCodeElement.textContent = iframeElement.outerHTML +} + + +/** + * Updates the time of the Start at ${time} text in the share widget + * + */ +function updateStartAtDurationStr() { + const label = shareWidgetAddDurationButton.labels[0]; + const startAtDurationText = label.getElementsByTagName("span")[0]; + if (label) { + const duration = Math.floor(player.currentTime()); + if (duration == 0) { + startAtDurationText.innerHTML = "0:00" + return + } + + const durationStrVals = []; + const paddedDurationString = []; + + const days = Math.floor(duration / (60 * 60 * 24)); + const hours = Math.floor((duration / (60 * 60)) % 24); + const minutes = Math.floor((duration / 60) % 60); + const seconds = Math.floor(duration % 60); + + if (days !== 0) durationStrVals.push(days); + if (hours !== 0) durationStrVals.push(hours); + + durationStrVals.push(minutes); + durationStrVals.push(seconds); + + durationStrVals.forEach((val) => { + paddedDurationString.push(String(val).padStart(2, "0")) + }) + + startAtDurationText.innerHTML = paddedDurationString.join(":") + } +} + /** * Function for add time argument to url * @@ -154,25 +257,14 @@ player.on('timeupdate', function () { elem_iv_embed.href = addCurrentTimeToURL(base_url_iv_embed, domain); elem_iv_other.href = addCurrentTimeToURL(base_url_iv_other, domain); + + // Only modify share widget data if start at is selected + if (document.getElementById('share-add-duration').checked) { + modifySocialWidgetURLs(true); + } }); -var shareOptions = { - socials: ['fbFeed', 'tw', 'reddit', 'email'], - - get url() { - return addCurrentTimeToURL(short_url); - }, - title: player_data.title, - description: player_data.description, - image: player_data.thumbnail, - get embedCode() { - // Single quotes inside here required. HTML inserted as is into value attribute of input - return ""; - } -}; - if (location.pathname.startsWith('/embed/')) { var overlay_content = '

' + player_data.title + '

'; player.overlay({ @@ -219,11 +311,8 @@ if (isMobile()) { var playback_element = document.getElementsByClassName('vjs-playback-rate')[0]; operations_bar_element.append(playback_element); - // The share and http source selector element can't be fetched till the players ready. + // The http source selector element can't be fetched till the players ready. player.one('playing', function () { - var share_element = document.getElementsByClassName('vjs-share-control')[0]; - operations_bar_element.append(share_element); - if (!video_data.params.listen && video_data.params.quality === 'dash') { var http_source_selector = document.getElementsByClassName('vjs-http-source-selector vjs-menu-button')[0]; operations_bar_element.append(http_source_selector); @@ -725,9 +814,6 @@ addEventListener('keydown', function (e) { player.on('DOMMouseScroll', mouseScroll); }()); -// Since videojs-share can sometimes be blocked, we defer it until last -if (player.share) player.share(shareOptions); - // show the preferred caption by default if (player_data.preferred_caption_found) { player.ready(function () { @@ -785,4 +871,31 @@ addEventListener('DOMContentLoaded', function () { if (changeInstanceLink) changeInstanceLink.addEventListener('click', function () { changeInstanceLink.href = addCurrentTimeToURL(changeInstanceLink.href); }); + + shareWidgetDetailsBox = document.getElementById("share-widget"); + shareToSiteContainer = document.getElementsByClassName('share-site'); + shareWidgetAddDurationButton = document.getElementById("share-add-duration"); + + shareWidgetAddDurationButton.addEventListener("change", () => { + if (shareWidgetAddDurationButton.checked) { + modifySocialWidgetURLs(true) + updateStartAtDurationStr() + } else { + modifySocialWidgetURLs(false) + updateStartAtDurationStr() + } + }) + + shareWidgetDetailsBox.addEventListener("toggle", () => { + // If share widget is opened and the share widget start at checkbox is checked + // then we will update the time to the current video time + if (shareWidgetDetailsBox.open && shareWidgetAddDurationButton.checked) { + modifySocialWidgetURLs(true) + updateStartAtDurationStr() + } else { + // Uncheck when closed + shareWidgetAddDurationButton.checked = false + modifySocialWidgetURLs(false) + } + }) }); diff --git a/locales/en-US.json b/locales/en-US.json index c23f6bc3..9861b9a5 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -498,5 +498,11 @@ "toggle_theme": "Toggle Theme", "carousel_slide": "Slide {{current}} of {{total}}", "carousel_skip": "Skip the Carousel", - "carousel_go_to": "Go to slide `x`" + "carousel_go_to": "Go to slide `x`", + "video_share_widget_label": "Share", + "video_share_widget_embed": "Embed", + "video_share_widget_twitter": "Twitter", + "video_share_widget_reddit": "Reddit", + "video_share_widget_email": "Email", + "video_share_add_duration_checkbox_label": "Start at `x`" } diff --git a/src/invidious/views/components/player_sources.ecr b/src/invidious/views/components/player_sources.ecr index 9af3899c..9b38eaac 100644 --- a/src/invidious/views/components/player_sources.ecr +++ b/src/invidious/views/components/player_sources.ecr @@ -1,7 +1,6 @@ - @@ -11,7 +10,6 @@ - diff --git a/src/invidious/views/licenses.ecr b/src/invidious/views/licenses.ecr index 667cfa37..b7352dfa 100644 --- a/src/invidious/views/licenses.ecr +++ b/src/invidious/views/licenses.ecr @@ -219,20 +219,6 @@ - - - videojs-share.js - - - - Expat - - - - <%= translate(locale, "source") %> - - - videojs-vtt-thumbnails.js diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 6f9ced6f..47d6780c 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -155,6 +155,35 @@ we're going to need to do it here in order to allow for translations. <% end %>

+
+ <%=translate(locale, "video_share_widget_label")%> +
+
    + <% escaped_iv_watch_link = URI.encode_www_form(HOST_URL) + env.get?("current_page").as(String) %> + <% escaped_iv_embed_link = URI.encode_www_form(HOST_URL) + URI.encode_www_form(link_iv_embed.to_s.rstrip("?")) %> + + + + +
+ +
+
+ <% if user %> <% playlists = Invidious::Database::Playlists.select_user_created_playlists(user.email) %> <% if !playlists.empty? %> diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml index e9ccc9dd..8bd3404c 100644 --- a/videojs-dependencies.yml +++ b/videojs-dependencies.yml @@ -23,10 +23,6 @@ videojs-overlay: version: 2.1.4 shasum: 5a103b25374dbb753eb87960d8360c2e8f39cc05 -videojs-share: - version: 3.2.1 - shasum: 0a3024b981387b9d21c058c829760a72c14b8ceb - videojs-vr: version: 1.8.0 shasum: 7f2f07f760d8a329c615acd316e49da6ee8edd34