From 608313c1d1e0f5ec9b4ce9b1d6303f03530656d8 Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 29 Mar 2021 17:37:12 -0700 Subject: [PATCH 01/19] Update regex expressions to handle unexpected '};' --- src/invidious/helpers/helpers.cr | 2 +- src/invidious/videos.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 5d127e1a..5e49afb7 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -678,7 +678,7 @@ def create_notification_stream(env, topics, connection_channel) end def extract_initial_data(body) : Hash(String, JSON::Any) - return JSON.parse(body.match(/(window\["ytInitialData"\]|var\s*ytInitialData)\s*=\s*(?\{.*?\});/mx).try &.["info"] || "{}").as_h + return JSON.parse(body.match(/(window\["ytInitialData"\]|var\s*ytInitialData)\s*=\s*(?{.*?});<\/script>/mx).try &.["info"] || "{}").as_h end def proxy_file(response, env) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index e6d4c764..2b793a1b 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -818,7 +818,7 @@ end def extract_polymer_config(body) params = {} of String => JSON::Any - player_response = body.match(/(window\["ytInitialPlayerResponse"\]|var\sytInitialPlayerResponse)\s*=\s*(?{.*?});/m) + player_response = body.match(/(window\["ytInitialPlayerResponse"\]|var\sytInitialPlayerResponse)\s*=\s*(?{.*?});\s*var\s*meta/m) .try { |r| JSON.parse(r["info"]).as_h } if body.includes?("To continue with your YouTube experience, please fill out the form below.") || From bc5c0f65b8bf69da4d75596ac11a5d75b64c4b7d Mon Sep 17 00:00:00 2001 From: syeopite Date: Thu, 8 Apr 2021 04:15:15 -0700 Subject: [PATCH 02/19] Add watch on Invidious button to embeds --- assets/css/embed.css | 16 ++++++++++++++++ assets/js/player.js | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/assets/css/embed.css b/assets/css/embed.css index 12fefe58..466a284a 100644 --- a/assets/css/embed.css +++ b/assets/css/embed.css @@ -8,3 +8,19 @@ height: auto; z-index: -100; } + +.watch-on-invidious { + font-size: 1.3em !important; + font-weight: bold; + white-space: nowrap; + margin: 0 1em 0 1em !important; + order: 3; +} + +.watch-on-invidious > a { + color: white; +} + +.watch-on-invidious > a:hover { + color: rgba(0, 182, 240, 1);; +} diff --git a/assets/js/player.js b/assets/js/player.js index 1c6e336c..f1429233 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -566,3 +566,20 @@ if (navigator.vendor == "Apple Computer, Inc." && video_data.params.listen) { }); }); } + +// Watch on Invidious link +if (window.location.pathname.startsWith("/embed/")) { + const Button = videojs.getComponent('Button'); + let watch_on_invidious_button = new Button(player); + + // Create hyperlink for current instance + redirect_element = document.createElement("a"); + redirect_element.setAttribute("href", `http://${window.location.host}/watch?v=${window.location.pathname.replace("/embed/","")}`) + redirect_element.appendChild(document.createTextNode("Invidious")) + + watch_on_invidious_button.el().appendChild(redirect_element) + watch_on_invidious_button.addClass("watch-on-invidious") + + cb = player.getChild('ControlBar') + cb.addChild(watch_on_invidious_button) +}; From c106de02e957650bf6e29c0c47b39eeeff7c2fa8 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 30 Apr 2021 15:12:36 +0200 Subject: [PATCH 03/19] Added autofocus attribute to search input Since the main action of this form is to search something, the input should automatically be focused. --- src/invidious/views/search_homepage.ecr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/views/search_homepage.ecr b/src/invidious/views/search_homepage.ecr index b36500e9..8927c3f1 100644 --- a/src/invidious/views/search_homepage.ecr +++ b/src/invidious/views/search_homepage.ecr @@ -16,7 +16,7 @@ From 53cdb04be75d8ba6859a05393f202454072ed8a9 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sat, 10 Apr 2021 20:22:30 -0700 Subject: [PATCH 04/19] Improve mobile ux with videojs-mobile-ui --- assets/css/videojs-mobile-ui.css | 7 +++++++ assets/js/player.js | 6 ++++++ assets/js/videojs-mobile-ui.min.js | 7 +++++++ src/invidious/views/components/player_sources.ecr | 2 ++ 4 files changed, 22 insertions(+) create mode 100644 assets/css/videojs-mobile-ui.css create mode 100644 assets/js/videojs-mobile-ui.min.js diff --git a/assets/css/videojs-mobile-ui.css b/assets/css/videojs-mobile-ui.css new file mode 100644 index 00000000..c307274a --- /dev/null +++ b/assets/css/videojs-mobile-ui.css @@ -0,0 +1,7 @@ +/** + * videojs-mobile-ui + * @version 0.5.2 + * @copyright 2021 mister-ben + * @license MIT + */ +@keyframes fadeAndScale{0%{opacity:0}25%{opacity:1}100%{opacity:0}}.video-js.vjs-has-started .vjs-touch-overlay{position:absolute;pointer-events:auto;top:0}.video-js .vjs-touch-overlay{display:block;width:100%;height:100%;pointer-events:none}.video-js .vjs-touch-overlay.skip{opacity:0;animation:fadeAndScale 0.6s linear;background-repeat:no-repeat;background-position:80% center;background-size:10%;background-image:url('data:image/svg+xml;utf8,')}.video-js .vjs-touch-overlay.skip.reverse{background-position:20% center;background-image:url('data:image/svg+xml;utf8,')}.video-js .vjs-touch-overlay .vjs-play-control{top:50%;left:50%;transform:translate(-50%, -50%);position:absolute;width:30%;height:80%;pointer-events:none;opacity:0;transition:opacity 0.3s ease}.video-js .vjs-touch-overlay .vjs-play-control .vjs-icon-placeholder::before{content:'';background-size:60%;background-position:center center;background-repeat:no-repeat;background-image:url('data:image/svg+xml;utf8,')}.video-js .vjs-touch-overlay .vjs-play-control.vjs-paused .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8,')}.video-js .vjs-touch-overlay .vjs-play-control.vjs-ended .vjs-icon-placeholder::before{content:'';background-image:url('data:image/svg+xml;utf8,')}.video-js .vjs-touch-overlay.show-play-toggle .vjs-play-control{opacity:1;pointer-events:auto}.video-js.vjs-mobile-ui-disable-end.vjs-ended .vjs-touch-overlay{display:none} diff --git a/assets/js/player.js b/assets/js/player.js index 1c6e336c..16fd767b 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -73,6 +73,12 @@ if (location.pathname.startsWith('/embed/')) { }); } +// Detect mobile users and initalize mobileUi for better UX +// Detection code taken from https://stackoverflow.com/a/24600597 +if (/Mobi|Android/i.test(navigator.userAgent)) { + player.mobileUi(); +} + player.on('error', function (event) { if (player.error().code === 2 || player.error().code === 4) { setTimeout(function (event) { diff --git a/assets/js/videojs-mobile-ui.min.js b/assets/js/videojs-mobile-ui.min.js new file mode 100644 index 00000000..e624fbe2 --- /dev/null +++ b/assets/js/videojs-mobile-ui.min.js @@ -0,0 +1,7 @@ +/** + * videojs-mobile-ui + * @version 0.5.2 + * @copyright 2021 mister-ben + * @license MIT + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("video.js"),require("global/window")):"function"==typeof define&&define.amd?define(["video.js","global/window"],t):e.videojsMobileUi=t(e.videojs,e.window)}(this,function(e,t){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e,t=t&&t.hasOwnProperty("default")?t.default:t;var n=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},o=function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t},i=e.getComponent("Component"),r=e.dom||e,a=function(e){function i(t,r){n(this,i);var a=o(this,e.call(this,t,r));return a.seekSeconds=r.seekSeconds,a.tapTimeout=r.tapTimeout,a.addChild("playToggle",{}),t.on(["playing","userinactive"],function(e){a.removeClass("show-play-toggle")}),0===a.player_.options_.inactivityTimeout&&(a.player_.options_.inactivityTimeout=5e3),a.enable(),a}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}(i,e),i.prototype.createEl=function(){return r.createEl("div",{className:"vjs-touch-overlay",tabIndex:-1})},i.prototype.handleTap=function(e){var n=this;e.target===this.el_&&(e.preventDefault(),this.firstTapCaptured?(this.firstTapCaptured=!1,this.timeout&&t.clearTimeout(this.timeout),this.handleDoubleTap(e)):(this.firstTapCaptured=!0,this.timeout=t.setTimeout(function(){n.firstTapCaptured=!1,n.handleSingleTap(e)},this.tapTimeout)))},i.prototype.handleSingleTap=function(e){this.removeClass("skip"),this.toggleClass("show-play-toggle")},i.prototype.handleDoubleTap=function(e){var n=this,o=this.el_.getBoundingClientRect(),i=e.changedTouches[0].clientX-o.left;if(i<.4*o.width)this.player_.currentTime(Math.max(0,this.player_.currentTime()-this.seekSeconds)),this.addClass("reverse");else{if(!(i>o.width-.4*o.width))return;this.player_.currentTime(Math.min(this.player_.duration(),this.player_.currentTime()+this.seekSeconds)),this.removeClass("reverse")}this.removeClass("show-play-toggle"),this.removeClass("skip"),t.requestAnimationFrame(function(){n.addClass("skip")})},i.prototype.enable=function(){this.firstTapCaptured=!1,this.on("touchend",this.handleTap)},i.prototype.disable=function(){this.off("touchend",this.handleTap)},i}(i);i.registerComponent("TouchOverlay",a);var s={fullscreen:{enterOnRotate:!0,exitOnRotate:!0,lockOnRotate:!0,iOS:!1},touchControls:{seekSeconds:10,tapTimeout:300,disableOnEnd:!1}},l=t.screen,u=function(n,o){n.addClass("vjs-mobile-ui"),(o.touchControls.disableOnEnd||"function"==typeof n.endscreen)&&n.addClass("vjs-mobile-ui-disable-end"),o.fullscreen.iOS&&e.browser.IS_IOS&&e.browser.IOS_VERSION>9&&!n.el_.ownerDocument.querySelector(".bc-iframe")&&(n.tech_.el_.setAttribute("playsinline","playsinline"),n.tech_.supportsFullScreen=function(){return!1});var i=void 0,r=e.VERSION.split("."),a=parseInt(r[0],10),s=parseInt(r[1],10);i=a<7||7===a&&s<7?Array.prototype.indexOf.call(n.el_.children,n.getChild("ControlBar").el_):n.children_.indexOf(n.getChild("ControlBar")),n.addChild("TouchOverlay",o.touchControls,i);var u=!1,c=function(){var i="number"==typeof t.orientation?t.orientation:l&&l.orientation&&l.orientation.angle?t.orientation:(e.log("angle unknown"),0);90!==i&&270!==i&&-90!==i||!o.enterOnRotate||!1===n.paused()&&(n.requestFullscreen(),o.fullscreen.lockOnRotate&&l.orientation&&l.orientation.lock&&l.orientation.lock("landscape").then(function(){u=!0}).catch(function(){e.log("orientation lock not allowed")})),0!==i&&180!==i||!o.exitOnRotate||n.isFullscreen()&&n.exitFullscreen()};e.browser.IS_IOS?t.addEventListener("orientationchange",c):l.orientation&&(l.orientation.onchange=c),n.on("ended",function(e){!0===u&&(l.orientation.unlock(),u=!1)})},c=function(){var t=this,n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};(n.forceForTesting||e.browser.IS_ANDROID||e.browser.IS_IOS)&&this.ready(function(){u(t,e.mergeOptions(s,n))})};return(e.registerPlugin||e.plugin)("mobileUi",c),c.VERSION="0.5.2",c}); \ No newline at end of file diff --git a/src/invidious/views/components/player_sources.ecr b/src/invidious/views/components/player_sources.ecr index d950e0da..0374c983 100644 --- a/src/invidious/views/components/player_sources.ecr +++ b/src/invidious/views/components/player_sources.ecr @@ -3,7 +3,9 @@ + + From ac1ac2cfed89b959e21f2a6c7ca11730dacc20ae Mon Sep 17 00:00:00 2001 From: syeopite Date: Sat, 10 Apr 2021 21:04:11 -0700 Subject: [PATCH 05/19] Add mobile-ui js to licenses --- src/invidious/views/licenses.ecr | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/invidious/views/licenses.ecr b/src/invidious/views/licenses.ecr index aae8bb19..c2ada992 100644 --- a/src/invidious/views/licenses.ecr +++ b/src/invidious/views/licenses.ecr @@ -149,6 +149,20 @@ + + + videojs-mobile-ui.min.js + + + + MIT + + + + <%= translate(locale, "source") %> + + + videojs-markers.min.js From 06b27d932d2d441c8382b466fcd22711319e9c17 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sat, 10 Apr 2021 21:35:14 -0700 Subject: [PATCH 06/19] Move player styling to separate file --- assets/css/default.css | 179 ------------------ assets/css/player.css | 178 +++++++++++++++++ .../views/components/player_sources.ecr | 1 + 3 files changed, 179 insertions(+), 179 deletions(-) create mode 100644 assets/css/player.css diff --git a/assets/css/default.css b/assets/css/default.css index a725b456..17264532 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -310,194 +310,15 @@ footer a { } } -/* Control Bar */ -@media screen and (max-width: 640px) { - .video-js .vjs-control-bar, - .vjs-menu-button-popup .vjs-menu .vjs-menu-content { - overflow-x: scroll; - } -} - -ul.vjs-menu-content::-webkit-scrollbar { - display: none; -} - -.vjs-user-inactive { - cursor: none; -} - -.video-js .vjs-text-track-display > div > div > div { - background-color: rgba(0, 0, 0, 0.75) !important; - border-radius: 9px !important; - padding: 5px !important; -} - -.vjs-play-control, -.vjs-volume-panel, -.vjs-current-time, -.vjs-time-control, -.vjs-duration, -.vjs-progress-control, -.vjs-remaining-time { - order: 1; -} - -.vjs-captions-button { - order: 2; -} - -.vjs-quality-selector, -.video-js .vjs-http-source-selector { - order: 3; -} - -.vjs-playback-rate { - order: 4; -} - -.vjs-share-control { - order: 5; -} - -.vjs-fullscreen-control { - order: 6; -} - -.vjs-playback-rate > .vjs-menu { - width: 50px; -} - -.vjs-control-bar { - display: flex; - flex-direction: row; - scrollbar-width: none; -} - -.vjs-control-bar::-webkit-scrollbar { - display: none; -} - -.video-js .vjs-icon-cog { - font-size: 18px; -} - -.video-js .vjs-control-bar, -.vjs-menu-button-popup .vjs-menu .vjs-menu-content { - background-color: rgba(35, 35, 35, 0.75); -} - -.vjs-menu li.vjs-menu-item:focus, -.vjs-menu li.vjs-menu-item:hover { - background-color: rgba(255, 255, 255, 0.75); - color: rgba(49, 49, 51, 0.75); -} - -.vjs-menu li.vjs-selected, -.vjs-menu li.vjs-selected:focus, -.vjs-menu li.vjs-selected:hover { - background-color: rgba(0, 182, 240, 0.75); -} - -/* Progress Bar */ -.video-js .vjs-slider { - background-color: rgba(15, 15, 15, 0.5); -} - fieldset > select, span > select { color: rgba(49, 49, 51, 1); } -.video-js .vjs-load-progress, -.video-js .vjs-load-progress div { - background: rgba(87, 87, 88, 1); -} - -.video-js .vjs-slider:hover, -.video-js button:hover { - color: rgba(0, 182, 240, 1); -} - -.video-js .vjs-play-progress { - background-color: rgba(0, 182, 240, 1); -} - -/* Overlay */ -.video-js .vjs-overlay { - background-color: rgba(35, 35, 35, 0.75); - color: rgba(255, 255, 255, 1); -} - -/* ProgressBar marker */ -.vjs-marker { - background-color: rgba(255, 255, 255, 1); - z-index: 0; -} - -/* Big "Play" Button */ -.video-js .vjs-big-play-button { - background-color: rgba(35, 35, 35, 0.5); -} - -.video-js:hover .vjs-big-play-button { - background-color: rgba(35, 35, 35, 0.75); -} - -.video-js .vjs-current-time, -.video-js .vjs-time-divider, -.video-js .vjs-duration { - display: block; -} - -.video-js .vjs-time-divider { - min-width: 0px; - padding-left: 0px; - padding-right: 0px; -} - -.video-js .vjs-poster { - background-size: cover; - object-fit: cover; -} - -.player-dimensions.vjs-fluid { - padding-top: 82vh; -} - -video.video-js { - position: absolute; - height: 100%; -} - -#player-container { - position: relative; - padding-bottom: 82vh; - height: 0; -} - .pure-control-group label { word-wrap: normal; } -.video-js.player-style-invidious { - /* This is already the default */ -} - -.video-js.player-style-youtube .vjs-control-bar { - display: flex; - flex-direction: row; -} -.video-js.player-style-youtube .vjs-big-play-button { - /* - Styles copied from video-js.min.css, definition of - .vjs-big-play-centered .vjs-big-play-button - */ - top: 50%; - left: 50%; - margin-top: -0.81666em; - margin-left: -1.5em; -} - /* * Light theme */ diff --git a/assets/css/player.css b/assets/css/player.css new file mode 100644 index 00000000..9721c047 --- /dev/null +++ b/assets/css/player.css @@ -0,0 +1,178 @@ +/* Control Bar */ +@media screen and (max-width: 640px) { + .video-js .vjs-control-bar, + .vjs-menu-button-popup .vjs-menu .vjs-menu-content { + overflow-x: scroll; + } +} + +ul.vjs-menu-content::-webkit-scrollbar { + display: none; +} + +.vjs-user-inactive { + cursor: none; +} + +.video-js .vjs-text-track-display > div > div > div { + background-color: rgba(0, 0, 0, 0.75) !important; + border-radius: 9px !important; + padding: 5px !important; +} + +.vjs-play-control, +.vjs-volume-panel, +.vjs-current-time, +.vjs-time-control, +.vjs-duration, +.vjs-progress-control, +.vjs-remaining-time { + order: 1; +} + +.vjs-captions-button { + order: 2; +} + +.vjs-quality-selector, +.video-js .vjs-http-source-selector { + order: 3; +} + +.vjs-playback-rate { + order: 4; +} + +.vjs-share-control { + order: 5; +} + +.vjs-fullscreen-control { + order: 6; +} + +.vjs-playback-rate > .vjs-menu { + width: 50px; +} + +.vjs-control-bar { + display: flex; + flex-direction: row; + scrollbar-width: none; +} + +.vjs-control-bar::-webkit-scrollbar { + display: none; +} + +.video-js .vjs-icon-cog { + font-size: 18px; +} + +.video-js .vjs-control-bar, +.vjs-menu-button-popup .vjs-menu .vjs-menu-content { + background-color: rgba(35, 35, 35, 0.75); +} + +.vjs-menu li.vjs-menu-item:focus, +.vjs-menu li.vjs-menu-item:hover { + background-color: rgba(255, 255, 255, 0.75); + color: rgba(49, 49, 51, 0.75); +} + +.vjs-menu li.vjs-selected, +.vjs-menu li.vjs-selected:focus, +.vjs-menu li.vjs-selected:hover { + background-color: rgba(0, 182, 240, 0.75); +} + +/* Progress Bar */ +.video-js .vjs-slider { + background-color: rgba(15, 15, 15, 0.5); +} + +.video-js .vjs-load-progress, +.video-js .vjs-load-progress div { + background: rgba(87, 87, 88, 1); +} + +.video-js .vjs-slider:hover, +.video-js button:hover { + color: rgba(0, 182, 240, 1); +} + +.video-js .vjs-play-progress { + background-color: rgba(0, 182, 240, 1); +} + +/* Overlay */ +.video-js .vjs-overlay { + background-color: rgba(35, 35, 35, 0.75); + color: rgba(255, 255, 255, 1); +} + +/* ProgressBar marker */ +.vjs-marker { + background-color: rgba(255, 255, 255, 1); + z-index: 0; +} + +/* Big "Play" Button */ +.video-js .vjs-big-play-button { + background-color: rgba(35, 35, 35, 0.5); +} + +.video-js:hover .vjs-big-play-button { + background-color: rgba(35, 35, 35, 0.75); +} + +.video-js .vjs-current-time, +.video-js .vjs-time-divider, +.video-js .vjs-duration { + display: block; +} + +.video-js .vjs-time-divider { + min-width: 0px; + padding-left: 0px; + padding-right: 0px; +} + +.video-js .vjs-poster { + background-size: cover; + object-fit: cover; +} + +.player-dimensions.vjs-fluid { + padding-top: 82vh; +} + +video.video-js { + position: absolute; + height: 100%; +} + +#player-container { + position: relative; + padding-bottom: 82vh; + height: 0; +} + +.video-js.player-style-invidious { + /* This is already the default */ +} + +.video-js.player-style-youtube .vjs-control-bar { + display: flex; + flex-direction: row; +} +.video-js.player-style-youtube .vjs-big-play-button { + /* + Styles copied from video-js.min.css, definition of + .vjs-big-play-centered .vjs-big-play-button + */ + top: 50%; + left: 50%; + margin-top: -0.81666em; + margin-left: -1.5em; +} diff --git a/src/invidious/views/components/player_sources.ecr b/src/invidious/views/components/player_sources.ecr index 0374c983..a99fdbca 100644 --- a/src/invidious/views/components/player_sources.ecr +++ b/src/invidious/views/components/player_sources.ecr @@ -4,6 +4,7 @@ + From 1924d75c2b13a8962d612250eb9cbde0aa8542ab Mon Sep 17 00:00:00 2001 From: syeopite Date: Sat, 10 Apr 2021 23:12:35 -0700 Subject: [PATCH 07/19] Improve mobile user interface for video player --- assets/css/player.css | 37 +++++++++++++++++++++++++--------- assets/js/player.js | 47 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/assets/css/player.css b/assets/css/player.css index 9721c047..0583e327 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -1,11 +1,3 @@ -/* Control Bar */ -@media screen and (max-width: 640px) { - .video-js .vjs-control-bar, - .vjs-menu-button-popup .vjs-menu .vjs-menu-content { - overflow-x: scroll; - } -} - ul.vjs-menu-content::-webkit-scrollbar { display: none; } @@ -104,7 +96,7 @@ ul.vjs-menu-content::-webkit-scrollbar { .video-js .vjs-play-progress { background-color: rgba(0, 182, 240, 1); } - +vjs-menu-content /* Overlay */ .video-js .vjs-overlay { background-color: rgba(35, 35, 35, 0.75); @@ -176,3 +168,30 @@ video.video-js { margin-top: -0.81666em; margin-left: -1.5em; } + +.mobile-operations-bar { + display: flex; + position: absolute; + top: 0; + right: 1px !important; + left: initial !important; + width: initial !important; +} + +.mobile-operations-bar ul { + position: absolute !important; + bottom: unset !important; + 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 16fd767b..e6a6dbac 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -74,9 +74,52 @@ if (location.pathname.startsWith('/embed/')) { } // Detect mobile users and initalize mobileUi for better UX -// Detection code taken from https://stackoverflow.com/a/24600597 -if (/Mobi|Android/i.test(navigator.userAgent)) { +// Detection code taken from https://stackoverflow.com/a/20293441 + +function isMobile() { + try{ document.createEvent("TouchEvent"); return true; } + catch(e){ return false; } +} + +if (isMobile()) { player.mobileUi(); + + buttons = ["playToggle", "volumePanel", "captionsButton"]; + + if (video_data.params.quality !== 'dash') { + buttons.push("qualitySelector") + } + + // Create new control bar object for operation buttons + const ControlBar = videojs.getComponent("controlBar"); + let operations_bar = new ControlBar(player, { + children: [], + playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0] + }); + buttons.slice(1).forEach(child => operations_bar.addChild(child)) + + // Remove operation buttons from primary control bar + primary_control_bar = player.getChild("controlBar"); + buttons.forEach(child => primary_control_bar.removeChild(child)); + + operations_bar_element = operations_bar.el(); + operations_bar_element.className += " mobile-operations-bar" + player.addChild(operations_bar) + + // Playback menu doesn't work when its initalized outside of the primary control bar + 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. + player.one("playing", () => { + share_element = document.getElementsByClassName("vjs-share-control")[0] + operations_bar_element.append(share_element) + + if (video_data.params.quality === 'dash') { + http_source_selector = document.getElementsByClassName("vjs-http-source-selector vjs-menu-button")[0] + operations_bar_element.append(http_source_selector) + } + }) } player.on('error', function (event) { From d6585d7583018010467cead2956d375067b9acce Mon Sep 17 00:00:00 2001 From: syeopite Date: Mon, 3 May 2021 02:25:32 -0700 Subject: [PATCH 08/19] Overhaul Invidious's "Youtube" player style --- assets/css/player.css | 93 +++++++++++++++++++++++++++++++++---------- assets/js/player.js | 1 + 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/assets/css/player.css b/assets/css/player.css index 0583e327..656fb48c 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -1,3 +1,75 @@ +/* Youtube player style */ +.video-js.player-style-youtube .vjs-progress-control { + height: 0; +} + +.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control { + position: absolute; + right: 0; + left: 0; + width: 100%; + margin: 0; +} + +.video-js.player-style-youtube .vjs-control-bar { + background: linear-gradient(rgba(0,0,0,0.1), rgba(0, 0, 0,0.5)); +} + +.video-js.player-style-youtube .vjs-slider { + background-color: rgba(255,255,255,0.2); +} + +.video-js.player-style-youtube .vjs-load-progress > div { + background-color: rgba(255,255,255,0.5); +} + +.video-js.player-style-youtube .vjs-play-progress { + background-color: red; +} + +.video-js.player-style-youtube .vjs-progress-control:hover .vjs-progress-holder { + font-size: 15px; +} + +.video-js.player-style-youtube .vjs-control-bar > .vjs-spacer { + flex: 1; + order: 2; +} + +.video-js.player-style-youtube .vjs-play-progress .vjs-time-tooltip { + display: none; +} + +.video-js.player-style-youtube .vjs-play-progress::before { + color: red; + font-size: 0.85em; + display: none; +} + +.video-js.player-style-youtube .vjs-progress-holder:hover .vjs-play-progress::before { + display: unset; +} + +.video-js.player-style-youtube .vjs-control-bar { + display: flex; + flex-direction: row; +} + +.video-js.player-style-youtube .vjs-big-play-button { + /* + Styles copied from video-js.min.css, definition of + .vjs-big-play-centered .vjs-big-play-button + */ + top: 50%; + left: 50%; + margin-top: -0.81666em; + margin-left: -1.5em; +} + +.video-js.player-style-youtube .vjs-menu-button-popup .vjs-menu { + margin-bottom: 2em; +} + ul.vjs-menu-content::-webkit-scrollbar { display: none; } @@ -93,7 +165,7 @@ ul.vjs-menu-content::-webkit-scrollbar { color: rgba(0, 182, 240, 1); } -.video-js .vjs-play-progress { +.video-js.player-style-invidious .vjs-play-progress { background-color: rgba(0, 182, 240, 1); } vjs-menu-content @@ -150,25 +222,6 @@ video.video-js { height: 0; } -.video-js.player-style-invidious { - /* This is already the default */ -} - -.video-js.player-style-youtube .vjs-control-bar { - display: flex; - flex-direction: row; -} -.video-js.player-style-youtube .vjs-big-play-button { - /* - Styles copied from video-js.min.css, definition of - .vjs-big-play-centered .vjs-big-play-button - */ - top: 50%; - left: 50%; - margin-top: -0.81666em; - margin-left: -1.5em; -} - .mobile-operations-bar { display: flex; position: absolute; diff --git a/assets/js/player.js b/assets/js/player.js index e6a6dbac..68fc805b 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -14,6 +14,7 @@ var options = { 'durationDisplay', 'progressControl', 'remainingTimeDisplay', + 'Spacer', 'captionsButton', 'qualitySelector', 'playbackRateMenuButton', From f876cd5a6a8202a34a088eaaf711a8744fd0ec29 Mon Sep 17 00:00:00 2001 From: Maykin-99 <73132218+Maykin-99@users.noreply.github.com> Date: Sun, 9 May 2021 10:26:08 +0000 Subject: [PATCH 09/19] Revert "Set correct permissions in Dockerfile" --- docker/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 01abc6f5..f7d990d7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,12 +18,12 @@ RUN apk add --no-cache librsvg ttf-opensans WORKDIR /invidious RUN addgroup -g 1000 -S invidious && \ adduser -u 1000 -S invidious -G invidious -COPY --chown=invidious ./assets/ ./assets/ +COPY ./assets/ ./assets/ COPY --chown=invidious ./config/config.* ./config/ RUN mv -n config/config.example.yml config/config.yml RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: postgres/' config/config.yml -COPY --chown=invidious ./config/sql/ ./config/sql/ -COPY --chown=invidious ./locales/ ./locales/ +COPY ./config/sql/ ./config/sql/ +COPY ./locales/ ./locales/ COPY --from=builder /invidious/invidious . EXPOSE 3000 From a0fe229c7ac7496cf460413650eae51b9561fcab Mon Sep 17 00:00:00 2001 From: Maykin-99 <73132218+Maykin-99@users.noreply.github.com> Date: Sun, 9 May 2021 10:29:06 +0000 Subject: [PATCH 10/19] make assets, config and locales readable only --- docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index f7d990d7..87884403 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -25,6 +25,7 @@ RUN sed -i 's/host: \(127.0.0.1\|localhost\)/host: postgres/' config/config.yml COPY ./config/sql/ ./config/sql/ COPY ./locales/ ./locales/ COPY --from=builder /invidious/invidious . +RUN chmod o+rX -R ./assets ./config ./locales EXPOSE 3000 USER invidious From 533d0a1fd4661ff85b8b3d82edf34cef581fd79c Mon Sep 17 00:00:00 2001 From: sh4dowb Date: Mon, 11 Nov 2019 19:06:06 +0300 Subject: [PATCH 11/19] Added "Read More" button for video descriptions --- src/invidious/helpers/helpers.cr | 1 + src/invidious/users.cr | 1 + src/invidious/videos.cr | 6 ++++++ src/invidious/views/preferences.ecr | 6 ++++++ src/invidious/views/watch.ecr | 13 ++++++++++++- 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 3767cb50..12fac04c 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -50,6 +50,7 @@ struct ConfigPreferences property thin_mode : Bool = false property unseen_only : Bool = false property video_loop : Bool = false + property extend_desc : Bool = false property volume : Int32 = 100 def to_tuple diff --git a/src/invidious/users.cr b/src/invidious/users.cr index 8fef64a0..e4ebb4d1 100644 --- a/src/invidious/users.cr +++ b/src/invidious/users.cr @@ -78,6 +78,7 @@ struct Preferences property thin_mode : Bool = CONFIG.default_user_preferences.thin_mode property unseen_only : Bool = CONFIG.default_user_preferences.unseen_only property video_loop : Bool = CONFIG.default_user_preferences.video_loop + property extend_desc : Bool = CONFIG.default_user_preferences.extend_desc property volume : Int32 = CONFIG.default_user_preferences.volume module BoolToString diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 38646311..2f90669c 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -242,6 +242,7 @@ struct VideoPreferences property speed : Float32 | Float64 property video_end : Float64 | Int32 property video_loop : Bool + property extend_desc : Bool property video_start : Float64 | Int32 property volume : Int32 end @@ -1050,6 +1051,7 @@ def process_video_params(query, preferences) related_videos = query["related_videos"]?.try { |q| (q == "true" || q == "1").to_unsafe } speed = query["speed"]?.try &.rchop("x").to_f? video_loop = query["loop"]?.try { |q| (q == "true" || q == "1").to_unsafe } + extend_desc = query["extend_desc"]?.try { |q| (q == "true" || q == "1").to_unsafe } volume = query["volume"]?.try &.to_i? if preferences @@ -1068,6 +1070,7 @@ def process_video_params(query, preferences) related_videos ||= preferences.related_videos.to_unsafe speed ||= preferences.speed video_loop ||= preferences.video_loop.to_unsafe + extend_desc ||= preferences.extend_desc.to_unsafe volume ||= preferences.volume end @@ -1085,6 +1088,7 @@ def process_video_params(query, preferences) related_videos ||= CONFIG.default_user_preferences.related_videos.to_unsafe speed ||= CONFIG.default_user_preferences.speed video_loop ||= CONFIG.default_user_preferences.video_loop.to_unsafe + extend_desc ||= CONFIG.default_user_preferences.extend_desc.to_unsafe volume ||= CONFIG.default_user_preferences.volume annotations = annotations == 1 @@ -1095,6 +1099,7 @@ def process_video_params(query, preferences) local = local == 1 related_videos = related_videos == 1 video_loop = video_loop == 1 + extend_desc = extend_desc == 1 if CONFIG.disabled?("dash") && quality == "dash" quality = "high" @@ -1141,6 +1146,7 @@ def process_video_params(query, preferences) speed: speed, video_end: video_end, video_loop: video_loop, + extend_desc: extend_desc, video_start: video_start, volume: volume, }) diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr index c1f10818..40350583 100644 --- a/src/invidious/views/preferences.ecr +++ b/src/invidious/views/preferences.ecr @@ -106,6 +106,12 @@ checked<% end %>> +
+ + checked<% end %>> +
+ + <%= translate(locale, "Visual preferences") %>
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index a86e23b2..788b0dcb 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -228,7 +228,18 @@

