From c201ea53ba4b82195d9b3cd7dd939b93802d7a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Fri, 27 May 2022 13:36:13 +0000 Subject: [PATCH 01/98] Add 404 status code on all possible endpoints --- src/invidious/channels/about.cr | 7 ++++++- src/invidious/channels/community.cr | 8 ++++++-- src/invidious/comments.cr | 4 ++-- src/invidious/exceptions.cr | 4 ++++ src/invidious/playlists.cr | 2 +- src/invidious/routes/api/manifest.cr | 2 ++ src/invidious/routes/api/v1/authenticated.cr | 2 ++ src/invidious/routes/api/v1/channels.cr | 6 ++++++ src/invidious/routes/api/v1/videos.cr | 8 ++++++++ src/invidious/routes/channels.cr | 9 +++++++-- src/invidious/routes/embed.cr | 6 ++++++ src/invidious/routes/feeds.cr | 2 ++ src/invidious/routes/playlists.cr | 14 +++++++++++++- src/invidious/routes/video_playback.cr | 8 +++++++- src/invidious/routes/watch.cr | 3 +++ src/invidious/videos.cr | 6 +++++- 16 files changed, 80 insertions(+), 11 deletions(-) diff --git a/src/invidious/channels/about.cr b/src/invidious/channels/about.cr index da71e9a8..31b19bbe 100644 --- a/src/invidious/channels/about.cr +++ b/src/invidious/channels/about.cr @@ -31,7 +31,12 @@ def get_about_info(ucid, locale) : AboutChannel end if initdata.dig?("alerts", 0, "alertRenderer", "type") == "ERROR" - raise InfoException.new(initdata["alerts"][0]["alertRenderer"]["text"]["simpleText"].as_s) + error_message = initdata["alerts"][0]["alertRenderer"]["text"]["simpleText"].as_s + if error_message == "This channel does not exist." + raise NotFoundException.new(error_message) + else + raise InfoException.new(error_message) + end end if browse_endpoint = initdata["onResponseReceivedActions"]?.try &.[0]?.try &.["navigateAction"]?.try &.["endpoint"]?.try &.["browseEndpoint"]? diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 4701ecbd..ebef0edb 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -6,7 +6,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) end if response.status_code != 200 - raise InfoException.new("This channel does not exist.") + raise NotFoundException.new("This channel does not exist.") end ucid = response.body.match(/https:\/\/www.youtube.com\/channel\/(?UC[a-zA-Z0-9_-]{22})/).not_nil!["ucid"] @@ -49,7 +49,11 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) error_message = (message["text"]["simpleText"]? || message["text"]["runs"]?.try &.[0]?.try &.["text"]?) .try &.as_s || "" - raise InfoException.new(error_message) + if error_message == "This channel does not exist." + raise NotFoundException.new(error_message) + else + raise InfoException.new(error_message) + end end response = JSON.build do |json| diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr index d8496978..f2e63265 100644 --- a/src/invidious/comments.cr +++ b/src/invidious/comments.cr @@ -95,7 +95,7 @@ def fetch_youtube_comments(id, cursor, format, locale, thin_mode, region, sort_b contents = body["contents"]? header = body["header"]? else - raise InfoException.new("Could not fetch comments") + raise NotFoundException.new("Comments not found.") end if !contents @@ -290,7 +290,7 @@ def fetch_reddit_comments(id, sort_by = "confidence") thread = result[0].data.as(RedditListing).children[0].data.as(RedditLink) else - raise InfoException.new("Could not fetch comments") + raise NotFoundException.new("Comments not found.") end client.close diff --git a/src/invidious/exceptions.cr b/src/invidious/exceptions.cr index bfaa3fd5..1706ba6a 100644 --- a/src/invidious/exceptions.cr +++ b/src/invidious/exceptions.cr @@ -18,3 +18,7 @@ class BrokenTubeException < Exception return "Missing JSON element \"#{@element}\"" end end + +# Exception used to hold the bogus UCID during a channel search. +class NotFoundException < InfoException +end diff --git a/src/invidious/playlists.cr b/src/invidious/playlists.cr index aefa34cc..c4eb7507 100644 --- a/src/invidious/playlists.cr +++ b/src/invidious/playlists.cr @@ -317,7 +317,7 @@ def get_playlist(plid : String) if playlist = Invidious::Database::Playlists.select(id: plid) return playlist else - raise InfoException.new("Playlist does not exist.") + raise NotFoundException.new("Playlist does not exist.") end else return fetch_playlist(plid) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index 8bc36946..f8766b66 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -16,6 +16,8 @@ module Invidious::Routes::API::Manifest video = get_video(id, region: region) rescue ex : VideoRedirect return env.redirect env.request.resource.gsub(id, ex.video_id) + rescue ex : NotFoundException + haltf env, status_code: 404 rescue ex haltf env, status_code: 403 end diff --git a/src/invidious/routes/api/v1/authenticated.cr b/src/invidious/routes/api/v1/authenticated.cr index b559a01a..1f5ad8ef 100644 --- a/src/invidious/routes/api/v1/authenticated.cr +++ b/src/invidious/routes/api/v1/authenticated.cr @@ -237,6 +237,8 @@ module Invidious::Routes::API::V1::Authenticated begin video = get_video(video_id) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex return error_json(500, ex) end diff --git a/src/invidious/routes/api/v1/channels.cr b/src/invidious/routes/api/v1/channels.cr index 8650976d..6b81c546 100644 --- a/src/invidious/routes/api/v1/channels.cr +++ b/src/invidious/routes/api/v1/channels.cr @@ -13,6 +13,8 @@ module Invidious::Routes::API::V1::Channels rescue ex : ChannelRedirect env.response.headers["Location"] = env.request.resource.gsub(ucid, ex.channel_id) return error_json(302, "Channel is unavailable", {"authorId" => ex.channel_id}) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex return error_json(500, ex) end @@ -170,6 +172,8 @@ module Invidious::Routes::API::V1::Channels rescue ex : ChannelRedirect env.response.headers["Location"] = env.request.resource.gsub(ucid, ex.channel_id) return error_json(302, "Channel is unavailable", {"authorId" => ex.channel_id}) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex return error_json(500, ex) end @@ -205,6 +209,8 @@ module Invidious::Routes::API::V1::Channels rescue ex : ChannelRedirect env.response.headers["Location"] = env.request.resource.gsub(ucid, ex.channel_id) return error_json(302, "Channel is unavailable", {"authorId" => ex.channel_id}) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex return error_json(500, ex) end diff --git a/src/invidious/routes/api/v1/videos.cr b/src/invidious/routes/api/v1/videos.cr index a9f891f5..1b7b4fa7 100644 --- a/src/invidious/routes/api/v1/videos.cr +++ b/src/invidious/routes/api/v1/videos.cr @@ -12,6 +12,8 @@ module Invidious::Routes::API::V1::Videos rescue ex : VideoRedirect env.response.headers["Location"] = env.request.resource.gsub(id, ex.video_id) return error_json(302, "Video is unavailable", {"videoId" => ex.video_id}) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex return error_json(500, ex) end @@ -42,6 +44,8 @@ module Invidious::Routes::API::V1::Videos rescue ex : VideoRedirect env.response.headers["Location"] = env.request.resource.gsub(id, ex.video_id) return error_json(302, "Video is unavailable", {"videoId" => ex.video_id}) + rescue ex : NotFoundException + haltf env, 404 rescue ex haltf env, 500 end @@ -167,6 +171,8 @@ module Invidious::Routes::API::V1::Videos rescue ex : VideoRedirect env.response.headers["Location"] = env.request.resource.gsub(id, ex.video_id) return error_json(302, "Video is unavailable", {"videoId" => ex.video_id}) + rescue ex : NotFoundException + haltf env, 404 rescue ex haltf env, 500 end @@ -324,6 +330,8 @@ module Invidious::Routes::API::V1::Videos begin comments = fetch_youtube_comments(id, continuation, format, locale, thin_mode, region, sort_by: sort_by) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex return error_json(500, ex) end diff --git a/src/invidious/routes/channels.cr b/src/invidious/routes/channels.cr index cd2e3323..c6e02cbd 100644 --- a/src/invidious/routes/channels.cr +++ b/src/invidious/routes/channels.cr @@ -85,6 +85,9 @@ module Invidious::Routes::Channels rescue ex : InfoException env.response.status_code = 500 error_message = ex.message + rescue ex : NotFoundException + env.response.status_code = 404 + error_message = ex.message rescue ex return error_template(500, ex) end @@ -118,7 +121,7 @@ module Invidious::Routes::Channels resolved_url = YoutubeAPI.resolve_url("https://youtube.com#{env.request.path}#{yt_url_params.size > 0 ? "?#{yt_url_params}" : ""}") ucid = resolved_url["endpoint"]["browseEndpoint"]["browseId"] rescue ex : InfoException | KeyError - raise InfoException.new(translate(locale, "This channel does not exist.")) + return error_template(404, translate(locale, "This channel does not exist.")) end selected_tab = env.request.path.split("/")[-1] @@ -141,7 +144,7 @@ module Invidious::Routes::Channels user = env.params.query["user"]? if !user - raise InfoException.new("This channel does not exist.") + return error_template(404, "This channel does not exist.") else env.redirect "/user/#{user}#{uri_params}" end @@ -197,6 +200,8 @@ module Invidious::Routes::Channels channel = get_about_info(ucid, locale) rescue ex : ChannelRedirect return env.redirect env.request.resource.gsub(ucid, ex.channel_id) + rescue ex : NotFoundException + return error_template(404, ex) rescue ex return error_template(500, ex) end diff --git a/src/invidious/routes/embed.cr b/src/invidious/routes/embed.cr index 207970b0..84da9993 100644 --- a/src/invidious/routes/embed.cr +++ b/src/invidious/routes/embed.cr @@ -7,6 +7,8 @@ module Invidious::Routes::Embed playlist = get_playlist(plid) offset = env.params.query["index"]?.try &.to_i? || 0 videos = get_playlist_videos(playlist, offset: offset) + rescue ex : NotFoundException + return error_template(404, ex) rescue ex return error_template(500, ex) end @@ -60,6 +62,8 @@ module Invidious::Routes::Embed playlist = get_playlist(plid) offset = env.params.query["index"]?.try &.to_i? || 0 videos = get_playlist_videos(playlist, offset: offset) + rescue ex : NotFoundException + return error_template(404, ex) rescue ex return error_template(500, ex) end @@ -119,6 +123,8 @@ module Invidious::Routes::Embed video = get_video(id, region: params.region) rescue ex : VideoRedirect return env.redirect env.request.resource.gsub(id, ex.video_id) + rescue ex : NotFoundException + return error_template(404, ex) rescue ex return error_template(500, ex) end diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index b5b58399..31120ecb 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -150,6 +150,8 @@ module Invidious::Routes::Feeds channel = get_about_info(ucid, locale) rescue ex : ChannelRedirect return env.redirect env.request.resource.gsub(ucid, ex.channel_id) + rescue ex : NotFoundException + return error_atom(404, ex) rescue ex return error_atom(500, ex) end diff --git a/src/invidious/routes/playlists.cr b/src/invidious/routes/playlists.cr index de981d81..fe7e4e1c 100644 --- a/src/invidious/routes/playlists.cr +++ b/src/invidious/routes/playlists.cr @@ -66,7 +66,13 @@ module Invidious::Routes::Playlists user = user.as(User) playlist_id = env.params.query["list"] - playlist = get_playlist(playlist_id) + begin + playlist = get_playlist(playlist_id) + rescue ex : NotFoundException + return error_template(404, ex) + rescue ex + return error_template(500, ex) + end subscribe_playlist(user, playlist) env.redirect "/playlist?list=#{playlist.id}" @@ -304,6 +310,8 @@ module Invidious::Routes::Playlists playlist_id = env.params.query["playlist_id"] playlist = get_playlist(playlist_id).as(InvidiousPlaylist) raise "Invalid user" if playlist.author != user.email + rescue ex : NotFoundException + return error_json(404, ex) rescue ex if redirect return error_template(400, ex) @@ -334,6 +342,8 @@ module Invidious::Routes::Playlists begin video = get_video(video_id) + rescue ex : NotFoundException + return error_json(404, ex) rescue ex if redirect return error_template(500, ex) @@ -394,6 +404,8 @@ module Invidious::Routes::Playlists begin playlist = get_playlist(plid) + rescue ex : NotFoundException + return error_template(404, ex) rescue ex return error_template(500, ex) end diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr index 3a92ef96..560f9c19 100644 --- a/src/invidious/routes/video_playback.cr +++ b/src/invidious/routes/video_playback.cr @@ -265,7 +265,13 @@ module Invidious::Routes::VideoPlayback return error_template(403, "Administrator has disabled this endpoint.") end - video = get_video(id, region: region) + begin + video = get_video(id, region: region) + rescue ex : NotFoundException + return error_template(404, ex) + rescue ex + return error_template(500, ex) + end fmt = video.fmt_stream.find(nil) { |f| f["itag"].as_i == itag } || video.adaptive_fmts.find(nil) { |f| f["itag"].as_i == itag } url = fmt.try &.["url"]?.try &.as_s diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr index 7280de4f..fe1d8e54 100644 --- a/src/invidious/routes/watch.cr +++ b/src/invidious/routes/watch.cr @@ -63,6 +63,9 @@ module Invidious::Routes::Watch video = get_video(id, region: params.region) rescue ex : VideoRedirect return env.redirect env.request.resource.gsub(id, ex.video_id) + rescue ex : NotFoundException + LOGGER.error("get_video not found: #{id} : #{ex.message}") + return error_template(404, ex) rescue ex LOGGER.error("get_video: #{id} : #{ex.message}") return error_template(500, ex) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index f65b05bb..20204d81 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -1159,7 +1159,11 @@ def fetch_video(id, region) end if reason = info["reason"]? - raise InfoException.new(reason.as_s || "") + if reason == "Video unavailable" + raise NotFoundException.new(reason.as_s || "") + else + raise InfoException.new(reason.as_s || "") + end end video = Video.new({ From 7e4840867e447701d8756a32c0f8e3981c466e66 Mon Sep 17 00:00:00 2001 From: meow Date: Wed, 1 Jun 2022 17:16:07 +0300 Subject: [PATCH 02/98] CSS. Wider settings name to less word wrap --- assets/css/default.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/css/default.css b/assets/css/default.css index 61b7819f..8691ccba 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -527,3 +527,6 @@ p, /* Center the "invidious" logo on the search page */ #logo > h1 { text-align: center; } + +/* Wider settings name to less word wrap */ +.pure-form-aligned .pure-control-group label { width: 19em; } From a57414307e3d11f230e9953fbc4d641cecceb024 Mon Sep 17 00:00:00 2001 From: meow Date: Mon, 6 Jun 2022 01:00:20 +0300 Subject: [PATCH 03/98] CSS. Small IE11 fixes --- assets/css/default.css | 10 +++++++--- assets/css/search.css | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index 61b7819f..c360e982 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -204,7 +204,8 @@ img.thumbnail { margin: 1px; border: 1px solid; - border-color: #0000 #0000 #CCC #0000; + border-color: rgba(0,0,0,0); + border-bottom-color: #CCC; border-radius: 0; box-shadow: none; @@ -214,7 +215,8 @@ img.thumbnail { .searchbar input[type="search"]:focus { margin: 0 0 0.5px 0; border: 2px solid; - border-color: #0000 #0000 #FED #0000; + border-color: rgba(0,0,0,0); + border-bottom-color: #FED; } /* https://stackoverflow.com/a/55170420 */ @@ -234,7 +236,7 @@ input[type="search"]::-webkit-search-cancel-button { } .user-field div { - width: initial; + width: auto; } .user-field div:not(:last-child) { @@ -527,3 +529,5 @@ p, /* Center the "invidious" logo on the search page */ #logo > h1 { text-align: center; } + +:-ms-input-placeholder { color: #888; } diff --git a/assets/css/search.css b/assets/css/search.css index 5ca141d0..448a7512 100644 --- a/assets/css/search.css +++ b/assets/css/search.css @@ -68,7 +68,7 @@ fieldset, legend { .filter-options label { margin: 0 10px; } -#filters-apply { text-align: end; } +#filters-apply { text-align: right; } /* Error message */ From 38eb4ccbc4eccdcddf0b3a18718475312ac7dd23 Mon Sep 17 00:00:00 2001 From: meow Date: Mon, 6 Jun 2022 21:51:47 +0300 Subject: [PATCH 04/98] CSS. Small IE11 fixes --- assets/css/search.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/css/search.css b/assets/css/search.css index 448a7512..7036fd28 100644 --- a/assets/css/search.css +++ b/assets/css/search.css @@ -68,7 +68,10 @@ fieldset, legend { .filter-options label { margin: 0 10px; } -#filters-apply { text-align: right; } +#filters-apply { + text-align: right; /* IE11 only */ + text-align: end; /* Override for compatible browsers */ +} /* Error message */ From 7db6e43e3f68f32dd375dda244285fd7f82b3d29 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Wed, 22 Jun 2022 19:10:46 +0800 Subject: [PATCH 05/98] Fix captions Captions should automatically show according to preferences. --- assets/js/player.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/player.js b/assets/js/player.js index 7d099e66..8486d875 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -673,7 +673,7 @@ if (player.share) player.share(shareOptions); // show the preferred caption by default if (player_data.preferred_caption_found) { player.ready(function () { - player.textTracks()[1].mode = 'showing'; + player.textTracks()[0].mode = 'showing'; }); } From f6b1cbd5d0338e65f56adb3aaf71738534afda6a Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Wed, 22 Jun 2022 19:33:02 +0800 Subject: [PATCH 06/98] Player MobileUi fast forward/backward rate The fast forward/backward seconds will be adjusted according to playback rate (same as YouTube app behavior). 5 seconds is used when the playback rate is 1x. Previously it was 10 seconds. I believe most of the users watch videos at 2x, so the change will not be obvious. --- assets/js/player.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/js/player.js b/assets/js/player.js index 7d099e66..7930a3d3 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -145,7 +145,7 @@ function isMobile() { } if (isMobile()) { - player.mobileUi(); + player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } }); var buttons = ['playToggle', 'volumePanel', 'captionsButton']; @@ -274,6 +274,9 @@ function updateCookie(newVolume, newSpeed) { player.on('ratechange', function () { updateCookie(null, player.playbackRate()); + if (isMobile()) { + player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } }); + } }); player.on('volumechange', function () { From ac685f65e9011b226f580917efc8392b16e5a8f4 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Thu, 23 Jun 2022 01:01:11 +0800 Subject: [PATCH 07/98] Fix captions textTracks 0 in DASH mode shows debug messages. Use textTracks 1 in DASH mode, and textTracks 0 in non-DASH mode and audio mode. --- assets/js/player.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/assets/js/player.js b/assets/js/player.js index 8486d875..aef50926 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -673,7 +673,12 @@ if (player.share) player.share(shareOptions); // show the preferred caption by default if (player_data.preferred_caption_found) { player.ready(function () { - player.textTracks()[0].mode = 'showing'; + if (!video_data.params.listen && video_data.params.quality === 'dash') { + // play.textTracks()[0] on DASH mode is showing some debug messages + player.textTracks()[1].mode = 'showing'; + } else { + player.textTracks()[0].mode = 'showing'; + } }); } From 140b6c1227754356145acd7b76820e3921745ef8 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Thu, 23 Jun 2022 02:13:22 +0800 Subject: [PATCH 08/98] DASH playback force highest quality m4a Since VideoJS is unable to handle adaptive audio quality, the best audo quality is forced for every video quality. --- src/invidious/routes/api/manifest.cr | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index 8bc36946..8b5bfc06 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -61,7 +61,18 @@ module Invidious::Routes::API::Manifest next if mime_streams.empty? xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true) do + # ignore the 64k m4a stream, only consider the 128k m4a stream + best_m4a_stream = mime_streams[0] + best_m4a_stream_bitrate = 0 mime_streams.each do |fmt| + bandwidth = fmt["bitrate"].as_i + if (bandwidth > best_m4a_stream_bitrate) + best_m4a_stream_bitrate = bandwidth + best_m4a_stream = fmt + end + end + + [best_m4a_stream].each do |fmt| # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) From 81abebd14493d4207a663d6f575d945a23b03170 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Thu, 23 Jun 2022 02:27:46 +0800 Subject: [PATCH 09/98] Highest quality m4a on audio only mode as default Audio mode will automatically select highest quality m4a as default. --- src/invidious/views/components/player.ecr | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index fffefc9a..a342097e 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -7,14 +7,25 @@ <% else %> <% if params.listen %> - <% audio_streams.each_with_index do |fmt, i| + <% # ignore the 64k m4a stream, only consider the 128k m4a stream + best_m4a_stream_index = 0 + best_m4a_stream_bitrate = 0 + audio_streams.each_with_index do |fmt, i| + bandwidth = fmt["bitrate"].as_i + if (fmt["mimeType"].as_s.starts_with?("audio/mp4") && bandwidth > best_m4a_stream_bitrate) + best_m4a_stream_bitrate = bandwidth + best_m4a_stream_index = i + end + end + + audio_streams.each_with_index do |fmt, i| src_url = "/latest_version?id=#{video.id}&itag=#{fmt["itag"]}" src_url += "&local=true" if params.local bitrate = fmt["bitrate"] mimetype = HTML.escape(fmt["mimeType"].as_s) - selected = i == 0 ? true : false + selected = i == best_m4a_stream_index ? true : false %> <% if !params.local && !CONFIG.disabled?("local") %> From 3013782b7b39295b34c3f5a72274efc625748a7f Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Thu, 23 Jun 2022 03:03:54 +0800 Subject: [PATCH 10/98] formatting --- src/invidious/routes/api/manifest.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index 8b5bfc06..b8466df1 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -71,7 +71,7 @@ module Invidious::Routes::API::Manifest best_m4a_stream = fmt end end - + [best_m4a_stream].each do |fmt| # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) From c75bf35f59864c9f7e37816d657e913f29b40123 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:26:30 +0800 Subject: [PATCH 11/98] Update DASH format to serve 2 audio to player player.audioTracks() can successfully show tracks_: Array(2) --- src/invidious/routes/api/manifest.cr | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index b8466df1..476ff65a 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -46,7 +46,7 @@ module Invidious::Routes::API::Manifest end end - audio_streams = video.audio_streams + audio_streams = video.audio_streams.sort_by { |stream| {stream["bitrate"].as_i} }.reverse! video_streams = video.video_streams.sort_by { |stream| {stream["width"].as_i, stream["fps"].as_i} }.reverse! manifest = XML.build(indent: " ", encoding: "UTF-8") do |xml| @@ -60,19 +60,8 @@ module Invidious::Routes::API::Manifest mime_streams = audio_streams.select { |stream| stream["mimeType"].as_s.starts_with? mime_type } next if mime_streams.empty? - xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true) do - # ignore the 64k m4a stream, only consider the 128k m4a stream - best_m4a_stream = mime_streams[0] - best_m4a_stream_bitrate = 0 - mime_streams.each do |fmt| - bandwidth = fmt["bitrate"].as_i - if (bandwidth > best_m4a_stream_bitrate) - best_m4a_stream_bitrate = bandwidth - best_m4a_stream = fmt - end - end - - [best_m4a_stream].each do |fmt| + mime_streams.each do |fmt| + xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, lang: i.to_s) do # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) @@ -90,9 +79,8 @@ module Invidious::Routes::API::Manifest end end end + i += 1 end - - i += 1 end potential_heights = {4320, 2160, 1440, 1080, 720, 480, 360, 240, 144} From a62adccd3d2e80377d200cb3890d00eea6dd5c8b Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 16:33:02 +0800 Subject: [PATCH 12/98] change lang to label lang has to be BCP 47 standard. Using label also can let video.js know there are 2 audio tracks. --- src/invidious/routes/api/manifest.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index 476ff65a..b9f81622 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -61,7 +61,7 @@ module Invidious::Routes::API::Manifest next if mime_streams.empty? mime_streams.each do |fmt| - xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, lang: i.to_s) do + xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: i.to_s) do # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) From 32ecf30c821bab26d4adb83714dedb4e01253f99 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 17:19:11 +0800 Subject: [PATCH 13/98] Add audioTrackButton --- assets/js/player.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/player.js b/assets/js/player.js index 7d099e66..c2a5f42e 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -17,6 +17,7 @@ var options = { 'remainingTimeDisplay', 'Spacer', 'captionsButton', + 'audioTrackButton', 'qualitySelector', 'playbackRateMenuButton', 'fullscreenToggle' From 09ff370ddca17aae9baf73af4c920c7790f1e70a Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 17:19:40 +0800 Subject: [PATCH 14/98] Change player.css order --- assets/css/player.css | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/assets/css/player.css b/assets/css/player.css index 304375b5..8a7cfdab 100644 --- a/assets/css/player.css +++ b/assets/css/player.css @@ -101,23 +101,27 @@ ul.vjs-menu-content::-webkit-scrollbar { order: 2; } -.vjs-quality-selector, -.video-js .vjs-http-source-selector { +.vjs-audio-button { order: 3; } -.vjs-playback-rate { +.vjs-quality-selector, +.video-js .vjs-http-source-selector { order: 4; } -.vjs-share-control { +.vjs-playback-rate { order: 5; } -.vjs-fullscreen-control { +.vjs-share-control { order: 6; } +.vjs-fullscreen-control { + order: 7; +} + .vjs-playback-rate > .vjs-menu { width: 50px; } From e0f6988eb59b08341f781ffb2b6bf47f6ee6ab16 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 18:52:34 +0800 Subject: [PATCH 15/98] DASH Default to high quality m4a --- src/invidious/routes/api/manifest.cr | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index b9f81622..ca72be26 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -61,7 +61,7 @@ module Invidious::Routes::API::Manifest next if mime_streams.empty? mime_streams.each do |fmt| - xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: i.to_s) do + xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: fmt["bitrate"].to_s + "k") do # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) @@ -70,6 +70,8 @@ module Invidious::Routes::API::Manifest itag = fmt["itag"].as_i url = fmt["url"].as_s + xml.element("Role", schemeIdUri: "urn:mpeg:dash:role:2011", value: i == 0 ? "main" : "alternate") + xml.element("Representation", id: fmt["itag"], codecs: codecs, bandwidth: bandwidth) do xml.element("AudioChannelConfiguration", schemeIdUri: "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", value: "2") From c7d468578f1c7cd8f166321a81b7bcc121483ec8 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 19:03:35 +0800 Subject: [PATCH 16/98] Update MobileUi --- assets/js/player.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/player.js b/assets/js/player.js index c2a5f42e..0416e2b0 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -148,7 +148,7 @@ function isMobile() { if (isMobile()) { player.mobileUi(); - var buttons = ['playToggle', 'volumePanel', 'captionsButton']; + var buttons = ['playToggle', 'volumePanel', 'captionsButton', 'audioTrackButton']; if (video_data.params.quality !== 'dash') buttons.push('qualitySelector'); From cc9ce916c6e8119eb36b54917215d2ef53f64793 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 19:24:20 +0800 Subject: [PATCH 17/98] Update MobileUi --- assets/js/player.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/js/player.js b/assets/js/player.js index 0416e2b0..1b01ac36 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -148,9 +148,10 @@ function isMobile() { if (isMobile()) { player.mobileUi(); - var buttons = ['playToggle', 'volumePanel', 'captionsButton', 'audioTrackButton']; + var buttons = ['playToggle', 'volumePanel', 'captionsButton']; - if (video_data.params.quality !== 'dash') buttons.push('qualitySelector'); + if (!video_data.params.listen && video_data.params.quality === 'dash') buttons.push('audioTrackButton'); + if (video_data.params.listen || video_data.params.quality !== 'dash') buttons.push('qualitySelector'); // Create new control bar object for operation buttons const ControlBar = videojs.getComponent('controlBar'); @@ -177,7 +178,7 @@ if (isMobile()) { var share_element = document.getElementsByClassName('vjs-share-control')[0]; operations_bar_element.append(share_element); - if (video_data.params.quality === 'dash') { + 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); } From 3f1d88282ed2878608032ec605fe17e61197d8ed Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sat, 25 Jun 2022 19:26:14 +0800 Subject: [PATCH 18/98] Update some comments --- src/invidious/views/components/player.ecr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index a342097e..9f42ae77 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -7,7 +7,7 @@ <% else %> <% if params.listen %> - <% # ignore the 64k m4a stream, only consider the 128k m4a stream + <% # default to 128k m4a stream best_m4a_stream_index = 0 best_m4a_stream_bitrate = 0 audio_streams.each_with_index do |fmt, i| From 2851d993ad0079433ee9028c4f2e7854096ed9f0 Mon Sep 17 00:00:00 2001 From: 11tuvork28 Date: Sun, 3 Jul 2022 14:03:30 +0200 Subject: [PATCH 19/98] updated comment to represent current structure --- src/invidious/yt_backend/extractors.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index c4326cab..b9609eb9 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -417,7 +417,7 @@ private module Extractors # {"tabRenderer": { # "endpoint": {...} # "title": "Playlists", - # "selected": true, + # "selected": true, # Is nil unless tab is selected # "content": {...}, # ... # }} From 15d2cfba90428f8c1bb3e7ce88599078dc0ae6f0 Mon Sep 17 00:00:00 2001 From: 11tuvork28 Date: Sun, 3 Jul 2022 14:03:42 +0200 Subject: [PATCH 20/98] Fix `Missing hash key: "selected" (KeyError)` --- src/invidious/yt_backend/extractors_utils.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/yt_backend/extractors_utils.cr b/src/invidious/yt_backend/extractors_utils.cr index 3d5e5787..f8245160 100644 --- a/src/invidious/yt_backend/extractors_utils.cr +++ b/src/invidious/yt_backend/extractors_utils.cr @@ -84,7 +84,7 @@ end def extract_selected_tab(tabs) # Extract the selected tab from the array of tabs Youtube returns - return selected_target = tabs.as_a.select(&.["tabRenderer"]?.try &.["selected"].as_bool)[0]["tabRenderer"] + return selected_target = tabs.as_a.select(&.["tabRenderer"]?.try &.["selected"]?.try &.as_bool)[0]["tabRenderer"] end def fetch_continuation_token(items : Array(JSON::Any)) From a8b72d834231a6b353d7dda31e93b2e4907800fd Mon Sep 17 00:00:00 2001 From: 11tuvork28 Date: Sun, 3 Jul 2022 14:23:34 +0200 Subject: [PATCH 21/98] Fixed community tab --- src/invidious/channels/community.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 4701ecbd..4c32ea20 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -13,7 +13,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) if !continuation || continuation.empty? initial_data = extract_initial_data(response.body) - body = initial_data["contents"]?.try &.["twoColumnBrowseResultsRenderer"]["tabs"].as_a.select { |tab| tab["tabRenderer"]?.try &.["selected"].as_bool.== true }[0]? + body = initial_data["contents"]?.try &.["twoColumnBrowseResultsRenderer"]["tabs"].as_a.select { |tab| tab["tabRenderer"]?.try &.["selected"]?.try &.as_bool == true }[0]? if !body raise InfoException.new("Could not extract community tab.") From 864f27ef72b084461e327640f80aa45a8f250b0f Mon Sep 17 00:00:00 2001 From: 11tuvork28 Date: Sun, 3 Jul 2022 14:59:33 +0200 Subject: [PATCH 22/98] switched to extract_selected_tab for the community tab --- src/invidious/channels/community.cr | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 4c32ea20..aaed9567 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -13,13 +13,11 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) if !continuation || continuation.empty? initial_data = extract_initial_data(response.body) - body = initial_data["contents"]?.try &.["twoColumnBrowseResultsRenderer"]["tabs"].as_a.select { |tab| tab["tabRenderer"]?.try &.["selected"]?.try &.as_bool == true }[0]? + body = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"])["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"] if !body raise InfoException.new("Could not extract community tab.") end - - body = body["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"] else continuation = produce_channel_community_continuation(ucid, continuation) From 0e3820b634cd94a647af099805d3957cd5c8998c Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 14 Jun 2022 00:04:19 +0200 Subject: [PATCH 23/98] Add #to_http_params method to Query (Fixes #3148) --- spec/invidious/search/query_spec.cr | 42 +++++++++++++++++++++++++++++ src/invidious/routes/search.cr | 6 +++++ src/invidious/search/query.cr | 12 ++++++++- src/invidious/views/search.ecr | 10 ------- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/spec/invidious/search/query_spec.cr b/spec/invidious/search/query_spec.cr index 4853e9e9..063b69f1 100644 --- a/spec/invidious/search/query_spec.cr +++ b/spec/invidious/search/query_spec.cr @@ -197,4 +197,46 @@ Spectator.describe Invidious::Search::Query do ) end end + + describe "#to_http_params" do + it "formats regular search" do + query = described_class.new( + HTTP::Params.parse("q=The+Simpsons+hiding+in+bush&duration=short"), + Invidious::Search::Query::Type::Regular, nil + ) + + params = query.to_http_params + + expect(params).to have_key("duration") + expect(params["duration"]?).to eq("short") + + expect(params).to have_key("q") + expect(params["q"]?).to eq("The Simpsons hiding in bush") + + # Check if there aren't other parameters + params.delete("duration") + params.delete("q") + expect(params).to be_empty + end + + it "formats channel search" do + query = described_class.new( + HTTP::Params.parse("q=channel:UC2DjFE7Xf11URZqWBigcVOQ%20multimeter"), + Invidious::Search::Query::Type::Regular, nil + ) + + params = query.to_http_params + + expect(params).to have_key("channel") + expect(params["channel"]?).to eq("UC2DjFE7Xf11URZqWBigcVOQ") + + expect(params).to have_key("q") + expect(params["q"]?).to eq("multimeter") + + # Check if there aren't other parameters + params.delete("channel") + params.delete("q") + expect(params).to be_empty + end + end end diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index 6f8bffea..2a9705cf 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -59,6 +59,12 @@ module Invidious::Routes::Search return error_template(500, ex) end + params = query.to_http_params + url_prev_page = "/search?#{params}&page=#{query.page - 1}" + url_next_page = "/search?#{params}&page=#{query.page + 1}" + + redirect_url = Invidious::Frontend::Misc.redirect_url(env) + env.set "search", query.text templated "search" end diff --git a/src/invidious/search/query.cr b/src/invidious/search/query.cr index 34b36b1d..24e79609 100644 --- a/src/invidious/search/query.cr +++ b/src/invidious/search/query.cr @@ -57,7 +57,7 @@ module Invidious::Search # Get the page number (also common to all search types) @page = params["page"]?.try &.to_i? || 1 - # Stop here is raw query in empty + # Stop here if raw query is empty # NOTE: maybe raise in the future? return if self.empty_raw_query? @@ -127,6 +127,16 @@ module Invidious::Search return items end + # Return the HTTP::Params corresponding to this Query (invidious format) + def to_http_params : HTTP::Params + params = @filters.to_iv_params + + params["q"] = @query + params["channel"] = @channel if !@channel.empty? + + return params + end + # TODO: clean code private def unnest_items(all_items) : Array(SearchItem) items = [] of SearchItem diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index 7110703e..254449a1 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -3,16 +3,6 @@ <% end %> -<%- - search_query_encoded = URI.encode_www_form(query.text, space_to_plus: true) - filter_params = query.filters.to_iv_params - - url_prev_page = "/search?q=#{search_query_encoded}&#{filter_params}&page=#{query.page - 1}" - url_next_page = "/search?q=#{search_query_encoded}&#{filter_params}&page=#{query.page + 1}" - - redirect_url = Invidious::Frontend::Misc.redirect_url(env) --%> - <%= Invidious::Frontend::SearchFilters.generate(query.filters, query.text, query.page, locale) %>
From 99bc230fe64512b3f87095bb8111b24e15aa4285 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 15 Jun 2022 21:14:38 +0200 Subject: [PATCH 24/98] Fix missing hash key: "availableCountries" (Closes #3047) --- src/invidious/channels/about.cr | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/invidious/channels/about.cr b/src/invidious/channels/about.cr index 565f2bca..1d7947a6 100644 --- a/src/invidious/channels/about.cr +++ b/src/invidious/channels/about.cr @@ -54,9 +54,6 @@ def get_about_info(ucid, locale) : AboutChannel banner = banners.try &.[-1]?.try &.["url"].as_s? description_node = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"] - - is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool - allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map(&.as_s) else author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s @@ -74,13 +71,17 @@ def get_about_info(ucid, locale) : AboutChannel # end description_node = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]? - - is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool - allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map(&.as_s) end + is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool + + allowed_regions = initdata + .dig?("microformat", "microformatDataRenderer", "availableCountries") + .try &.as_a.map(&.as_s) || [] of String + description = !description_node.nil? ? description_node.as_s : "" description_html = HTML.escape(description) + if !description_node.nil? if description_node.as_h?.nil? description_node = text_to_parsed_content(description_node.as_s) From ce32873ef8450461ba55ec50aed93d427aa23084 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 15 Jun 2022 21:55:33 +0200 Subject: [PATCH 25/98] Remove item (video/channel/mix) thumbnail from keyboard navigation tree --- src/invidious/views/components/item.ecr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index fb7ad1dc..4f3cf279 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -5,7 +5,7 @@ <% if !env.get("preferences").as(Preferences).thin_mode %>
- "/> + "/>
<% end %>

<%= HTML.escape(item.author) %><% if !item.author_verified.nil? && item.author_verified %> <% end %>

@@ -23,7 +23,7 @@
<% if !env.get("preferences").as(Preferences).thin_mode %>
- "/> + "/>

<%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %>

<% end %> @@ -36,7 +36,7 @@
<% if !env.get("preferences").as(Preferences).thin_mode %>
- + <% if item.length_seconds != 0 %>

<%= recode_length_seconds(item.length_seconds) %>

<% end %> @@ -51,7 +51,7 @@
<% if !env.get("preferences").as(Preferences).thin_mode %>
- + <% if plid_form = env.get?("remove_playlist_items") %>
" method="post"> "> @@ -103,7 +103,7 @@ <% if !env.get("preferences").as(Preferences).thin_mode %>
- + <% if env.get? "show_watched" %> " method="post"> "> From 06af5a004e4c6cda28e5a4cc13ee47ed3cf9c155 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 15 Jun 2022 22:26:50 +0200 Subject: [PATCH 26/98] Remove useless link in item forms (buttons on thumbnail) --- src/invidious/views/components/item.ecr | 23 +++++-------------- src/invidious/views/feeds/history.ecr | 4 +--- .../views/user/subscription_manager.ecr | 4 +--- src/invidious/views/user/token_manager.ecr | 4 +--- 4 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 4f3cf279..0e959ff2 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -52,15 +52,12 @@ <% if !env.get("preferences").as(Preferences).thin_mode %>
+ <% if plid_form = env.get?("remove_playlist_items") %> " method="post"> ">

- - - +

<% end %> @@ -108,24 +105,16 @@
" method="post"> ">

- - - +

<% elsif plid_form = env.get? "add_playlist_items" %>
" method="post"> ">

- - - +

<% end %> diff --git a/src/invidious/views/feeds/history.ecr b/src/invidious/views/feeds/history.ecr index 6c1243c5..471d21db 100644 --- a/src/invidious/views/feeds/history.ecr +++ b/src/invidious/views/feeds/history.ecr @@ -38,9 +38,7 @@
" method="post"> ">

- - - +

diff --git a/src/invidious/views/user/subscription_manager.ecr b/src/invidious/views/user/subscription_manager.ecr index c2a89ca2..c9801f09 100644 --- a/src/invidious/views/user/subscription_manager.ecr +++ b/src/invidious/views/user/subscription_manager.ecr @@ -39,9 +39,7 @@

" method="post"> "> - - "> - + ">

diff --git a/src/invidious/views/user/token_manager.ecr b/src/invidious/views/user/token_manager.ecr index 79f905a1..a73fa048 100644 --- a/src/invidious/views/user/token_manager.ecr +++ b/src/invidious/views/user/token_manager.ecr @@ -31,9 +31,7 @@

" method="post"> "> - - "> - + ">

From 8332ad0f1684cd5af68daeb4f7bc5cb04ff03669 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 20 Jun 2022 23:19:05 +0200 Subject: [PATCH 27/98] Fix syntax errors in shell scripts --- scripts/deploy-database.sh | 4 ++-- scripts/install-dependencies.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/deploy-database.sh b/scripts/deploy-database.sh index ed9464e6..fa24b8f0 100644 --- a/scripts/deploy-database.sh +++ b/scripts/deploy-database.sh @@ -6,7 +6,7 @@ interactive=true -if [ "$1" == "--no-interactive" ]; then +if [ "$1" = "--no-interactive" ]; then interactive=false fi @@ -21,7 +21,7 @@ sudo systemctl enable postgresql.service # Create databse and user # -if [ "$interactive" == "true" ]; then +if [ "$interactive" = "true" ]; then sudo -u postgres -- createuser -P kemal sudo -u postgres -- createdb -O kemal invidious else diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index 27e0bf15..1e67bdaf 100644 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -74,7 +74,7 @@ install_apt() { sudo apt-get install --yes --no-install-recommends \ libssl-dev libxml2-dev libyaml-dev libgmp-dev libevent-dev \ libpcre3-dev libreadline-dev libsqlite3-dev zlib1g-dev \ - crystal postgres git librsvg2-bin make + crystal postgresql-13 git librsvg2-bin make } install_yum() { From eb226e1dcf4ca88776aa42402e8d80fd5f14ae96 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 21 Jun 2022 01:01:25 +0200 Subject: [PATCH 28/98] Remove all backend code related to dislikes --- src/invidious/videos.cr | 41 ++++------------------------------- src/invidious/views/watch.ecr | 8 +++---- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 1504e390..3204c98d 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -323,7 +323,7 @@ struct Video json.field "viewCount", self.views json.field "likeCount", self.likes - json.field "dislikeCount", self.dislikes + json.field "dislikeCount", 0_i64 json.field "paid", self.paid json.field "premium", self.premium @@ -354,7 +354,7 @@ struct Video json.field "lengthSeconds", self.length_seconds json.field "allowRatings", self.allow_ratings - json.field "rating", self.average_rating + json.field "rating", 0_i64 json.field "isListed", self.is_listed json.field "liveNow", self.live_now json.field "isUpcoming", self.is_upcoming @@ -556,11 +556,6 @@ struct Video info["dislikes"]?.try &.as_i64 || 0_i64 end - def average_rating : Float64 - # (likes / (likes + dislikes) * 4 + 1) - info["videoDetails"]["averageRating"]?.try { |t| t.as_f? || t.as_i64?.try &.to_f64 }.try &.round(4) || 0.0 - end - def published : Time info .dig?("microformat", "playerMicroformatRenderer", "publishDate") @@ -813,14 +808,6 @@ struct Video return info.dig?("streamingData", "adaptiveFormats", 0, "projectionType").try &.as_s end - def wilson_score : Float64 - ci_lower_bound(likes, likes + dislikes).round(4) - end - - def engagement : Float64 - (((likes + dislikes) / views) * 100).round(4) - end - def reason : String? info["reason"]?.try &.as_s end @@ -1005,7 +992,7 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ params["relatedVideos"] = JSON::Any.new(related) - # Likes/dislikes + # Likes toplevel_buttons = video_primary_renderer .try &.dig?("videoActions", "menuRenderer", "topLevelButtons") @@ -1023,30 +1010,10 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ LOGGER.trace("extract_video_info: Found \"likes\" button. Button text is \"#{likes_txt}\"") LOGGER.debug("extract_video_info: Likes count is #{likes}") if likes end - - dislikes_button = toplevel_buttons.as_a - .find(&.dig("toggleButtonRenderer", "defaultIcon", "iconType").as_s.== "DISLIKE") - .try &.["toggleButtonRenderer"] - - if dislikes_button - dislikes_txt = (dislikes_button["defaultText"]? || dislikes_button["toggledText"]?) - .try &.dig?("accessibility", "accessibilityData", "label") - dislikes = dislikes_txt.as_s.gsub(/\D/, "").to_i64? if dislikes_txt - - LOGGER.trace("extract_video_info: Found \"dislikes\" button. Button text is \"#{dislikes_txt}\"") - LOGGER.debug("extract_video_info: Dislikes count is #{dislikes}") if dislikes - end - end - - if likes && likes != 0_i64 && (!dislikes || dislikes == 0_i64) - if rating = player_response.dig?("videoDetails", "averageRating").try { |x| x.as_i64? || x.as_f? } - dislikes = (likes * ((5 - rating)/(rating - 1))).round.to_i64 - LOGGER.debug("extract_video_info: Dislikes count (using fallback method) is #{dislikes}") - end end params["likes"] = JSON::Any.new(likes || 0_i64) - params["dislikes"] = JSON::Any.new(dislikes || 0_i64) + params["dislikes"] = JSON::Any.new(0_i64) # Description diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index d1fdcce2..50c63d21 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -173,7 +173,7 @@ we're going to need to do it here in order to allow for translations.

<%= number_with_separator(video.views) %>

<%= number_with_separator(video.likes) %>

-

+

<%= translate(locale, "Genre: ") %> <% if !video.genre_url %> <%= video.genre %> @@ -185,9 +185,9 @@ we're going to need to do it here in order to allow for translations.

<%= translate(locale, "License: ") %><%= video.license %>

<% end %>

<%= translate(locale, "Family friendly? ") %><%= translate_bool(locale, video.is_family_friendly) %>

-

<%= translate(locale, "Wilson score: ") %><%= video.wilson_score %>

-

-

<%= translate(locale, "Engagement: ") %><%= video.engagement %>%

+ + + <% if video.allowed_regions.size != REGIONS.size %>

<% if video.allowed_regions.size < REGIONS.size // 2 %> From f7b1dcc271bb14bf8962c9375c413c0cf01d880b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Thu, 23 Jun 2022 21:32:02 +0200 Subject: [PATCH 29/98] Don't treat LIVE_STREAM_OFFLINE playability status as an error (fixes #3155) --- src/invidious/videos.cr | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 3204c98d..d9a7d846 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -895,13 +895,20 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ player_response = YoutubeAPI.player(video_id: video_id, params: "", client_config: client_config) - if player_response.dig?("playabilityStatus", "status").try &.as_s != "OK" + playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s + + if playability_status != "OK" subreason = player_response.dig?("playabilityStatus", "errorScreen", "playerErrorMessageRenderer", "subreason") reason = subreason.try &.[]?("simpleText").try &.as_s reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("") reason ||= player_response.dig("playabilityStatus", "reason").as_s + params["reason"] = JSON::Any.new(reason) - return params + + # Stop here if video is not a scheduled livestream + if playability_status != "LIVE_STREAM_OFFLINE" + return params + end end params["shortDescription"] = player_response.dig?("videoDetails", "shortDescription") || JSON::Any.new(nil) From 5556a996cdbcdd4ff060a2f46b842220c84f3c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Wed, 6 Jul 2022 19:59:05 +0000 Subject: [PATCH 30/98] Update comment for NotFoundException --- src/invidious/exceptions.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/exceptions.cr b/src/invidious/exceptions.cr index 1706ba6a..471a199a 100644 --- a/src/invidious/exceptions.cr +++ b/src/invidious/exceptions.cr @@ -19,6 +19,6 @@ class BrokenTubeException < Exception end end -# Exception used to hold the bogus UCID during a channel search. +# Exception threw when an element is not found. class NotFoundException < InfoException end From d00839ec689a7aa2bdc959d1ad13108778668bff Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:10 +0200 Subject: [PATCH 31/98] Update Russian translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Russian translation Update Russian translation Update Russian translation Co-authored-by: AHOHNMYC Co-authored-by: Hosted Weblate Co-authored-by: Егор Ермаков --- locales/ru.json | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index 00d24502..4680e350 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -75,11 +75,11 @@ "light": "светлая", "preferences_thin_mode_label": "Облегчённое оформление: ", "preferences_category_misc": "Прочие настройки", - "preferences_automatic_instance_redirect_label": "Автоматическое перенаправление на зеркало сайта (переход на redirect.invidious.io): ", + "preferences_automatic_instance_redirect_label": "Автоматическая смена зеркала (переход на redirect.invidious.io): ", "preferences_category_subscription": "Настройки подписок", "preferences_annotations_subscribed_label": "Всегда показывать аннотации на каналах из ваших подписок? ", - "Redirect homepage to feed: ": "Отображать видео с каналов, на которые вы подписаны, как главную страницу: ", - "preferences_max_results_label": "Число видео, на которые вы подписаны, в ленте: ", + "Redirect homepage to feed: ": "Показывать подписки на главной странице: ", + "preferences_max_results_label": "Число видео в ленте: ", "preferences_sort_label": "Сортировать видео: ", "published": "по дате публикации", "published - reverse": "по дате публикации в обратном порядке", @@ -158,7 +158,7 @@ "View more comments on Reddit": "Посмотреть больше комментариев на Reddit", "View `x` comments": { "([^.,0-9]|^)1([^.,0-9]|$)": "Показано `x` комментариев", - "": "Показано`x` комментариев" + "": "Показано `x` комментариев" }, "View Reddit comments": "Смотреть комментарии с Reddit", "Hide replies": "Скрыть ответы", @@ -186,7 +186,7 @@ "Could not fetch comments": "Не удаётся загрузить комментарии", "`x` ago": "`x` назад", "Load more": "Загрузить ещё", - "Could not create mix.": "Не удаётся создать микс.", + "Could not create mix.": "Не удалось создать микс.", "Empty playlist": "Плейлист пуст", "Not a playlist.": "Некорректный плейлист.", "Playlist does not exist.": "Плейлист не существует.", @@ -486,5 +486,6 @@ "search_filters_features_option_vr180": "VR180", "search_message_change_filters_or_query": "Попробуйте расширить поисковый запрос или изменить фильтры.", "search_filters_duration_option_medium": "Средние (4 - 20 минут)", - "search_filters_apply_button": "Применить фильтры" + "search_filters_apply_button": "Применить фильтры", + "Popular enabled: ": "Популярное включено: " } From e90f4a2cbfb04f38d5c3f675315b78dc359a02f2 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:10 +0200 Subject: [PATCH 32/98] =?UTF-8?q?Update=20Norwegian=20Bokm=C3=A5l=20transl?= =?UTF-8?q?ation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Allan Nordhøy Co-authored-by: Hosted Weblate --- locales/nb-NO.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/locales/nb-NO.json b/locales/nb-NO.json index 8d80c10c..77c688d5 100644 --- a/locales/nb-NO.json +++ b/locales/nb-NO.json @@ -460,5 +460,16 @@ "Russian (auto-generated)": "Russisk (laget automatisk)", "Dutch (auto-generated)": "Nederlandsk (laget automatisk)", "Turkish (auto-generated)": "Tyrkisk (laget automatisk)", - "search_filters_title": "Filtrer" + "search_filters_title": "Filtrer", + "Popular enabled: ": "Populære påskrudd: ", + "search_message_change_filters_or_query": "Prøv ett mindre snevert søk og/eller endre filterne.", + "search_filters_duration_option_medium": "Middels (4–20 minutter)", + "search_message_no_results": "Resultatløst.", + "search_filters_type_option_all": "Alle typer", + "search_filters_duration_option_none": "Uvilkårlig varighet", + "search_message_use_another_instance": " Du kan også søke på en annen instans.", + "search_filters_date_label": "Opplastningsdato", + "search_filters_apply_button": "Bruk valgte filtre", + "search_filters_date_option_none": "Siden begynnelsen", + "search_filters_features_option_vr180": "VR180" } From d16c3ed40aef286b284971f6750b80371eb957a2 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:10 +0200 Subject: [PATCH 33/98] Update Italian translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update Italian translation Co-authored-by: Hosted Weblate Co-authored-by: Pietro Cappuccino Co-authored-by: ㅤAbsurdUsername --- locales/it.json | 56 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/locales/it.json b/locales/it.json index 7ba5ff2d..ac83ac58 100644 --- a/locales/it.json +++ b/locales/it.json @@ -28,7 +28,7 @@ "Import and Export Data": "Importazione ed esportazione dati", "Import": "Importa", "Import Invidious data": "Importa dati Invidious in formato JSON", - "Import YouTube subscriptions": "Importa le iscrizioni da YouTube", + "Import YouTube subscriptions": "Importa le iscrizioni da YouTube/OPML", "Import FreeTube subscriptions (.db)": "Importa le iscrizioni da FreeTube (.db)", "Import NewPipe subscriptions (.json)": "Importa le iscrizioni da NewPipe (.json)", "Import NewPipe data (.zip)": "Importa i dati di NewPipe (.zip)", @@ -340,7 +340,7 @@ "%A %B %-d, %Y": "%A %-d %B %Y", "(edited)": "(modificato)", "YouTube comment permalink": "Link permanente al commento di YouTube", - "permalink": "permalink", + "permalink": "perma-collegamento", "`x` marked it with a ❤": "`x` l'ha contrassegnato con un ❤", "Audio mode": "Modalità audio", "Video mode": "Modalità video", @@ -385,7 +385,7 @@ "preferences_quality_dash_option_144p": "144p", "Released under the AGPLv3 on Github.": "Rilasciato su GitHub con licenza AGPLv3.", "preferences_quality_option_medium": "Media", - "preferences_quality_option_small": "Piccola", + "preferences_quality_option_small": "Limitata", "preferences_quality_dash_option_best": "Migliore", "preferences_quality_dash_option_worst": "Peggiore", "invidious": "Invidious", @@ -393,7 +393,7 @@ "preferences_quality_option_hd720": "HD720", "preferences_quality_dash_option_auto": "Automatica", "videoinfo_watch_on_youTube": "Guarda su YouTube", - "preferences_extend_desc_label": "Espandi automaticamente la descrizione del video: ", + "preferences_extend_desc_label": "Estendi automaticamente la descrizione del video: ", "preferences_vr_mode_label": "Video interattivi a 360 gradi: ", "Show less": "Mostra di meno", "Switch Invidious Instance": "Cambia istanza Invidious", @@ -425,5 +425,51 @@ "search_filters_type_option_show": "Serie", "search_filters_duration_option_short": "Corto (< 4 minuti)", "search_filters_duration_option_long": "Lungo (> 20 minuti)", - "search_filters_features_option_purchased": "Acquistato" + "search_filters_features_option_purchased": "Acquistato", + "comments_view_x_replies": "Vedi {{count}} risposta", + "comments_view_x_replies_plural": "Vedi {{count}} risposte", + "comments_points_count": "{{count}} punto", + "comments_points_count_plural": "{{count}} punti", + "Portuguese (auto-generated)": "Portoghese (auto-generato)", + "crash_page_you_found_a_bug": "Sembra che tu abbia trovato un bug in Invidious!", + "crash_page_switch_instance": "provato a usare un'altra istanza", + "crash_page_before_reporting": "Prima di segnalare un bug, assicurati di aver:", + "crash_page_read_the_faq": "letto le domande più frequenti (FAQ)", + "crash_page_search_issue": "cercato tra i problemi esistenti su GitHub", + "crash_page_report_issue": "Se niente di tutto ciò ha aiutato, per favore apri un nuovo problema su GitHub (preferibilmente in inglese) e includi il seguente testo nel tuo messaggio (NON tradurre il testo):", + "Popular enabled: ": "Popolare attivato: ", + "English (United Kingdom)": "Inglese (Regno Unito)", + "Portuguese (Brazil)": "Portoghese (Brasile)", + "preferences_watch_history_label": "Attiva cronologia di riproduzione: ", + "French (auto-generated)": "Francese (auto-generato)", + "search_message_use_another_instance": " Puoi anche cercare in un'altra istanza.", + "search_message_no_results": "Nessun risultato trovato.", + "search_message_change_filters_or_query": "Prova ad ampliare la ricerca e/o modificare i filtri.", + "English (United States)": "Inglese (Stati Uniti)", + "Cantonese (Hong Kong)": "Cantonese (Hong Kong)", + "Chinese": "Cinese", + "Chinese (China)": "Cinese (Cina)", + "Chinese (Hong Kong)": "Cinese (Hong Kong)", + "Chinese (Taiwan)": "Cinese (Taiwan)", + "Dutch (auto-generated)": "Olandese (auto-generato)", + "German (auto-generated)": "Tedesco (auto-generato)", + "Indonesian (auto-generated)": "Indonesiano (auto-generato)", + "Interlingue": "Interlingua", + "Italian (auto-generated)": "Italiano (auto-generato)", + "Japanese (auto-generated)": "Giapponese (auto-generato)", + "Korean (auto-generated)": "Coreano (auto-generato)", + "Russian (auto-generated)": "Russo (auto-generato)", + "Spanish (auto-generated)": "Spagnolo (auto-generato)", + "Spanish (Mexico)": "Spagnolo (Messico)", + "Spanish (Spain)": "Spagnolo (Spagna)", + "Turkish (auto-generated)": "Turco (auto-generato)", + "Vietnamese (auto-generated)": "Vietnamita (auto-generato)", + "search_filters_date_label": "Data caricamento", + "search_filters_date_option_none": "Qualunque data", + "search_filters_type_option_all": "Qualunque tipo", + "search_filters_duration_option_none": "Qualunque durata", + "search_filters_duration_option_medium": "Media (4 - 20 minuti)", + "search_filters_features_option_vr180": "VR180", + "search_filters_apply_button": "Applica filtri selezionati", + "crash_page_refresh": "provato a ricaricare la pagina" } From 57f60bf173b674374facedd52ea06aff209c0316 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:10 +0200 Subject: [PATCH 34/98] Update Ukrainian translation Co-authored-by: Hosted Weblate Co-authored-by: Ihor Hordiichuk --- locales/uk.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/uk.json b/locales/uk.json index 23f56c9a..0cc14579 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -486,5 +486,6 @@ "search_filters_features_option_purchased": "Придбано", "search_filters_sort_option_relevance": "Відповідні", "search_filters_sort_option_rating": "Рейтингові", - "search_filters_sort_option_views": "Популярні" + "search_filters_sort_option_views": "Популярні", + "Popular enabled: ": "Популярне ввімкнено: " } From 85927853f9520826bf1aa5e0fe2908fdc5722a3c Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:11 +0200 Subject: [PATCH 35/98] Update Chinese (Traditional) translation Co-authored-by: Hosted Weblate Co-authored-by: Jeff Huang --- locales/zh-TW.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 4b6fa71b..90614e48 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -454,5 +454,6 @@ "search_filters_title": "過濾條件", "search_filters_date_label": "上傳日期", "search_filters_type_option_all": "任何類型", - "search_filters_date_option_none": "任何日期" + "search_filters_date_option_none": "任何日期", + "Popular enabled: ": "已啟用人氣: " } From 168f86ef893c687212650b4ef6f8c470ec1d131e Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:11 +0200 Subject: [PATCH 36/98] Update Portuguese (Brazil) translation Co-authored-by: Hosted Weblate Co-authored-by: The Cats --- locales/pt-BR.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/pt-BR.json b/locales/pt-BR.json index df149564..9576d646 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -470,5 +470,6 @@ "Spanish (Spain)": "Espanhol (Espanha)", "Turkish (auto-generated)": "Turco (gerado automaticamente)", "search_filters_duration_option_medium": "Médio (4 - 20 minutos)", - "search_filters_features_option_vr180": "VR180" + "search_filters_features_option_vr180": "VR180", + "Popular enabled: ": "Popular habilitado: " } From 8752b8bb3fc58c36e333c8a1f64fce109dec963a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:11 +0200 Subject: [PATCH 37/98] Update Finnish translation Co-authored-by: Hosted Weblate Co-authored-by: Markus Mikkonen --- locales/fi.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/fi.json b/locales/fi.json index 2aa64ea7..cbb18825 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -470,5 +470,6 @@ "search_filters_duration_option_medium": "Keskipituinen (4 - 20 minuuttia)", "search_message_use_another_instance": " Voit myös hakea toisella instanssilla.", "search_filters_date_option_none": "Milloin tahansa", - "search_filters_type_option_all": "Mikä tahansa tyyppi" + "search_filters_type_option_all": "Mikä tahansa tyyppi", + "Popular enabled: ": "Suosittu käytössä: " } From 1ba0ab982bae9b68175709890e50952f6a20054c Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:11 +0200 Subject: [PATCH 38/98] Update Croatian translation Co-authored-by: Hosted Weblate Co-authored-by: Milo Ivir --- locales/hr.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/hr.json b/locales/hr.json index 7eb065dc..54eef7f9 100644 --- a/locales/hr.json +++ b/locales/hr.json @@ -107,7 +107,7 @@ "preferences_feed_menu_label": "Izbornik za feedove: ", "preferences_show_nick_label": "Prikaži nadimak na vrhu: ", "Top enabled: ": "Najbolji aktivirani: ", - "CAPTCHA enabled: ": "Aktivirani CAPTCHA: ", + "CAPTCHA enabled: ": "CAPTCHA aktiviran: ", "Login enabled: ": "Prijava aktivirana: ", "Registration enabled: ": "Registracija aktivirana: ", "Report statistics: ": "Izvještaj o statistici: ", @@ -486,5 +486,6 @@ "search_filters_duration_option_none": "Bilo koje duljine", "search_filters_duration_option_medium": "Srednje (4 – 20 minuta)", "search_filters_apply_button": "Primijeni odabrane filtre", - "search_filters_type_option_all": "Bilo koja vrsta" + "search_filters_type_option_all": "Bilo koja vrsta", + "Popular enabled: ": "Popularni aktivirani: " } From 68e65e968a01c1101c85b876e001374a240f089f Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:11 +0200 Subject: [PATCH 39/98] Update Portuguese translation Co-authored-by: Hosted Weblate Co-authored-by: SC --- locales/pt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/pt.json b/locales/pt.json index 1abe46fa..654cfdeb 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -470,5 +470,6 @@ "search_filters_date_label": "Data de publicação", "search_filters_date_option_none": "Qualquer data", "search_filters_type_option_all": "Qualquer tipo", - "search_filters_duration_option_none": "Qualquer duração" + "search_filters_duration_option_none": "Qualquer duração", + "Popular enabled: ": "Página \"popular\" ativada: " } From 66a08ace1df92df7c3e577d9244d32ac31a4e8ca Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:11 +0200 Subject: [PATCH 40/98] Update Slovenian translation Co-authored-by: Damjan Gerl Co-authored-by: Hosted Weblate --- locales/sl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/sl.json b/locales/sl.json index 9165e714..288f8da5 100644 --- a/locales/sl.json +++ b/locales/sl.json @@ -502,5 +502,6 @@ "crash_page_refresh": "poskušal/a osvežiti stran", "crash_page_before_reporting": "Preden prijaviš napako, se prepričaj, da si:", "crash_page_search_issue": "preiskal/a obstoječe težave na GitHubu", - "crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim odpri novo težavo v GitHubu (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):" + "crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim odpri novo težavo v GitHubu (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):", + "Popular enabled: ": "Priljubljeni omogočeni: " } From f460afca359f4929226a0668c5e958cf58a394fd Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:12 +0200 Subject: [PATCH 41/98] Update Chinese (Simplified) translation Co-authored-by: Eric Co-authored-by: Hosted Weblate --- locales/zh-CN.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/zh-CN.json b/locales/zh-CN.json index ed180628..ff48e101 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -454,5 +454,6 @@ "search_message_change_filters_or_query": "尝试扩大你的搜索查询和/或更改过滤器。", "search_filters_duration_option_none": "任意时长", "search_filters_type_option_all": "任意类型", - "search_filters_features_option_vr180": "VR180" + "search_filters_features_option_vr180": "VR180", + "Popular enabled: ": "已启用流行度: " } From 063e5e359eaae48b818860f372486aead2061586 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:12 +0200 Subject: [PATCH 42/98] Update Turkish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hosted Weblate Co-authored-by: Oğuz Ersen --- locales/tr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/tr.json b/locales/tr.json index b1991c35..bd499746 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -470,5 +470,6 @@ "search_filters_duration_option_medium": "Orta (4 - 20 dakika)", "search_filters_features_option_vr180": "VR180", "search_filters_title": "Filtreler", - "search_message_change_filters_or_query": "Arama sorgunuzu genişletmeyi ve/veya filtreleri değiştirmeyi deneyin." + "search_message_change_filters_or_query": "Arama sorgunuzu genişletmeyi ve/veya filtreleri değiştirmeyi deneyin.", + "Popular enabled: ": "Popüler etkin: " } From 65061b0514de38b3523ecc8d278efe45bf7581fe Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:12 +0200 Subject: [PATCH 43/98] Update Japanese translation Co-authored-by: Hosted Weblate Co-authored-by: uwu as a service --- locales/ja.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/locales/ja.json b/locales/ja.json index 20d3c20e..7918fe95 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -433,5 +433,10 @@ "Spanish (Spain)": "スペイン語 (スペイン)", "Vietnamese (auto-generated)": "ベトナム語 (自動生成)", "search_filters_title": "フィルタ", - "search_filters_features_option_three_sixty": "360°" + "search_filters_features_option_three_sixty": "360°", + "search_message_change_filters_or_query": "別のキーワードを試してみるか、検索フィルタを削除してください", + "search_message_no_results": "一致する検索結果はありませんでした", + "English (United States)": "英語 (アメリカ)", + "search_filters_date_label": "アップロード日", + "search_filters_features_option_vr180": "VR180" } From 0a315783ef0c6cac1805b368e8abd6380ed32044 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:12 +0200 Subject: [PATCH 44/98] Update Portuguese (Portugal) translation Update Portuguese (Portugal) translation Co-authored-by: Hosted Weblate Co-authored-by: Tmpod --- locales/pt-PT.json | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/locales/pt-PT.json b/locales/pt-PT.json index a57a2939..b00ebc72 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -21,15 +21,15 @@ "No": "Não", "Import and Export Data": "Importar e exportar dados", "Import": "Importar", - "Import Invidious data": "Importar dados do Invidious", - "Import YouTube subscriptions": "Importar subscrições do YouTube", + "Import Invidious data": "Importar dados JSON do Invidious", + "Import YouTube subscriptions": "Importar subscrições OPML ou do YouTube", "Import FreeTube subscriptions (.db)": "Importar subscrições do FreeTube (.db)", "Import NewPipe subscriptions (.json)": "Importar subscrições do NewPipe (.json)", "Import NewPipe data (.zip)": "Importar dados do NewPipe (.zip)", "Export": "Exportar", "Export subscriptions as OPML": "Exportar subscrições como OPML", "Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportar subscrições como OPML (para NewPipe e FreeTube)", - "Export data as JSON": "Exportar dados como JSON", + "Export data as JSON": "Exportar dados do Invidious como JSON", "Delete account?": "Eliminar conta?", "History": "Histórico", "An alternative front-end to YouTube": "Uma interface alternativa ao YouTube", @@ -60,13 +60,13 @@ "preferences_volume_label": "Volume da reprodução: ", "preferences_comments_label": "Preferência dos comentários: ", "youtube": "YouTube", - "reddit": "reddit", + "reddit": "Reddit", "preferences_captions_label": "Legendas predefinidas: ", "Fallback captions: ": "Legendas alternativas: ", "preferences_related_videos_label": "Mostrar vídeos relacionados: ", "preferences_annotations_label": "Mostrar anotações sempre: ", "preferences_extend_desc_label": "Estender automaticamente a descrição do vídeo: ", - "preferences_vr_mode_label": "Vídeos interativos de 360 graus: ", + "preferences_vr_mode_label": "Vídeos interativos de 360 graus (necessita de WebGL): ", "preferences_category_visual": "Preferências visuais", "preferences_player_style_label": "Estilo do reprodutor: ", "Dark mode: ": "Modo escuro: ", @@ -374,5 +374,39 @@ "next_steps_error_message": "Pode tentar as seguintes opções: ", "next_steps_error_message_refresh": "Atualizar", "next_steps_error_message_go_to_youtube": "Ir ao YouTube", - "search_filters_title": "Filtro" + "search_filters_title": "Filtro", + "generic_videos_count": "{{count}} vídeo", + "generic_videos_count_plural": "{{count}} vídeos", + "generic_playlists_count": "{{count}} lista de reprodução", + "generic_playlists_count_plural": "{{count}} listas de reprodução", + "generic_subscriptions_count": "{{count}} subscrição", + "generic_subscriptions_count_plural": "{{count}} subscrições", + "generic_views_count": "{{count}} visualização", + "generic_views_count_plural": "{{count}} visualizações", + "generic_subscribers_count": "{{count}} subscritor", + "generic_subscribers_count_plural": "{{count}} subscritores", + "preferences_quality_dash_option_4320p": "4320p", + "preferences_quality_dash_label": "Qualidade de vídeo DASH preferencial ", + "preferences_quality_dash_option_2160p": "2160p", + "subscriptions_unseen_notifs_count": "{{count}} notificação por ver", + "subscriptions_unseen_notifs_count_plural": "{{count}} notificações por ver", + "Popular enabled: ": "Página \"Popular\" ativada: ", + "search_message_no_results": "Nenhum resultado encontrado.", + "preferences_quality_dash_option_auto": "Automática", + "preferences_region_label": "País para o conteúdo: ", + "preferences_quality_dash_option_1440p": "1440p", + "preferences_quality_dash_option_720p": "720p", + "preferences_watch_history_label": "Ativar histórico de visualizações ", + "preferences_quality_dash_option_best": "Melhor", + "preferences_quality_dash_option_worst": "Pior", + "preferences_quality_dash_option_144p": "144p", + "invidious": "Invidious", + "preferences_quality_option_hd720": "HD720", + "preferences_quality_option_dash": "DASH (qualidade adaptativa)", + "preferences_quality_option_medium": "Média", + "preferences_quality_option_small": "Pequena", + "preferences_quality_dash_option_1080p": "1080p", + "preferences_quality_dash_option_480p": "480p", + "preferences_quality_dash_option_360p": "360p", + "preferences_quality_dash_option_240p": "240p" } From da776c935f93a805d43645c747dbe4774a4fc4ac Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:12 +0200 Subject: [PATCH 45/98] Update Indonesian translation Update Indonesian translation Co-authored-by: Hosted Weblate Co-authored-by: liimee Co-authored-by: uwu as a service --- locales/id.json | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/locales/id.json b/locales/id.json index c96495c3..d150cece 100644 --- a/locales/id.json +++ b/locales/id.json @@ -346,7 +346,7 @@ "Community": "Komunitas", "search_filters_sort_option_relevance": "Relevansi", "search_filters_sort_option_rating": "Penilaian", - "search_filters_sort_option_date": "Tanggal unggah", + "search_filters_sort_option_date": "Tanggal Unggah", "search_filters_sort_option_views": "Jumlah ditonton", "search_filters_type_label": "Tipe", "search_filters_duration_label": "Durasi", @@ -421,5 +421,31 @@ "search_filters_title": "Saring", "search_message_no_results": "Tidak ada hasil yang ditemukan.", "search_message_change_filters_or_query": "Coba perbanyak kueri pencarian dan/atau ubah filter Anda.", - "search_message_use_another_instance": " Anda juga bisa mencari di peladen lain." + "search_message_use_another_instance": " Anda juga bisa mencari di peladen lain.", + "Indonesian (auto-generated)": "Indonesia (dibuat secara otomatis)", + "Japanese (auto-generated)": "Jepang (dibuat secara otomatis)", + "Korean (auto-generated)": "Korea (dibuat secara otomatis)", + "Portuguese (Brazil)": "Portugis (Brasil)", + "Russian (auto-generated)": "Rusia (dibuat secara otomatis)", + "Spanish (Mexico)": "Spanyol (Meksiko)", + "Spanish (Spain)": "Spanyol (Spanyol)", + "Vietnamese (auto-generated)": "Vietnam (dibuat secara otomatis)", + "search_filters_features_option_vr180": "VR180", + "Spanish (auto-generated)": "Spanyol (dibuat secara otomatis)", + "Chinese": "Bahasa Cina", + "Chinese (Taiwan)": "Bahasa Cina (Taiwan)", + "Chinese (Hong Kong)": "Bahasa Cina (Hong Kong)", + "Chinese (China)": "Bahasa Cina (China)", + "French (auto-generated)": "Perancis (dibuat secara otomatis)", + "German (auto-generated)": "Jerman (dibuat secara otomatis)", + "Italian (auto-generated)": "Italia (dibuat secara otomatis)", + "Portuguese (auto-generated)": "Portugis (dibuat secara otomatis)", + "Turkish (auto-generated)": "Turki (dibuat secara otomatis)", + "search_filters_date_label": "Tanggal unggah", + "search_filters_type_option_all": "Segala jenis", + "search_filters_apply_button": "Terapkan saringan yang dipilih", + "Dutch (auto-generated)": "Belanda (dihasilkan secara otomatis)", + "search_filters_date_option_none": "Tanggal berapa pun", + "search_filters_duration_option_none": "Durasi berapa pun", + "search_filters_duration_option_medium": "Sedang (4 - 20 menit)" } From 5f23c6358abf0dab86b476c15f6495700d0d2b12 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 6 Jul 2022 23:25:13 +0200 Subject: [PATCH 46/98] Update Czech translation Update Czech translation Co-authored-by: Fjuro Co-authored-by: Hosted Weblate --- locales/cs.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/cs.json b/locales/cs.json index d590b5b8..97f108d7 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -88,7 +88,7 @@ "Only show latest unwatched video from channel: ": "Zobrazit jen nejnovější nezhlédnuté video z daného kanálu: ", "preferences_unseen_only_label": "Zobrazit jen již nezhlédnuté: ", "preferences_notifications_only_label": "Zobrazit pouze upozornění (pokud nějaká jsou): ", - "Enable web notifications": "Povolit webové upozornění", + "Enable web notifications": "Povolit webová upozornění", "`x` uploaded a video": "`x` nahrál(a) video", "`x` is live": "`x` je živě", "preferences_category_data": "Nastavení dat", @@ -486,5 +486,6 @@ "search_filters_features_option_purchased": "Zakoupeno", "search_filters_sort_label": "Řadit dle", "search_filters_sort_option_relevance": "Relevantnost", - "search_filters_apply_button": "Použít vybrané filtry" + "search_filters_apply_button": "Použít vybrané filtry", + "Popular enabled: ": "Populární povoleno: " } From b19beac5b40bd1efbef1882b2160252ebf9a3134 Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sun, 10 Jul 2022 16:29:50 +0800 Subject: [PATCH 47/98] Update src/invidious/views/components/player.ecr better syntax Co-authored-by: Samantaz Fox --- src/invidious/views/components/player.ecr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 9f42ae77..c3c02df0 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -25,7 +25,7 @@ bitrate = fmt["bitrate"] mimetype = HTML.escape(fmt["mimeType"].as_s) - selected = i == best_m4a_stream_index ? true : false + selected = (i == best_m4a_stream_index) %> <% if !params.local && !CONFIG.disabled?("local") %> From cbcf31a4f98706ea675cafb7509b37dc2b0ceace Mon Sep 17 00:00:00 2001 From: 138138138 <78271024+138138138@users.noreply.github.com> Date: Sun, 10 Jul 2022 16:54:56 +0800 Subject: [PATCH 48/98] Skip OTF streams in DASH audio Skip OTF streams, prevent creating empty AdaptationSet in DASH audio --- src/invidious/routes/api/manifest.cr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index ca72be26..52b94175 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -61,10 +61,10 @@ module Invidious::Routes::API::Manifest next if mime_streams.empty? mime_streams.each do |fmt| - xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: fmt["bitrate"].to_s + "k") do - # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) - next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) + # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) + next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) + xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: fmt["bitrate"].to_s + "k") do codecs = fmt["mimeType"].as_s.split("codecs=")[1].strip('"') bandwidth = fmt["bitrate"].as_i itag = fmt["itag"].as_i From 69ad57338f38662fb0a4d22aa58bec8dc7a5742c Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 11 Jul 2022 17:24:03 +0200 Subject: [PATCH 49/98] Mention why we use multiple AdaptationSet for audio --- src/invidious/routes/api/manifest.cr | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr index 52b94175..a857d18f 100644 --- a/src/invidious/routes/api/manifest.cr +++ b/src/invidious/routes/api/manifest.cr @@ -64,6 +64,10 @@ module Invidious::Routes::API::Manifest # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415) next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange")) + # Different representations of the same audio should be groupped into one AdaptationSet. + # However, most players don't support auto quality switching, so we have to trick them + # into providing a quality selector. + # See https://github.com/iv-org/invidious/issues/3074 for more details. xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, label: fmt["bitrate"].to_s + "k") do codecs = fmt["mimeType"].as_s.split("codecs=")[1].strip('"') bandwidth = fmt["bitrate"].as_i From 586000ca3d006959d23b8c78eafc55b1143f0aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Tue, 12 Jul 2022 08:38:22 +0000 Subject: [PATCH 50/98] add more explanation about checking the player dependencies --- src/invidious.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 4952b365..070b4d18 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -133,12 +133,13 @@ Invidious::Database.check_integrity(CONFIG) # Running the script by itself would show some colorful feedback while this doesn't. # Perhaps we should just move the script to runtime in order to get that feedback? - {% puts "\nChecking player dependencies...\n" %} + {% puts "\nChecking player dependencies, this may take more than 20 minutes... If it is stuck, check your internet connection.\n" %} {% if flag?(:minified_player_dependencies) %} {% puts run("../scripts/fetch-player-dependencies.cr", "--minified").stringify %} {% else %} {% puts run("../scripts/fetch-player-dependencies.cr").stringify %} {% end %} + {% puts "\nDone checking player dependencies, now compiling Invidious...\n" %} {% end %} # Start jobs From 6577cc0c8c08e563065ee11ff39c1aad76f16878 Mon Sep 17 00:00:00 2001 From: PrivateGER Date: Wed, 13 Jul 2022 21:55:06 +0200 Subject: [PATCH 51/98] Fix a dead link to Docker install documentation (#3198) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index fa14a8e8..eb83b020 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ # Using it will build an image from the locally cloned repository. # # If you want to use Invidious in production, see the docker-compose.yml file provided -# in the installation documentation: https://docs.invidious.io/Installation.md +# in the installation documentation: https://docs.invidious.io/installation/ version: "3" services: From 0338b26d5c7f9bc4442db3ef99e5490c282db250 Mon Sep 17 00:00:00 2001 From: AHOHNMYC <24810600+AHOHNMYC@users.noreply.github.com> Date: Thu, 14 Jul 2022 02:07:19 +0300 Subject: [PATCH 52/98] Include `_helpers.js` in embedded view --- src/invidious/views/embed.ecr | 1 + 1 file changed, 1 insertion(+) diff --git a/src/invidious/views/embed.ecr b/src/invidious/views/embed.ecr index ce5ff7f0..1bf5cc3e 100644 --- a/src/invidious/views/embed.ecr +++ b/src/invidious/views/embed.ecr @@ -11,6 +11,7 @@ <%= HTML.escape(video.title) %> - Invidious + From c8765385df16fff90cff82e1ed1e2056a4bc0ac3 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Thu, 14 Jul 2022 17:56:53 +0200 Subject: [PATCH 53/98] Fetch data from next endpoint for scheduled streams --- src/invidious/videos.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 19ee064c..50bb80c1 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -914,7 +914,7 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ params["shortDescription"] = player_response.dig?("videoDetails", "shortDescription") || JSON::Any.new(nil) # Don't fetch the next endpoint if the video is unavailable. - if !params["reason"]? + if {"OK", "LIVE_STREAM_OFFLINE"}.any?(playability_status) next_response = YoutubeAPI.next({"videoId": video_id, "params": ""}) player_response = player_response.merge(next_response) end From 6c4ed282bb8e2a6ed0c756ea012f6b1fa8e6cc48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Thu, 14 Jul 2022 21:26:58 +0000 Subject: [PATCH 54/98] HTML escape username --- src/invidious/views/template.ecr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index 4e2b29f0..caf5299f 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -68,7 +68,7 @@

<% if env.get("preferences").as(Preferences).show_nick %>
- <%= env.get("user").as(Invidious::User).email %> + <%= HTML.escape(env.get("user").as(Invidious::User).email) %>
<% end %>
From 049ed114fd2d7c3debf6277935d6dbf5aca6777a Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 18 Jul 2022 23:35:34 +0200 Subject: [PATCH 55/98] Separate video data fetching from parsing in videos.cr --- src/invidious/videos.cr | 78 +++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 50bb80c1..f87c6b47 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -886,13 +886,13 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)? end def extract_video_info(video_id : String, proxy_region : String? = nil, context_screen : String? = nil) - params = {} of String => JSON::Any - + # Init client config for the API client_config = YoutubeAPI::ClientConfig.new(proxy_region: proxy_region) if context_screen == "embed" client_config.client_type = YoutubeAPI::ClientType::TvHtml5ScreenEmbed end + # Fetch data from the player endpoint player_response = YoutubeAPI.player(video_id: video_id, params: "", client_config: client_config) playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s @@ -903,26 +903,29 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("") reason ||= player_response.dig("playabilityStatus", "reason").as_s - params["reason"] = JSON::Any.new(reason) - # Stop here if video is not a scheduled livestream if playability_status != "LIVE_STREAM_OFFLINE" - return params + return { + "reason" => JSON::Any.new(reason), + } end + else + reason = nil end - params["shortDescription"] = player_response.dig?("videoDetails", "shortDescription") || JSON::Any.new(nil) - # Don't fetch the next endpoint if the video is unavailable. if {"OK", "LIVE_STREAM_OFFLINE"}.any?(playability_status) next_response = YoutubeAPI.next({"videoId": video_id, "params": ""}) player_response = player_response.merge(next_response) end + params = parse_video_info(video_id, player_response) + params["reason"] = JSON::Any.new(reason) if reason + # Fetch the video streams using an Android client in order to get the decrypted URLs and # maybe fix throttling issues (#2194).See for the explanation about the decrypted URLs: # https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 - if !params["reason"]? + if reason.nil? if context_screen == "embed" client_config.client_type = YoutubeAPI::ClientType::AndroidScreenEmbed else @@ -940,10 +943,15 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ end end + # TODO: clean that up {"captions", "microformat", "playabilityStatus", "storyboards", "videoDetails"}.each do |f| params[f] = player_response[f] if player_response[f]? end + return params +end + +def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any)) : Hash(String, JSON::Any) # Top level elements main_results = player_response.dig?("contents", "twoColumnWatchNextResults") @@ -997,8 +1005,6 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ end end - params["relatedVideos"] = JSON::Any.new(related) - # Likes toplevel_buttons = video_primary_renderer @@ -1019,42 +1025,36 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ end end - params["likes"] = JSON::Any.new(likes || 0_i64) - params["dislikes"] = JSON::Any.new(0_i64) - # Description + short_description = player_response.dig?("videoDetails", "shortDescription") + description_html = video_secondary_renderer.try &.dig?("description", "runs") .try &.as_a.try { |t| content_to_comment_html(t, video_id) } - params["descriptionHtml"] = JSON::Any.new(description_html || "

") - # Video metadata metadata = video_secondary_renderer .try &.dig?("metadataRowContainer", "metadataRowContainerRenderer", "rows") .try &.as_a - params["genre"] = params["microformat"]?.try &.["playerMicroformatRenderer"]?.try &.["category"]? || JSON::Any.new("") - params["genreUrl"] = JSON::Any.new(nil) + genre = player_response.dig?("microformat", "playerMicroformatRenderer", "category") + genre_ucid = nil + license = nil metadata.try &.each do |row| - title = row["metadataRowRenderer"]?.try &.["title"]?.try &.["simpleText"]?.try &.as_s + metadata_title = row.dig?("metadataRowRenderer", "title", "simpleText").try &.as_s contents = row.dig?("metadataRowRenderer", "contents", 0) - if title.try &.== "Category" + if metadata_title == "Category" contents = contents.try &.dig?("runs", 0) - params["genre"] = JSON::Any.new(contents.try &.["text"]?.try &.as_s || "") - params["genreUcid"] = JSON::Any.new(contents.try &.["navigationEndpoint"]?.try &.["browseEndpoint"]? - .try &.["browseId"]?.try &.as_s || "") - elsif title.try &.== "License" - contents = contents.try &.["runs"]? - .try &.as_a[0]? - - params["license"] = JSON::Any.new(contents.try &.["text"]?.try &.as_s || "") - elsif title.try &.== "Licensed to YouTube by" - params["license"] = JSON::Any.new(contents.try &.["simpleText"]?.try &.as_s || "") + genre = contents.try &.["text"]? + genre_ucid = contents.try &.dig?("navigationEndpoint", "browseEndpoint", "browseId") + elsif metadata_title == "License" + license = contents.try &.dig?("runs", 0, "text") + elsif metadata_title == "Licensed to YouTube by" + license = contents.try &.["simpleText"]? end end @@ -1062,20 +1062,30 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_ if author_info = video_secondary_renderer.try &.dig?("owner", "videoOwnerRenderer") author_thumbnail = author_info.dig?("thumbnail", "thumbnails", 0, "url") - params["authorThumbnail"] = JSON::Any.new(author_thumbnail.try &.as_s || "") - author_verified = has_verified_badge?(author_info["badges"]?) - params["authorVerified"] = JSON::Any.new(author_verified) subs_text = author_info["subscriberCountText"]? .try { |t| t["simpleText"]? || t.dig?("runs", 0, "text") } .try &.as_s.split(" ", 2)[0] - - params["subCountText"] = JSON::Any.new(subs_text || "-") end # Return data + params = { + "shortDescription" => JSON::Any.new(short_description.try &.as_s || nil), + "relatedVideos" => JSON::Any.new(related), + "likes" => JSON::Any.new(likes || 0_i64), + "dislikes" => JSON::Any.new(0_i64), + "descriptionHtml" => JSON::Any.new(description_html || "

"), + "genre" => JSON::Any.new(genre.try &.as_s || ""), + "genreUrl" => JSON::Any.new(nil), + "genreUcid" => JSON::Any.new(genre_ucid.try &.as_s || ""), + "license" => JSON::Any.new(license.try &.as_s || ""), + "authorThumbnail" => JSON::Any.new(author_thumbnail.try &.as_s || ""), + "authorVerified" => JSON::Any.new(author_verified), + "subCountText" => JSON::Any.new(subs_text || "-"), + } + return params end From 5e090778aef347e20e118e54073b5b6eb5035ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Tue, 19 Jul 2022 09:12:50 +0200 Subject: [PATCH 56/98] Use alpine 3.16 for crystal 1.4.1 Until crystal 1.5 has been tested. --- docker/Dockerfile.arm64 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile.arm64 b/docker/Dockerfile.arm64 index a703e870..35d3fa7b 100644 --- a/docker/Dockerfile.arm64 +++ b/docker/Dockerfile.arm64 @@ -1,5 +1,5 @@ -FROM alpine:edge AS builder -RUN apk add --no-cache 'crystal=1.4.1-r1' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev +FROM alpine:3.16 AS builder +RUN apk add --no-cache 'crystal=1.4.1-r0' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev ARG release @@ -34,7 +34,7 @@ RUN if [ ${release} == 1 ] ; then \ --link-flags "-lxml2 -llzma"; \ fi -FROM alpine:edge +FROM alpine:3.16 RUN apk add --no-cache librsvg ttf-opensans WORKDIR /invidious RUN addgroup -g 1000 -S invidious && \ From 7e648840a1215ddeb8b110eb867893826b73384c Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 19 Jul 2022 21:05:49 +0200 Subject: [PATCH 57/98] Move InfoException to exceptions.cr --- src/invidious/exceptions.cr | 8 ++++++++ src/invidious/helpers/errors.cr | 8 -------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/invidious/exceptions.cr b/src/invidious/exceptions.cr index 471a199a..05be73a6 100644 --- a/src/invidious/exceptions.cr +++ b/src/invidious/exceptions.cr @@ -1,3 +1,11 @@ +# InfoExceptions are for displaying information to the user. +# +# An InfoException might or might not indicate that something went wrong. +# Historically Invidious didn't differentiate between these two options, so to +# maintain previous functionality InfoExceptions do not print backtraces. +class InfoException < Exception +end + # Exception used to hold the bogus UCID during a channel search. class ChannelSearchException < InfoException getter channel : String diff --git a/src/invidious/helpers/errors.cr b/src/invidious/helpers/errors.cr index b80dcdaf..6e5a975d 100644 --- a/src/invidious/helpers/errors.cr +++ b/src/invidious/helpers/errors.cr @@ -1,11 +1,3 @@ -# InfoExceptions are for displaying information to the user. -# -# An InfoException might or might not indicate that something went wrong. -# Historically Invidious didn't differentiate between these two options, so to -# maintain previous functionality InfoExceptions do not print backtraces. -class InfoException < Exception -end - # ------------------- # Issue template # ------------------- From 0ed4f1a9a4a51797805046d627a16df172405ecc Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Thu, 21 Jul 2022 00:33:39 +0200 Subject: [PATCH 58/98] Add unit tests for scheduled livestreams --- mocks | 2 +- .../videos/scheduled_live_extract_spec.cr | 113 ++++++++++++++++++ spec/parsers_helper.cr | 1 + 3 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 spec/invidious/videos/scheduled_live_extract_spec.cr diff --git a/mocks b/mocks index 02033719..c401dd92 160000 --- a/mocks +++ b/mocks @@ -1 +1 @@ -Subproject commit 020337194dd482c47ee2d53cd111d0ebf2831e52 +Subproject commit c401dd9203434b561022242c24b0c200d72284c0 diff --git a/spec/invidious/videos/scheduled_live_extract_spec.cr b/spec/invidious/videos/scheduled_live_extract_spec.cr new file mode 100644 index 00000000..6e531bbd --- /dev/null +++ b/spec/invidious/videos/scheduled_live_extract_spec.cr @@ -0,0 +1,113 @@ +require "../../parsers_helper.cr" + +Spectator.describe Invidious::Hashtag do + it "parses scheduled livestreams data (test 1)" do + # Enable mock + _player = load_mock("video/scheduled_live_nintendo.player") + _next = load_mock("video/scheduled_live_nintendo.next") + + raw_data = _player.merge!(_next) + info = parse_video_info("QMGibBzTu0g", raw_data) + + # Some basic verifications + expect(typeof(info)).to eq(Hash(String, JSON::Any)) + + expect(info["shortDescription"].as_s).to eq( + "Tune in on 6/22 at 7 a.m. PT for a livestreamed Xenoblade Chronicles 3 Direct presentation featuring roughly 20 minutes of information about the upcoming RPG adventure for Nintendo Switch." + ) + expect(info["descriptionHtml"].as_s).to eq( + "Tune in on 6/22 at 7 a.m. PT for a livestreamed Xenoblade Chronicles 3 Direct presentation featuring roughly 20 minutes of information about the upcoming RPG adventure for Nintendo Switch." + ) + + expect(info["likes"].as_i).to eq(2_283) + + expect(info["genre"].as_s).to eq("Gaming") + expect(info["genreUrl"].raw).to be_nil + expect(info["genreUcid"].as_s).to be_empty + expect(info["license"].as_s).to be_empty + + expect(info["authorThumbnail"].as_s).to eq( + "https://yt3.ggpht.com/ytc/AKedOLTt4vtjREUUNdHlyu9c4gtJjG90M9jQheRlLKy44A=s48-c-k-c0x00ffffff-no-rj" + ) + + expect(info["authorVerified"].as_bool).to be_true + expect(info["subCountText"].as_s).to eq("8.5M") + + expect(info["relatedVideos"].as_a.size).to eq(20) + + # related video #1 + expect(info["relatedVideos"][3]["id"].as_s).to eq("a-SN3lLIUEo") + expect(info["relatedVideos"][3]["author"].as_s).to eq("Nintendo") + expect(info["relatedVideos"][3]["ucid"].as_s).to eq("UCGIY_O-8vW4rfX98KlMkvRg") + expect(info["relatedVideos"][3]["view_count"].as_s).to eq("147796") + expect(info["relatedVideos"][3]["short_view_count"].as_s).to eq("147K") + expect(info["relatedVideos"][3]["author_verified"].as_s).to eq("true") + + # Related video #2 + expect(info["relatedVideos"][16]["id"].as_s).to eq("l_uC1jFK0lo") + expect(info["relatedVideos"][16]["author"].as_s).to eq("Nintendo") + expect(info["relatedVideos"][16]["ucid"].as_s).to eq("UCGIY_O-8vW4rfX98KlMkvRg") + expect(info["relatedVideos"][16]["view_count"].as_s).to eq("53510") + expect(info["relatedVideos"][16]["short_view_count"].as_s).to eq("53K") + expect(info["relatedVideos"][16]["author_verified"].as_s).to eq("true") + end + + it "parses scheduled livestreams data (test 2)" do + # Enable mock + _player = load_mock("video/scheduled_live_PBD-Podcast.player") + _next = load_mock("video/scheduled_live_PBD-Podcast.next") + + raw_data = _player.merge!(_next) + info = parse_video_info("RG0cjYbXxME", raw_data) + + # Some basic verifications + expect(typeof(info)).to eq(Hash(String, JSON::Any)) + + expect(info["shortDescription"].as_s).to start_with( + <<-TXT + PBD Podcast Episode 171. In this episode, Patrick Bet-David is joined by Dr. Patrick Moore and Adam Sosnick. + + Join the channel to get exclusive access to perks: https://bit.ly/3Q9rSQL + TXT + ) + expect(info["descriptionHtml"].as_s).to start_with( + <<-TXT + PBD Podcast Episode 171. In this episode, Patrick Bet-David is joined by Dr. Patrick Moore and Adam Sosnick. + + Join the channel to get exclusive access to perks: bit.ly/3Q9rSQL + TXT + ) + + expect(info["likes"].as_i).to eq(22) + + expect(info["genre"].as_s).to eq("Entertainment") + expect(info["genreUrl"].raw).to be_nil + expect(info["genreUcid"].as_s).to be_empty + expect(info["license"].as_s).to be_empty + + expect(info["authorThumbnail"].as_s).to eq( + "https://yt3.ggpht.com/61ArDiQshJrvSXcGLhpFfIO3hlMabe2fksitcf6oGob0Mdr5gztdkXxRljICUodL4iuTSrtxW4A=s48-c-k-c0x00ffffff-no-rj" + ) + + expect(info["authorVerified"].as_bool).to be_false + expect(info["subCountText"].as_s).to eq("227K") + + expect(info["relatedVideos"].as_a.size).to eq(20) + + # related video #1 + expect(info["relatedVideos"][2]["id"]).to eq("La9oLLoI5Rc") + expect(info["relatedVideos"][2]["author"]).to eq("Tom Bilyeu") + expect(info["relatedVideos"][2]["ucid"]).to eq("UCnYMOamNKLGVlJgRUbamveA") + expect(info["relatedVideos"][2]["view_count"]).to eq("13329149") + expect(info["relatedVideos"][2]["short_view_count"]).to eq("13M") + expect(info["relatedVideos"][2]["author_verified"]).to eq("true") + + # Related video #2 + expect(info["relatedVideos"][9]["id"]).to eq("IQ_4fvpzYuA") + expect(info["relatedVideos"][9]["author"]).to eq("Business Today") + expect(info["relatedVideos"][9]["ucid"]).to eq("UCaPHWiExfUWaKsUtENLCv5w") + expect(info["relatedVideos"][9]["view_count"]).to eq("26432") + expect(info["relatedVideos"][9]["short_view_count"]).to eq("26K") + expect(info["relatedVideos"][9]["author_verified"]).to eq("true") + end +end diff --git a/spec/parsers_helper.cr b/spec/parsers_helper.cr index 6155fe33..e9154875 100644 --- a/spec/parsers_helper.cr +++ b/spec/parsers_helper.cr @@ -6,6 +6,7 @@ require "protodec/utils" require "spectator" +require "../src/invidious/exceptions" require "../src/invidious/helpers/macros" require "../src/invidious/helpers/logger" require "../src/invidious/helpers/utils" From 210c2a88550c6b8a11e22a7a718b7cf078cfe606 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Mon, 25 Jul 2022 12:38:17 +0000 Subject: [PATCH 59/98] Fix updated sources not returned inside map func This fix the issue reported in https://github.com/iv-org/invidious/issues/2055#issuecomment-1192894698 --- assets/js/player.js | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/player.js b/assets/js/player.js index 287b7ea1..b75e7134 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -68,6 +68,7 @@ player.on('error', function () { // add local=true to all current sources player.src(player.currentSources().map(function (source) { source.src += '&local=true'; + return source; })); } else if (reloadMakesSense) { setTimeout(function () { From 644ba469451f2348219f99684e44e42a276bfd46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Sat, 6 Aug 2022 10:09:45 +0000 Subject: [PATCH 60/98] Remove mentions that decrypt_polling is broken And add notice about bandwidth usage, related to https://github.com/iv-org/invidious/issues/3234 --- config/config.example.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index ae9509d2..3e8faf20 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -349,8 +349,8 @@ feed_threads: 1 ## Enable/Disable the polling job that keeps the decryption ## function (for "secured" videos) up to date. ## -## Note: This part of the code is currently broken, so changing -## this setting has no impact. +## Note: This part of the code generate a small amount of data every minute. +## This may not be desired if you have bandwidth limits set by your ISP. ## ## Accepted values: true, false ## Default: true From 0c64a86ebec8844a3aadbe44265776767d810aae Mon Sep 17 00:00:00 2001 From: Emilien Devos Date: Sat, 6 Aug 2022 15:12:45 +0200 Subject: [PATCH 61/98] crystal 1.5.0 to CI and update crystal version --- .github/workflows/ci.yml | 1 + .github/workflows/container-release.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc80c75c..7e10be8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,7 @@ jobs: - 1.2.2 - 1.3.2 - 1.4.0 + - 1.5.0 include: - crystal: nightly stable: false diff --git a/.github/workflows/container-release.yml b/.github/workflows/container-release.yml index 212487c8..7e427e6e 100644 --- a/.github/workflows/container-release.yml +++ b/.github/workflows/container-release.yml @@ -27,7 +27,7 @@ jobs: - name: Install Crystal uses: crystal-lang/install-crystal@v1.6.0 with: - crystal: 1.2.2 + crystal: 1.5.0 - name: Run lint run: | From 5df700a56e93e777666817b43765bb63f311ea5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Jab=C5=82on=CC=81ski?= Date: Sat, 6 Aug 2022 17:14:17 +0200 Subject: [PATCH 62/98] Added image tag to RSS channel for favicon rendering https://validator.w3.org/feed/docs/rss2.html#ltimagegtSubelementOfLtchannelgt --- src/invidious/routes/feeds.cr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index 44a87175..b601db94 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -204,6 +204,12 @@ module Invidious::Routes::Feeds xml.element("uri") { xml.text "#{HOST_URL}/channel/#{channel.ucid}" } end + xml.element("image") do + xml.element("url") { xml.text channel.author_thumbnail } + xml.element("title") { xml.text channel.author } + xml.element("link", rel: "self", href: "#{HOST_URL}#{env.request.resource}") + end + videos.each do |video| video.to_xml(channel.auto_generated, params, xml) end From b55c1a35bf8af2626f9eccdb371e54a9c2c771a2 Mon Sep 17 00:00:00 2001 From: Emilien Devos Date: Sat, 6 Aug 2022 19:01:57 +0200 Subject: [PATCH 63/98] Set cookies to Lax --- src/invidious/user/cookies.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/user/cookies.cr b/src/invidious/user/cookies.cr index 65e079ec..654efc15 100644 --- a/src/invidious/user/cookies.cr +++ b/src/invidious/user/cookies.cr @@ -18,7 +18,7 @@ struct Invidious::User expires: Time.utc + 2.years, secure: SECURE, http_only: true, - samesite: HTTP::Cookie::SameSite::Strict + samesite: HTTP::Cookie::SameSite::Lax ) end @@ -32,7 +32,7 @@ struct Invidious::User expires: Time.utc + 2.years, secure: SECURE, http_only: false, - samesite: HTTP::Cookie::SameSite::Strict + samesite: HTTP::Cookie::SameSite::Lax ) end end From 3d77642a1e2a94c1314a59b60279157ae4f49b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89milien=20Devos?= Date: Sat, 6 Aug 2022 19:09:10 +0200 Subject: [PATCH 64/98] Disable decrypt_polling by default + add comment (#3244) --- config/config.example.yml | 9 ++++++--- src/invidious/config.cr | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 3e8faf20..10734c3a 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -352,10 +352,13 @@ feed_threads: 1 ## Note: This part of the code generate a small amount of data every minute. ## This may not be desired if you have bandwidth limits set by your ISP. ## -## Accepted values: true, false -## Default: true +## Note 2: This part of the code is currently broken, so changing +## this setting has no impact. ## -#decrypt_polling: true +## Accepted values: true, false +## Default: false +## +#decrypt_polling: false # ----------------------------- diff --git a/src/invidious/config.cr b/src/invidious/config.cr index a077c7fd..786b65df 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -75,7 +75,7 @@ class Config @[YAML::Field(converter: Preferences::URIConverter)] property database_url : URI = URI.parse("") # Use polling to keep decryption function up to date - property decrypt_polling : Bool = true + property decrypt_polling : Bool = false # Used for crawling channels: threads should check all videos uploaded by a channel property full_refresh : Bool = false # Used to tell Invidious it is behind a proxy, so links to resources should be https:// From fc97929dee4f57ac634d9c2dcd5aa77d5c3f70e3 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 6 Aug 2022 23:28:19 +0200 Subject: [PATCH 65/98] Bump android app version --- src/invidious/yt_backend/youtube_api.cr | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index 2678ac6c..d2073f73 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -5,6 +5,8 @@ module YoutubeAPI extend self + private ANDROID_APP_VERSION = "17.29.35" + # Enumerate used to select one of the clients supported by the API enum ClientType Web @@ -45,19 +47,19 @@ module YoutubeAPI }, ClientType::Android => { name: "ANDROID", - version: "16.20", + version: ANDROID_APP_VERSION, api_key: "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w", screen: "", # ?? }, ClientType::AndroidEmbeddedPlayer => { name: "ANDROID_EMBEDDED_PLAYER", # 55 - version: "16.20", + version: ANDROID_APP_VERSION, api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", screen: "", # None? }, ClientType::AndroidScreenEmbed => { name: "ANDROID", # 3 - version: "16.20", + version: ANDROID_APP_VERSION, api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", screen: "EMBED", }, From f353589a5343448941eb3a7231c14fbff6cc00bf Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 6 Aug 2022 23:47:16 +0200 Subject: [PATCH 66/98] Bump web clients versions --- src/invidious/yt_backend/youtube_api.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index d2073f73..31be285a 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -23,25 +23,25 @@ module YoutubeAPI HARDCODED_CLIENTS = { ClientType::Web => { name: "WEB", - version: "2.20210721.00.00", + version: "2.20220804.07.00", api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", screen: "WATCH_FULL_SCREEN", }, ClientType::WebEmbeddedPlayer => { name: "WEB_EMBEDDED_PLAYER", # 56 - version: "1.20210721.1.0", + version: "1.20220803.01.00", api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", screen: "EMBED", }, ClientType::WebMobile => { name: "MWEB", - version: "2.20210726.08.00", + version: "2.20220805.01.00", api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", screen: "", # None }, ClientType::WebScreenEmbed => { name: "WEB", - version: "2.20210721.00.00", + version: "2.20220804.00.00", api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", screen: "EMBED", }, From 9e7c2dcdbb9c7af4ae1e91c3322deda6615b8fcf Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 6 Aug 2022 23:49:36 +0200 Subject: [PATCH 67/98] Move the default API key to a constant for clarity --- src/invidious/yt_backend/youtube_api.cr | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index 31be285a..b5b01286 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -5,6 +5,8 @@ module YoutubeAPI extend self + private DEFAULT_API_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" + private ANDROID_APP_VERSION = "17.29.35" # Enumerate used to select one of the clients supported by the API @@ -24,25 +26,25 @@ module YoutubeAPI ClientType::Web => { name: "WEB", version: "2.20220804.07.00", - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "WATCH_FULL_SCREEN", }, ClientType::WebEmbeddedPlayer => { name: "WEB_EMBEDDED_PLAYER", # 56 version: "1.20220803.01.00", - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "EMBED", }, ClientType::WebMobile => { name: "MWEB", version: "2.20220805.01.00", - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "", # None }, ClientType::WebScreenEmbed => { name: "WEB", version: "2.20220804.00.00", - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "EMBED", }, ClientType::Android => { @@ -54,19 +56,19 @@ module YoutubeAPI ClientType::AndroidEmbeddedPlayer => { name: "ANDROID_EMBEDDED_PLAYER", # 55 version: ANDROID_APP_VERSION, - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "", # None? }, ClientType::AndroidScreenEmbed => { name: "ANDROID", # 3 version: ANDROID_APP_VERSION, - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "EMBED", }, ClientType::TvHtml5ScreenEmbed => { name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER", version: "2.0", - api_key: "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", + api_key: DEFAULT_API_KEY, screen: "EMBED", }, } From 349d90b60e3cece2a125669688e978932d8d9795 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sun, 7 Aug 2022 00:24:35 +0200 Subject: [PATCH 68/98] Add IOS clients --- src/invidious/yt_backend/youtube_api.cr | 33 ++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index b5b01286..c66b155e 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -8,6 +8,7 @@ module YoutubeAPI private DEFAULT_API_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" private ANDROID_APP_VERSION = "17.29.35" + private IOS_APP_VERSION = "17.30.1" # Enumerate used to select one of the clients supported by the API enum ClientType @@ -15,9 +16,15 @@ module YoutubeAPI WebEmbeddedPlayer WebMobile WebScreenEmbed + Android AndroidEmbeddedPlayer AndroidScreenEmbed + + IOS + IOSEmbedded + IOSMusic + TvHtml5ScreenEmbed end @@ -47,6 +54,9 @@ module YoutubeAPI api_key: DEFAULT_API_KEY, screen: "EMBED", }, + + # Android + ClientType::Android => { name: "ANDROID", version: ANDROID_APP_VERSION, @@ -65,6 +75,27 @@ module YoutubeAPI api_key: DEFAULT_API_KEY, screen: "EMBED", }, + + # IOS + + ClientType::IOS => { + name: "IOS", # 5 + version: IOS_APP_VERSION, + api_key: "AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc", + }, + ClientType::IOSEmbedded => { + name: "IOS_MESSAGES_EXTENSION", # 66 + version: IOS_APP_VERSION, + api_key: DEFAULT_API_KEY, + }, + ClientType::IOSMusic => { + name: "IOS_MUSIC", # 26 + version: "4.32", + api_key: "AIzaSyBAETezhkwP0ZWA02RsqT1zu78Fpt0bC_s", + }, + + # TV app + ClientType::TvHtml5ScreenEmbed => { name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER", version: "2.0", @@ -135,7 +166,7 @@ module YoutubeAPI # :ditto: def screen : String - HARDCODED_CLIENTS[@client_type][:screen] + HARDCODED_CLIENTS[@client_type][:screen]? || "" end # Convert to string, for logging purposes From 618ab01cd75fe43ff8c47fc2454e12eedd41b56e Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sun, 7 Aug 2022 00:36:22 +0200 Subject: [PATCH 69/98] Add TVHtml5 client --- src/invidious/yt_backend/youtube_api.cr | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index c66b155e..c8e61539 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -25,6 +25,7 @@ module YoutubeAPI IOSEmbedded IOSMusic + TvHtml5 TvHtml5ScreenEmbed end @@ -96,8 +97,13 @@ module YoutubeAPI # TV app + ClientType::TvHtml5 => { + name: "TVHTML5", # 7 + version: "7.20220325", + api_key: DEFAULT_API_KEY, + }, ClientType::TvHtml5ScreenEmbed => { - name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER", + name: "TVHTML5_SIMPLY_EMBEDDED_PLAYER", # 85 version: "2.0", api_key: DEFAULT_API_KEY, screen: "EMBED", From 23855c09dc2988947d7ee63ab4c3f8590660884b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sun, 7 Aug 2022 00:37:09 +0200 Subject: [PATCH 70/98] Remove 'screen' where not required --- src/invidious/yt_backend/youtube_api.cr | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index c8e61539..2b3db742 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -47,7 +47,6 @@ module YoutubeAPI name: "MWEB", version: "2.20220805.01.00", api_key: DEFAULT_API_KEY, - screen: "", # None }, ClientType::WebScreenEmbed => { name: "WEB", @@ -62,13 +61,11 @@ module YoutubeAPI name: "ANDROID", version: ANDROID_APP_VERSION, api_key: "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w", - screen: "", # ?? }, ClientType::AndroidEmbeddedPlayer => { name: "ANDROID_EMBEDDED_PLAYER", # 55 version: ANDROID_APP_VERSION, api_key: DEFAULT_API_KEY, - screen: "", # None? }, ClientType::AndroidScreenEmbed => { name: "ANDROID", # 3 From d24506baedb0fbcca1e26f7d47f13716c2666b54 Mon Sep 17 00:00:00 2001 From: amarakon Date: Sat, 6 Aug 2022 20:42:08 -0400 Subject: [PATCH 71/98] Add Ytfzf to projects using Invidious --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9ed68a4b..6068a66b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Translation Status - + Awesome Humane Tech @@ -28,17 +28,17 @@

An open source alternative front-end to YouTube

Website -  •  +  •  Instances list  •  FAQ -  •  +  •  Documentation  •  Contribute  •  Donate - +
Chat with us:
Matrix @@ -153,6 +153,7 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab, - [WatchTube](https://github.com/WatchTubeTeam/WatchTube): Powerful YouTube client for Apple Watch. - [Yattee](https://github.com/yattee/yattee): Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV. - [TubiTui](https://codeberg.org/777/TubiTui): A lightweight, libre, TUI-based YouTube client. +- [Ytfzf](https://github.com/pystardust/ytfzf): A posix script to find and watch youtube videos from the terminal. (Without API) ## Liability From 246955b68a16aefc4e682e8f704f551f4a72b1bf Mon Sep 17 00:00:00 2001 From: Emilien Devos Date: Sat, 6 Aug 2022 18:41:59 +0200 Subject: [PATCH 72/98] if case for sectionListRenderer --- src/invidious/yt_backend/extractors.cr | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index b9609eb9..dc65cc52 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -435,20 +435,22 @@ private module Extractors raw_items = [] of JSON::Any content = extract_selected_tab(target["tabs"])["content"] - content["sectionListRenderer"]["contents"].as_a.each do |renderer_container| - renderer_container_contents = renderer_container["itemSectionRenderer"]["contents"][0] + if section_list_contents = content.dig?("sectionListRenderer", "contents") + section_list_contents.as_a.each do |renderer_container| + renderer_container_contents = renderer_container["itemSectionRenderer"]["contents"][0] - # Category extraction - if items_container = renderer_container_contents["shelfRenderer"]? - raw_items << renderer_container_contents - next - elsif items_container = renderer_container_contents["gridRenderer"]? - else - items_container = renderer_container_contents - end + # Category extraction + if items_container = renderer_container_contents["shelfRenderer"]? + raw_items << renderer_container_contents + next + elsif items_container = renderer_container_contents["gridRenderer"]? + else + items_container = renderer_container_contents + end - items_container["items"]?.try &.as_a.each do |item| - raw_items << item + items_container["items"]?.try &.as_a.each do |item| + raw_items << item + end end end From 218f7be1a7ec6cb679d7d324be3e64c6d79da127 Mon Sep 17 00:00:00 2001 From: Emilien Devos Date: Sun, 7 Aug 2022 19:14:16 +0200 Subject: [PATCH 73/98] For android client send sdk version to youtube --- src/invidious/yt_backend/youtube_api.cr | 29 +++++++++++++++++-------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index 2b3db742..30d7613b 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -8,6 +8,7 @@ module YoutubeAPI private DEFAULT_API_KEY = "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" private ANDROID_APP_VERSION = "17.29.35" + private ANDROID_SDK_VERSION = 30_i64 private IOS_APP_VERSION = "17.30.1" # Enumerate used to select one of the clients supported by the API @@ -58,9 +59,10 @@ module YoutubeAPI # Android ClientType::Android => { - name: "ANDROID", - version: ANDROID_APP_VERSION, - api_key: "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w", + name: "ANDROID", + version: ANDROID_APP_VERSION, + api_key: "AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w", + android_sdk_version: ANDROID_SDK_VERSION, }, ClientType::AndroidEmbeddedPlayer => { name: "ANDROID_EMBEDDED_PLAYER", # 55 @@ -68,10 +70,11 @@ module YoutubeAPI api_key: DEFAULT_API_KEY, }, ClientType::AndroidScreenEmbed => { - name: "ANDROID", # 3 - version: ANDROID_APP_VERSION, - api_key: DEFAULT_API_KEY, - screen: "EMBED", + name: "ANDROID", # 3 + version: ANDROID_APP_VERSION, + api_key: DEFAULT_API_KEY, + screen: "EMBED", + android_sdk_version: ANDROID_SDK_VERSION, }, # IOS @@ -172,6 +175,10 @@ module YoutubeAPI HARDCODED_CLIENTS[@client_type][:screen]? || "" end + def android_sdk_version : Int64? + HARDCODED_CLIENTS[@client_type][:android_sdk_version]? + end + # Convert to string, for logging purposes def to_s return { @@ -201,7 +208,7 @@ module YoutubeAPI "gl" => client_config.region || "US", # Can't be empty! "clientName" => client_config.name, "clientVersion" => client_config.version, - }, + } of String => String | Int64, } # Add some more context if it exists in the client definitions @@ -212,7 +219,11 @@ module YoutubeAPI if client_config.screen == "EMBED" client_context["thirdParty"] = { "embedUrl" => "https://www.youtube.com/embed/dQw4w9WgXcQ", - } + } of String => String | Int64 + end + + if android_sdk_version = client_config.android_sdk_version + client_context["client"]["androidSdkVersion"] = android_sdk_version end return client_context From c23ad25899152c4837777dbc983809f436f7062a Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 9 Aug 2022 23:39:53 +0200 Subject: [PATCH 74/98] routing: remove HEAD from HTTP methods Kemal automatically creates an associated HEAD route for all GET routes --- src/invidious/routing.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index bd72c577..9e95f7db 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -1,5 +1,5 @@ module Invidious::Routing - {% for http_method in {"get", "post", "delete", "options", "patch", "put", "head"} %} + {% for http_method in {"get", "post", "delete", "options", "patch", "put"} %} macro {{http_method.id}}(path, controller, method = :handle) {{http_method.id}} \{{ path }} do |env| From e22cc73f32577afe8098c70184760d8b75ce0189 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 9 Aug 2022 23:56:34 +0200 Subject: [PATCH 75/98] routing: register user routes with a function, rather than a macro --- src/invidious.cr | 5 +--- src/invidious/routing.cr | 52 +++++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 070b4d18..91bf6935 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -389,7 +389,7 @@ end Invidious::Routing.get "/hashtag/:hashtag", Invidious::Routes::Search, :hashtag # User routes - define_user_routes() + Invidious::Routing.register_user_routes # Feeds Invidious::Routing.get "/view_all_playlists", Invidious::Routes::Feeds, :view_all_playlists_redirect @@ -410,9 +410,6 @@ end Invidious::Routing.post "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_post Invidious::Routing.get "/modify_notifications", Invidious::Routes::Notifications, :modify - - Invidious::Routing.post "/subscription_ajax", Invidious::Routes::Subscriptions, :toggle_subscription - Invidious::Routing.get "/subscription_manager", Invidious::Routes::Subscriptions, :subscription_manager {% end %} Invidious::Routing.get "/ggpht/*", Invidious::Routes::Images, :ggpht diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 9e95f7db..23119e62 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -1,4 +1,6 @@ module Invidious::Routing + extend self + {% for http_method in {"get", "post", "delete", "options", "patch", "put"} %} macro {{http_method.id}}(path, controller, method = :handle) @@ -8,33 +10,35 @@ module Invidious::Routing end {% end %} -end -macro define_user_routes - # User login/out - Invidious::Routing.get "/login", Invidious::Routes::Login, :login_page - Invidious::Routing.post "/login", Invidious::Routes::Login, :login - Invidious::Routing.post "/signout", Invidious::Routes::Login, :signout - Invidious::Routing.get "/Captcha", Invidious::Routes::Login, :captcha + def register_user_routes + # User login/out + get "/login", Routes::Login, :login_page + post "/login", Routes::Login, :login + post "/signout", Routes::Login, :signout + get "/Captcha", Routes::Login, :captcha - # User preferences - Invidious::Routing.get "/preferences", Invidious::Routes::PreferencesRoute, :show - Invidious::Routing.post "/preferences", Invidious::Routes::PreferencesRoute, :update - Invidious::Routing.get "/toggle_theme", Invidious::Routes::PreferencesRoute, :toggle_theme - Invidious::Routing.get "/data_control", Invidious::Routes::PreferencesRoute, :data_control - Invidious::Routing.post "/data_control", Invidious::Routes::PreferencesRoute, :update_data_control + # User preferences + get "/preferences", Routes::PreferencesRoute, :show + post "/preferences", Routes::PreferencesRoute, :update + get "/toggle_theme", Routes::PreferencesRoute, :toggle_theme + get "/data_control", Routes::PreferencesRoute, :data_control + post "/data_control", Routes::PreferencesRoute, :update_data_control - # User account management - Invidious::Routing.get "/change_password", Invidious::Routes::Account, :get_change_password - Invidious::Routing.post "/change_password", Invidious::Routes::Account, :post_change_password - Invidious::Routing.get "/delete_account", Invidious::Routes::Account, :get_delete - Invidious::Routing.post "/delete_account", Invidious::Routes::Account, :post_delete - Invidious::Routing.get "/clear_watch_history", Invidious::Routes::Account, :get_clear_history - Invidious::Routing.post "/clear_watch_history", Invidious::Routes::Account, :post_clear_history - Invidious::Routing.get "/authorize_token", Invidious::Routes::Account, :get_authorize_token - Invidious::Routing.post "/authorize_token", Invidious::Routes::Account, :post_authorize_token - Invidious::Routing.get "/token_manager", Invidious::Routes::Account, :token_manager - Invidious::Routing.post "/token_ajax", Invidious::Routes::Account, :token_ajax + # User account management + get "/change_password", Routes::Account, :get_change_password + post "/change_password", Routes::Account, :post_change_password + get "/delete_account", Routes::Account, :get_delete + post "/delete_account", Routes::Account, :post_delete + get "/clear_watch_history", Routes::Account, :get_clear_history + post "/clear_watch_history", Routes::Account, :post_clear_history + get "/authorize_token", Routes::Account, :get_authorize_token + post "/authorize_token", Routes::Account, :post_authorize_token + get "/token_manager", Routes::Account, :token_manager + post "/token_ajax", Routes::Account, :token_ajax + post "/subscription_ajax", Routes::Subscriptions, :toggle_subscription + get "/subscription_manager", Routes::Subscriptions, :subscription_manager + end end macro define_v1_api_routes From 176247091d5df6fe7d9b772ef3e1ff09d3bc9c1c Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:07:47 +0200 Subject: [PATCH 76/98] routing: register API routes with a function, rather than a macro --- src/invidious.cr | 2 +- src/invidious/routing.cr | 113 +++++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 91bf6935..1188710f 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -420,7 +420,7 @@ Invidious::Routing.get "/yts/img/:name", Invidious::Routes::Images, :yts_image Invidious::Routing.get "/vi/:id/:name", Invidious::Routes::Images, :thumbnails # API routes (macro) -define_v1_api_routes() +Invidious::Routing.register_api_v1_routes # Video playback (macros) define_api_manifest_routes() diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 23119e62..9e8ce34d 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -11,6 +11,10 @@ module Invidious::Routing {% end %} + # ------------------- + # Invidious routes + # ------------------- + def register_user_routes # User login/out get "/login", Routes::Login, :login_page @@ -39,75 +43,78 @@ module Invidious::Routing post "/subscription_ajax", Routes::Subscriptions, :toggle_subscription get "/subscription_manager", Routes::Subscriptions, :subscription_manager end -end -macro define_v1_api_routes - {{namespace = Invidious::Routes::API::V1}} - # Videos - Invidious::Routing.get "/api/v1/videos/:id", {{namespace}}::Videos, :videos - Invidious::Routing.get "/api/v1/storyboards/:id", {{namespace}}::Videos, :storyboards - Invidious::Routing.get "/api/v1/captions/:id", {{namespace}}::Videos, :captions - Invidious::Routing.get "/api/v1/annotations/:id", {{namespace}}::Videos, :annotations - Invidious::Routing.get "/api/v1/comments/:id", {{namespace}}::Videos, :comments + # ------------------- + # API routes + # ------------------- - # Feeds - Invidious::Routing.get "/api/v1/trending", {{namespace}}::Feeds, :trending - Invidious::Routing.get "/api/v1/popular", {{namespace}}::Feeds, :popular + def register_api_v1_routes + {% begin %} + {{namespace = Routes::API::V1}} - # Channels - Invidious::Routing.get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home - {% for route in {"videos", "latest", "playlists", "community", "search"} %} - Invidious::Routing.get "/api/v1/channels/#{{{route}}}/:ucid", {{namespace}}::Channels, :{{route}} - Invidious::Routing.get "/api/v1/channels/:ucid/#{{{route}}}", {{namespace}}::Channels, :{{route}} - {% end %} + # Videos + get "/api/v1/videos/:id", {{namespace}}::Videos, :videos + get "/api/v1/storyboards/:id", {{namespace}}::Videos, :storyboards + get "/api/v1/captions/:id", {{namespace}}::Videos, :captions + get "/api/v1/annotations/:id", {{namespace}}::Videos, :annotations + get "/api/v1/comments/:id", {{namespace}}::Videos, :comments - # 301 redirects to new /api/v1/channels/community/:ucid and /:ucid/community - Invidious::Routing.get "/api/v1/channels/comments/:ucid", {{namespace}}::Channels, :channel_comments_redirect - Invidious::Routing.get "/api/v1/channels/:ucid/comments", {{namespace}}::Channels, :channel_comments_redirect + # Feeds + get "/api/v1/trending", {{namespace}}::Feeds, :trending + get "/api/v1/popular", {{namespace}}::Feeds, :popular + # Channels + get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home + {% for route in {"videos", "latest", "playlists", "community", "search"} %} + get "/api/v1/channels/#{{{route}}}/:ucid", {{namespace}}::Channels, :{{route}} + get "/api/v1/channels/:ucid/#{{{route}}}", {{namespace}}::Channels, :{{route}} + {% end %} - # Search - Invidious::Routing.get "/api/v1/search", {{namespace}}::Search, :search - Invidious::Routing.get "/api/v1/search/suggestions", {{namespace}}::Search, :search_suggestions + # 301 redirects to new /api/v1/channels/community/:ucid and /:ucid/community + get "/api/v1/channels/comments/:ucid", {{namespace}}::Channels, :channel_comments_redirect + get "/api/v1/channels/:ucid/comments", {{namespace}}::Channels, :channel_comments_redirect - # Authenticated + # Search + get "/api/v1/search", {{namespace}}::Search, :search + get "/api/v1/search/suggestions", {{namespace}}::Search, :search_suggestions - # The notification APIs cannot be extracted yet! They require the *local* notifications constant defined in invidious.cr - # - # Invidious::Routing.get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications - # Invidious::Routing.post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications + # Authenticated - Invidious::Routing.get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences - Invidious::Routing.post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences + # The notification APIs cannot be extracted yet! They require the *local* notifications constant defined in invidious.cr + # + # Invidious::Routing.get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications + # Invidious::Routing.post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications - Invidious::Routing.get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed + get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences + post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences - Invidious::Routing.get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions - Invidious::Routing.post "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :subscribe_channel - Invidious::Routing.delete "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :unsubscribe_channel + get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed + get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions + post "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :subscribe_channel + delete "/api/v1/auth/subscriptions/:ucid", {{namespace}}::Authenticated, :unsubscribe_channel - Invidious::Routing.get "/api/v1/auth/playlists", {{namespace}}::Authenticated, :list_playlists - Invidious::Routing.post "/api/v1/auth/playlists", {{namespace}}::Authenticated, :create_playlist - Invidious::Routing.patch "/api/v1/auth/playlists/:plid",{{namespace}}:: Authenticated, :update_playlist_attribute - Invidious::Routing.delete "/api/v1/auth/playlists/:plid", {{namespace}}::Authenticated, :delete_playlist + get "/api/v1/auth/playlists", {{namespace}}::Authenticated, :list_playlists + post "/api/v1/auth/playlists", {{namespace}}::Authenticated, :create_playlist + patch "/api/v1/auth/playlists/:plid",{{namespace}}:: Authenticated, :update_playlist_attribute + delete "/api/v1/auth/playlists/:plid", {{namespace}}::Authenticated, :delete_playlist + post "/api/v1/auth/playlists/:plid/videos", {{namespace}}::Authenticated, :insert_video_into_playlist + delete "/api/v1/auth/playlists/:plid/videos/:index", {{namespace}}::Authenticated, :delete_video_in_playlist + get "/api/v1/auth/tokens", {{namespace}}::Authenticated, :get_tokens + post "/api/v1/auth/tokens/register", {{namespace}}::Authenticated, :register_token + post "/api/v1/auth/tokens/unregister", {{namespace}}::Authenticated, :unregister_token - Invidious::Routing.post "/api/v1/auth/playlists/:plid/videos", {{namespace}}::Authenticated, :insert_video_into_playlist - Invidious::Routing.delete "/api/v1/auth/playlists/:plid/videos/:index", {{namespace}}::Authenticated, :delete_video_in_playlist + get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications + post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications - Invidious::Routing.get "/api/v1/auth/tokens", {{namespace}}::Authenticated, :get_tokens - Invidious::Routing.post "/api/v1/auth/tokens/register", {{namespace}}::Authenticated, :register_token - Invidious::Routing.post "/api/v1/auth/tokens/unregister", {{namespace}}::Authenticated, :unregister_token - - Invidious::Routing.get "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications - Invidious::Routing.post "/api/v1/auth/notifications", {{namespace}}::Authenticated, :notifications - - # Misc - Invidious::Routing.get "/api/v1/stats", {{namespace}}::Misc, :stats - Invidious::Routing.get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist - Invidious::Routing.get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist - Invidious::Routing.get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes + # Misc + get "/api/v1/stats", {{namespace}}::Misc, :stats + get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist + get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist + get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes + {% end %} + end end macro define_api_manifest_routes From 389ae7a57395f1b3fbf540deebbad73d0674e715 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:09:58 +0200 Subject: [PATCH 77/98] routing: register playback routes with a function, rather than a macro --- src/invidious.cr | 4 ++-- src/invidious/routing.cr | 50 ++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 1188710f..f244cea5 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -423,8 +423,8 @@ Invidious::Routing.get "/vi/:id/:name", Invidious::Routes::Images, :thumbnails Invidious::Routing.register_api_v1_routes # Video playback (macros) -define_api_manifest_routes() -define_video_playback_routes() +Invidious::Routing.register_api_manifest_routes +Invidious::Routing.register_video_playback_routes error 404 do |env| if md = env.request.path.match(/^\/(?([a-zA-Z0-9_-]{11})|(\w+))$/) diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 9e8ce34d..25cbfa48 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -44,6 +44,33 @@ module Invidious::Routing get "/subscription_manager", Routes::Subscriptions, :subscription_manager end + # ------------------- + # Youtube routes + # ------------------- + + def register_api_manifest_routes + get "/api/manifest/dash/id/:id", Routes::API::Manifest, :get_dash_video_id + + get "/api/manifest/dash/id/videoplayback", Routes::API::Manifest, :get_dash_video_playback + get "/api/manifest/dash/id/videoplayback/*", Routes::API::Manifest, :get_dash_video_playback_greedy + + options "/api/manifest/dash/id/videoplayback", Routes::API::Manifest, :options_dash_video_playback + options "/api/manifest/dash/id/videoplayback/*", Routes::API::Manifest, :options_dash_video_playback + + get "/api/manifest/hls_playlist/*", Routes::API::Manifest, :get_hls_playlist + get "/api/manifest/hls_variant/*", Routes::API::Manifest, :get_hls_variant + end + + def register_video_playback_routes + get "/videoplayback", Routes::VideoPlayback, :get_video_playback + get "/videoplayback/*", Routes::VideoPlayback, :get_video_playback_greedy + + options "/videoplayback", Routes::VideoPlayback, :options_video_playback + options "/videoplayback/*", Routes::VideoPlayback, :options_video_playback + + get "/latest_version", Routes::VideoPlayback, :latest_version + end + # ------------------- # API routes # ------------------- @@ -116,26 +143,3 @@ module Invidious::Routing {% end %} end end - -macro define_api_manifest_routes - Invidious::Routing.get "/api/manifest/dash/id/:id", Invidious::Routes::API::Manifest, :get_dash_video_id - - Invidious::Routing.get "/api/manifest/dash/id/videoplayback", Invidious::Routes::API::Manifest, :get_dash_video_playback - Invidious::Routing.get "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::API::Manifest, :get_dash_video_playback_greedy - - Invidious::Routing.options "/api/manifest/dash/id/videoplayback", Invidious::Routes::API::Manifest, :options_dash_video_playback - Invidious::Routing.options "/api/manifest/dash/id/videoplayback/*", Invidious::Routes::API::Manifest, :options_dash_video_playback - - Invidious::Routing.get "/api/manifest/hls_playlist/*", Invidious::Routes::API::Manifest, :get_hls_playlist - Invidious::Routing.get "/api/manifest/hls_variant/*", Invidious::Routes::API::Manifest, :get_hls_variant -end - -macro define_video_playback_routes - Invidious::Routing.get "/videoplayback", Invidious::Routes::VideoPlayback, :get_video_playback - Invidious::Routing.get "/videoplayback/*", Invidious::Routes::VideoPlayback, :get_video_playback_greedy - - Invidious::Routing.options "/videoplayback", Invidious::Routes::VideoPlayback, :options_video_playback - Invidious::Routing.options "/videoplayback/*", Invidious::Routes::VideoPlayback, :options_video_playback - - Invidious::Routing.get "/latest_version", Invidious::Routes::VideoPlayback, :latest_version -end From 3ac4390d11d7eecbd49e3db79376942e8706783b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:14:26 +0200 Subject: [PATCH 78/98] routing: move channel routes registration to Invidious::Routing --- src/invidious.cr | 21 +-------------------- src/invidious/routing.cr | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index f244cea5..969804a6 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -334,26 +334,7 @@ end Invidious::Routing.get "/privacy", Invidious::Routes::Misc, :privacy Invidious::Routing.get "/licenses", Invidious::Routes::Misc, :licenses - Invidious::Routing.get "/channel/:ucid", Invidious::Routes::Channels, :home - Invidious::Routing.get "/channel/:ucid/home", Invidious::Routes::Channels, :home - Invidious::Routing.get "/channel/:ucid/videos", Invidious::Routes::Channels, :videos - Invidious::Routing.get "/channel/:ucid/playlists", Invidious::Routes::Channels, :playlists - Invidious::Routing.get "/channel/:ucid/community", Invidious::Routes::Channels, :community - Invidious::Routing.get "/channel/:ucid/about", Invidious::Routes::Channels, :about - Invidious::Routing.get "/channel/:ucid/live", Invidious::Routes::Channels, :live - Invidious::Routing.get "/user/:user/live", Invidious::Routes::Channels, :live - Invidious::Routing.get "/c/:user/live", Invidious::Routes::Channels, :live - - ["", "/videos", "/playlists", "/community", "/about"].each do |path| - # /c/LinusTechTips - Invidious::Routing.get "/c/:user#{path}", Invidious::Routes::Channels, :brand_redirect - # /user/linustechtips | Not always the same as /c/ - Invidious::Routing.get "/user/:user#{path}", Invidious::Routes::Channels, :brand_redirect - # /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow - Invidious::Routing.get "/attribution_link#{path}", Invidious::Routes::Channels, :brand_redirect - # /profile?user=linustechtips - Invidious::Routing.get "/profile/#{path}", Invidious::Routes::Channels, :profile - end + Invidious::Routing.register_channel_routes Invidious::Routing.get "/watch", Invidious::Routes::Watch, :handle Invidious::Routing.post "/watch_ajax", Invidious::Routes::Watch, :mark_watched diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 25cbfa48..203aa024 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -48,6 +48,29 @@ module Invidious::Routing # Youtube routes # ------------------- + def register_channel_routes + get "/channel/:ucid", Routes::Channels, :home + get "/channel/:ucid/home", Routes::Channels, :home + get "/channel/:ucid/videos", Routes::Channels, :videos + get "/channel/:ucid/playlists", Routes::Channels, :playlists + get "/channel/:ucid/community", Routes::Channels, :community + get "/channel/:ucid/about", Routes::Channels, :about + get "/channel/:ucid/live", Routes::Channels, :live + get "/user/:user/live", Routes::Channels, :live + get "/c/:user/live", Routes::Channels, :live + + ["", "/videos", "/playlists", "/community", "/about"].each do |path| + # /c/LinusTechTips + get "/c/:user#{path}", Routes::Channels, :brand_redirect + # /user/linustechtips | Not always the same as /c/ + get "/user/:user#{path}", Routes::Channels, :brand_redirect + # /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow + get "/attribution_link#{path}", Routes::Channels, :brand_redirect + # /profile?user=linustechtips + get "/profile/#{path}", Routes::Channels, :profile + end + end + def register_api_manifest_routes get "/api/manifest/dash/id/:id", Routes::API::Manifest, :get_dash_video_id From e2532de766bec9a2e967d551776823b83f44e995 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:20:04 +0200 Subject: [PATCH 79/98] routing: move image proxy routes registration to Invidious::Routing --- src/invidious.cr | 7 +------ src/invidious/routing.cr | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 969804a6..9daf5380 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -393,12 +393,7 @@ end Invidious::Routing.get "/modify_notifications", Invidious::Routes::Notifications, :modify {% end %} -Invidious::Routing.get "/ggpht/*", Invidious::Routes::Images, :ggpht -Invidious::Routing.options "/sb/:authority/:id/:storyboard/:index", Invidious::Routes::Images, :options_storyboard -Invidious::Routing.get "/sb/:authority/:id/:storyboard/:index", Invidious::Routes::Images, :get_storyboard -Invidious::Routing.get "/s_p/:id/:name", Invidious::Routes::Images, :s_p_image -Invidious::Routing.get "/yts/img/:name", Invidious::Routes::Images, :yts_image -Invidious::Routing.get "/vi/:id/:name", Invidious::Routes::Images, :thumbnails +Invidious::Routing.register_image_routes # API routes (macro) Invidious::Routing.register_api_v1_routes diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 203aa024..45ae7c6b 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -94,6 +94,15 @@ module Invidious::Routing get "/latest_version", Routes::VideoPlayback, :latest_version end + def register_image_routes + get "/ggpht/*", Routes::Images, :ggpht + options "/sb/:authority/:id/:storyboard/:index", Routes::Images, :options_storyboard + get "/sb/:authority/:id/:storyboard/:index", Routes::Images, :get_storyboard + get "/s_p/:id/:name", Routes::Images, :s_p_image + get "/yts/img/:name", Routes::Images, :yts_image + get "/vi/:id/:name", Routes::Images, :thumbnails + end + # ------------------- # API routes # ------------------- From 906466d7fb31686b208f04172dbd6ecaa9e1f1c6 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:22:40 +0200 Subject: [PATCH 80/98] routing: move watch/embed routes registration to Invidious::Routing --- src/invidious.cr | 17 ++--------------- src/invidious/routing.cr | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 9daf5380..b9c88114 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -333,23 +333,10 @@ end Invidious::Routing.get "/", Invidious::Routes::Misc, :home Invidious::Routing.get "/privacy", Invidious::Routes::Misc, :privacy Invidious::Routing.get "/licenses", Invidious::Routes::Misc, :licenses - - Invidious::Routing.register_channel_routes - - Invidious::Routing.get "/watch", Invidious::Routes::Watch, :handle - Invidious::Routing.post "/watch_ajax", Invidious::Routes::Watch, :mark_watched - Invidious::Routing.get "/watch/:id", Invidious::Routes::Watch, :redirect - Invidious::Routing.get "/shorts/:id", Invidious::Routes::Watch, :redirect - Invidious::Routing.get "/clip/:clip", Invidious::Routes::Watch, :clip - Invidious::Routing.get "/w/:id", Invidious::Routes::Watch, :redirect - Invidious::Routing.get "/v/:id", Invidious::Routes::Watch, :redirect - Invidious::Routing.get "/e/:id", Invidious::Routes::Watch, :redirect Invidious::Routing.get "/redirect", Invidious::Routes::Misc, :cross_instance_redirect - Invidious::Routing.post "/download", Invidious::Routes::Watch, :download - - Invidious::Routing.get "/embed/", Invidious::Routes::Embed, :redirect - Invidious::Routing.get "/embed/:id", Invidious::Routes::Embed, :show + Invidious::Routing.register_channel_routes + Invidious::Routing.register_watch_routes Invidious::Routing.get "/create_playlist", Invidious::Routes::Playlists, :new Invidious::Routing.post "/create_playlist", Invidious::Routes::Playlists, :create diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 45ae7c6b..4f6db78c 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -71,6 +71,22 @@ module Invidious::Routing end end + def register_watch_routes + get "/watch", Routes::Watch, :handle + post "/watch_ajax", Routes::Watch, :mark_watched + get "/watch/:id", Routes::Watch, :redirect + get "/shorts/:id", Routes::Watch, :redirect + get "/clip/:clip", Routes::Watch, :clip + get "/w/:id", Routes::Watch, :redirect + get "/v/:id", Routes::Watch, :redirect + get "/e/:id", Routes::Watch, :redirect + + post "/download", Routes::Watch, :download + + get "/embed/", Routes::Embed, :redirect + get "/embed/:id", Routes::Embed, :show + end + def register_api_manifest_routes get "/api/manifest/dash/id/:id", Routes::API::Manifest, :get_dash_video_id From 5503914abe28eefdc89ca9a4762cc434a351f378 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:26:41 +0200 Subject: [PATCH 81/98] routing: move playlist routes registration to Invidious::Routing --- src/invidious.cr | 14 ++------------ src/invidious/routing.cr | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index b9c88114..f134886f 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -338,18 +338,8 @@ end Invidious::Routing.register_channel_routes Invidious::Routing.register_watch_routes - Invidious::Routing.get "/create_playlist", Invidious::Routes::Playlists, :new - Invidious::Routing.post "/create_playlist", Invidious::Routes::Playlists, :create - Invidious::Routing.get "/subscribe_playlist", Invidious::Routes::Playlists, :subscribe - Invidious::Routing.get "/delete_playlist", Invidious::Routes::Playlists, :delete_page - Invidious::Routing.post "/delete_playlist", Invidious::Routes::Playlists, :delete - Invidious::Routing.get "/edit_playlist", Invidious::Routes::Playlists, :edit - Invidious::Routing.post "/edit_playlist", Invidious::Routes::Playlists, :update - Invidious::Routing.get "/add_playlist_items", Invidious::Routes::Playlists, :add_playlist_items_page - Invidious::Routing.post "/playlist_ajax", Invidious::Routes::Playlists, :playlist_ajax - Invidious::Routing.get "/playlist", Invidious::Routes::Playlists, :show - Invidious::Routing.get "/mix", Invidious::Routes::Playlists, :mix - Invidious::Routing.get "/watch_videos", Invidious::Routes::Playlists, :watch_videos + Invidious::Routing.register_iv_playlist_routes + Invidious::Routing.register_yt_playlist_routes Invidious::Routing.get "/opensearch.xml", Invidious::Routes::Search, :opensearch Invidious::Routing.get "/results", Invidious::Routes::Search, :results diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 4f6db78c..4074ef18 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -44,6 +44,18 @@ module Invidious::Routing get "/subscription_manager", Routes::Subscriptions, :subscription_manager end + def register_iv_playlist_routes + get "/create_playlist", Routes::Playlists, :new + post "/create_playlist", Routes::Playlists, :create + get "/subscribe_playlist", Routes::Playlists, :subscribe + get "/delete_playlist", Routes::Playlists, :delete_page + post "/delete_playlist", Routes::Playlists, :delete + get "/edit_playlist", Routes::Playlists, :edit + post "/edit_playlist", Routes::Playlists, :update + get "/add_playlist_items", Routes::Playlists, :add_playlist_items_page + post "/playlist_ajax", Routes::Playlists, :playlist_ajax + end + # ------------------- # Youtube routes # ------------------- @@ -87,6 +99,12 @@ module Invidious::Routing get "/embed/:id", Routes::Embed, :show end + def register_yt_playlist_routes + get "/playlist", Routes::Playlists, :show + get "/mix", Routes::Playlists, :mix + get "/watch_videos", Routes::Playlists, :watch_videos + end + def register_api_manifest_routes get "/api/manifest/dash/id/:id", Routes::API::Manifest, :get_dash_video_id From 0a4d793556e89e48b1a4caceaf8b8730b4b69d73 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:31:15 +0200 Subject: [PATCH 82/98] routing: move search routes registration to Invidious::Routing --- src/invidious.cr | 5 +---- src/invidious/routing.cr | 11 +++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index f134886f..e880db19 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -341,10 +341,7 @@ end Invidious::Routing.register_iv_playlist_routes Invidious::Routing.register_yt_playlist_routes - Invidious::Routing.get "/opensearch.xml", Invidious::Routes::Search, :opensearch - Invidious::Routing.get "/results", Invidious::Routes::Search, :results - Invidious::Routing.get "/search", Invidious::Routes::Search, :search - Invidious::Routing.get "/hashtag/:hashtag", Invidious::Routes::Search, :hashtag + Invidious::Routing.register_search_routes # User routes Invidious::Routing.register_user_routes diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 4074ef18..828deaf9 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -105,6 +105,17 @@ module Invidious::Routing get "/watch_videos", Routes::Playlists, :watch_videos end + def register_search_routes + get "/opensearch.xml", Routes::Search, :opensearch + get "/results", Routes::Search, :results + get "/search", Routes::Search, :search + get "/hashtag/:hashtag", Routes::Search, :hashtag + end + + # ------------------- + # Media proxy routes + # ------------------- + def register_api_manifest_routes get "/api/manifest/dash/id/:id", Routes::API::Manifest, :get_dash_video_id From 223e74569aa3355857ee37f84b0eac2a8dd24b3d Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:44:21 +0200 Subject: [PATCH 83/98] routing: move feed routes registration to Invidious::Routing --- src/invidious.cr | 14 +------------- src/invidious/routing.cr | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index e880db19..4a3b28b1 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -346,19 +346,7 @@ end # User routes Invidious::Routing.register_user_routes - # Feeds - Invidious::Routing.get "/view_all_playlists", Invidious::Routes::Feeds, :view_all_playlists_redirect - Invidious::Routing.get "/feed/playlists", Invidious::Routes::Feeds, :playlists - Invidious::Routing.get "/feed/popular", Invidious::Routes::Feeds, :popular - Invidious::Routing.get "/feed/trending", Invidious::Routes::Feeds, :trending - Invidious::Routing.get "/feed/subscriptions", Invidious::Routes::Feeds, :subscriptions - Invidious::Routing.get "/feed/history", Invidious::Routes::Feeds, :history - - # RSS Feeds - Invidious::Routing.get "/feed/channel/:ucid", Invidious::Routes::Feeds, :rss_channel - Invidious::Routing.get "/feed/private", Invidious::Routes::Feeds, :rss_private - Invidious::Routing.get "/feed/playlist/:plid", Invidious::Routes::Feeds, :rss_playlist - Invidious::Routing.get "/feeds/videos.xml", Invidious::Routes::Feeds, :rss_videos + Invidious::Routing.register_feed_routes # Support push notifications via PubSubHubbub Invidious::Routing.get "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_get diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 828deaf9..e9657bba 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -56,6 +56,22 @@ module Invidious::Routing post "/playlist_ajax", Routes::Playlists, :playlist_ajax end + def register_feed_routes + # Feeds + get "/view_all_playlists", Routes::Feeds, :view_all_playlists_redirect + get "/feed/playlists", Routes::Feeds, :playlists + get "/feed/popular", Routes::Feeds, :popular + get "/feed/trending", Routes::Feeds, :trending + get "/feed/subscriptions", Routes::Feeds, :subscriptions + get "/feed/history", Routes::Feeds, :history + + # RSS Feeds + get "/feed/channel/:ucid", Routes::Feeds, :rss_channel + get "/feed/private", Routes::Feeds, :rss_private + get "/feed/playlist/:plid", Routes::Feeds, :rss_playlist + get "/feeds/videos.xml", Routes::Feeds, :rss_videos + end + # ------------------- # Youtube routes # ------------------- From 1e25894f7ec37044698f9fddf60813e0199b921b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:48:09 +0200 Subject: [PATCH 84/98] routing: move the remaining routes registration to a wrapper function --- src/invidious.cr | 35 +---------------------------------- src/invidious/routing.cr | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 4a3b28b1..95e4c225 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -329,40 +329,7 @@ before_all do |env| env.set "current_page", URI.encode_www_form(current_page) end -{% unless flag?(:api_only) %} - Invidious::Routing.get "/", Invidious::Routes::Misc, :home - Invidious::Routing.get "/privacy", Invidious::Routes::Misc, :privacy - Invidious::Routing.get "/licenses", Invidious::Routes::Misc, :licenses - Invidious::Routing.get "/redirect", Invidious::Routes::Misc, :cross_instance_redirect - - Invidious::Routing.register_channel_routes - Invidious::Routing.register_watch_routes - - Invidious::Routing.register_iv_playlist_routes - Invidious::Routing.register_yt_playlist_routes - - Invidious::Routing.register_search_routes - - # User routes - Invidious::Routing.register_user_routes - - Invidious::Routing.register_feed_routes - - # Support push notifications via PubSubHubbub - Invidious::Routing.get "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_get - Invidious::Routing.post "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_post - - Invidious::Routing.get "/modify_notifications", Invidious::Routes::Notifications, :modify -{% end %} - -Invidious::Routing.register_image_routes - -# API routes (macro) -Invidious::Routing.register_api_v1_routes - -# Video playback (macros) -Invidious::Routing.register_api_manifest_routes -Invidious::Routing.register_video_playback_routes +Invidious::Routing.register_all error 404 do |env| if md = env.request.path.match(/^\/(?([a-zA-Z0-9_-]{11})|(\w+))$/) diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index e9657bba..8084b3e4 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -11,6 +11,37 @@ module Invidious::Routing {% end %} + def register_all + {% unless flag?(:api_only) %} + get "/", Routes::Misc, :home + get "/privacy", Routes::Misc, :privacy + get "/licenses", Routes::Misc, :licenses + get "/redirect", Routes::Misc, :cross_instance_redirect + + self.register_channel_routes + self.register_watch_routes + + self.register_iv_playlist_routes + self.register_yt_playlist_routes + + self.register_search_routes + + self.register_user_routes + self.register_feed_routes + + # Support push notifications via PubSubHubbub + get "/feed/webhook/:token", Routes::Feeds, :push_notifications_get + post "/feed/webhook/:token", Routes::Feeds, :push_notifications_post + + get "/modify_notifications", Routes::Notifications, :modify + {% end %} + + self.register_image_routes + self.register_api_v1_routes + self.register_api_manifest_routes + self.register_video_playback_routes + end + # ------------------- # Invidious routes # ------------------- From 870350fd612008a3694159ef933831943fca68b4 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 00:52:09 +0200 Subject: [PATCH 85/98] routes: move before_all logic to its own module --- src/invidious.cr | 153 +---------------------------- src/invidious/routes/before_all.cr | 152 ++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 148 deletions(-) create mode 100644 src/invidious/routes/before_all.cr diff --git a/src/invidious.cr b/src/invidious.cr index 95e4c225..4a3b0003 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -178,155 +178,10 @@ def popular_videos Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get end +# Routing + before_all do |env| - preferences = Preferences.from_json("{}") - - begin - if prefs_cookie = env.request.cookies["PREFS"]? - preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value)) - else - if language_header = env.request.headers["Accept-Language"]? - if language = ANG.language_negotiator.best(language_header, LOCALES.keys) - preferences.locale = language.header - end - end - end - rescue - preferences = Preferences.from_json("{}") - end - - env.set "preferences", preferences - env.response.headers["X-XSS-Protection"] = "1; mode=block" - env.response.headers["X-Content-Type-Options"] = "nosniff" - - # Allow media resources to be loaded from google servers - # TODO: check if *.youtube.com can be removed - if CONFIG.disabled?("local") || !preferences.local - extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443" - else - extra_media_csp = "" - end - - # Only allow the pages at /embed/* to be embedded - if env.request.resource.starts_with?("/embed") - frame_ancestors = "'self' http: https:" - else - frame_ancestors = "'none'" - end - - # TODO: Remove style-src's 'unsafe-inline', requires to remove all - # inline styles (, style=" [..] ") - env.response.headers["Content-Security-Policy"] = { - "default-src 'none'", - "script-src 'self'", - "style-src 'self' 'unsafe-inline'", - "img-src 'self' data:", - "font-src 'self' data:", - "connect-src 'self'", - "manifest-src 'self'", - "media-src 'self' blob:" + extra_media_csp, - "child-src 'self' blob:", - "frame-src 'self'", - "frame-ancestors " + frame_ancestors, - }.join("; ") - - env.response.headers["Referrer-Policy"] = "same-origin" - - # Ask the chrom*-based browsers to disable FLoC - # See: https://blog.runcloud.io/google-floc/ - env.response.headers["Permissions-Policy"] = "interest-cohort=()" - - if (Kemal.config.ssl || CONFIG.https_only) && CONFIG.hsts - env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload" - end - - next if { - "/sb/", - "/vi/", - "/s_p/", - "/yts/", - "/ggpht/", - "/api/manifest/", - "/videoplayback", - "/latest_version", - "/download", - }.any? { |r| env.request.resource.starts_with? r } - - if env.request.cookies.has_key? "SID" - sid = env.request.cookies["SID"].value - - if sid.starts_with? "v1:" - raise "Cannot use token as SID" - end - - # Invidious users only have SID - if !env.request.cookies.has_key? "SSID" - if email = Invidious::Database::SessionIDs.select_email(sid) - user = Invidious::Database::Users.select!(email: email) - csrf_token = generate_response(sid, { - ":authorize_token", - ":playlist_ajax", - ":signout", - ":subscription_ajax", - ":token_ajax", - ":watch_ajax", - }, HMAC_KEY, 1.week) - - preferences = user.preferences - env.set "preferences", preferences - - env.set "sid", sid - env.set "csrf_token", csrf_token - env.set "user", user - end - else - headers = HTTP::Headers.new - headers["Cookie"] = env.request.headers["Cookie"] - - begin - user, sid = get_user(sid, headers, false) - csrf_token = generate_response(sid, { - ":authorize_token", - ":playlist_ajax", - ":signout", - ":subscription_ajax", - ":token_ajax", - ":watch_ajax", - }, HMAC_KEY, 1.week) - - preferences = user.preferences - env.set "preferences", preferences - - env.set "sid", sid - env.set "csrf_token", csrf_token - env.set "user", user - rescue ex - end - end - end - - dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s - thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s - thin_mode = thin_mode == "true" - locale = env.params.query["hl"]? || preferences.locale - - preferences.dark_mode = dark_mode - preferences.thin_mode = thin_mode - preferences.locale = locale - env.set "preferences", preferences - - current_page = env.request.path - if env.request.query - query = HTTP::Params.parse(env.request.query.not_nil!) - - if query["referer"]? - query["referer"] = get_referer(env, "/") - end - - current_page += "?#{query}" - end - - env.set "current_page", URI.encode_www_form(current_page) + Invidious::Routes::BeforeAll.handle(env) end Invidious::Routing.register_all @@ -386,6 +241,8 @@ static_headers do |response| response.headers.add("Cache-Control", "max-age=2629800") end +# Init Kemal + public_folder "assets" Kemal.config.powered_by_header = false diff --git a/src/invidious/routes/before_all.cr b/src/invidious/routes/before_all.cr new file mode 100644 index 00000000..8e2a253f --- /dev/null +++ b/src/invidious/routes/before_all.cr @@ -0,0 +1,152 @@ +module Invidious::Routes::BeforeAll + def self.handle(env) + preferences = Preferences.from_json("{}") + + begin + if prefs_cookie = env.request.cookies["PREFS"]? + preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value)) + else + if language_header = env.request.headers["Accept-Language"]? + if language = ANG.language_negotiator.best(language_header, LOCALES.keys) + preferences.locale = language.header + end + end + end + rescue + preferences = Preferences.from_json("{}") + end + + env.set "preferences", preferences + env.response.headers["X-XSS-Protection"] = "1; mode=block" + env.response.headers["X-Content-Type-Options"] = "nosniff" + + # Allow media resources to be loaded from google servers + # TODO: check if *.youtube.com can be removed + if CONFIG.disabled?("local") || !preferences.local + extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443" + else + extra_media_csp = "" + end + + # Only allow the pages at /embed/* to be embedded + if env.request.resource.starts_with?("/embed") + frame_ancestors = "'self' http: https:" + else + frame_ancestors = "'none'" + end + + # TODO: Remove style-src's 'unsafe-inline', requires to remove all + # inline styles (, style=" [..] ") + env.response.headers["Content-Security-Policy"] = { + "default-src 'none'", + "script-src 'self'", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data:", + "font-src 'self' data:", + "connect-src 'self'", + "manifest-src 'self'", + "media-src 'self' blob:" + extra_media_csp, + "child-src 'self' blob:", + "frame-src 'self'", + "frame-ancestors " + frame_ancestors, + }.join("; ") + + env.response.headers["Referrer-Policy"] = "same-origin" + + # Ask the chrom*-based browsers to disable FLoC + # See: https://blog.runcloud.io/google-floc/ + env.response.headers["Permissions-Policy"] = "interest-cohort=()" + + if (Kemal.config.ssl || CONFIG.https_only) && CONFIG.hsts + env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload" + end + + return if { + "/sb/", + "/vi/", + "/s_p/", + "/yts/", + "/ggpht/", + "/api/manifest/", + "/videoplayback", + "/latest_version", + "/download", + }.any? { |r| env.request.resource.starts_with? r } + + if env.request.cookies.has_key? "SID" + sid = env.request.cookies["SID"].value + + if sid.starts_with? "v1:" + raise "Cannot use token as SID" + end + + # Invidious users only have SID + if !env.request.cookies.has_key? "SSID" + if email = Invidious::Database::SessionIDs.select_email(sid) + user = Invidious::Database::Users.select!(email: email) + csrf_token = generate_response(sid, { + ":authorize_token", + ":playlist_ajax", + ":signout", + ":subscription_ajax", + ":token_ajax", + ":watch_ajax", + }, HMAC_KEY, 1.week) + + preferences = user.preferences + env.set "preferences", preferences + + env.set "sid", sid + env.set "csrf_token", csrf_token + env.set "user", user + end + else + headers = HTTP::Headers.new + headers["Cookie"] = env.request.headers["Cookie"] + + begin + user, sid = get_user(sid, headers, false) + csrf_token = generate_response(sid, { + ":authorize_token", + ":playlist_ajax", + ":signout", + ":subscription_ajax", + ":token_ajax", + ":watch_ajax", + }, HMAC_KEY, 1.week) + + preferences = user.preferences + env.set "preferences", preferences + + env.set "sid", sid + env.set "csrf_token", csrf_token + env.set "user", user + rescue ex + end + end + end + + dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s + thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s + thin_mode = thin_mode == "true" + locale = env.params.query["hl"]? || preferences.locale + + preferences.dark_mode = dark_mode + preferences.thin_mode = thin_mode + preferences.locale = locale + env.set "preferences", preferences + + current_page = env.request.path + if env.request.query + query = HTTP::Params.parse(env.request.query.not_nil!) + + if query["referer"]? + query["referer"] = get_referer(env, "/") + end + + current_page += "?#{query}" + end + + env.set "current_page", URI.encode_www_form(current_page) + end +end From 88ea794fdb6222011020b6fc778f6cd5da70484a Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 01:00:44 +0200 Subject: [PATCH 86/98] routes: move error 404 logic to its own module --- src/invidious.cr | 44 +------------------------------ src/invidious/routes/errors.cr | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 43 deletions(-) create mode 100644 src/invidious/routes/errors.cr diff --git a/src/invidious.cr b/src/invidious.cr index 4a3b0003..aff879e3 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -187,49 +187,7 @@ end Invidious::Routing.register_all error 404 do |env| - if md = env.request.path.match(/^\/(?([a-zA-Z0-9_-]{11})|(\w+))$/) - item = md["id"] - - # Check if item is branding URL e.g. https://youtube.com/gaming - response = YT_POOL.client &.get("/#{item}") - - if response.status_code == 301 - response = YT_POOL.client &.get(URI.parse(response.headers["Location"]).request_target) - end - - if response.body.empty? - env.response.headers["Location"] = "/" - halt env, status_code: 302 - end - - html = XML.parse_html(response.body) - ucid = html.xpath_node(%q(//link[@rel="canonical"])).try &.["href"].split("/")[-1] - - if ucid - env.response.headers["Location"] = "/channel/#{ucid}" - halt env, status_code: 302 - end - - params = [] of String - env.params.query.each do |k, v| - params << "#{k}=#{v}" - end - params = params.join("&") - - url = "/watch?v=#{item}" - if !params.empty? - url += "&#{params}" - end - - # Check if item is video ID - if item.match(/^[a-zA-Z0-9_-]{11}$/) && YT_POOL.client &.head("/watch?v=#{item}").status_code != 404 - env.response.headers["Location"] = url - halt env, status_code: 302 - end - end - - env.response.headers["Location"] = "/" - halt env, status_code: 302 + Invidious::Routes::ErrorRoutes.error_404(env) end error 500 do |env, ex| diff --git a/src/invidious/routes/errors.cr b/src/invidious/routes/errors.cr new file mode 100644 index 00000000..b138b562 --- /dev/null +++ b/src/invidious/routes/errors.cr @@ -0,0 +1,47 @@ +module Invidious::Routes::ErrorRoutes + def self.error_404(env) + if md = env.request.path.match(/^\/(?([a-zA-Z0-9_-]{11})|(\w+))$/) + item = md["id"] + + # Check if item is branding URL e.g. https://youtube.com/gaming + response = YT_POOL.client &.get("/#{item}") + + if response.status_code == 301 + response = YT_POOL.client &.get(URI.parse(response.headers["Location"]).request_target) + end + + if response.body.empty? + env.response.headers["Location"] = "/" + haltf env, status_code: 302 + end + + html = XML.parse_html(response.body) + ucid = html.xpath_node(%q(//link[@rel="canonical"])).try &.["href"].split("/")[-1] + + if ucid + env.response.headers["Location"] = "/channel/#{ucid}" + haltf env, status_code: 302 + end + + params = [] of String + env.params.query.each do |k, v| + params << "#{k}=#{v}" + end + params = params.join("&") + + url = "/watch?v=#{item}" + if !params.empty? + url += "&#{params}" + end + + # Check if item is video ID + if item.match(/^[a-zA-Z0-9_-]{11}$/) && YT_POOL.client &.head("/watch?v=#{item}").status_code != 404 + env.response.headers["Location"] = url + haltf env, status_code: 302 + end + end + + env.response.headers["Location"] = "/" + haltf env, status_code: 302 + end +end From 848a60aa9bfca457ae6e1a470d6fcf3ef03a1f38 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 01:01:31 +0200 Subject: [PATCH 87/98] routes: remove useless 'locale' variable in error 505 handler --- src/invidious.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index aff879e3..0601d5b2 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -191,7 +191,6 @@ error 404 do |env| end error 500 do |env, ex| - locale = env.get("preferences").as(Preferences).locale error_template(500, ex) end From cb8a375c5e7ae79cad8daa9430b65c68f50e2885 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 10 Aug 2022 20:50:49 +0200 Subject: [PATCH 88/98] routing: Directly call Kemal's add_route function --- src/invidious/routing.cr | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 8084b3e4..b1cef086 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -4,7 +4,11 @@ module Invidious::Routing {% for http_method in {"get", "post", "delete", "options", "patch", "put"} %} macro {{http_method.id}}(path, controller, method = :handle) - {{http_method.id}} \{{ path }} do |env| + unless !Kemal::Utils.path_starts_with_slash?(\{{path}}) + raise Kemal::Exceptions::InvalidPathStartException.new({{http_method}}, \{{path}}) + end + + Kemal::RouteHandler::INSTANCE.add_route({{http_method.upcase}}, \{{path}}) do |env| \{{ controller }}.\{{ method.id }}(env) end end From 008983c8e37685e47960b80e35fd65d6b380c44c Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:46 +0200 Subject: [PATCH 89/98] Update Sinhala translation Update Sinhala translation Add Sinhala translation Co-authored-by: DilshanH Co-authored-by: Hosted Weblate --- locales/si.json | 126 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 locales/si.json diff --git a/locales/si.json b/locales/si.json new file mode 100644 index 00000000..69501343 --- /dev/null +++ b/locales/si.json @@ -0,0 +1,126 @@ +{ + "generic_views_count": "බැලීම් {{count}}", + "generic_views_count_plural": "බැලීම් {{count}}", + "generic_videos_count": "{{count}} වීඩියෝව", + "generic_videos_count_plural": "වීඩියෝ {{count}}", + "generic_subscribers_count": "ග්‍රාහකයන් {{count}}", + "generic_subscribers_count_plural": "ග්‍රාහකයන් {{count}}", + "generic_subscriptions_count": "දායකත්ව {{count}}", + "generic_subscriptions_count_plural": "දායකත්ව {{count}}", + "Shared `x` ago": "`x` පෙර බෙදා ගන්නා ලදී", + "Unsubscribe": "දායක නොවන්න", + "View playlist on YouTube": "YouTube හි ධාවන ලැයිස්තුව බලන්න", + "newest": "අලුත්ම", + "oldest": "පැරණිතම", + "popular": "ජනප්‍රිය", + "last": "අවසන්", + "Cannot change password for Google accounts": "Google ගිණුම් සඳහා මුරපදය වෙනස් කළ නොහැක", + "Authorize token?": "ටෝකනය අනුමත කරනවා ද?", + "Authorize token for `x`?": "`x` සඳහා ටෝකනය අනුමත කරනවා ද?", + "Yes": "ඔව්", + "Import and Export Data": "දත්ත ආනයනය සහ අපනයනය කිරීම", + "Import": "ආනයන", + "Import Invidious data": "Invidious JSON දත්ත ආයාත කරන්න", + "Import FreeTube subscriptions (.db)": "FreeTube දායකත්වයන් (.db) ආයාත කරන්න", + "Import NewPipe subscriptions (.json)": "NewPipe දායකත්වයන් (.json) ආයාත කරන්න", + "Import NewPipe data (.zip)": "NewPipe දත්ත (.zip) ආයාත කරන්න", + "Export": "අපනයන", + "Export data as JSON": "Invidious දත්ත JSON ලෙස අපනයනය කරන්න", + "Delete account?": "ගිණුම මකාදමනවා ද?", + "History": "ඉතිහාසය", + "An alternative front-end to YouTube": "YouTube සඳහා විකල්ප ඉදිරිපස අන්තයක්", + "source": "මූලාශ්‍රය", + "Log in/register": "පුරන්න/ලියාපදිංචිවන්න", + "Log in with Google": "Google සමඟ පුරන්න", + "Password": "මුරපදය", + "Time (h:mm:ss):": "වේලාව (h:mm:ss):", + "Sign In": "පුරන්න", + "Preferences": "මනාපයන්", + "preferences_category_player": "වීඩියෝ ධාවක මනාපයන්", + "preferences_video_loop_label": "නැවත නැවතත්: ", + "preferences_autoplay_label": "ස්වයංක්‍රීය වාදනය: ", + "preferences_continue_label": "මීලඟට වාදනය කරන්න: ", + "preferences_continue_autoplay_label": "මීළඟ වීඩියෝව ස්වයංක්‍රීයව ධාවනය කරන්න: ", + "preferences_local_label": "Proxy වීඩියෝ: ", + "preferences_watch_history_label": "නැරඹුම් ඉතිහාසය සබල කරන්න: ", + "preferences_speed_label": "පෙරනිමි වේගය: ", + "preferences_quality_option_dash": "DASH (අනුවර්තිත ගුණත්වය)", + "preferences_quality_option_medium": "මධ්‍යස්ථ", + "preferences_quality_dash_label": "කැමති DASH වීඩියෝ ගුණත්වය: ", + "preferences_quality_dash_option_4320p": "4320p", + "preferences_quality_dash_option_1080p": "1080p", + "preferences_quality_dash_option_480p": "480p", + "preferences_quality_dash_option_360p": "360p", + "preferences_quality_dash_option_144p": "144p", + "preferences_volume_label": "ධාවකයේ හඬ: ", + "preferences_comments_label": "පෙරනිමි අදහස්: ", + "youtube": "YouTube", + "reddit": "Reddit", + "invidious": "Invidious", + "preferences_captions_label": "පෙරනිමි උපසිරැසි: ", + "preferences_related_videos_label": "අදාළ වීඩියෝ පෙන්වන්න: ", + "preferences_annotations_label": "අනුසටහන් පෙන්වන්න: ", + "preferences_vr_mode_label": "අන්තර්ක්‍රියාකාරී අංශක 360 වීඩියෝ (WebGL අවශ්‍යයි): ", + "preferences_region_label": "අන්තර්ගත රට: ", + "preferences_player_style_label": "වීඩියෝ ධාවක විලාසය: ", + "Dark mode: ": "අඳුරු මාදිලිය: ", + "preferences_dark_mode_label": "තේමාව: ", + "light": "ආලෝකමත්", + "generic_playlists_count": "{{count}} ධාවන ලැයිස්තුව", + "generic_playlists_count_plural": "ධාවන ලැයිස්තු {{count}}", + "LIVE": "සජීව", + "Subscribe": "දායක වන්න", + "View channel on YouTube": "YouTube හි නාලිකාව බලන්න", + "Next page": "ඊළඟ පිටුව", + "Previous page": "පෙර පිටුව", + "Clear watch history?": "නැරඹුම් ඉතිහාසය මකාදමනවා ද?", + "No": "නැත", + "Log in": "පුරන්න", + "New password": "නව මුරපදය", + "Import YouTube subscriptions": "YouTube/OPML දායකත්වයන් ආයාත කරන්න", + "Register": "ලියාපදිංචිවන්න", + "New passwords must match": "නව මුරපද ගැලපිය යුතුය", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML ලෙස දායකත්වයන් අපනයනය කරන්න (NewPipe සහ FreeTube සඳහා)", + "Export subscriptions as OPML": "දායකත්වයන් OPML ලෙස අපනයනය කරන්න", + "JavaScript license information": "JavaScript බලපත්‍ර තොරතුරු", + "User ID": "පරිශීලක කේතය", + "Text CAPTCHA": "CAPTCHA පෙල", + "Image CAPTCHA": "CAPTCHA රූපය", + "Google verification code": "Google සත්‍යාපන කේතය", + "E-mail": "විද්‍යුත් තැපෑල", + "preferences_quality_label": "කැමති වීඩියෝ ගුණත්වය: ", + "preferences_quality_option_hd720": "HD720", + "preferences_quality_dash_option_auto": "ස්වයංක්‍රීය", + "preferences_quality_option_small": "කුඩා", + "preferences_quality_dash_option_best": "උසස්", + "preferences_quality_dash_option_2160p": "2160p", + "preferences_quality_dash_option_1440p": "1440p", + "preferences_quality_dash_option_720p": "720p", + "preferences_quality_dash_option_240p": "240p", + "preferences_extend_desc_label": "වීඩියෝ විස්තරය ස්වයංක්‍රීයව දිගහරින්න: ", + "preferences_category_visual": "දෘශ්‍ය මනාපයන්", + "dark": "අඳුරු", + "preferences_category_misc": "විවිධ මනාප", + "preferences_category_subscription": "දායකත්ව මනාප", + "Redirect homepage to feed: ": "මුල් පිටුව පෝෂණය වෙත හරවා යවන්න: ", + "preferences_max_results_label": "සංග්‍රහයේ පෙන්වන වීඩියෝ ගණන: ", + "preferences_sort_label": "වීඩියෝ වර්ග කරන්න: ", + "alphabetically": "අකාරාදී ලෙස", + "alphabetically - reverse": "අකාරාදී - ආපසු", + "channel name": "නාලිකාවේ නම", + "Only show latest video from channel: ": "නාලිකාවේ නවතම වීඩියෝව පමණක් පෙන්වන්න: ", + "preferences_unseen_only_label": "නොබැලූ පමණක් පෙන්වන්න: ", + "Enable web notifications": "වෙබ් දැනුම්දීම් සබල කරන්න", + "Import/export data": "දත්ත ආනයනය / අපනයනය", + "Change password": "මුරපදය වෙනස් කරන්න", + "Manage subscriptions": "දායකත්ව කළමනාකරණය", + "Manage tokens": "ටෝකන කළමනාකරණය", + "Watch history": "නැරඹුම් ඉතිහාසය", + "Save preferences": "මනාප සුරකින්න", + "Token": "ටෝකනය", + "View privacy policy.": "රහස්‍යතා ප්‍රතිපත්තිය බලන්න.", + "Only show latest unwatched video from channel: ": "නාලිකාවේ නවතම නැරඹන නොලද වීඩියෝව පමණක් පෙන්වන්න: ", + "preferences_category_data": "දත්ත මනාප", + "Clear watch history": "නැරඹුම් ඉතිහාසය මකාදැමීම", + "Subscriptions": "දායකත්ව" +} From 190b45086c75df2f76f8abd83460fe50de81bdaf Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:46 +0200 Subject: [PATCH 90/98] Update Russian translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hosted Weblate Co-authored-by: Егор Ермаков --- locales/ru.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index 4680e350..962c82ec 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -102,13 +102,13 @@ "Manage tokens": "Управление токенами", "Watch history": "История просмотров", "Delete account": "Удалить аккаунт", - "preferences_category_admin": "Администраторские настройки", + "preferences_category_admin": "Настройки администратора", "preferences_default_home_label": "Главная страница по умолчанию: ", "preferences_feed_menu_label": "Меню ленты видео: ", "preferences_show_nick_label": "Показать ник вверху: ", "Top enabled: ": "Включить топ видео? ", "CAPTCHA enabled: ": "Включить капчу? ", - "Login enabled: ": "Включить авторизацию? ", + "Login enabled: ": "Включить авторизацию: ", "Registration enabled: ": "Включить регистрацию? ", "Report statistics: ": "Сообщать статистику? ", "Save preferences": "Сохранить настройки", @@ -195,7 +195,7 @@ "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле «токен»", "Erroneous challenge": "Неправильный ответ в «challenge»", "Erroneous token": "Неправильный токен", - "No such user": "Недопустимое имя пользователя", + "No such user": "Пользователь не найден", "Token is expired, please try again": "Срок действия токена истёк, попробуйте позже", "English": "Английский", "English (auto-generated)": "Английский (созданы автоматически)", From 4c23062d1e20bb7cec5fa4225488998715146971 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:46 +0200 Subject: [PATCH 91/98] Update Arabic translation Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa --- locales/ar.json | 80 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index c6ed19ce..38963281 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -368,7 +368,7 @@ "footer_donate_page": "تبرّع", "preferences_region_label": "بلد المحتوى: ", "preferences_quality_dash_label": "جودة فيديو DASH المفضلة: ", - "preferences_quality_option_dash": "DASH (جودة تكييفية)", + "preferences_quality_option_dash": "DASH (الجودة التلقائية)", "preferences_quality_option_hd720": "HD720", "preferences_quality_option_medium": "متوسطة", "preferences_quality_option_small": "صغيرة", @@ -459,5 +459,81 @@ "Spanish (Spain)": "الإسبانية (إسبانيا)", "crash_page_search_issue": "بحثت عن المشكلات الموجودة على GitHub ", "search_filters_title": "معامل الفرز", - "search_message_no_results": "لا توجد نتائج." + "search_message_no_results": "لا توجد نتائج.", + "search_message_change_filters_or_query": "حاول توسيع استعلام البحث و / أو تغيير عوامل التصفية.", + "search_filters_date_label": "تاريخ الرفع", + "generic_count_weeks_0": "{{count}} أسبوع", + "generic_count_weeks_1": "{{count}} أسبوع", + "generic_count_weeks_2": "{{count}} أسبوع", + "generic_count_weeks_3": "{{count}} أسبوع", + "generic_count_weeks_4": "{{count}} أسابيع", + "generic_count_weeks_5": "{{count}} أسبوع", + "Popular enabled: ": "تم تمكين الشعبية: ", + "search_filters_duration_option_medium": "متوسط (4-20 دقيقة)", + "search_filters_date_option_none": "أي تاريخ", + "search_filters_type_option_all": "أي نوع", + "search_filters_features_option_vr180": "VR180", + "generic_count_minutes_0": "{{count}} دقيقة", + "generic_count_minutes_1": "{{count}} دقيقة", + "generic_count_minutes_2": "{{count}} دقيقة", + "generic_count_minutes_3": "{{count}} دقيقة", + "generic_count_minutes_4": "{{count}} دقائق", + "generic_count_minutes_5": "{{count}} دقيقة", + "generic_count_hours_0": "{{count}} ساعة", + "generic_count_hours_1": "{{count}} ساعة", + "generic_count_hours_2": "{{count}} ساعة", + "generic_count_hours_3": "{{count}} ساعة", + "generic_count_hours_4": "{{count}} ساعات", + "generic_count_hours_5": "{{count}} ساعة", + "comments_view_x_replies_0": "عرض رد {{count}}", + "comments_view_x_replies_1": "عرض رد {{count}}", + "comments_view_x_replies_2": "عرض رد {{count}}", + "comments_view_x_replies_3": "عرض رد {{count}}", + "comments_view_x_replies_4": "عرض الردود {{count}}", + "comments_view_x_replies_5": "عرض رد {{count}}", + "search_message_use_another_instance": " يمكنك أيضًا البحث عن في مثيل آخر .", + "comments_points_count_0": "{{count}} نقطة", + "comments_points_count_1": "{{count}} نقطة", + "comments_points_count_2": "{{count}} نقطة", + "comments_points_count_3": "{{count}} نقطة", + "comments_points_count_4": "{{count}} نقاط", + "comments_points_count_5": "{{count}} نقطة", + "generic_count_years_0": "{{count}} السنة", + "generic_count_years_1": "{{count}} السنة", + "generic_count_years_2": "{{count}} السنة", + "generic_count_years_3": "{{count}} السنة", + "generic_count_years_4": "{{count}} سنوات", + "generic_count_years_5": "{{count}} السنة", + "tokens_count_0": "الرمز المميز {{count}}", + "tokens_count_1": "الرمز المميز {{count}}", + "tokens_count_2": "الرمز المميز {{count}}", + "tokens_count_3": "الرمز المميز {{count}}", + "tokens_count_4": "الرموز المميزة {{count}}", + "tokens_count_5": "الرمز المميز {{count}}", + "search_filters_apply_button": "تطبيق الفلاتر المحددة", + "search_filters_duration_option_none": "أي مدة", + "subscriptions_unseen_notifs_count_0": "{{count}} إشعار غير مرئي", + "subscriptions_unseen_notifs_count_1": "{{count}} إشعار غير مرئي", + "subscriptions_unseen_notifs_count_2": "{{count}} إشعار غير مرئي", + "subscriptions_unseen_notifs_count_3": "{{count}} إشعار غير مرئي", + "subscriptions_unseen_notifs_count_4": "{{count}} إشعارات غير مرئية", + "subscriptions_unseen_notifs_count_5": "{{count}} إشعار غير مرئي", + "generic_count_days_0": "{{count}} يوم", + "generic_count_days_1": "{{count}} يوم", + "generic_count_days_2": "{{count}} يوم", + "generic_count_days_3": "{{count}} يوم", + "generic_count_days_4": "{{count}} أيام", + "generic_count_days_5": "{{count}} يوم", + "generic_count_months_0": "{{count}} شهر", + "generic_count_months_1": "{{count}} شهر", + "generic_count_months_2": "{{count}} شهر", + "generic_count_months_3": "{{count}} شهر", + "generic_count_months_4": "{{count}} شهور", + "generic_count_months_5": "{{count}} شهر", + "generic_count_seconds_0": "{{count}} ثانية", + "generic_count_seconds_1": "{{count}} ثانية", + "generic_count_seconds_2": "{{count}} ثانية", + "generic_count_seconds_3": "{{count}} ثانية", + "generic_count_seconds_4": "{{count}} ثوانٍ", + "generic_count_seconds_5": "{{count}} ثانية" } From 5c71adb137af128c6abb05ae2ee54efd6f46a956 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:46 +0200 Subject: [PATCH 92/98] =?UTF-8?q?Update=20Norwegian=20Bokm=C3=A5l=20transl?= =?UTF-8?q?ation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hosted Weblate Co-authored-by: Petter Reinholdtsen --- locales/nb-NO.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/nb-NO.json b/locales/nb-NO.json index 77c688d5..7e964515 100644 --- a/locales/nb-NO.json +++ b/locales/nb-NO.json @@ -461,12 +461,12 @@ "Dutch (auto-generated)": "Nederlandsk (laget automatisk)", "Turkish (auto-generated)": "Tyrkisk (laget automatisk)", "search_filters_title": "Filtrer", - "Popular enabled: ": "Populære påskrudd: ", + "Popular enabled: ": "Populære aktiv: ", "search_message_change_filters_or_query": "Prøv ett mindre snevert søk og/eller endre filterne.", "search_filters_duration_option_medium": "Middels (4–20 minutter)", "search_message_no_results": "Resultatløst.", "search_filters_type_option_all": "Alle typer", - "search_filters_duration_option_none": "Uvilkårlig varighet", + "search_filters_duration_option_none": "Enhver varighet", "search_message_use_another_instance": " Du kan også søke på en annen instans.", "search_filters_date_label": "Opplastningsdato", "search_filters_apply_button": "Bruk valgte filtre", From 89c12f2585fd07b2c83c7ae80e03040190abc587 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:46 +0200 Subject: [PATCH 93/98] Update Italian translation Co-authored-by: atilluF --- locales/it.json | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/locales/it.json b/locales/it.json index ac83ac58..facf2594 100644 --- a/locales/it.json +++ b/locales/it.json @@ -14,7 +14,7 @@ "newest": "più recente", "oldest": "più vecchio", "popular": "Tendenze", - "last": "durare", + "last": "ultimo", "Next page": "Pagina successiva", "Previous page": "Pagina precedente", "Clear watch history?": "Eliminare la cronologia dei video guardati?", @@ -158,7 +158,7 @@ "generic_views_count_plural": "{{count}} visualizzazioni", "Premieres in `x`": "In anteprima in `x`", "Premieres `x`": "In anteprima `x`", - "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Ciao! Sembra che tu abbia disattivato JavaScript. Clicca qui per visualizzare i commenti. Considera che potrebbe volerci più tempo.", + "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Ciao, Sembra che tu abbia disattivato JavaScript. Clicca qui per visualizzare i commenti, ma considera che il caricamento potrebbe richiedere più tempo.", "View YouTube comments": "Visualizza i commenti da YouTube", "View more comments on Reddit": "Visualizza più commenti su Reddit", "View `x` comments": { @@ -212,7 +212,7 @@ "Azerbaijani": "Azero", "Bangla": "Bengalese", "Basque": "Basco", - "Belarusian": "Biellorusso", + "Belarusian": "Bielorusso", "Bosnian": "Bosniaco", "Bulgarian": "Bulgaro", "Burmese": "Birmano", @@ -238,10 +238,10 @@ "Haitian Creole": "Creolo haitiano", "Hausa": "Lingua hausa", "Hawaiian": "Hawaiano", - "Hebrew": "Ebreo", + "Hebrew": "Ebraico", "Hindi": "Hindi", "Hmong": "Hmong", - "Hungarian": "Ungarese", + "Hungarian": "Ungherese", "Icelandic": "Islandese", "Igbo": "Igbo", "Indonesian": "Indonesiano", @@ -254,7 +254,7 @@ "Khmer": "Khmer", "Korean": "Coreano", "Kurdish": "Curdo", - "Kyrgyz": "Kirghize", + "Kyrgyz": "Kirghiso", "Lao": "Lao", "Latin": "Latino", "Latvian": "Lettone", @@ -269,7 +269,7 @@ "Marathi": "Marathi", "Mongolian": "Mongolo", "Nepali": "Nepalese", - "Norwegian Bokmål": "Norvegese", + "Norwegian Bokmål": "Norvegese bokmål", "Nyanja": "Nyanja", "Pashto": "Pashtu", "Persian": "Persiano", @@ -278,7 +278,7 @@ "Punjabi": "Punjabi", "Romanian": "Rumeno", "Russian": "Russo", - "Samoan": "Samoan", + "Samoan": "Samoano", "Scottish Gaelic": "Gaelico scozzese", "Serbian": "Serbo", "Shona": "Shona", @@ -293,15 +293,15 @@ "Sundanese": "Sudanese", "Swahili": "Swahili", "Swedish": "Svedese", - "Tajik": "Tajik", + "Tajik": "Tagico", "Tamil": "Tamil", "Telugu": "Telugu", - "Thai": "Thaï", + "Thai": "Thailandese", "Turkish": "Turco", "Ukrainian": "Ucraino", "Urdu": "Urdu", "Uzbek": "Uzbeco", - "Vietnamese": "Vietnamese", + "Vietnamese": "Vietnamita", "Welsh": "Gallese", "Western Frisian": "Frisone occidentale", "Xhosa": "Xhosa", @@ -364,7 +364,7 @@ "search_filters_type_option_channel": "Canale", "search_filters_type_option_playlist": "Playlist", "search_filters_type_option_movie": "Film", - "search_filters_features_option_hd": "AD", + "search_filters_features_option_hd": "HD", "search_filters_features_option_subtitles": "Sottotitoli / CC", "search_filters_features_option_c_commons": "Creative Commons", "search_filters_features_option_three_d": "3D", @@ -383,7 +383,7 @@ "preferences_quality_dash_option_4320p": "4320p", "search_filters_features_option_three_sixty": "360°", "preferences_quality_dash_option_144p": "144p", - "Released under the AGPLv3 on Github.": "Rilasciato su GitHub con licenza AGPLv3.", + "Released under the AGPLv3 on Github.": "Pubblicato su GitHub con licenza AGPLv3.", "preferences_quality_option_medium": "Media", "preferences_quality_option_small": "Limitata", "preferences_quality_dash_option_best": "Migliore", @@ -430,7 +430,7 @@ "comments_view_x_replies_plural": "Vedi {{count}} risposte", "comments_points_count": "{{count}} punto", "comments_points_count_plural": "{{count}} punti", - "Portuguese (auto-generated)": "Portoghese (auto-generato)", + "Portuguese (auto-generated)": "Portoghese (generati automaticamente)", "crash_page_you_found_a_bug": "Sembra che tu abbia trovato un bug in Invidious!", "crash_page_switch_instance": "provato a usare un'altra istanza", "crash_page_before_reporting": "Prima di segnalare un bug, assicurati di aver:", @@ -441,7 +441,7 @@ "English (United Kingdom)": "Inglese (Regno Unito)", "Portuguese (Brazil)": "Portoghese (Brasile)", "preferences_watch_history_label": "Attiva cronologia di riproduzione: ", - "French (auto-generated)": "Francese (auto-generato)", + "French (auto-generated)": "Francese (generati automaticamente)", "search_message_use_another_instance": " Puoi anche cercare in un'altra istanza.", "search_message_no_results": "Nessun risultato trovato.", "search_message_change_filters_or_query": "Prova ad ampliare la ricerca e/o modificare i filtri.", @@ -451,15 +451,15 @@ "Chinese (China)": "Cinese (Cina)", "Chinese (Hong Kong)": "Cinese (Hong Kong)", "Chinese (Taiwan)": "Cinese (Taiwan)", - "Dutch (auto-generated)": "Olandese (auto-generato)", - "German (auto-generated)": "Tedesco (auto-generato)", - "Indonesian (auto-generated)": "Indonesiano (auto-generato)", + "Dutch (auto-generated)": "Olandese (generati automaticamente)", + "German (auto-generated)": "Tedesco (generati automaticamente)", + "Indonesian (auto-generated)": "Indonesiano (generati automaticamente)", "Interlingue": "Interlingua", - "Italian (auto-generated)": "Italiano (auto-generato)", - "Japanese (auto-generated)": "Giapponese (auto-generato)", - "Korean (auto-generated)": "Coreano (auto-generato)", - "Russian (auto-generated)": "Russo (auto-generato)", - "Spanish (auto-generated)": "Spagnolo (auto-generato)", + "Italian (auto-generated)": "Italiano (generati automaticamente)", + "Japanese (auto-generated)": "Giapponese (generati automaticamente)", + "Korean (auto-generated)": "Coreano (generati automaticamente)", + "Russian (auto-generated)": "Russo (generati automaticamente)", + "Spanish (auto-generated)": "Spagnolo (generati automaticamente)", "Spanish (Mexico)": "Spagnolo (Messico)", "Spanish (Spain)": "Spagnolo (Spagna)", "Turkish (auto-generated)": "Turco (auto-generato)", From fd0417b14cc3a40f53a901b9fb4be97057c0985b Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:46 +0200 Subject: [PATCH 94/98] Update Greek translation Co-authored-by: Hosted Weblate Co-authored-by: THANOS SIOURDAKIS --- locales/el.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locales/el.json b/locales/el.json index 048a520b..d91d64fc 100644 --- a/locales/el.json +++ b/locales/el.json @@ -449,5 +449,6 @@ "videoinfo_invidious_embed_link": "Σύνδεσμος Ενσωμάτωσης", "search_filters_type_option_show": "Μπάρα προόδου διαβάσματος", "preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ", - "search_filters_title": "Φίλτρο" + "search_filters_title": "Φίλτρο", + "search_message_no_results": "Δεν" } From 7b9693bca49cf39aae221a9c131e3def6b2af9df Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:47 +0200 Subject: [PATCH 95/98] Update German translation Co-authored-by: Hosted Weblate Co-authored-by: Pixelcode --- locales/de.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/locales/de.json b/locales/de.json index 24b83bb3..3ac32a31 100644 --- a/locales/de.json +++ b/locales/de.json @@ -367,7 +367,7 @@ "adminprefs_modified_source_code_url_label": "URL zum Repositorie des modifizierten Quellcodes", "search_filters_duration_option_short": "Kurz (< 4 Minuten)", "preferences_region_label": "Land der Inhalte: ", - "preferences_quality_option_dash": "DASH (automatische Qualität)", + "preferences_quality_option_dash": "DASH (adaptive Qualität)", "preferences_quality_option_hd720": "HD720", "preferences_quality_option_medium": "Mittel", "preferences_quality_option_small": "Niedrig", @@ -460,5 +460,16 @@ "Chinese (Taiwan)": "Chinesisch (Taiwan)", "Korean (auto-generated)": "Koreanisch (automatisch generiert)", "Portuguese (auto-generated)": "Portugiesisch (automatisch generiert)", - "search_filters_title": "Filtern" + "search_filters_title": "Filtern", + "search_message_change_filters_or_query": "Versuchen Sie, Ihre Suchanfrage zu erweitern und/oder die Filter zu ändern.", + "search_message_use_another_instance": " Sie können auch auf einer anderen Instanz suchen.", + "Popular enabled: ": "„Beliebt“-Seite aktiviert: ", + "search_message_no_results": "Keine Ergebnisse gefunden.", + "search_filters_duration_option_medium": "Mittel (4 - 20 Minuten)", + "search_filters_features_option_vr180": "VR180", + "search_filters_type_option_all": "Beliebiger Typ", + "search_filters_apply_button": "Ausgewählte Filter anwenden", + "search_filters_duration_option_none": "Beliebige Länge", + "search_filters_date_label": "Upload-Datum", + "search_filters_date_option_none": "Beliebiges Datum" } From 56fe591eee15a9503c764b64fc17fa0494159b89 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:47 +0200 Subject: [PATCH 96/98] Update Portuguese (Portugal) translation Co-authored-by: Hosted Weblate Co-authored-by: Tmpod --- locales/pt-PT.json | 56 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/locales/pt-PT.json b/locales/pt-PT.json index b00ebc72..5313915b 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -408,5 +408,59 @@ "preferences_quality_dash_option_1080p": "1080p", "preferences_quality_dash_option_480p": "480p", "preferences_quality_dash_option_360p": "360p", - "preferences_quality_dash_option_240p": "240p" + "preferences_quality_dash_option_240p": "240p", + "Video unavailable": "Vídeo indisponível", + "Russian (auto-generated)": "Russo (geradas automaticamente)", + "comments_view_x_replies": "Ver {{count}} resposta", + "comments_view_x_replies_plural": "Ver {{count}} respostas", + "comments_points_count": "{{count}} ponto", + "comments_points_count_plural": "{{count}} pontos", + "English (United Kingdom)": "Inglês (Reino Unido)", + "Chinese (Hong Kong)": "Chinês (Hong Kong)", + "Chinese (Taiwan)": "Chinês (Taiwan)", + "Dutch (auto-generated)": "Holandês (geradas automaticamente)", + "French (auto-generated)": "Francês (geradas automaticamente)", + "German (auto-generated)": "Alemão (geradas automaticamente)", + "Indonesian (auto-generated)": "Indonésio (geradas automaticamente)", + "Interlingue": "Interlingue", + "Italian (auto-generated)": "Italiano (geradas automaticamente)", + "Japanese (auto-generated)": "Japonês (geradas automaticamente)", + "Korean (auto-generated)": "Coreano (geradas automaticamente)", + "Portuguese (auto-generated)": "Português (geradas automaticamente)", + "Portuguese (Brazil)": "Português (Brasil)", + "Spanish (Spain)": "Espanhol (Espanha)", + "Vietnamese (auto-generated)": "Vietnamita (geradas automaticamente)", + "search_filters_type_option_all": "Qualquer tipo", + "search_filters_duration_option_none": "Qualquer duração", + "search_filters_duration_option_short": "Curto (< 4 minutos)", + "search_filters_duration_option_medium": "Médio (4 - 20 minutos)", + "search_filters_duration_option_long": "Longo (> 20 minutos)", + "search_filters_features_option_purchased": "Comprado", + "search_filters_apply_button": "Aplicar filtros selecionados", + "videoinfo_watch_on_youTube": "Ver no YouTube", + "videoinfo_youTube_embed_link": "Embutir", + "adminprefs_modified_source_code_url_label": "URL do repositório do código-fonte modificado", + "videoinfo_invidious_embed_link": "Ligação embutida", + "none": "nenhum", + "videoinfo_started_streaming_x_ago": "Entrou em direto há `x`", + "download_subtitles": "Legendas - `x` (.vtt)", + "user_created_playlists": "`x` listas de reprodução criadas", + "user_saved_playlists": "`x` listas de reprodução guardadas", + "preferences_save_player_pos_label": "Guardar posição de reprodução: ", + "Turkish (auto-generated)": "Turco (geradas automaticamente)", + "Cantonese (Hong Kong)": "Cantonês (Hong Kong)", + "Chinese (China)": "Chinês (China)", + "Spanish (auto-generated)": "Espanhol (geradas automaticamente)", + "Spanish (Mexico)": "Espanhol (México)", + "English (United States)": "Inglês (Estados Unidos)", + "footer_donate_page": "Doar", + "footer_documentation": "Documentação", + "footer_source_code": "Código-fonte", + "footer_original_source_code": "Código-fonte original", + "footer_modfied_source_code": "Código-fonte modificado", + "Chinese": "Chinês", + "search_filters_date_label": "Data de carregamento", + "search_filters_date_option_none": "Qualquer data", + "search_filters_features_option_three_sixty": "360°", + "search_filters_features_option_vr180": "VR180" } From ed0ad587dccdef0b4e8fad457916a3573f0aca4f Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 11 Aug 2022 01:03:47 +0200 Subject: [PATCH 97/98] Update Indonesian translation Co-authored-by: Hosted Weblate Co-authored-by: uwu as a service --- locales/id.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/locales/id.json b/locales/id.json index d150cece..ad80efcf 100644 --- a/locales/id.json +++ b/locales/id.json @@ -126,7 +126,7 @@ "revoke": "cabut", "Subscriptions": "Langganan", "subscriptions_unseen_notifs_count_0": "{{count}} pemberitahuan belum dilihat", - "search": "cari", + "search": "Telusuri", "Log out": "Keluar", "Released under the AGPLv3 on Github.": "Dirilis di bawah AGPLv3 di GitHub.", "Source available here.": "Sumber tersedia di sini.", @@ -447,5 +447,6 @@ "Dutch (auto-generated)": "Belanda (dihasilkan secara otomatis)", "search_filters_date_option_none": "Tanggal berapa pun", "search_filters_duration_option_none": "Durasi berapa pun", - "search_filters_duration_option_medium": "Sedang (4 - 20 menit)" + "search_filters_duration_option_medium": "Sedang (4 - 20 menit)", + "Cantonese (Hong Kong)": "Bahasa Kanton (Hong Kong)" } From 9e58bc19c4baf7ca7da97c2f8b164789d041d9b8 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 13 Aug 2022 20:23:45 +0200 Subject: [PATCH 98/98] Fix #3265 --- src/invidious/routing.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index b1cef086..f409f13c 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -4,7 +4,7 @@ module Invidious::Routing {% for http_method in {"get", "post", "delete", "options", "patch", "put"} %} macro {{http_method.id}}(path, controller, method = :handle) - unless !Kemal::Utils.path_starts_with_slash?(\{{path}}) + unless Kemal::Utils.path_starts_with_slash?(\{{path}}) raise Kemal::Exceptions::InvalidPathStartException.new({{http_method}}, \{{path}}) end