diff options
25 files changed, 159 insertions, 84 deletions
diff --git a/assets/css/default.css b/assets/css/default.css index 9e283fb6..95c1f55c 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -19,6 +19,7 @@ body { font-size: 1.17em; font-weight: bold; vertical-align: middle; + border-radius: 50%; } .channel-profile > img { diff --git a/assets/js/watch.js b/assets/js/watch.js index 3909edd4..1579abf4 100644 --- a/assets/js/watch.js +++ b/assets/js/watch.js @@ -149,6 +149,8 @@ function get_playlist(plid, retries) { if (xhr.readyState == 4) { if (xhr.status == 200) { playlist.innerHTML = xhr.response.playlistHtml; + var nextVideo = document.getElementById(xhr.response.nextVideo); + nextVideo.parentNode.parentNode.scrollTop = nextVideo.offsetTop; if (xhr.response.nextVideo) { player.on('ended', function () { diff --git a/config/migrate-scripts/migrate-db-17cf077.sh b/config/migrate-scripts/migrate-db-17cf077.sh index 5e5bb214..1597311d 100755 --- a/config/migrate-scripts/migrate-db-17cf077.sh +++ b/config/migrate-scripts/migrate-db-17cf077.sh @@ -1,4 +1,7 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channels ADD COLUMN subscribed bool;" -psql invidious kemal -c "UPDATE channels SET subscribed = false;" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN subscribed bool;" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET subscribed = false;" diff --git a/config/migrate-scripts/migrate-db-1c8075c.sh b/config/migrate-scripts/migrate-db-1c8075c.sh index 63954397..b6f7b89c 100755 --- a/config/migrate-scripts/migrate-db-1c8075c.sh +++ b/config/migrate-scripts/migrate-db-1c8075c.sh @@ -1,7 +1,10 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channel_videos DROP COLUMN live_now CASCADE" -psql invidious kemal -c "ALTER TABLE channel_videos DROP COLUMN premiere_timestamp CASCADE" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious -psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN live_now bool" -psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos DROP COLUMN live_now CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos DROP COLUMN premiere_timestamp CASCADE" + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN live_now bool" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz" diff --git a/config/migrate-scripts/migrate-db-1eca969.sh b/config/migrate-scripts/migrate-db-1eca969.sh index f840d924..770a76d3 100755 --- a/config/migrate-scripts/migrate-db-1eca969.sh +++ b/config/migrate-scripts/migrate-db-1eca969.sh @@ -1,19 +1,22 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN title CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN views CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN likes CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN dislikes CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN wilson_score CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN published CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN description CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN language CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN author CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN ucid CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN allowed_regions CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN is_family_friendly CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN genre CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN genre_url CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN license CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN sub_count_text CASCADE" -psql invidious kemal -c "ALTER TABLE videos DROP COLUMN author_thumbnail CASCADE" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN title CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN views CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN likes CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN dislikes CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN wilson_score CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN published CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN description CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN language CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN author CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN ucid CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN allowed_regions CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN is_family_friendly CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN genre CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN genre_url CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN license CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN sub_count_text CASCADE" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE videos DROP COLUMN author_thumbnail CASCADE" diff --git a/config/migrate-scripts/migrate-db-30e6d29.sh b/config/migrate-scripts/migrate-db-30e6d29.sh index 3a377461..9d0b2d30 100755 --- a/config/migrate-scripts/migrate-db-30e6d29.sh +++ b/config/migrate-scripts/migrate-db-30e6d29.sh @@ -1,4 +1,7 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channels ADD COLUMN deleted bool;" -psql invidious kemal -c "UPDATE channels SET deleted = false;" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN deleted bool;" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET deleted = false;" diff --git a/config/migrate-scripts/migrate-db-3646395.sh b/config/migrate-scripts/migrate-db-3646395.sh index 830b85f2..b6efe239 100755 --- a/config/migrate-scripts/migrate-db-3646395.sh +++ b/config/migrate-scripts/migrate-db-3646395.sh @@ -1,5 +1,8 @@ #!/bin/sh -psql invidious kemal < config/sql/session_ids.sql -psql invidious kemal -c "INSERT INTO session_ids (SELECT unnest(id), email, CURRENT_TIMESTAMP FROM users) ON CONFLICT (id) DO NOTHING" -psql invidious kemal -c "ALTER TABLE users DROP COLUMN id" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" < config/sql/session_ids.sql +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "INSERT INTO session_ids (SELECT unnest(id), email, CURRENT_TIMESTAMP FROM users) ON CONFLICT (id) DO NOTHING" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE users DROP COLUMN id" diff --git a/config/migrate-scripts/migrate-db-3bcb98e.sh b/config/migrate-scripts/migrate-db-3bcb98e.sh index cb9fa6ab..444f65ed 100755 --- a/config/migrate-scripts/migrate-db-3bcb98e.sh +++ b/config/migrate-scripts/migrate-db-3bcb98e.sh @@ -1,3 +1,6 @@ #!/bin/sh -psql invidious kemal < config/sql/annotations.sql +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" < config/sql/annotations.sql diff --git a/config/migrate-scripts/migrate-db-52cb239.sh b/config/migrate-scripts/migrate-db-52cb239.sh index db8efeab..da977d97 100755 --- a/config/migrate-scripts/migrate-db-52cb239.sh +++ b/config/migrate-scripts/migrate-db-52cb239.sh @@ -1,3 +1,6 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN views bigint;" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN views bigint;" diff --git a/config/migrate-scripts/migrate-db-6e51189.sh b/config/migrate-scripts/migrate-db-6e51189.sh index ce728118..9132d3d7 100755 --- a/config/migrate-scripts/migrate-db-6e51189.sh +++ b/config/migrate-scripts/migrate-db-6e51189.sh @@ -1,4 +1,7 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN live_now bool;" -psql invidious kemal -c "UPDATE channel_videos SET live_now = false;" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN live_now bool;" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channel_videos SET live_now = false;" diff --git a/config/migrate-scripts/migrate-db-701b5ea.sh b/config/migrate-scripts/migrate-db-701b5ea.sh index 429531a2..46d60c00 100755 --- a/config/migrate-scripts/migrate-db-701b5ea.sh +++ b/config/migrate-scripts/migrate-db-701b5ea.sh @@ -1,3 +1,6 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE users ADD COLUMN feed_needs_update boolean" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE users ADD COLUMN feed_needs_update boolean" diff --git a/config/migrate-scripts/migrate-db-88b7097.sh b/config/migrate-scripts/migrate-db-88b7097.sh index 6bde8399..146ee92d 100755 --- a/config/migrate-scripts/migrate-db-88b7097.sh +++ b/config/migrate-scripts/migrate-db-88b7097.sh @@ -1,3 +1,6 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz;" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channel_videos ADD COLUMN premiere_timestamp timestamptz;" diff --git a/config/migrate-scripts/migrate-db-8e884fe.sh b/config/migrate-scripts/migrate-db-8e884fe.sh index 1c8dafd1..0d5de828 100755 --- a/config/migrate-scripts/migrate-db-8e884fe.sh +++ b/config/migrate-scripts/migrate-db-8e884fe.sh @@ -1,5 +1,8 @@ #!/bin/sh -psql invidious kemal -c "ALTER TABLE channels DROP COLUMN subscribed" -psql invidious kemal -c "ALTER TABLE channels ADD COLUMN subscribed timestamptz" -psql invidious kemal -c "UPDATE channels SET subscribed = '2019-01-01 00:00:00+00'" +[ -z "$POSTGRES_USER" ] && POSTGRES_USER=kemal +[ -z "$POSTGRES_DB" ] && POSTGRES_DB=invidious + +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels DROP COLUMN subscribed" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "ALTER TABLE channels ADD COLUMN subscribed timestamptz" +psql "$POSTGRES_DB" "$POSTGRES_USER" -c "UPDATE channels SET subscribed = '2019-01-01 00:00:00+00'" diff --git a/docker-compose.yml b/docker-compose.yml index b94f9813..ea1d2993 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,7 @@ services: POSTGRES_PASSWORD: kemal POSTGRES_USER: kemal healthcheck: - test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"] + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER $$POSTGRES_DB"] invidious: build: context: . diff --git a/docker/init-invidious-db.sh b/docker/init-invidious-db.sh index cc0e1c3f..22b4cc5f 100755 --- a/docker/init-invidious-db.sh +++ b/docker/init-invidious-db.sh @@ -1,10 +1,6 @@ #!/bin/bash set -eou pipefail -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE USER postgres; -EOSQL - psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channels.sql psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/videos.sql psql --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" < config/sql/channel_videos.sql diff --git a/src/invidious.cr b/src/invidious.cr index 9e67e216..73abe6b0 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1549,4 +1549,11 @@ Kemal.config.logger = LOGGER Kemal.config.host_binding = Kemal.config.host_binding != "0.0.0.0" ? Kemal.config.host_binding : CONFIG.host_binding Kemal.config.port = Kemal.config.port != 3000 ? Kemal.config.port : CONFIG.port Kemal.config.app_name = "Invidious" + +# Use in kemal's production mode. +# Users can also set the KEMAL_ENV environmental variable for this to be set automatically. +{% if flag?(:release) || flag?(:production) %} + Kemal.config.env = "production" if !ENV.has_key?("KEMAL_ENV") +{% end %} + Kemal.run diff --git a/src/invidious/helpers/extractors.cr b/src/invidious/helpers/extractors.cr index c8a6cd4a..1ebbe889 100644 --- a/src/invidious/helpers/extractors.cr +++ b/src/invidious/helpers/extractors.cr @@ -43,7 +43,7 @@ private module Parsers private def self.parse(item_contents, author_fallback) video_id = item_contents["videoId"].as_s - title = extract_text(item_contents["title"]) || "" + title = extract_text(item_contents["title"]?) || "" # Extract author information if author_info = item_contents.dig?("ownerText", "runs", 0) diff --git a/src/invidious/helpers/serialized_yt_data.cr b/src/invidious/helpers/serialized_yt_data.cr index 61356555..a9798f0c 100644 --- a/src/invidious/helpers/serialized_yt_data.cr +++ b/src/invidious/helpers/serialized_yt_data.cr @@ -237,8 +237,15 @@ class Category def to_json(locale, json : JSON::Builder) json.object do + json.field "type", "category" json.field "title", self.title - json.field "contents", self.contents + json.field "contents" do + json.array do + self.contents.each do |item| + item.to_json(locale, json) + end + end + end end end diff --git a/src/invidious/playlists.cr b/src/invidious/playlists.cr index f56cc2ea..5034844e 100644 --- a/src/invidious/playlists.cr +++ b/src/invidious/playlists.cr @@ -107,7 +107,7 @@ struct Playlist property updated : Time property thumbnail : String? - def to_json(offset, locale, json : JSON::Builder, continuation : String? = nil) + def to_json(offset, locale, json : JSON::Builder, video_id : String? = nil) json.object do json.field "type", "playlist" json.field "title", self.title @@ -142,7 +142,7 @@ struct Playlist json.field "videos" do json.array do - videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, continuation: continuation) + videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, video_id: video_id) videos.each_with_index do |video, index| video.to_json(locale, json) end @@ -151,12 +151,12 @@ struct Playlist end end - def to_json(offset, locale, json : JSON::Builder? = nil, continuation : String? = nil) + def to_json(offset, locale, json : JSON::Builder? = nil, video_id : String? = nil) if json - to_json(offset, locale, json, continuation: continuation) + to_json(offset, locale, json, video_id: video_id) else JSON.build do |json| - to_json(offset, locale, json, continuation: continuation) + to_json(offset, locale, json, video_id: video_id) end end end @@ -196,7 +196,7 @@ struct InvidiousPlaylist end end - def to_json(offset, locale, json : JSON::Builder, continuation : String? = nil) + def to_json(offset, locale, json : JSON::Builder, video_id : String? = nil) json.object do json.field "type", "invidiousPlaylist" json.field "title", self.title @@ -218,11 +218,11 @@ struct InvidiousPlaylist json.field "videos" do json.array do if !offset || offset == 0 - index = PG_DB.query_one?("SELECT index FROM playlist_videos WHERE plid = $1 AND id = $2 LIMIT 1", self.id, continuation, as: Int64) + index = PG_DB.query_one?("SELECT index FROM playlist_videos WHERE plid = $1 AND id = $2 LIMIT 1", self.id, video_id, as: Int64) offset = self.index.index(index) || 0 end - videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, continuation: continuation) + videos = get_playlist_videos(PG_DB, self, offset: offset, locale: locale, video_id: video_id) videos.each_with_index do |video, index| video.to_json(locale, json, offset + index) end @@ -231,12 +231,12 @@ struct InvidiousPlaylist end end - def to_json(offset, locale, json : JSON::Builder? = nil, continuation : String? = nil) + def to_json(offset, locale, json : JSON::Builder? = nil, video_id : String? = nil) if json - to_json(offset, locale, json, continuation: continuation) + to_json(offset, locale, json, video_id: video_id) else JSON.build do |json| - to_json(offset, locale, json, continuation: continuation) + to_json(offset, locale, json, video_id: video_id) end end end @@ -426,7 +426,7 @@ def fetch_playlist(plid, locale) }) end -def get_playlist_videos(db, playlist, offset, locale = nil, continuation = nil) +def get_playlist_videos(db, playlist, offset, locale = nil, video_id = nil) # Show empy playlist if requested page is out of range # (e.g, when a new playlist has been created, offset will be negative) if offset >= playlist.video_count || offset < 0 @@ -437,17 +437,26 @@ def get_playlist_videos(db, playlist, offset, locale = nil, continuation = nil) db.query_all("SELECT * FROM playlist_videos WHERE plid = $1 ORDER BY array_position($2, index) LIMIT 100 OFFSET $3", playlist.id, playlist.index, offset, as: PlaylistVideo) else - if offset >= 100 - # Normalize offset to match youtube's behavior (100 videos chunck per request) - offset = (offset / 100).to_i64 * 100_i64 + if video_id + initial_data = YoutubeAPI.next({ + "videoId" => video_id, + "playlistId" => playlist.id, + }) + offset = initial_data.dig?("contents", "twoColumnWatchNextResults", "playlist", "playlist", "currentIndex").try &.as_i || offset + end + + videos = [] of PlaylistVideo + until videos.size >= 200 || videos.size == playlist.video_count || offset >= playlist.video_count + # 100 videos per request ctoken = produce_playlist_continuation(playlist.id, offset) initial_data = YoutubeAPI.browse(ctoken) - else - initial_data = YoutubeAPI.browse("VL" + playlist.id, params: "") + videos += extract_playlist_videos(initial_data) + + offset += 100 end - return extract_playlist_videos(initial_data) + return videos end end @@ -523,8 +532,8 @@ def template_playlist(playlist) playlist["videos"].as_a.each do |video| html += <<-END_HTML - <li class="pure-menu-item"> - <a href="/watch?v=#{video["videoId"]}&list=#{playlist["playlistId"]}"> + <li class="pure-menu-item" id="#{video["videoId"]}"> + <a href="/watch?v=#{video["videoId"]}&list=#{playlist["playlistId"]}&index=#{video["index"]}"> <div class="thumbnail"> <img class="thumbnail" src="/vi/#{video["videoId"]}/mqdefault.jpg"> <p class="length">#{recode_length_seconds(video["lengthSeconds"].as_i)}</p> diff --git a/src/invidious/routes/api/v1/misc.cr b/src/invidious/routes/api/v1/misc.cr index cf95bd9b..80b59fd5 100644 --- a/src/invidious/routes/api/v1/misc.cr +++ b/src/invidious/routes/api/v1/misc.cr @@ -24,7 +24,7 @@ module Invidious::Routes::API::V1::Misc offset ||= env.params.query["page"]?.try &.to_i?.try { |page| (page - 1) * 100 } offset ||= 0 - continuation = env.params.query["continuation"]? + video_id = env.params.query["continuation"]? format = env.params.query["format"]? format ||= "json" @@ -46,12 +46,32 @@ module Invidious::Routes::API::V1::Misc return error_json(404, "Playlist does not exist.") end - response = playlist.to_json(offset, locale, continuation: continuation) + # includes into the playlist a maximum of 20 videos, before the offset + if offset > 0 + lookback = offset < 50 ? offset : 50 + response = playlist.to_json(offset - lookback, locale) + json_response = JSON.parse(response) + else + # Unless the continuation is really the offset 0, it becomes expensive. + # It happens when the offset is not set. + # First we find the actual offset, and then we lookback + # it shouldn't happen often though + + lookback = 0 + response = playlist.to_json(offset, locale, video_id: video_id) + json_response = JSON.parse(response) + + if json_response["videos"].as_a[0]["index"] != offset + offset = json_response["videos"].as_a[0]["index"].as_i + lookback = offset < 50 ? offset : 50 + response = playlist.to_json(offset - lookback, locale) + json_response = JSON.parse(response) + end + end if format == "html" - response = JSON.parse(response) - playlist_html = template_playlist(response) - index, next_video = response["videos"].as_a.skip(1).select { |video| !video["author"].as_s.empty? }[0]?.try { |v| {v["index"], v["videoId"]} } || {nil, nil} + playlist_html = template_playlist(json_response) + index, next_video = json_response["videos"].as_a.skip(1 + lookback).select { |video| !video["author"].as_s.empty? }[0]?.try { |v| {v["index"], v["videoId"]} } || {nil, nil} response = { "playlistHtml" => playlist_html, diff --git a/src/invidious/views/add_playlist_items.ecr b/src/invidious/views/add_playlist_items.ecr index 09eacbc8..c62861b0 100644 --- a/src/invidious/views/add_playlist_items.ecr +++ b/src/invidious/views/add_playlist_items.ecr @@ -41,7 +41,7 @@ <div class="pure-g h-box"> <div class="pure-u-1 pure-u-lg-1-5"> <% if page > 1 %> - <a href="/add_playlist_items?list=<%= plid %>&q=<%= HTML.escape(query.not_nil!) %>&page=<%= page - 1 %>"> + <a href="/add_playlist_items?list=<%= plid %>&q=<%= URI.encode_www_form(query.not_nil!) %>&page=<%= page - 1 %>"> <%= translate(locale, "Previous page") %> </a> <% end %> @@ -49,7 +49,7 @@ <div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right"> <% if count >= 20 %> - <a href="/add_playlist_items?list=<%= plid %>&q=<%= HTML.escape(query.not_nil!) %>&page=<%= page + 1 %>"> + <a href="/add_playlist_items?list=<%= plid %>&q=<%= URI.encode_www_form(query.not_nil!) %>&page=<%= page + 1 %>"> <%= translate(locale, "Next page") %> </a> <% end %> diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 09cfb76e..7f797e37 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -96,7 +96,7 @@ <div class="pure-g h-box"> <div class="pure-u-1 pure-u-lg-1-5"> <% if page > 1 %> - <a href="/channel/<%= ucid %>?page=<%= page - 1 %><% if sort_by != "newest" %>&sort_by=<%= HTML.escape(sort_by) %><% end %>"> + <a href="/channel/<%= ucid %>?page=<%= page - 1 %><% if sort_by != "newest" %>&sort_by=<%= URI.encode_www_form(sort_by) %><% end %>"> <%= translate(locale, "Previous page") %> </a> <% end %> @@ -104,7 +104,7 @@ <div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right"> <% if count == 60 %> - <a href="/channel/<%= ucid %>?page=<%= page + 1 %><% if sort_by != "newest" %>&sort_by=<%= HTML.escape(sort_by) %><% end %>"> + <a href="/channel/<%= ucid %>?page=<%= page + 1 %><% if sort_by != "newest" %>&sort_by=<%= URI.encode_www_form(sort_by) %><% end %>"> <%= translate(locale, "Next page") %> </a> <% end %> diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 84da1091..d084bfd4 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -48,7 +48,7 @@ <p dir="auto"><b><%= HTML.escape(item.author) %></b></p> </a> <% when PlaylistVideo %> - <a style="width:100%" href="/watch?v=<%= item.id %>&list=<%= item.plid %>"> + <a style="width:100%" href="/watch?v=<%= item.id %>&list=<%= item.plid %>&index=<%= item.index %>"> <% if !env.get("preferences").as(Preferences).thin_mode %> <div class="thumbnail"> <img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> @@ -87,7 +87,7 @@ <a title="<%=translate(locale, "Audio mode")%>" href="/watch?v=<%= item.id %>&list=<%= item.plid %>&listen=1"> <i class="icon ion-md-headset"></i> </a> - <a title="<%=translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=<%=HTML.escape("watch?v=#{item.id}&list=#{item.plid}")%>"> + <a title="<%=translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=<%=URI.encode_www_form("watch?v=#{item.id}&list=#{item.plid}")%>"> <i class="icon ion-md-jet"></i> </a> </div> @@ -163,7 +163,7 @@ <a title="<%=translate(locale, "Audio mode")%>" href="/watch?v=<%= item.id %>&listen=1"> <i class="icon ion-md-headset"></i> </a> - <a title="<%=translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=<%=HTML.escape("watch?v=#{item.id}")%>"> + <a title="<%=translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=<%=URI.encode_www_form("watch?v=#{item.id}")%>"> <i class="icon ion-md-jet"></i> </a> </div> diff --git a/src/invidious/views/playlists.ecr b/src/invidious/views/playlists.ecr index d9a17a9b..1245256f 100644 --- a/src/invidious/views/playlists.ecr +++ b/src/invidious/views/playlists.ecr @@ -96,7 +96,7 @@ <div class="pure-u-1 pure-u-md-4-5"></div> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right"> <% if continuation %> - <a href="/channel/<%= ucid %>/playlists?continuation=<%= continuation %><% if sort_by != "last" %>&sort_by=<%= HTML.escape(sort_by) %><% end %>"> + <a href="/channel/<%= ucid %>/playlists?continuation=<%= continuation %><% if sort_by != "last" %>&sort_by=<%= URI.encode_www_form(sort_by) %><% end %>"> <%= translate(locale, "Next page") %> </a> <% end %> diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index fd176e41..db374548 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -2,7 +2,7 @@ <title><%= search_query.not_nil!.size > 30 ? HTML.escape(query.not_nil![0,30].rstrip(".") + "...") : HTML.escape(query.not_nil!) %> - Invidious</title> <% end %> -<% search_query_encoded = env.get?("search").try { |x| URI.encode(x.as(String), space_to_plus: true) } %> +<% search_query_encoded = env.get?("search").try { |x| URI.encode_www_form(x.as(String), space_to_plus: true) } %> <!-- Search redirection and filtering UI --> <% if count == 0 %> @@ -23,7 +23,7 @@ <% if operator_hash.fetch("date", "all") == date %> <b><%= translate(locale, date) %></b> <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?date:[a-z]+/, "") + " date:" + date) %>&page=<%= page %>"> + <a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?date:[a-z]+/, "") + " date:" + date) %>&page=<%= page %>"> <%= translate(locale, date) %> </a> <% end %> @@ -38,7 +38,7 @@ <% if operator_hash.fetch("content_type", "all") == content_type %> <b><%= translate(locale, content_type) %></b> <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?content_type:[a-z]+/, "") + " content_type:" + content_type) %>&page=<%= page %>"> + <a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?content_type:[a-z]+/, "") + " content_type:" + content_type) %>&page=<%= page %>"> <%= translate(locale, content_type) %> </a> <% end %> @@ -53,7 +53,7 @@ <% if operator_hash.fetch("duration", "all") == duration %> <b><%= translate(locale, duration) %></b> <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?duration:[a-z]+/, "") + " duration:" + duration) %>&page=<%= page %>"> + <a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?duration:[a-z]+/, "") + " duration:" + duration) %>&page=<%= page %>"> <%= translate(locale, duration) %> </a> <% end %> @@ -68,11 +68,11 @@ <% if operator_hash.fetch("features", "all").includes?(feature) %> <b><%= translate(locale, feature) %></b> <% elsif operator_hash.has_key?("features") %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/features:/, "features:" + feature + ",")) %>&page=<%= page %>"> + <a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/features:/, "features:" + feature + ",")) %>&page=<%= page %>"> <%= translate(locale, feature) %> </a> <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil! + " features:" + feature) %>&page=<%= page %>"> + <a href="/search?q=<%= URI.encode_www_form(query.not_nil! + " features:" + feature) %>&page=<%= page %>"> <%= translate(locale, feature) %> </a> <% end %> @@ -87,7 +87,7 @@ <% if operator_hash.fetch("sort", "relevance") == sort %> <b><%= translate(locale, sort) %></b> <% else %> - <a href="/search?q=<%= HTML.escape(query.not_nil!.gsub(/ ?sort:[a-z]+/, "") + " sort:" + sort) %>&page=<%= page %>"> + <a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?sort:[a-z]+/, "") + " sort:" + sort) %>&page=<%= page %>"> <%= translate(locale, sort) %> </a> <% end %> |