- <%= video.description_html %> + <% if video.description.size < 200 || params.extend_desc %> + <%= video.description_html %> + <% else %> +
+ <%= video.description_html %> +
+ + <% end %>

From c5fae8426358c511bb8b66f01c931094e7418812 Mon Sep 17 00:00:00 2001 From: syeopite Date: Fri, 2 Apr 2021 01:03:26 -0700 Subject: [PATCH 12/19] Add functionality to read more button --- assets/css/default.css | 25 +++++++++++++++++++++++++ src/invidious/views/watch.ecr | 26 +++++++++++++++++++------- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index a725b456..919f13ec 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -669,3 +669,28 @@ See https://stackoverflow.com/a/34372979 for more info */ hr { margin: auto 0 auto 0; } + +/* Description Expandsion Styling*/ +#description-box { + display: flex; + flex-direction: column; +} + +#readmorebutton { + display: none +} + +#readmorebutton ~ div { + overflow: hidden; + height: 8.3em; +} + +#readmorebutton:checked ~ div { + overflow: unset; + height: 100%; +} + +#readmorebutton + label { + order: 1; + margin-top: 20px; +} diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 788b0dcb..90a20335 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -25,6 +25,19 @@ <%= rendered "components/player_sources" %> <%= HTML.escape(video.title) %> - Invidious + + + <% end %>