summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--assets/css/default.css1
-rw-r--r--assets/js/watch.js2
-rwxr-xr-xconfig/migrate-scripts/migrate-db-17cf077.sh7
-rwxr-xr-xconfig/migrate-scripts/migrate-db-1c8075c.sh11
-rwxr-xr-xconfig/migrate-scripts/migrate-db-1eca969.sh37
-rwxr-xr-xconfig/migrate-scripts/migrate-db-30e6d29.sh7
-rwxr-xr-xconfig/migrate-scripts/migrate-db-3646395.sh9
-rwxr-xr-xconfig/migrate-scripts/migrate-db-3bcb98e.sh5
-rwxr-xr-xconfig/migrate-scripts/migrate-db-52cb239.sh5
-rwxr-xr-xconfig/migrate-scripts/migrate-db-6e51189.sh7
-rwxr-xr-xconfig/migrate-scripts/migrate-db-701b5ea.sh5
-rwxr-xr-xconfig/migrate-scripts/migrate-db-88b7097.sh5
-rwxr-xr-xconfig/migrate-scripts/migrate-db-8e884fe.sh9
-rw-r--r--docker-compose.yml2
-rwxr-xr-xdocker/init-invidious-db.sh4
-rw-r--r--src/invidious.cr7
-rw-r--r--src/invidious/helpers/extractors.cr2
-rw-r--r--src/invidious/helpers/serialized_yt_data.cr9
-rw-r--r--src/invidious/playlists.cr49
-rw-r--r--src/invidious/routes/api/v1/misc.cr30
-rw-r--r--src/invidious/views/add_playlist_items.ecr4
-rw-r--r--src/invidious/views/channel.ecr4
-rw-r--r--src/invidious/views/components/item.ecr6
-rw-r--r--src/invidious/views/playlists.ecr2
-rw-r--r--src/invidious/views/search.ecr14
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 %>&amp;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 %>&amp;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 %>