From d16477602448f7f5ca0f04ffcebf3100575bf703 Mon Sep 17 00:00:00 2001 From: Chunky programmer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:27:26 -0400 Subject: [PATCH 01/28] Playlists: Fix paging for Invidious playlists --- src/invidious/routes/playlists.cr | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/invidious/routes/playlists.cr b/src/invidious/routes/playlists.cr index 8675fa45..a65ff64c 100644 --- a/src/invidious/routes/playlists.cr +++ b/src/invidious/routes/playlists.cr @@ -410,8 +410,13 @@ module Invidious::Routes::Playlists return error_template(500, ex) end - page_count = (playlist.video_count / 200).to_i - page_count += 1 if (playlist.video_count % 200) > 0 + if playlist.is_a? InvidiousPlaylist + page_count = (playlist.video_count / 100).to_i + page_count += 1 if (playlist.video_count % 100) > 0 + else + page_count = (playlist.video_count / 200).to_i + page_count += 1 if (playlist.video_count % 200) > 0 + end if page > page_count return env.redirect "/playlist?list=#{plid}&page=#{page_count}" @@ -422,7 +427,11 @@ module Invidious::Routes::Playlists end begin - videos = get_playlist_videos(playlist, offset: (page - 1) * 200) + if playlist.is_a? InvidiousPlaylist + videos = get_playlist_videos(playlist, offset: (page - 1) * 100) + else + videos = get_playlist_videos(playlist, offset: (page - 1) * 200) + end rescue ex return error_template(500, "Error encountered while retrieving playlist videos.
#{ex.message}") end From a38edd733002166d334261abb39a220c8972ca25 Mon Sep 17 00:00:00 2001 From: Omer Naveed Date: Sat, 1 Jul 2023 12:29:02 -0500 Subject: [PATCH 02/28] Fix Nil assertion failed in RSS feeds --- src/invidious/routes/feeds.cr | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index fc62c5a3..60f8db05 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -154,20 +154,26 @@ module Invidious::Routes::Feeds return error_atom(500, ex) end + namespaces = { + "yt" => "http://www.youtube.com/xml/schemas/2015", + "media" => "http://search.yahoo.com/mrss/", + "default" => "http://www.w3.org/2005/Atom", + } + response = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{channel.ucid}") - rss = XML.parse_html(response.body) + rss = XML.parse(response.body) - videos = rss.xpath_nodes("//feed/entry").map do |entry| - video_id = entry.xpath_node("videoid").not_nil!.content - title = entry.xpath_node("title").not_nil!.content + videos = rss.xpath_nodes("//default:feed/default:entry", namespaces).map do |entry| + video_id = entry.xpath_node("yt:videoId", namespaces).not_nil!.content + title = entry.xpath_node("default:title", namespaces).not_nil!.content - published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content) - updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) + published = Time.parse_rfc3339(entry.xpath_node("default:published", namespaces).not_nil!.content) + updated = Time.parse_rfc3339(entry.xpath_node("default:updated", namespaces).not_nil!.content) - author = entry.xpath_node("author/name").not_nil!.content - ucid = entry.xpath_node("channelid").not_nil!.content - description_html = entry.xpath_node("group/description").not_nil!.to_s - views = entry.xpath_node("group/community/statistics").not_nil!.["views"].to_i64 + author = entry.xpath_node("default:author/default:name", namespaces).not_nil!.content + ucid = entry.xpath_node("yt:channelId", namespaces).not_nil!.content + description_html = entry.xpath_node("media:group/media:description", namespaces).not_nil!.to_s + views = entry.xpath_node("media:group/media:community/media:statistics", namespaces).not_nil!.["views"].to_i64 SearchVideo.new({ title: title, From 0ba22ef391a7b350d139dfd256aa20a7e1f812ed Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 18 Apr 2023 00:04:49 +0200 Subject: [PATCH 03/28] I18n: Add a function to determine if a given locale is RTL --- src/invidious/helpers/i18n.cr | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/invidious/helpers/i18n.cr b/src/invidious/helpers/i18n.cr index a9ed1f64..76e477a4 100644 --- a/src/invidious/helpers/i18n.cr +++ b/src/invidious/helpers/i18n.cr @@ -165,3 +165,12 @@ def translate_bool(locale : String?, translation : Bool) return translate(locale, "No") end end + +def locale_is_rtl?(locale : String?) + # Fallback to en-US + return false if locale.nil? + + # Arabic, Persian, Hebrew + # See https://en.wikipedia.org/wiki/Right-to-left_script#List_of_RTL_scripts + return {"ar", "fa", "he"}.includes? locale +end From 462609d90d38ec8e9aada1d700cfbca46e906552 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Wed, 26 Apr 2023 22:30:13 +0200 Subject: [PATCH 04/28] Utils: Create a function to append parameters to a base URL --- src/invidious/http_server/utils.cr | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/invidious/http_server/utils.cr b/src/invidious/http_server/utils.cr index e3f1fa0f..222dfc4a 100644 --- a/src/invidious/http_server/utils.cr +++ b/src/invidious/http_server/utils.cr @@ -1,3 +1,5 @@ +require "uri" + module Invidious::HttpServer module Utils extend self @@ -16,5 +18,23 @@ module Invidious::HttpServer return "#{url.request_target}?#{params}" end end + + def add_params_to_url(url : String | URI, params : URI::Params) : URI + url = URI.parse(url) if url.is_a?(String) + + url_query = url.query || "" + + # Append the parameters + url.query = String.build do |str| + if !url_query.empty? + str << url_query + str << '&' + end + + str << params + end + + return url + end end end From c0887497447a24cad1f1e8b8268b8ccfbc78ae77 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 17 Apr 2023 21:25:00 +0200 Subject: [PATCH 05/28] HTML: Add code to generate page nav buttons --- src/invidious/frontend/pagination.cr | 97 ++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/invidious/frontend/pagination.cr diff --git a/src/invidious/frontend/pagination.cr b/src/invidious/frontend/pagination.cr new file mode 100644 index 00000000..3f931f4e --- /dev/null +++ b/src/invidious/frontend/pagination.cr @@ -0,0 +1,97 @@ +require "uri" + +module Invidious::Frontend::Pagination + extend self + + private def previous_page(str : String::Builder, locale : String?, url : String) + # Link + str << %() + + if locale_is_rtl?(locale) + # Inverted arrow ("previous" points to the right) + str << translate(locale, "Previous page") + str << "  " + str << %() + else + # Regular arrow ("previous" points to the left) + str << %() + str << "  " + str << translate(locale, "Previous page") + end + + str << "" + end + + private def next_page(str : String::Builder, locale : String?, url : String) + # Link + str << %() + + if locale_is_rtl?(locale) + # Inverted arrow ("next" points to the left) + str << %() + str << "  " + str << translate(locale, "Next page") + else + # Regular arrow ("next" points to the right) + str << translate(locale, "Next page") + str << "  " + str << %() + end + + str << "" + end + + def nav_numeric(locale : String?, *, base_url : String | URI, current_page : Int, show_next : Bool = true) + return String.build do |str| + str << %(
\n) + str << %(\n) + str << %(
\n\n) + end + end + + def nav_ctoken(locale : String?, *, base_url : String | URI, ctoken : String?) + return String.build do |str| + str << %(
\n) + str << %(\n) + str << %(
\n\n) + end + end +end From 57c7b922f7c3cd04d08bb6be9793464d31213fb1 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 17 Apr 2023 21:26:04 +0200 Subject: [PATCH 06/28] HTML: Make a dedicated ECR component for items + pagination --- src/invidious/views/components/items_paginated.ecr | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/invidious/views/components/items_paginated.ecr diff --git a/src/invidious/views/components/items_paginated.ecr b/src/invidious/views/components/items_paginated.ecr new file mode 100644 index 00000000..c82b1772 --- /dev/null +++ b/src/invidious/views/components/items_paginated.ecr @@ -0,0 +1,11 @@ +<%= page_nav_html %> + +
+ <%- videos.each do |item| -%> + <%= rendered "components/item" %> + <%- end -%> +
+ +<%= page_nav_html %> + + From 77d401cec257b1f8b1b5c233134789441083fcdc Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Thu, 20 Apr 2023 18:55:35 +0200 Subject: [PATCH 07/28] CSS: add styling for the new buttons --- assets/css/default.css | 54 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/assets/css/default.css b/assets/css/default.css index 431a0427..eb90c09c 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -115,6 +115,11 @@ div { padding-right: 10px; } + +/* + * Buttons + */ + body a.pure-button { color: rgba(0,0,0,.8); } @@ -127,14 +132,36 @@ body a.pure-button-primary, color: rgba(35, 35, 35, 1); } +.pure-button-primary, +.pure-button-secondary { + border: 1px solid #a0a0a0; + border-radius: 3px; + margin: 0 .4em; +} + +.dark-theme .pure-button-secondary { + background-color: #0002; + color: #ddd; +} + button.pure-button-primary:hover, -body a.pure-button-primary:hover, button.pure-button-primary:focus, +body a.pure-button-primary:hover, body a.pure-button-primary:focus { background-color: rgba(0, 182, 240, 1); color: #fff; } +button.pure-button-secondary:hover, +button.pure-button-secondary:focus { + border-color: rgba(0, 182, 240, 1); +} + + +/* + * Thumbnails + */ + div.thumbnail { padding: 28.125%; position: relative; @@ -192,6 +219,7 @@ div.watched-indicator { top: -0.7em; } + /* * Navbar */ @@ -347,6 +375,22 @@ p.video-data { margin: 0; font-weight: bold; font-size: 80%; } border: none; } + +/* + * Page navigation + */ + +.page-nav-container { margin: 15px 0 30px 0; } + +.page-prev-container { text-align: start; } +.page-next-container { text-align: end; } + +.page-prev-container, +.page-next-container { + display: inline-block; +} + + /* * Footer */ @@ -389,6 +433,7 @@ span > select { word-wrap: normal; } + /* * Light theme */ @@ -453,6 +498,7 @@ span > select { } } + /* * Dark theme */ @@ -539,6 +585,12 @@ body.dark-theme { } } + +/* + * Miscellanous + */ + + /*With commit d9528f5 all contents of the page is now within a flexbox. However, the hr element is rendered improperly within one. See https://stackoverflow.com/a/34372979 for more info */ From c4ef3bed9556700c4c4e8c02c394d16fd3aae03d Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 18 Apr 2023 00:04:01 +0200 Subject: [PATCH 08/28] HTML: Use the new pagination component for playlists --- src/invidious/routes/playlists.cr | 22 ++++++++++++++++ src/invidious/views/add_playlist_items.ecr | 30 +--------------------- src/invidious/views/edit_playlist.ecr | 25 +----------------- src/invidious/views/playlist.ecr | 25 +----------------- 4 files changed, 25 insertions(+), 77 deletions(-) diff --git a/src/invidious/routes/playlists.cr b/src/invidious/routes/playlists.cr index 1dd3f32e..604fe4e1 100644 --- a/src/invidious/routes/playlists.cr +++ b/src/invidious/routes/playlists.cr @@ -170,6 +170,13 @@ module Invidious::Routes::Playlists csrf_token = generate_response(sid, {":edit_playlist"}, HMAC_KEY) + # Pagination + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/playlist?list=#{playlist.id}", + current_page: page, + show_next: (videos.size == 100) + ) + templated "edit_playlist" end @@ -252,6 +259,14 @@ module Invidious::Routes::Playlists videos = [] of SearchVideo end + # Pagination + query_encoded = URI.encode_www_form(query.try &.text || "", space_to_plus: true) + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/add_playlist_items?list=#{playlist.id}&q=#{query_encoded}", + current_page: page, + show_next: (videos.size >= 20) + ) + env.set "add_playlist_items", plid templated "add_playlist_items" end @@ -427,6 +442,13 @@ module Invidious::Routes::Playlists env.set "remove_playlist_items", plid end + # Pagination + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/playlist?list=#{playlist.id}", + current_page: page, + show_next: (page_count != 1 && page < page_count) + ) + templated "playlist" end diff --git a/src/invidious/views/add_playlist_items.ecr b/src/invidious/views/add_playlist_items.ecr index bcba74cf..6aea82ae 100644 --- a/src/invidious/views/add_playlist_items.ecr +++ b/src/invidious/views/add_playlist_items.ecr @@ -31,33 +31,5 @@ -
- <% videos.each_slice(4) do |slice| %> - <% slice.each do |item| %> - <%= rendered "components/item" %> - <% end %> - <% end %> -
- - -<% if query %> - <%- query_encoded = URI.encode_www_form(query.text, space_to_plus: true) -%> -
-
- <% if query.page > 1 %> - - <%= translate(locale, "Previous page") %> - - <% end %> -
-
-
- <% if videos.size >= 20 %> - - <%= translate(locale, "Next page") %> - - <% end %> -
-
-<% end %> +<%= rendered "components/items_paginated" %> diff --git a/src/invidious/views/edit_playlist.ecr b/src/invidious/views/edit_playlist.ecr index 548104c8..d2981886 100644 --- a/src/invidious/views/edit_playlist.ecr +++ b/src/invidious/views/edit_playlist.ecr @@ -56,28 +56,5 @@
-
-<% videos.each do |item| %> - <%= rendered "components/item" %> -<% end %> -
- - -
-
- <% if page > 1 %> - - <%= translate(locale, "Previous page") %> - - <% end %> -
-
-
- <% if videos.size == 100 %> - - <%= translate(locale, "Next page") %> - - <% end %> -
-
+<%= rendered "components/items_paginated" %> diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index a04acf4c..08995a83 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -100,28 +100,5 @@ <% end %> -
-<% videos.each do |item| %> - <%= rendered "components/item" %> -<% end %> -
- - -
-
- <% if page > 1 %> - - <%= translate(locale, "Previous page") %> - - <% end %> -
-
-
- <% if page_count != 1 && page < page_count %> - - <%= translate(locale, "Next page") %> - - <% end %> -
-
+<%= rendered "components/items_paginated" %> From efaf7cb09c8aad606d59cacab71c4a0a269d785b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 18 Apr 2023 00:12:56 +0200 Subject: [PATCH 09/28] HTML: Use the new pagination component for search results --- src/invidious/routes/search.cr | 22 +++++++++++++-------- src/invidious/views/hashtag.ecr | 35 +-------------------------------- src/invidious/views/search.ecr | 35 +-------------------------------- 3 files changed, 16 insertions(+), 76 deletions(-) diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index 6c3088de..edf0351c 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -59,17 +59,21 @@ 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) + # Pagination + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/search?#{query.to_http_params}", + current_page: query.page, + show_next: (videos.size >= 20) + ) + if query.type == Invidious::Search::Query::Type::Channel env.set "search", "channel:#{query.channel} #{query.text}" else env.set "search", query.text end + templated "search" end end @@ -96,11 +100,13 @@ module Invidious::Routes::Search return error_template(500, ex) end - params = env.params.query.empty? ? "" : "&#{env.params.query}" - + # Pagination hashtag_encoded = URI.encode_www_form(hashtag, space_to_plus: false) - url_prev_page = "/hashtag/#{hashtag_encoded}?page=#{page - 1}#{params}" - url_next_page = "/hashtag/#{hashtag_encoded}?page=#{page + 1}#{params}" + page_nav_html = Frontend::Pagination.nav_numeric(locale, + base_url: "/hashtag/#{hashtag_encoded}", + current_page: page, + show_next: (videos.size >= 60) + ) templated "hashtag" end diff --git a/src/invidious/views/hashtag.ecr b/src/invidious/views/hashtag.ecr index 3351c21c..2000337e 100644 --- a/src/invidious/views/hashtag.ecr +++ b/src/invidious/views/hashtag.ecr @@ -4,38 +4,5 @@
-
-
- <%- if page > 1 -%> - <%= translate(locale, "Previous page") %> - <%- end -%> -
-
-
- <%- if videos.size >= 60 -%> - <%= translate(locale, "Next page") %> - <%- end -%> -
-
-
- <%- videos.each do |item| -%> - <%= rendered "components/item" %> - <%- end -%> -
- - - -
-
- <%- if page > 1 -%> - <%= translate(locale, "Previous page") %> - <%- end -%> -
-
-
- <%- if videos.size >= 60 -%> - <%= translate(locale, "Next page") %> - <%- end -%> -
-
+<%= rendered "components/items_paginated" %> diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index a7469e36..627a13b0 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -7,19 +7,6 @@ <%= Invidious::Frontend::SearchFilters.generate(query.filters, query.text, query.page, locale) %>
-
-
- <%- if query.page > 1 -%> - <%= translate(locale, "Previous page") %> - <%- end -%> -
-
-
- <%- if videos.size >= 20 -%> - <%= translate(locale, "Next page") %> - <%- end -%> -
-
<%- if videos.empty? -%>
@@ -30,25 +17,5 @@
<%- else -%> -
- <%- videos.each do |item| -%> - <%= rendered "components/item" %> - <%- end -%> -
+ <%= rendered "components/items_paginated" %> <%- end -%> - - - -
-
- <%- if query.page > 1 -%> - <%= translate(locale, "Previous page") %> - <%- end -%> -
-
-
- <%- if videos.size >= 20 -%> - <%= translate(locale, "Next page") %> - <%- end -%> -
-
From 7bd6d0ac4961e7f2433eb3268a45b78642229896 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Fri, 21 Apr 2023 00:28:11 +0200 Subject: [PATCH 10/28] HTML: Use the new pagination component for channel pages --- src/invidious/routes/playlists.cr | 14 +++++------ src/invidious/routes/search.cr | 8 +++--- src/invidious/views/channel.ecr | 25 ++++++------------- .../views/components/items_paginated.ecr | 2 +- src/invidious/views/search.ecr | 2 +- 5 files changed, 20 insertions(+), 31 deletions(-) diff --git a/src/invidious/routes/playlists.cr b/src/invidious/routes/playlists.cr index 604fe4e1..5cb96809 100644 --- a/src/invidious/routes/playlists.cr +++ b/src/invidious/routes/playlists.cr @@ -163,9 +163,9 @@ module Invidious::Routes::Playlists end begin - videos = get_playlist_videos(playlist, offset: (page - 1) * 100) + items = get_playlist_videos(playlist, offset: (page - 1) * 100) rescue ex - videos = [] of PlaylistVideo + items = [] of PlaylistVideo end csrf_token = generate_response(sid, {":edit_playlist"}, HMAC_KEY) @@ -174,7 +174,7 @@ module Invidious::Routes::Playlists page_nav_html = Frontend::Pagination.nav_numeric(locale, base_url: "/playlist?list=#{playlist.id}", current_page: page, - show_next: (videos.size == 100) + show_next: (items.size == 100) ) templated "edit_playlist" @@ -254,9 +254,9 @@ module Invidious::Routes::Playlists begin query = Invidious::Search::Query.new(env.params.query, :playlist, region) - videos = query.process.select(SearchVideo).map(&.as(SearchVideo)) + items = query.process.select(SearchVideo).map(&.as(SearchVideo)) rescue ex - videos = [] of SearchVideo + items = [] of SearchVideo end # Pagination @@ -264,7 +264,7 @@ module Invidious::Routes::Playlists page_nav_html = Frontend::Pagination.nav_numeric(locale, base_url: "/add_playlist_items?list=#{playlist.id}&q=#{query_encoded}", current_page: page, - show_next: (videos.size >= 20) + show_next: (items.size >= 20) ) env.set "add_playlist_items", plid @@ -433,7 +433,7 @@ module Invidious::Routes::Playlists end begin - videos = get_playlist_videos(playlist, offset: (page - 1) * 200) + items = get_playlist_videos(playlist, offset: (page - 1) * 200) rescue ex return error_template(500, "Error encountered while retrieving playlist videos.
#{ex.message}") end diff --git a/src/invidious/routes/search.cr b/src/invidious/routes/search.cr index edf0351c..5be33533 100644 --- a/src/invidious/routes/search.cr +++ b/src/invidious/routes/search.cr @@ -52,7 +52,7 @@ module Invidious::Routes::Search user = env.get? "user" begin - videos = query.process + items = query.process rescue ex : ChannelSearchException return error_template(404, "Unable to find channel with id of '#{HTML.escape(ex.channel)}'. Are you sure that's an actual channel id? It should look like 'UC4QobU6STFB0P71PMvOGN5A'.") rescue ex @@ -65,7 +65,7 @@ module Invidious::Routes::Search page_nav_html = Frontend::Pagination.nav_numeric(locale, base_url: "/search?#{query.to_http_params}", current_page: query.page, - show_next: (videos.size >= 20) + show_next: (items.size >= 20) ) if query.type == Invidious::Search::Query::Type::Channel @@ -95,7 +95,7 @@ module Invidious::Routes::Search end begin - videos = Invidious::Hashtag.fetch(hashtag, page) + items = Invidious::Hashtag.fetch(hashtag, page) rescue ex return error_template(500, ex) end @@ -105,7 +105,7 @@ module Invidious::Routes::Search page_nav_html = Frontend::Pagination.nav_numeric(locale, base_url: "/hashtag/#{hashtag_encoded}", current_page: page, - show_next: (videos.size >= 60) + show_next: (items.size >= 60) ) templated "hashtag" diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 6e62a471..91fe40b9 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -15,7 +15,12 @@ youtube_url = "https://www.youtube.com#{relative_url}" redirect_url = Invidious::Frontend::Misc.redirect_url(env) --%> + + page_nav_html = IV::Frontend::Pagination.nav_ctoken(locale, + base_url: relative_url, + ctoken: next_continuation + ) +%> <% content_for "header" do %> <%- if selected_tab.videos? -%> @@ -43,21 +48,5 @@
-
-<% items.each do |item| %> - <%= rendered "components/item" %> -<% end %> -
- - -
-
-
- <% if next_continuation %> - - <%= translate(locale, "Next page") %> - - <% end %> -
-
+<%= rendered "components/items_paginated" %> diff --git a/src/invidious/views/components/items_paginated.ecr b/src/invidious/views/components/items_paginated.ecr index c82b1772..4534a0a3 100644 --- a/src/invidious/views/components/items_paginated.ecr +++ b/src/invidious/views/components/items_paginated.ecr @@ -1,7 +1,7 @@ <%= page_nav_html %>
- <%- videos.each do |item| -%> + <%- items.each do |item| -%> <%= rendered "components/item" %> <%- end -%>
diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index 627a13b0..b1300214 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -8,7 +8,7 @@
-<%- if videos.empty? -%> +<%- if items.empty? -%>
<%= translate(locale, "search_message_no_results") %>

From b6bbfb9b200fc920854ce91835026da0fd6552db Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 22 Apr 2023 12:58:46 +0200 Subject: [PATCH 11/28] HTML: Use new buttons for thumbnail overlays In addition, this commit also heavily changes the structure of the generic "video card" item. Main benefits: * Improved accessibility for keyboard users * Many styling glitches were fixed * PlaylistVideos now use the same items as the rest * Elements all have distinct CSS classes * Design can be expanded to add more icons --- assets/css/default.css | 51 ++++---- src/invidious/views/components/item.ecr | 157 ++++++++++-------------- src/invidious/views/feeds/history.ecr | 8 +- 3 files changed, 98 insertions(+), 118 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index eb90c09c..48cb4264 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -152,9 +152,15 @@ body a.pure-button-primary:focus { color: #fff; } -button.pure-button-secondary:hover, -button.pure-button-secondary:focus { - border-color: rgba(0, 182, 240, 1); +.pure-button-secondary:hover, +.pure-button-secondary:focus { + color: rgb(0, 182, 240); + border-color: rgb(0, 182, 240); +} + +.pure-button-secondary.low-profile { + padding: 5px 10px; + margin: 0; } @@ -163,21 +169,19 @@ button.pure-button-secondary:focus { */ div.thumbnail { - padding: 28.125%; position: relative; + width: 100%; box-sizing: border-box; } img.thumbnail { - position: absolute; + display: block; /* See: https://stackoverflow.com/a/11635197 */ width: 100%; - height: 100%; - left: 0; - top: 0; object-fit: cover; } div.watched-overlay { + z-index: 50; position: absolute; top: 0; left: 0; @@ -195,28 +199,27 @@ div.watched-indicator { background-color: red; } -.length { +div.thumbnail > .top-left-overlay, +div.thumbnail > .bottom-right-overlay { z-index: 100; position: absolute; - background-color: rgba(35, 35, 35, 0.75); - color: #fff; - border-radius: 2px; - padding: 2px; + padding: 0; + margin: 0; font-size: 16px; - right: 0.25em; - bottom: -0.75em; } -.watched { - z-index: 100; - position: absolute; - background-color: rgba(35, 35, 35, 0.75); +.top-left-overlay { top: 0.6em; left: 0.6em; } +.bottom-right-overlay { bottom: 0.6em; right: 0.6em; } + +.length { + padding: 1px; + margin: -2px 0; color: #fff; - border-radius: 2px; - padding: 4px 8px 4px 8px; - font-size: 16px; - left: 0.2em; - top: -0.7em; + border-radius: 3px; +} + +.length, .top-left-overlay button { + background-color: rgba(35, 35, 35, 0.85); } diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 7cfd38db..f05e1338 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -7,7 +7,7 @@ <% if !env.get("preferences").as(Preferences).thin_mode %>
- " alt="" /> + " alt="" />
<% end %>

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

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

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

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

<%= recode_length_seconds(item.length_seconds) %>

<% end %> @@ -54,104 +54,79 @@

<%= HTML.escape(item.author) %>

- <% when PlaylistVideo %> - - <% if !env.get("preferences").as(Preferences).thin_mode %> -
- - - <% if plid_form = env.get?("remove_playlist_items") %> -
" method="post"> - "> -

- -

-
- <% end %> - - <% if item.responds_to?(:live_now) && item.live_now %> -

<%= translate(locale, "LIVE") %>

- <% elsif item.length_seconds != 0 %> -

<%= recode_length_seconds(item.length_seconds) %>

- <% end %> - - <% if item_watched %> -
-
- <% end %> -
- <% end %> -

<%= HTML.escape(item.title) %>

-
- -
- - <% endpoint_params = "?v=#{item.id}&list=#{item.plid}" %> - <%= rendered "components/video-context-buttons" %> -
- -
-
- <% if item.responds_to?(:premiere_timestamp) && item.premiere_timestamp.try &.> Time.utc %> -

<%= translate(locale, "Premieres in `x`", recode_date((item.premiere_timestamp.as(Time) - Time.utc).ago, locale)) %>

- <% elsif Time.utc - item.published > 1.minute %> -

<%= translate(locale, "Shared `x` ago", recode_date(item.published, locale)) %>

- <% end %> -
- - <% if item.responds_to?(:views) && item.views %> -
-

<%= translate_count(locale, "generic_views_count", item.views || 0, NumberFormatting::Short) %>

-
- <% end %> -
<% when Category %> <% else %> - - <% if !env.get("preferences").as(Preferences).thin_mode %> -
- - <% if env.get? "show_watched" %> -
" method="post"> - "> -

- -

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

- -

-
- <% end %> + <%- + # `endpoint_params` is used for the "video-context-buttons" component + if item.is_a?(PlaylistVideo) + link_url = "/watch?v=#{item.id}&list=#{item.plid}&index=#{item.index}" + endpoint_params = "?v=#{item.id}&list=#{item.plid}" + else + link_url = "/watch?v=#{item.id}" + endpoint_params = "?v=#{item.id}" + end + -%> - <% if item.responds_to?(:live_now) && item.live_now %> -

<%= translate(locale, "LIVE") %>

- <% elsif item.length_seconds != 0 %> -

<%= recode_length_seconds(item.length_seconds) %>

- <% end %> +
+ <%- if !env.get("preferences").as(Preferences).thin_mode -%> + + + + <%- end -%> - <% if item_watched %> -
-
- <% end %> -
+
+ <%- if env.get? "show_watched" -%> +
" method="post"> + "> + +
+ <%- end -%> + + <%- if plid_form = env.get?("add_playlist_items") -%> + <%- form_parameters = "action_add_video=1&video_id=#{item.id}&playlist_id=#{plid_form}&referer=#{env.get("current_page")}" -%> +
+ "> + +
+ <%- elsif item.is_a?(PlaylistVideo) && (plid_form = env.get?("remove_playlist_items")) -%> + <%- form_parameters = "action_remove_video=1&set_video_id=#{item.index}&playlist_id=#{plid_form}&referer=#{env.get("current_page")}" -%> +
+ "> + +
+ <%- end -%> +
+ +
+ <%- if item.responds_to?(:live_now) && item.live_now -%> +

 <%= translate(locale, "LIVE") %>

+ <%- elsif item.length_seconds != 0 -%> +

<%= recode_length_seconds(item.length_seconds) %>

+ <%- end -%> +
+ + <% if item_watched %> +
+
<% end %> -

<%= HTML.escape(item.title) %>

- +
+ + diff --git a/src/invidious/views/feeds/history.ecr b/src/invidious/views/feeds/history.ecr index 2234b297..5301a232 100644 --- a/src/invidious/views/feeds/history.ecr +++ b/src/invidious/views/feeds/history.ecr @@ -35,12 +35,14 @@ <% if !env.get("preferences").as(Preferences).thin_mode %>
+ +
" method="post"> "> -

- -

+
+

<% end %> From 080c7446c6c26c5d8670107cf4161ba4609e5e4a Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 22 Apr 2023 17:50:34 +0200 Subject: [PATCH 12/28] HTML: Use new buttons for playlists (save/delete/add videos/etc...) --- assets/css/default.css | 2 +- locales/en-US.json | 6 +++ locales/fr.json | 6 +++ src/invidious/views/components/item.ecr | 10 ++-- src/invidious/views/edit_playlist.ecr | 64 +++++++++++----------- src/invidious/views/playlist.ecr | 70 ++++++++++++++++--------- 6 files changed, 94 insertions(+), 64 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index 48cb4264..7a99a0db 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -219,7 +219,7 @@ div.thumbnail > .bottom-right-overlay { } .length, .top-left-overlay button { - background-color: rgba(35, 35, 35, 0.85); + background-color: rgba(35, 35, 35, 0.85) !important; } diff --git a/locales/en-US.json b/locales/en-US.json index e13ba968..c41a631a 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -9,6 +9,11 @@ "generic_subscribers_count_plural": "{{count}} subscribers", "generic_subscriptions_count": "{{count}} subscription", "generic_subscriptions_count_plural": "{{count}} subscriptions", + "generic_button_delete": "Delete", + "generic_button_edit": "Edit", + "generic_button_save": "Save", + "generic_button_cancel": "Cancel", + "generic_button_rss": "RSS", "LIVE": "LIVE", "Shared `x` ago": "Shared `x` ago", "Unsubscribe": "Unsubscribe", @@ -170,6 +175,7 @@ "Title": "Title", "Playlist privacy": "Playlist privacy", "Editing playlist `x`": "Editing playlist `x`", + "playlist_button_add_items": "Add videos", "Show more": "Show more", "Show less": "Show less", "Watch on YouTube": "Watch on YouTube", diff --git a/locales/fr.json b/locales/fr.json index d2607a49..2eb4dd2b 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -9,6 +9,11 @@ "generic_subscribers_count_plural": "{{count}} abonnés", "generic_subscriptions_count": "{{count}} abonnement", "generic_subscriptions_count_plural": "{{count}} abonnements", + "generic_button_delete": "Supprimer", + "generic_button_edit": "Editer", + "generic_button_save": "Enregistrer", + "generic_button_cancel": "Annuler", + "generic_button_rss": "RSS", "LIVE": "EN DIRECT", "Shared `x` ago": "Ajoutée il y a `x`", "Unsubscribe": "Se désabonner", @@ -149,6 +154,7 @@ "Title": "Titre", "Playlist privacy": "Paramètres de confidentialité de la liste de lecture", "Editing playlist `x`": "Modifier la liste de lecture `x`", + "playlist_button_add_items": "Ajouter des vidéos", "Show more": "Afficher plus", "Show less": "Afficher moins", "Watch on YouTube": "Voir la vidéo sur Youtube", diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index f05e1338..decdcb2f 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -71,6 +71,11 @@ <%- if !env.get("preferences").as(Preferences).thin_mode -%> + + <% if item_watched %> +
+
+ <% end %>
<%- end -%> @@ -109,11 +114,6 @@

<%= recode_length_seconds(item.length_seconds) %>

<%- end -%>
- - <% if item_watched %> -
-
- <% end %>
diff --git a/src/invidious/views/edit_playlist.ecr b/src/invidious/views/edit_playlist.ecr index d2981886..34157c67 100644 --- a/src/invidious/views/edit_playlist.ecr +++ b/src/invidious/views/edit_playlist.ecr @@ -6,35 +6,43 @@ <% end %>
-
-
+
+ +
+ +
+

+
+
+ +
+
<%= HTML.escape(playlist.author) %> | <%= translate_count(locale, "generic_videos_count", playlist.video_count) %> | - <%= translate(locale, "Updated `x` ago", recode_date(playlist.updated, locale)) %> | - "> - -
-
-

-
- -
-
-
-

+
@@ -44,14 +52,6 @@ -<% if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email %> -
-

- -

-
-<% end %> -

diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index 08995a83..8d4d116d 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -7,8 +7,51 @@ <% end %>
-
+

<%= title %>

+
+ +
+ <%- if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email -%> + + + + <%- else -%> +
+ <%- if IV::Database::Playlists.exists?(playlist.id) -%> + +  <%= translate(locale, "Subscribe") %> + + <%- else -%> + +  <%= translate(locale, "Unsubscribe") %> + + <%- end -%> +
+ <%- end -%> + + +
+
+ +
+
<% if playlist.is_a? InvidiousPlaylist %> <% if playlist.author == user.try &.email %> @@ -54,37 +97,12 @@
<% end %>
-
-

-
- <% if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email %> -
-
- <% else %> - <% if Invidious::Database::Playlists.exists?(playlist.id) %> -
- <% else %> -
- <% end %> - <% end %> -
-
-

-
<%= playlist.description_html %>
-<% if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email %> -
-

- -

-
-<% end %> -

From 43dcab225caca7034346a79da340e434cdb4d407 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 22 Apr 2023 19:45:45 +0200 Subject: [PATCH 13/28] HTML: merge MixVideo with other types in item.ecr --- src/invidious/views/components/item.ecr | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index decdcb2f..0fa9c807 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -34,26 +34,6 @@

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

- <% when MixVideo %> - - <% if !env.get("preferences").as(Preferences).thin_mode %> -
- - <% if item.length_seconds != 0 %> -

<%= recode_length_seconds(item.length_seconds) %>

- <% end %> - - <% if item_watched %> -
-
- <% end %> -
- <% end %> -

<%= HTML.escape(item.title) %>

-
- -

<%= HTML.escape(item.author) %>

-
<% when Category %> <% else %> <%- @@ -61,6 +41,9 @@ if item.is_a?(PlaylistVideo) link_url = "/watch?v=#{item.id}&list=#{item.plid}&index=#{item.index}" endpoint_params = "?v=#{item.id}&list=#{item.plid}" + elsif item.is_a?(MixVideo) + link_url = "/watch?v=#{item.id}&list=#{item.rdid}" + endpoint_params = "?v=#{item.id}&list=#{item.rdid}" else link_url = "/watch?v=#{item.id}" endpoint_params = "?v=#{item.id}" @@ -134,7 +117,7 @@
<% if item.responds_to?(:premiere_timestamp) && item.premiere_timestamp.try &.> Time.utc %>

<%= translate(locale, "Premieres in `x`", recode_date((item.premiere_timestamp.as(Time) - Time.utc).ago, locale)) %>

- <% elsif Time.utc - item.published > 1.minute %> + <% elsif item.responds_to?(:published) && (Time.utc - item.published) > 1.minute %>

<%= translate(locale, "Shared `x` ago", recode_date(item.published, locale)) %>

<% end %>
From 8718f2068859b12174cecf4af11c30bfe64103a6 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 22 Apr 2023 19:59:01 +0200 Subject: [PATCH 14/28] HTML: Fix thin mode/thumbnail on other items --- src/invidious/views/components/item.ecr | 71 +++++++++++++++++-------- src/invidious/views/feeds/history.ecr | 28 +++++----- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 0fa9c807..9b73f7ee 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -1,39 +1,64 @@ -<% item_watched = !item.is_a?(SearchChannel | SearchPlaylist | InvidiousPlaylist | Category) && env.get?("user").try &.as(User).watched.index(item.id) != nil %> +<%- + thin_mode = env.get("preferences").as(Preferences).thin_mode + item_watched = !item.is_a?(SearchChannel | SearchPlaylist | InvidiousPlaylist | Category) && env.get?("user").try &.as(User).watched.index(item.id) != nil + author_verified = item.responds_to?(:author_verified) && item.author_verified +-%>
<% case item when %> <% when SearchChannel %> - - <% if !env.get("preferences").as(Preferences).thin_mode %> + <% if !thin_mode %> +
" alt="" />
- <% end %> -

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

-
+ + <% end %> + + +

<%= translate_count(locale, "generic_subscribers_count", item.subscriber_count, NumberFormatting::Separator) %>

<% if !item.auto_generated %>

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

<% end %>
<%= item.description_html %>
<% when SearchPlaylist, InvidiousPlaylist %> - <% if item.id.starts_with? "RD" %> - <% url = "/mix?list=#{item.id}&continuation=#{URI.parse(item.thumbnail || "/vi/-----------").request_target.split("/")[2]}" %> - <% else %> - <% url = "/playlist?list=#{item.id}" %> - <% end %> + <%- + if item.id.starts_with? "RD" + link_url = "/mix?list=#{item.id}&continuation=#{URI.parse(item.thumbnail || "/vi/-----------").request_target.split("/")[2]}" + else + link_url = "/playlist?list=#{item.id}" + end + -%> - - <% if !env.get("preferences").as(Preferences).thin_mode %> - + + + + <% when Category %> <% else %> <%- @@ -106,7 +131,7 @@
diff --git a/src/invidious/views/feeds/history.ecr b/src/invidious/views/feeds/history.ecr index 5301a232..83ea7238 100644 --- a/src/invidious/views/feeds/history.ecr +++ b/src/invidious/views/feeds/history.ecr @@ -31,22 +31,20 @@ <% watched.each do |item| %>
- - <% if !env.get("preferences").as(Preferences).thin_mode %> -
- +
+ + + -
-
" method="post"> - "> - -
-
-
-

- <% end %> - +
+
" method="post"> + "> + +
+
+
+

<% end %> From cc30b00f8ca00572348c1ee266df907c69726c13 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 22 Apr 2023 20:25:26 +0200 Subject: [PATCH 15/28] CSS: fix light/dark themes for pure buttons --- assets/css/default.css | 74 +++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index 7a99a0db..f671c3bf 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -139,25 +139,6 @@ body a.pure-button-primary, margin: 0 .4em; } -.dark-theme .pure-button-secondary { - background-color: #0002; - color: #ddd; -} - -button.pure-button-primary:hover, -button.pure-button-primary:focus, -body a.pure-button-primary:hover, -body a.pure-button-primary:focus { - background-color: rgba(0, 182, 240, 1); - color: #fff; -} - -.pure-button-secondary:hover, -.pure-button-secondary:focus { - color: rgb(0, 182, 240); - border-color: rgb(0, 182, 240); -} - .pure-button-secondary.low-profile { padding: 5px 10px; margin: 0; @@ -219,6 +200,7 @@ div.thumbnail > .bottom-right-overlay { } .length, .top-left-overlay button { + color: #eee; background-color: rgba(35, 35, 35, 0.85) !important; } @@ -449,9 +431,18 @@ span > select { color: #075A9E !important; } -.light-theme a.pure-button-primary:hover, -.light-theme a.pure-button-primary:focus { +.light-theme .pure-button-primary:hover, +.light-theme .pure-button-primary:focus, +.light-theme .pure-button-secondary:hover, +.light-theme .pure-button-secondary:focus { color: #fff !important; + border-color: rgba(0, 182, 240, 0.75) !important; + background-color: rgba(0, 182, 240, 0.75) !important; +} + +.light-theme .pure-button-secondary:not(.low-profile) { + color: #335d7a; + background-color: #fff2; } .light-theme a { @@ -479,9 +470,18 @@ span > select { color: #075A9E !important; } - .no-theme a.pure-button-primary:hover, - .no-theme a.pure-button-primary:focus { + .no-theme .pure-button-primary:hover, + .no-theme .pure-button-primary:focus, + .no-theme .pure-button-secondary:hover, + .no-theme .pure-button-secondary:focus { color: #fff !important; + border-color: rgba(0, 182, 240, 0.75) !important; + background-color: rgba(0, 182, 240, 0.75) !important; + } + + .no-theme .pure-button-secondary:not(.low-profile) { + color: #335d7a; + background-color: #fff2; } .no-theme a { @@ -514,6 +514,20 @@ span > select { color: rgb(0, 182, 240); } +.dark-theme .pure-button-primary:hover, +.dark-theme .pure-button-primary:focus, +.dark-theme .pure-button-secondary:hover, +.dark-theme .pure-button-secondary:focus { + color: #fff !important; + border-color: rgb(0, 182, 240) !important; + background-color: rgba(0, 182, 240, 1) !important; +} + +.dark-theme .pure-button-secondary { + background-color: #0002; + color: #ddd; +} + .dark-theme a { color: #a0a0a0; text-decoration: none; @@ -554,6 +568,20 @@ body.dark-theme { color: rgb(0, 182, 240); } + .no-theme .pure-button-primary:hover, + .no-theme .pure-button-primary:focus, + .no-theme .pure-button-secondary:hover, + .no-theme .pure-button-secondary:focus { + color: #fff !important; + border-color: rgb(0, 182, 240) !important; + background-color: rgba(0, 182, 240, 1) !important; + } + + .no-theme .pure-button-secondary { + background-color: #0002; + color: #ddd; + } + .no-theme a { color: #a0a0a0; text-decoration: none; From 42fa6ad2a30038cd7cdc705f5da2bffdc9714349 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 24 Apr 2023 19:44:15 +0200 Subject: [PATCH 16/28] HTML/CSS: Fix buttons' responsiveness --- assets/css/default.css | 94 ++++++++++++++----- .../components/video-context-buttons.ecr | 4 +- src/invidious/views/playlist.ecr | 18 ++-- 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index f671c3bf..21121f4d 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -1,3 +1,7 @@ +/* + * Common attributes + */ + html, body { font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", Roboto, Oxygen, @@ -11,6 +15,16 @@ body { min-height: 100vh; } +.h-box { + padding-left: 1em; + padding-right: 1em; +} + +.v-box { + padding-top: 1em; + padding-bottom: 1em; +} + .deleted { background-color: rgb(255, 0, 0, 0.5); } @@ -20,6 +34,34 @@ body { margin-bottom: 20px; } +.title { + margin: 0.5em 0 1em 0; +} + +/* A flex container */ +.flexible { + display: flex; + align-items: center; +} + +.flex-left { + display: flex; + flex: 1 1 auto; + flex-flow: row wrap; + justify-content: flex-start; +} +.flex-right { + display: flex; + flex: 2 0 auto; + flex-flow: row nowrap; + justify-content: flex-end; +} + + +/* + * Channel page + */ + .channel-profile > * { font-size: 1.17em; font-weight: bold; @@ -90,16 +132,6 @@ body a.channel-owner { } } -.h-box { - padding-left: 1em; - padding-right: 1em; -} - -.v-box { - padding-top: 1em; - padding-bottom: 1em; -} - div { overflow-wrap: break-word; word-wrap: break-word; @@ -144,9 +176,15 @@ body a.pure-button-primary, margin: 0; } +/* Has to be combined with flex-left/right */ +.button-container { + flex-flow: wrap; + gap: 0.5em 0.75em; +} + /* - * Thumbnails + * Video thumbnails */ div.thumbnail { @@ -280,6 +318,11 @@ input[type="search"]::-webkit-search-cancel-button { margin-right: 1em; } + +/* + * Responsive rules + */ + @media only screen and (max-aspect-ratio: 16/9) { .player-dimensions.vjs-fluid { padding-top: 46.86% !important; @@ -298,20 +341,28 @@ input[type="search"]::-webkit-search-cancel-button { .navbar > div { display: flex; justify-content: center; - } - - .navbar > div:not(:last-child) { - margin-bottom: 1em; + margin-bottom: 25px; } .navbar > .searchbar > form { - width: 60%; + width: 75%; } h1 { font-size: 1.25em; margin: 0.42em 0; } + + /* Space out the subscribe & RSS buttons and align them to the left */ + .title.flexible { display: block; } + .title.flexible > .flex-right { margin: 0.75em 0; justify-content: flex-start; } + + /* Space out buttons to make them easier to tap */ + .user-field { font-size: 125%; } + .user-field > :not(:last-child) { margin-right: 1.75em; } + + .icon-buttons { font-size: 125%; } + .icon-buttons > :not(:last-child) { margin-right: 0.75em; } } @media screen and (max-width: 320px) { @@ -328,10 +379,6 @@ input[type="search"]::-webkit-search-cancel-button { .video-card-row { margin: 15px 0; } -.flexible { display: flex; } -.flex-left { flex: 1 1 100%; flex-wrap: wrap; } -.flex-right { flex: 1 0 auto; flex-wrap: nowrap; } - p.channel-name { margin: 0; } p.video-data { margin: 0; font-weight: bold; font-size: 80%; } @@ -659,12 +706,7 @@ label[for="music-desc-expansion"]:hover { } /* Bidi (bidirectional text) support */ -h1, -h2, -h3, -h4, -h5, -p, +h1, h2, h3, h4, h5, p, #descriptionWrapper, #description-box, #music-description-box { diff --git a/src/invidious/views/components/video-context-buttons.ecr b/src/invidious/views/components/video-context-buttons.ecr index ddb6c983..385ed6b3 100644 --- a/src/invidious/views/components/video-context-buttons.ecr +++ b/src/invidious/views/components/video-context-buttons.ecr @@ -1,4 +1,4 @@ -
+
" href="https://www.youtube.com/watch<%=endpoint_params%>"> @@ -6,7 +6,7 @@ " href="/watch<%=endpoint_params%>&listen=1"> - + <% if env.get("preferences").as(Preferences).automatic_instance_redirect%> " href="/redirect?referer=%2Fwatch<%=URI.encode_www_form(endpoint_params)%>"> diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index 8d4d116d..ee9ba87b 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -6,30 +6,28 @@ <% end %> -
-
-

<%= title %>

-
+
+

<%= title %>

-
+
<%- if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email -%> -
+ -
+ -
+ <%- else -%> -
+
<%- if IV::Database::Playlists.exists?(playlist.id) -%>  <%= translate(locale, "Subscribe") %> @@ -42,7 +40,7 @@
<%- end -%> -
+
 <%= translate(locale, "generic_button_rss") %> From 411208bbd211d7effe278eabe23d5e2f502b5ea6 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 24 Apr 2023 20:28:40 +0200 Subject: [PATCH 17/28] HTML: Reorder buttons on the channel and watch pages --- .../views/components/channel_info.ecr | 29 ++++++++--------- .../views/components/subscribe_widget.ecr | 6 ---- src/invidious/views/watch.ecr | 31 ++++++++++++------- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/invidious/views/components/channel_info.ecr b/src/invidious/views/components/channel_info.ecr index 59888760..f4164f31 100644 --- a/src/invidious/views/components/channel_info.ecr +++ b/src/invidious/views/components/channel_info.ecr @@ -8,29 +8,30 @@
<% end %> -
-
+
+
<%= author %><% if !channel.verified.nil? && channel.verified %> <% end %>
-
-

- -

+ +
+
+ <% sub_count_text = number_to_short_text(channel.sub_count) %> + <%= rendered "components/subscribe_widget" %> +
+ +
-
-

<%= channel.description_html %>

-
-
- -
- <% sub_count_text = number_to_short_text(channel.sub_count) %> - <%= rendered "components/subscribe_widget" %> +

<%= channel.description_html %>

diff --git a/src/invidious/views/components/subscribe_widget.ecr b/src/invidious/views/components/subscribe_widget.ecr index b9d5f783..05e4e253 100644 --- a/src/invidious/views/components/subscribe_widget.ecr +++ b/src/invidious/views/components/subscribe_widget.ecr @@ -1,22 +1,18 @@ <% if user %> <% if subscriptions.includes? ucid %> -

" method="post"> ">
-

<% else %> -

" method="post"> ">
-

<% end %> <% else %> -

"> <%= translate(locale, "Subscribe") %> | <%= sub_count_text %> -

<% end %> diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 5b3190f3..4f4354a9 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -204,19 +204,28 @@ we're going to need to do it here in order to allow for translations.
-
- -
- <% if !video.author_thumbnail.empty? %> - - <% end %> - <%= author %><% if !video.author_verified.nil? && video.author_verified %> <% end %> + +
+ + +
+
+ <% sub_count_text = video.sub_count_text %> + <%= rendered "components/subscribe_widget" %>
- - - <% sub_count_text = video.sub_count_text %> - <%= rendered "components/subscribe_widget" %> +
+
+

<% if video.premiere_timestamp.try &.> Time.utc %> <%= video.premiere_timestamp.try { |t| translate(locale, "Premieres `x`", t.to_s("%B %-d, %R UTC")) } %> From 06b2bab795ebf54e9c6a396e37a129a87d39675a Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Mon, 24 Apr 2023 22:19:46 +0200 Subject: [PATCH 18/28] HTML: Fix thumbnails of related videos (watch page) --- src/invidious/views/watch.ecr | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 4f4354a9..9275631c 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -304,15 +304,26 @@ we're going to need to do it here in order to allow for translations. <% video.related_videos.each do |rv| %> <% if rv["id"]? %> - &listen=<%= params.listen %>"> - <% if !env.get("preferences").as(Preferences).thin_mode %> -

+
+ + - <% end %> -

<%= rv["title"] %>

-
+ + <%- end -%> + +
+ <%- if (length_seconds = rv["length_seconds"]?.try &.to_i?) && length_seconds != 0 -%> +

<%= recode_length_seconds(length_seconds) %>

+ <%- end -%> +
+
+ + +
<% if rv["ucid"]? %> @@ -330,6 +341,8 @@ we're going to need to do it here in order to allow for translations. %>
+ +
<% end %> <% end %>
From c17404890ca9618ebc828a06bc88ff2bd79e811e Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Tue, 23 May 2023 22:49:44 +0200 Subject: [PATCH 19/28] HTML: Use the new pagination component for history/subscriptions --- src/invidious/routes/feeds.cr | 8 +++++++ src/invidious/views/feeds/history.ecr | 24 ++++++-------------- src/invidious/views/feeds/subscriptions.ecr | 25 +++++++-------------- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index fc62c5a3..a8246b2e 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -102,6 +102,10 @@ module Invidious::Routes::Feeds end env.set "user", user + # Used for pagination links + base_url = "/feed/subscriptions" + base_url += "?max_results=#{max_results}" if env.params.query.has_key?("max_results") + templated "feeds/subscriptions" end @@ -129,6 +133,10 @@ module Invidious::Routes::Feeds end watched ||= [] of String + # Used for pagination links + base_url = "/feed/history" + base_url += "?max_results=#{max_results}" if env.params.query.has_key?("max_results") + templated "feeds/history" end diff --git a/src/invidious/views/feeds/history.ecr b/src/invidious/views/feeds/history.ecr index 83ea7238..bda4e1f3 100644 --- a/src/invidious/views/feeds/history.ecr +++ b/src/invidious/views/feeds/history.ecr @@ -50,20 +50,10 @@ <% end %>
- +<%= + IV::Frontend::Pagination.nav_numeric(locale, + base_url: base_url, + current_page: page, + show_next: (watched.size >= max_results) + ) +%> diff --git a/src/invidious/views/feeds/subscriptions.ecr b/src/invidious/views/feeds/subscriptions.ecr index 9c69c5b0..c36bd00f 100644 --- a/src/invidious/views/feeds/subscriptions.ecr +++ b/src/invidious/views/feeds/subscriptions.ecr @@ -56,6 +56,7 @@ +
<% videos.each do |item| %> <%= rendered "components/item" %> @@ -64,20 +65,10 @@ -
- -
-
- <% if (videos.size + notifications.size) == max_results %> - &max_results=<%= max_results %><% end %>"> - <%= translate(locale, "Next page") %> - - <% end %> -
-
+<%= + IV::Frontend::Pagination.nav_numeric(locale, + base_url: base_url, + current_page: page, + show_next: ((videos.size + notifications.size) == max_results) + ) +%> From 9b75f79fb553403d0af7b2f9a1212a1e93bcf85b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 8 Jul 2023 21:17:44 +0200 Subject: [PATCH 20/28] HTML/CSS: Add thumbnail placeholder in thin mode This change is required to make the overlay buttons functional (add to and delete from playlist, mark as watched, etc.) --- assets/css/default.css | 5 +++++ src/invidious/views/components/item.ecr | 8 +++++++- src/invidious/views/watch.ecr | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/assets/css/default.css b/assets/css/default.css index 21121f4d..c31b24e5 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -199,6 +199,11 @@ img.thumbnail { object-fit: cover; } +.thumbnail-placeholder { + min-height: 50px; + border: 2px dotted; +} + div.watched-overlay { z-index: 50; position: absolute; diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 9b73f7ee..7ffd2d93 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -14,6 +14,8 @@ " alt="" /> + <%- else -%> +
<% end %>
@@ -41,6 +43,8 @@ " alt="" /> + <%- else -%> +
<%- end -%>
@@ -76,7 +80,7 @@ -%>
- <%- if !env.get("preferences").as(Preferences).thin_mode -%> + <%- if !thin_mode -%> @@ -85,6 +89,8 @@
<% end %>
+ <%- else -%> +
<%- end -%>
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 9275631c..498d57a1 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -311,6 +311,8 @@ we're going to need to do it here in order to allow for translations. &listen=<%= params.listen %>"> /mqdefault.jpg" alt="" /> + <%- else -%> +
<%- end -%>
From f2fa3da9d2f8ffc1684997526ddd5b3357d88897 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Wed, 12 Jul 2023 11:06:34 -0700 Subject: [PATCH 21/28] Add support for releases and podcasts tabs --- locales/en-US.json | 2 + src/invidious/channels/playlists.cr | 18 +++++++ src/invidious/frontend/channel_page.cr | 2 + src/invidious/routes/api/v1/channels.cr | 62 ++++++++++++++++++++++++- src/invidious/routes/channels.cr | 44 +++++++++++++++++- src/invidious/routing.cr | 5 ++ src/invidious/views/channel.ecr | 2 + src/invidious/yt_backend/extractors.cr | 5 +- 8 files changed, 134 insertions(+), 6 deletions(-) diff --git a/locales/en-US.json b/locales/en-US.json index e13ba968..29dd7a40 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -474,6 +474,8 @@ "channel_tab_videos_label": "Videos", "channel_tab_shorts_label": "Shorts", "channel_tab_streams_label": "Livestreams", + "channel_tab_podcasts_label": "Podcasts", + "channel_tab_releases_label": "Releases", "channel_tab_playlists_label": "Playlists", "channel_tab_community_label": "Community", "channel_tab_channels_label": "Channels" diff --git a/src/invidious/channels/playlists.cr b/src/invidious/channels/playlists.cr index 8dc824b2..91029fe3 100644 --- a/src/invidious/channels/playlists.cr +++ b/src/invidious/channels/playlists.cr @@ -26,3 +26,21 @@ def fetch_channel_playlists(ucid, author, continuation, sort_by) return extract_items(initial_data, author, ucid) end + +def fetch_channel_podcasts(ucid, author, continuation) + if continuation + initial_data = YoutubeAPI.browse(continuation) + else + initial_data = YoutubeAPI.browse(ucid, params: "Eghwb2RjYXN0c_IGBQoDugEA") + end + return extract_items(initial_data, author, ucid) +end + +def fetch_channel_releases(ucid, author, continuation) + if continuation + initial_data = YoutubeAPI.browse(continuation) + else + initial_data = YoutubeAPI.browse(ucid, params: "EghyZWxlYXNlc_IGBQoDsgEA") + end + return extract_items(initial_data, author, ucid) +end diff --git a/src/invidious/frontend/channel_page.cr b/src/invidious/frontend/channel_page.cr index 53745dd5..fe7d6d6e 100644 --- a/src/invidious/frontend/channel_page.cr +++ b/src/invidious/frontend/channel_page.cr @@ -5,6 +5,8 @@ module Invidious::Frontend::ChannelPage Videos Shorts Streams + Podcasts + Releases Playlists Community Channels diff --git a/src/invidious/routes/api/v1/channels.cr b/src/invidious/routes/api/v1/channels.cr index bcb4db2c..adf05d30 100644 --- a/src/invidious/routes/api/v1/channels.cr +++ b/src/invidious/routes/api/v1/channels.cr @@ -245,7 +245,7 @@ module Invidious::Routes::API::V1::Channels channel = nil # Make the compiler happy get_channel() - items, continuation = fetch_channel_playlists(channel.ucid, channel.author, continuation, sort_by) + items, next_continuation = fetch_channel_playlists(channel.ucid, channel.author, continuation, sort_by) JSON.build do |json| json.object do @@ -257,7 +257,65 @@ module Invidious::Routes::API::V1::Channels end end - json.field "continuation", continuation + json.field "continuation", next_continuation if next_continuation + end + end + end + + def self.podcasts(env) + locale = env.get("preferences").as(Preferences).locale + + env.response.content_type = "application/json" + + ucid = env.params.url["ucid"] + continuation = env.params.query["continuation"]? + + # Use the macro defined above + channel = nil # Make the compiler happy + get_channel() + + items, next_continuation = fetch_channel_podcasts(channel.ucid, channel.author, continuation) + + JSON.build do |json| + json.object do + json.field "playlists" do + json.array do + items.each do |item| + item.to_json(locale, json) if item.is_a?(SearchPlaylist) + end + end + end + + json.field "continuation", next_continuation if next_continuation + end + end + end + + def self.releases(env) + locale = env.get("preferences").as(Preferences).locale + + env.response.content_type = "application/json" + + ucid = env.params.url["ucid"] + continuation = env.params.query["continuation"]? + + # Use the macro defined above + channel = nil # Make the compiler happy + get_channel() + + items, next_continuation = fetch_channel_releases(channel.ucid, channel.author, continuation) + + JSON.build do |json| + json.object do + json.field "playlists" do + json.array do + items.each do |item| + item.to_json(locale, json) if item.is_a?(SearchPlaylist) + end + end + end + + json.field "continuation", next_continuation if next_continuation end end end diff --git a/src/invidious/routes/channels.cr b/src/invidious/routes/channels.cr index 16621994..9892ae2a 100644 --- a/src/invidious/routes/channels.cr +++ b/src/invidious/routes/channels.cr @@ -27,7 +27,7 @@ module Invidious::Routes::Channels item.author end end - items = items.select(SearchPlaylist).map(&.as(SearchPlaylist)) + items = items.select(SearchPlaylist) items.each(&.author = "") else sort_options = {"newest", "oldest", "popular"} @@ -105,13 +105,53 @@ module Invidious::Routes::Channels channel.ucid, channel.author, continuation, (sort_by || "last") ) - items = items.select(SearchPlaylist).map(&.as(SearchPlaylist)) + items = items.select(SearchPlaylist) items.each(&.author = "") selected_tab = Frontend::ChannelPage::TabsAvailable::Playlists templated "channel" end + def self.podcasts(env) + data = self.fetch_basic_information(env) + return data if !data.is_a?(Tuple) + + locale, user, subscriptions, continuation, ucid, channel = data + + sort_by = "" + sort_options = [] of String + + items, next_continuation = fetch_channel_podcasts( + channel.ucid, channel.author, continuation + ) + + items = items.select(SearchPlaylist) + items.each(&.author = "") + + selected_tab = Frontend::ChannelPage::TabsAvailable::Podcasts + templated "channel" + end + + def self.releases(env) + data = self.fetch_basic_information(env) + return data if !data.is_a?(Tuple) + + locale, user, subscriptions, continuation, ucid, channel = data + + sort_by = "" + sort_options = [] of String + + items, next_continuation = fetch_channel_releases( + channel.ucid, channel.author, continuation + ) + + items = items.select(SearchPlaylist) + items.each(&.author = "") + + selected_tab = Frontend::ChannelPage::TabsAvailable::Releases + templated "channel" + end + def self.community(env) data = self.fetch_basic_information(env) if !data.is_a?(Tuple) diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index daaf4d88..9c43171c 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -118,6 +118,8 @@ module Invidious::Routing get "/channel/:ucid/videos", Routes::Channels, :videos get "/channel/:ucid/shorts", Routes::Channels, :shorts get "/channel/:ucid/streams", Routes::Channels, :streams + get "/channel/:ucid/podcasts", Routes::Channels, :podcasts + get "/channel/:ucid/releases", Routes::Channels, :releases get "/channel/:ucid/playlists", Routes::Channels, :playlists get "/channel/:ucid/community", Routes::Channels, :community get "/channel/:ucid/channels", Routes::Channels, :channels @@ -228,6 +230,9 @@ module Invidious::Routing get "/api/v1/channels/:ucid", {{namespace}}::Channels, :home get "/api/v1/channels/:ucid/shorts", {{namespace}}::Channels, :shorts get "/api/v1/channels/:ucid/streams", {{namespace}}::Channels, :streams + get "/api/v1/channels/:ucid/podcasts", {{namespace}}::Channels, :podcasts + get "/api/v1/channels/:ucid/releases", {{namespace}}::Channels, :releases + get "/api/v1/channels/:ucid/channels", {{namespace}}::Channels, :channels {% for route in {"videos", "latest", "playlists", "community", "search"} %} diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 6e62a471..066e25b5 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -9,6 +9,8 @@ when .streams? then "/channel/#{ucid}/streams" when .playlists? then "/channel/#{ucid}/playlists" when .channels? then "/channel/#{ucid}/channels" + when .podcasts? then "/channel/#{ucid}/podcasts" + when .releases? then "/channel/#{ucid}/releases" else "/channel/#{ucid}" end diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index 6686e6e7..e5029dc5 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -408,8 +408,8 @@ private module Parsers # Returns nil when the given object isn't a RichItemRenderer # # A richItemRenderer seems to be a simple wrapper for a videoRenderer, used - # by the result page for hashtags. It is located inside a continuationItems - # container. + # by the result page for hashtags and for the podcast tab on channels. + # It is located inside a continuationItems container for hashtags. # module RichItemRendererParser def self.process(item : JSON::Any, author_fallback : AuthorFallback) @@ -421,6 +421,7 @@ private module Parsers private def self.parse(item_contents, author_fallback) child = VideoRendererParser.process(item_contents, author_fallback) child ||= ReelItemRendererParser.process(item_contents, author_fallback) + child ||= PlaylistRendererParser.process(item_contents, author_fallback) return child end From 05cc5033910cabe7008832e8917b93ee3112a540 Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sat, 15 Jul 2023 12:57:26 +0000 Subject: [PATCH 22/28] Fix lint --- src/invidious/views/channel.ecr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 066e25b5..4b50e7a0 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -9,8 +9,8 @@ when .streams? then "/channel/#{ucid}/streams" when .playlists? then "/channel/#{ucid}/playlists" when .channels? then "/channel/#{ucid}/channels" - when .podcasts? then "/channel/#{ucid}/podcasts" - when .releases? then "/channel/#{ucid}/releases" + when .podcasts? then "/channel/#{ucid}/podcasts" + when .releases? then "/channel/#{ucid}/releases" else "/channel/#{ucid}" end From 70145cba31fb7fa14dafa3493c9133c01f642116 Mon Sep 17 00:00:00 2001 From: ChunkyProgrammer <78101139+ChunkyProgrammer@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:49:36 -0700 Subject: [PATCH 23/28] Community: Parse `Quiz` attachments --- src/invidious/channels/community.cr | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index aac4bc8a..671f6dee 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -216,6 +216,22 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) parse_item(attachment) .as(SearchPlaylist) .to_json(locale, json) + when .has_key?("quizRenderer") + json.object do + attachment = attachment["quizRenderer"] + json.field "type", "quiz" + json.field "totalVotes", short_text_to_number(attachment["totalVotes"]["simpleText"].as_s.split(" ")[0]) + json.field "choices" do + json.array do + attachment["choices"].as_a.each do |choice| + json.object do + json.field "text", choice.dig("text", "runs", 0, "text").as_s + json.field "isCorrect", choice["isCorrect"].as_bool + end + end + end + end + end else json.object do json.field "type", "unknown" From c1a69e4a4a8b581ec743b7b3f741097d6596cb3b Mon Sep 17 00:00:00 2001 From: Samantaz Fox Date: Sun, 16 Jul 2023 17:23:23 +0200 Subject: [PATCH 24/28] Channels: Use innertube to fetch the community tab --- src/invidious/channels/community.cr | 54 +++++++++----------------- src/invidious/yt_backend/extractors.cr | 26 ++++++++----- 2 files changed, 34 insertions(+), 46 deletions(-) diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index aac4bc8a..1a54a946 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -1,49 +1,31 @@ private IMAGE_QUALITIES = {320, 560, 640, 1280, 2000} # TODO: Add "sort_by" -def fetch_channel_community(ucid, continuation, locale, format, thin_mode) - response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en") - if response.status_code != 200 - response = YT_POOL.client &.get("/user/#{ucid}/community?gl=US&hl=en") - end +def fetch_channel_community(ucid, cursor, locale, format, thin_mode) + if cursor.nil? + # Egljb21tdW5pdHk%3D is the protobuf object to load "community" + initial_data = YoutubeAPI.browse(ucid, params: "Egljb21tdW5pdHk%3D") - if response.status_code != 200 - 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"] - - if !continuation || continuation.empty? - initial_data = extract_initial_data(response.body) - body = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"])["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"] - - if !body - raise InfoException.new("Could not extract community tab.") + items = [] of JSON::Any + extract_items(initial_data) do |item| + items << item end else - continuation = produce_channel_community_continuation(ucid, continuation) + continuation = produce_channel_community_continuation(ucid, cursor) + initial_data = YoutubeAPI.browse(continuation: continuation) - headers = HTTP::Headers.new - headers["cookie"] = response.cookies.add_request_headers(headers)["cookie"] + container = initial_data.dig?("continuationContents", "itemSectionContinuation", "contents") - session_token = response.body.match(/"XSRF_TOKEN":"(?[^"]+)"/).try &.["session_token"]? || "" - post_req = { - session_token: session_token, - } + raise InfoException.new("Can't extract community data") if container.nil? - body = YoutubeAPI.browse(continuation) - - body = body.dig?("continuationContents", "itemSectionContinuation") || - body.dig?("continuationContents", "backstageCommentsContinuation") - - if !body - raise InfoException.new("Could not extract continuation.") - end + items = container.as_a end - posts = body["contents"].as_a + return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode) +end - if message = posts[0]["messageRenderer"]? +def extract_channel_community(items, *, ucid, locale, format, thin_mode) + if message = items[0]["messageRenderer"]? error_message = (message["text"]["simpleText"]? || message["text"]["runs"]?.try &.[0]?.try &.["text"]?) .try &.as_s || "" @@ -59,7 +41,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) json.field "authorId", ucid json.field "comments" do json.array do - posts.each do |post| + items.each do |post| comments = post["backstagePostThreadRenderer"]?.try &.["comments"]? || post["backstageCommentsContinuation"]? @@ -242,7 +224,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) end end end - if cont = posts.dig?(-1, "continuationItemRenderer", "continuationEndpoint", "continuationCommand", "token") + if cont = items.dig?(-1, "continuationItemRenderer", "continuationEndpoint", "continuationCommand", "token") json.field "continuation", extract_channel_community_cursor(cont.as_s) end end diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index e5029dc5..8cf59d50 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -608,19 +608,25 @@ private module Extractors private def self.unpack_section_list(contents) raw_items = [] of JSON::Any - 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"]? + contents.as_a.each do |item| + if item_section_content = item.dig?("itemSectionRenderer", "contents") + raw_items += self.unpack_item_section(item_section_content) else - items_container = renderer_container_contents + raw_items << item end + end - items_container["items"]?.try &.as_a.each do |item| + return raw_items + end + + private def self.unpack_item_section(contents) + raw_items = [] of JSON::Any + + contents.as_a.each do |item| + # Category extraction + if container = item.dig?("gridRenderer", "items") || item.dig?("items") + raw_items += container.as_a + else raw_items << item end end From c5fe96e93603db58d6767928eedc658e8b58e59f Mon Sep 17 00:00:00 2001 From: syeopite Date: Wed, 26 Jul 2023 07:19:12 -0700 Subject: [PATCH 25/28] Remove lsquic from codebase --- config/config.example.yml | 21 --- shard.lock | 4 - shard.yml | 3 - src/invidious.cr | 2 +- src/invidious/config.cr | 2 - src/invidious/routes/images.cr | 142 +++----------------- src/invidious/yt_backend/connection_pool.cr | 37 +---- src/invidious/yt_backend/youtube_api.cr | 14 +- 8 files changed, 32 insertions(+), 193 deletions(-) diff --git a/config/config.example.yml b/config/config.example.yml index 34070fe5..e925a5e3 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -140,27 +140,6 @@ https_only: false ## #pool_size: 100 -## -## Enable/Disable the use of QUIC (HTTP/3) when connecting -## to the youtube API and websites ('youtube.com', 'ytimg.com'). -## QUIC's main advantages are its lower latency and lower bandwidth -## use, compared to its predecessors. However, the current version -## of QUIC used in invidious is still based on the IETF draft 31, -## meaning that the underlying library may still not be fully -## optimized. You can read more about QUIC at the link below: -## https://datatracker.ietf.org/doc/html/draft-ietf-quic-transport-31 -## -## Note: you should try both options and see what is the best for your -## instance. In general QUIC is recommended for public instances. Your -## mileage may vary. -## -## Note 2: Using QUIC prevents some captcha challenges from appearing. -## See: https://github.com/iv-org/invidious/issues/957#issuecomment-576424042 -## -## Accepted values: true, false -## Default: false -## -#use_quic: false ## ## Additional cookies to be sent when requesting the youtube API. diff --git a/shard.lock b/shard.lock index 235e4c25..55fcfe46 100644 --- a/shard.lock +++ b/shard.lock @@ -24,10 +24,6 @@ shards: git: https://github.com/jeromegn/kilt.git version: 0.6.1 - lsquic: - git: https://github.com/iv-org/lsquic.cr.git - version: 2.18.1-2 - pg: git: https://github.com/will/crystal-pg.git version: 0.24.0 diff --git a/shard.yml b/shard.yml index 7ee0bb2a..e929160d 100644 --- a/shard.yml +++ b/shard.yml @@ -25,9 +25,6 @@ dependencies: protodec: github: iv-org/protodec version: ~> 0.1.5 - lsquic: - github: iv-org/lsquic.cr - version: ~> 2.18.1-2 athena-negotiation: github: athena-framework/negotiation version: ~> 0.1.1 diff --git a/src/invidious.cr b/src/invidious.cr index 84e1895d..e0bd0101 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -90,7 +90,7 @@ SOFTWARE = { "branch" => "#{CURRENT_BRANCH}", } -YT_POOL = YoutubeConnectionPool.new(YT_URL, capacity: CONFIG.pool_size, use_quic: CONFIG.use_quic) +YT_POOL = YoutubeConnectionPool.new(YT_URL, capacity: CONFIG.pool_size) # CLI Kemal.config.extra_options do |parser| diff --git a/src/invidious/config.cr b/src/invidious/config.cr index e5f1e822..cee33ce1 100644 --- a/src/invidious/config.cr +++ b/src/invidious/config.cr @@ -126,8 +126,6 @@ class Config property host_binding : String = "0.0.0.0" # Pool size for HTTP requests to youtube.com and ytimg.com (each domain has a separate pool of `pool_size`) property pool_size : Int32 = 100 - # Use quic transport for youtube api - property use_quic : Bool = false # Saved cookies in "name1=value1; name2=value2..." format @[YAML::Field(converter: Preferences::StringToCookies)] diff --git a/src/invidious/routes/images.cr b/src/invidious/routes/images.cr index 594a7869..b6a2e110 100644 --- a/src/invidious/routes/images.cr +++ b/src/invidious/routes/images.cr @@ -3,17 +3,7 @@ module Invidious::Routes::Images def self.ggpht(env) url = env.request.path.lchop("/ggpht") - headers = ( - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - HTTP::Headers{":authority" => "yt3.ggpht.com"} - else - HTTP::Headers.new - end - {% else %} - HTTP::Headers.new - {% end %} - ) + headers = HTTP::Headers.new REQUEST_HEADERS_WHITELIST.each do |header| if env.request.headers[header]? @@ -42,22 +32,9 @@ module Invidious::Routes::Images } begin - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - YT_POOL.client &.get(url, headers) do |resp| - return request_proc.call(resp) - end - else - HTTP::Client.get("https://yt3.ggpht.com#{url}") do |resp| - return request_proc.call(resp) - end - end - {% else %} - # This can likely be optimized into a (small) pool sometime in the future. - HTTP::Client.get("https://yt3.ggpht.com#{url}") do |resp| - return request_proc.call(resp) - end - {% end %} + HTTP::Client.get("https://yt3.ggpht.com#{url}") do |resp| + return request_proc.call(resp) + end rescue ex end end @@ -78,10 +55,6 @@ module Invidious::Routes::Images headers = HTTP::Headers.new - {% unless flag?(:disable_quic) %} - headers[":authority"] = "#{authority}.ytimg.com" - {% end %} - REQUEST_HEADERS_WHITELIST.each do |header| if env.request.headers[header]? headers[header] = env.request.headers[header] @@ -107,22 +80,9 @@ module Invidious::Routes::Images } begin - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - YT_POOL.client &.get(url, headers) do |resp| - return request_proc.call(resp) - end - else - HTTP::Client.get("https://#{authority}.ytimg.com#{url}") do |resp| - return request_proc.call(resp) - end - end - {% else %} - # This can likely be optimized into a (small) pool sometime in the future. - HTTP::Client.get("https://#{authority}.ytimg.com#{url}") do |resp| - return request_proc.call(resp) - end - {% end %} + HTTP::Client.get("https://#{authority}.ytimg.com#{url}") do |resp| + return request_proc.call(resp) + end rescue ex end end @@ -133,17 +93,7 @@ module Invidious::Routes::Images name = env.params.url["name"] url = env.request.resource - headers = ( - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - HTTP::Headers{":authority" => "i9.ytimg.com"} - else - HTTP::Headers.new - end - {% else %} - HTTP::Headers.new - {% end %} - ) + headers = HTTP::Headers.new REQUEST_HEADERS_WHITELIST.each do |header| if env.request.headers[header]? @@ -169,22 +119,9 @@ module Invidious::Routes::Images } begin - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - YT_POOL.client &.get(url, headers) do |resp| - return request_proc.call(resp) - end - else - HTTP::Client.get("https://i9.ytimg.com#{url}") do |resp| - return request_proc.call(resp) - end - end - {% else %} - # This can likely be optimized into a (small) pool sometime in the future. - HTTP::Client.get("https://i9.ytimg.com#{url}") do |resp| - return request_proc.call(resp) - end - {% end %} + HTTP::Client.get("https://i9.ytimg.com#{url}") do |resp| + return request_proc.call(resp) + end rescue ex end end @@ -223,41 +160,16 @@ module Invidious::Routes::Images id = env.params.url["id"] name = env.params.url["name"] - headers = ( - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - HTTP::Headers{":authority" => "i.ytimg.com"} - else - HTTP::Headers.new - end - {% else %} - HTTP::Headers.new - {% end %} - ) + headers = HTTP::Headers.new if name == "maxres.jpg" build_thumbnails(id).each do |thumb| thumbnail_resource_path = "/vi/#{id}/#{thumb[:url]}.jpg" - # Logic here is short enough that manually typing them out should be fine. - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - if YT_POOL.client &.head(thumbnail_resource_path, headers).status_code == 200 - name = thumb[:url] + ".jpg" - break - end - else - if HTTP::Client.head("https://i.ytimg.com#{thumbnail_resource_path}").status_code == 200 - name = thumb[:url] + ".jpg" - break - end - end - {% else %} - # This can likely be optimized into a (small) pool sometime in the future. - if HTTP::Client.head("https://i.ytimg.com#{thumbnail_resource_path}").status_code == 200 - name = thumb[:url] + ".jpg" - break - end - {% end %} + # This can likely be optimized into a (small) pool sometime in the future. + if HTTP::Client.head("https://i.ytimg.com#{thumbnail_resource_path}").status_code == 200 + name = thumb[:url] + ".jpg" + break + end end end @@ -287,22 +199,10 @@ module Invidious::Routes::Images } begin - {% unless flag?(:disable_quic) %} - if CONFIG.use_quic - YT_POOL.client &.get(url, headers) do |resp| - return request_proc.call(resp) - end - else - HTTP::Client.get("https://i.ytimg.com#{url}") do |resp| - return request_proc.call(resp) - end - end - {% else %} - # This can likely be optimized into a (small) pool sometime in the future. - HTTP::Client.get("https://i.ytimg.com#{url}") do |resp| - return request_proc.call(resp) - end - {% end %} + # This can likely be optimized into a (small) pool sometime in the future. + HTTP::Client.get("https://i.ytimg.com#{url}") do |resp| + return request_proc.call(resp) + end rescue ex end end diff --git a/src/invidious/yt_backend/connection_pool.cr b/src/invidious/yt_backend/connection_pool.cr index 658731cf..e9eb726c 100644 --- a/src/invidious/yt_backend/connection_pool.cr +++ b/src/invidious/yt_backend/connection_pool.cr @@ -1,11 +1,3 @@ -{% unless flag?(:disable_quic) %} - require "lsquic" - - alias HTTPClientType = QUIC::Client | HTTP::Client -{% else %} - alias HTTPClientType = HTTP::Client -{% end %} - def add_yt_headers(request) if request.headers["User-Agent"] == "Crystal" request.headers["User-Agent"] ||= "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" @@ -26,11 +18,11 @@ struct YoutubeConnectionPool property! url : URI property! capacity : Int32 property! timeout : Float64 - property pool : DB::Pool(HTTPClientType) + property pool : DB::Pool(HTTP::Client) - def initialize(url : URI, @capacity = 5, @timeout = 5.0, use_quic = true) + def initialize(url : URI, @capacity = 5, @timeout = 5.0) @url = url - @pool = build_pool(use_quic) + @pool = build_pool() end def client(region = nil, &block) @@ -43,11 +35,7 @@ struct YoutubeConnectionPool response = yield conn rescue ex conn.close - {% unless flag?(:disable_quic) %} - conn = CONFIG.use_quic ? QUIC::Client.new(url) : HTTP::Client.new(url) - {% else %} - conn = HTTP::Client.new(url) - {% end %} + conn = HTTP::Client.new(url) conn.family = (url.host == "www.youtube.com") ? CONFIG.force_resolve : Socket::Family::INET conn.family = Socket::Family::INET if conn.family == Socket::Family::UNSPEC @@ -61,19 +49,9 @@ struct YoutubeConnectionPool response end - private def build_pool(use_quic) - DB::Pool(HTTPClientType).new(initial_pool_size: 0, max_pool_size: capacity, max_idle_pool_size: capacity, checkout_timeout: timeout) do - conn = nil # Declare - {% unless flag?(:disable_quic) %} - if use_quic - conn = QUIC::Client.new(url) - else - conn = HTTP::Client.new(url) - end - {% else %} - conn = HTTP::Client.new(url) - {% end %} - + private def build_pool + DB::Pool(HTTP::Client).new(initial_pool_size: 0, max_pool_size: capacity, max_idle_pool_size: capacity, checkout_timeout: timeout) do + conn = HTTP::Client.new(url) conn.family = (url.host == "www.youtube.com") ? CONFIG.force_resolve : Socket::Family::INET conn.family = Socket::Family::INET if conn.family == Socket::Family::UNSPEC conn.before_request { |r| add_yt_headers(r) } if url.host == "www.youtube.com" @@ -83,7 +61,6 @@ struct YoutubeConnectionPool end def make_client(url : URI, region = nil) - # TODO: Migrate any applicable endpoints to QUIC client = HTTPClient.new(url, OpenSSL::SSL::Context::Client.insecure) client.family = (url.host == "www.youtube.com") ? CONFIG.force_resolve : Socket::Family::UNSPEC client.before_request { |r| add_yt_headers(r) } if url.host == "www.youtube.com" diff --git a/src/invidious/yt_backend/youtube_api.cr b/src/invidious/yt_backend/youtube_api.cr index 3dd9e9d8..aef9ddd9 100644 --- a/src/invidious/yt_backend/youtube_api.cr +++ b/src/invidious/yt_backend/youtube_api.cr @@ -595,17 +595,9 @@ module YoutubeAPI LOGGER.trace("YoutubeAPI: POST data: #{data}") # Send the POST request - if {{ !flag?(:disable_quic) }} && CONFIG.use_quic - # Using QUIC client - body = YT_POOL.client(client_config.proxy_region, - &.post(url, headers: headers, body: data.to_json) - ).body - else - # Using HTTP client - body = YT_POOL.client(client_config.proxy_region) do |client| - client.post(url, headers: headers, body: data.to_json) do |response| - self._decompress(response.body_io, response.headers["Content-Encoding"]?) - end + body = YT_POOL.client(client_config.proxy_region) do |client| + client.post(url, headers: headers, body: data.to_json) do |response| + self._decompress(response.body_io, response.headers["Content-Encoding"]?) end end From a8ba02051b261a634050ea7f621451d84ca61607 Mon Sep 17 00:00:00 2001 From: syeopite Date: Wed, 26 Jul 2023 07:25:19 -0700 Subject: [PATCH 26/28] Remove(?) lsquic from make and docker files --- .github/workflows/container-release.yml | 29 ++----------------------- Makefile | 6 ----- docker/Dockerfile | 11 +--------- docker/Dockerfile.arm64 | 11 +--------- 4 files changed, 4 insertions(+), 53 deletions(-) diff --git a/.github/workflows/container-release.yml b/.github/workflows/container-release.yml index 86aec94f..13bbf34c 100644 --- a/.github/workflows/container-release.yml +++ b/.github/workflows/container-release.yml @@ -52,7 +52,7 @@ jobs: username: ${{ secrets.QUAY_USERNAME }} password: ${{ secrets.QUAY_PASSWORD }} - - name: Build and push Docker AMD64 image without QUIC for Push Event + - name: Build and push Docker AMD64 image for Push Event if: github.ref == 'refs/heads/master' uses: docker/build-push-action@v3 with: @@ -64,9 +64,8 @@ jobs: tags: quay.io/invidious/invidious:${{ github.sha }},quay.io/invidious/invidious:latest build-args: | "release=1" - "disable_quic=1" - - name: Build and push Docker ARM64 image without QUIC for Push Event + - name: Build and push Docker ARM64 image for Push Event if: github.ref == 'refs/heads/master' uses: docker/build-push-action@v3 with: @@ -78,28 +77,4 @@ jobs: tags: quay.io/invidious/invidious:${{ github.sha }}-arm64,quay.io/invidious/invidious:latest-arm64 build-args: | "release=1" - "disable_quic=1" - - name: Build and push Docker AMD64 image with QUIC for Push Event - if: github.ref == 'refs/heads/master' - uses: docker/build-push-action@v3 - with: - context: . - file: docker/Dockerfile - platforms: linux/amd64 - labels: quay.expires-after=12w - push: true - tags: quay.io/invidious/invidious:${{ github.sha }}-quic,quay.io/invidious/invidious:latest-quic - build-args: release=1 - - - name: Build and push Docker ARM64 image with QUIC for Push Event - if: github.ref == 'refs/heads/master' - uses: docker/build-push-action@v3 - with: - context: . - file: docker/Dockerfile.arm64 - platforms: linux/arm64/v8 - labels: quay.expires-after=12w - push: true - tags: quay.io/invidious/invidious:${{ github.sha }}-arm64-quic,quay.io/invidious/invidious:latest-arm64-quic - build-args: release=1 diff --git a/Makefile b/Makefile index d4657792..9eb195df 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ RELEASE := 1 STATIC := 0 -DISABLE_QUIC := 1 NO_DBG_SYMBOLS := 0 @@ -27,10 +26,6 @@ else FLAGS += --debug endif -ifeq ($(DISABLE_QUIC), 1) - FLAGS += -Ddisable_quic -endif - ifeq ($(API_ONLY), 1) FLAGS += -Dapi_only endif @@ -115,7 +110,6 @@ help: @echo " STATIC Link libraries statically (Default: 0)" @echo "" @echo " API_ONLY Build invidious without a GUI (Default: 0)" - @echo " DISABLE_QUIC Disable support for QUIC (Default: 0)" @echo " NO_DBG_SYMBOLS Strip debug symbols (Default: 0)" diff --git a/docker/Dockerfile b/docker/Dockerfile index 57864883..761bbdca 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,15 +2,12 @@ FROM crystallang/crystal:1.4.1-alpine AS builder RUN apk add --no-cache sqlite-static yaml-static ARG release -ARG disable_quic WORKDIR /invidious COPY ./shard.yml ./shard.yml COPY ./shard.lock ./shard.lock RUN shards install --production -COPY --from=quay.io/invidious/lsquic-compiled /root/liblsquic.a ./lib/lsquic/src/lsquic/ext/liblsquic.a - COPY ./src/ ./src/ # TODO: .git folder is required for building – this is destructive. # See definition of CURRENT_BRANCH, CURRENT_COMMIT and CURRENT_VERSION. @@ -24,13 +21,7 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml RUN crystal spec --warnings all \ --link-flags "-lxml2 -llzma" -RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \ - crystal build ./src/invidious.cr \ - --release \ - -Ddisable_quic \ - --static --warnings all \ - --link-flags "-lxml2 -llzma"; \ - elif [[ "${release}" == 1 ]] ; then \ +RUN if [[ "${release}" == 1 ]] ; then \ crystal build ./src/invidious.cr \ --release \ --static --warnings all \ diff --git a/docker/Dockerfile.arm64 b/docker/Dockerfile.arm64 index 10135efb..cf9231fb 100644 --- a/docker/Dockerfile.arm64 +++ b/docker/Dockerfile.arm64 @@ -2,15 +2,12 @@ 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 -ARG disable_quic WORKDIR /invidious COPY ./shard.yml ./shard.yml COPY ./shard.lock ./shard.lock RUN shards install --production -COPY --from=quay.io/invidious/lsquic-compiled /root/liblsquic.a ./lib/lsquic/src/lsquic/ext/liblsquic.a - COPY ./src/ ./src/ # TODO: .git folder is required for building – this is destructive. # See definition of CURRENT_BRANCH, CURRENT_COMMIT and CURRENT_VERSION. @@ -24,13 +21,7 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml RUN crystal spec --warnings all \ --link-flags "-lxml2 -llzma" -RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \ - crystal build ./src/invidious.cr \ - --release \ - -Ddisable_quic \ - --static --warnings all \ - --link-flags "-lxml2 -llzma"; \ - elif [[ "${release}" == 1 ]] ; then \ +RUN if [[ "${release}" == 1 ]] ; then \ crystal build ./src/invidious.cr \ --release \ --static --warnings all \ From 0d27eef047d24f8c7b3f9528502bc5828cad3c73 Mon Sep 17 00:00:00 2001 From: Fabio Henrique Date: Sun, 6 Aug 2023 12:29:19 +0000 Subject: [PATCH 27/28] update ameba version fix shard.yml authors typo --- shard.lock | 7 ++++--- shard.yml | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/shard.lock b/shard.lock index 55fcfe46..efb60a59 100644 --- a/shard.lock +++ b/shard.lock @@ -1,5 +1,9 @@ version: 2.0 shards: + ameba: + git: https://github.com/crystal-ameba/ameba.git + version: 1.5.0 + athena-negotiation: git: https://github.com/athena-framework/negotiation.git version: 0.1.1 @@ -44,6 +48,3 @@ shards: git: https://github.com/crystal-lang/crystal-sqlite3.git version: 0.18.0 - ameba: - git: https://github.com/crystal-ameba/ameba.git - version: 0.14.3 diff --git a/shard.yml b/shard.yml index e929160d..be06a7df 100644 --- a/shard.yml +++ b/shard.yml @@ -3,7 +3,7 @@ version: 0.20.1 authors: - Omar Roth - - Invidous team + - Invidious team targets: invidious: @@ -35,7 +35,7 @@ development_dependencies: version: ~> 0.10.4 ameba: github: crystal-ameba/ameba - version: ~> 0.14.3 + version: ~> 1.5.0 crystal: ">= 1.0.0, < 2.0.0" From 2f6b2688bb8042c29942e46767dc78836f21fb57 Mon Sep 17 00:00:00 2001 From: syeopite Date: Sun, 6 Aug 2023 12:20:05 -0700 Subject: [PATCH 28/28] Use workaround for fetching streaming URLs YouTube appears to be A/B testing some new integrity checks. Adding the parameter "CgIQBg" to InnerTube player requests appears to workaround the problem See https://github.com/TeamNewPipe/NewPipeExtractor/pull/1084 --- src/invidious/videos/parser.cr | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 9cc0ffdc..2a09d187 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -55,8 +55,9 @@ def extract_video_info(video_id : String, proxy_region : String? = nil) client_config = YoutubeAPI::ClientConfig.new(proxy_region: proxy_region) # Fetch data from the player endpoint - # 8AEB param is used to fetch YouTube stories - player_response = YoutubeAPI.player(video_id: video_id, params: "8AEB", client_config: client_config) + # CgIQBg is a workaround for streaming URLs that returns a 403. + # See https://github.com/iv-org/invidious/issues/4027#issuecomment-1666944520 + player_response = YoutubeAPI.player(video_id: video_id, params: "CgIQBg", client_config: client_config) playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s @@ -135,8 +136,9 @@ end def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig) : Hash(String, JSON::Any)? LOGGER.debug("try_fetch_streaming_data: [#{id}] Using #{client_config.client_type} client.") - # 8AEB param is used to fetch YouTube stories - response = YoutubeAPI.player(video_id: id, params: "8AEB", client_config: client_config) + # CgIQBg is a workaround for streaming URLs that returns a 403. + # See https://github.com/iv-org/invidious/issues/4027#issuecomment-1666944520 + response = YoutubeAPI.player(video_id: id, params: "CgIQBg", client_config: client_config) playability_status = response["playabilityStatus"]["status"] LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.")