Compare commits

...

8 Commits

Author SHA1 Message Date
Fijxu 9c62c8b660 Add option to disable easy to abuse API endpoints (#5630)
* Add option to disable easy to abuse API endpoints

The API endpoints that will be disabled when this option are:
 - /api/v1/videos
 - /api/v1/clips
 - /api/v1/transcripts

There is still API endponts that need some sort of validation or
connection/proxying to Invidious companion like
`/api/v1/captions` and `/api/v1/storyboards` since they also do a video
request to Invidious companion. I'm not sure if the `/next` API endpoint
could be used to gather that type of information, if so, that would be
better.

Closes #5599

* Rename configuration variable, add additional comment for the option
2026-06-30 11:55:47 +02:00
unrealtournament d61dd7205c [RefreshChannelsJob] Reduce backoff if no errors occur (#5759)
* [RefreshChannelsJob] Reduce backoff if no errors occur

* Change log level for backoff decrease
2026-06-30 10:29:03 +02:00
Émilien (perso) 77ad41678b fix: security issue playlist deletion cross user (#5790)
fixes #5777
2026-06-30 10:28:51 +02:00
Fijxu ab099e46cf chore: update User-Agent header for Youtube requests (#5794) 2026-06-30 10:28:26 +02:00
dependabot[bot] fe2b6dbd67 chore(deps): bump actions/cache from 5 to 6 (#5793)
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 17:26:26 -04:00
Emilien efb9269e58 Prepare for next release 2026-06-27 21:38:06 +02:00
Émilien (perso) ad0bd9289f Release v2.20260626.0 (#5787)
* Release v2.20260626.0

* chore: improve changelog x1

Co-authored-by: Fijxu <fijxu@nadeko.net>

* chore: apply second suggestion

Co-authored-by: Fijxu <fijxu@nadeko.net>

---------

Co-authored-by: Fijxu <fijxu@nadeko.net>
2026-06-27 21:34:03 +02:00
Fijxu 08f862292a fix: fix playlists not showing any videos due to outdated playlist parsing (#5774)
* TODO, CHANGE COMMIT MESSAGE WHEN DONE

* save

* fix: fix playlists videos parsing

* revert text

* do not shadow outer item variable
2026-06-24 23:54:34 -04:00
13 changed files with 187 additions and 38 deletions
+2 -2
View File
@@ -65,7 +65,7 @@ jobs:
crystal: ${{ matrix.crystal }} crystal: ${{ matrix.crystal }}
- name: Cache Shards - name: Cache Shards
uses: actions/cache@v5 uses: actions/cache@v6
with: with:
path: | path: |
./lib ./lib
@@ -137,7 +137,7 @@ jobs:
crystal: latest crystal: latest
- name: Cache Shards - name: Cache Shards
uses: actions/cache@v5 uses: actions/cache@v6
with: with:
path: | path: |
./lib ./lib
+87
View File
@@ -2,6 +2,93 @@
## vX.Y.0 (future) ## vX.Y.0 (future)
## v2.20260626.0
### Wrap-up
This release hardens playlists, channels, and search, adds a privacy option for searches, and modernizes the packaging and CI pipeline.
Searches can now be submitted via `POST` so queries do not leak into server logs or browser history, Invidious cookies work across alternative domains, and "Watch on YouTube" / embed redirects use the correct timestamp and host. Playlist and channel parsing issues got fixed: outdated playlist parsing that hid all videos, paid course videos breaking imports, RSS feeds exposing private playlists without auth, broken author verification badges, and channel videos/playlists not loading from search. Thumbnail paths `/pl_c` / `/tvfilm_banner` are now supported, YouTube comments that were written in Japanese, Chinese, Korean and probably other languages do not longer swallows the last character when an emoji is present in the comment, and the search filters dropped the deprecated "sort by rating/date" options.
Packaging moves Docker builds to the 84codes Crystal compiler image, updates OpenSSL to 3.6.2 and Crystal to 1.20.x in OCI, bumps Alpine to 3.24, and unifies the ARM64 and AMD64 Dockerfiles. Developers benefit from continued encapsulation of constants/helpers/translation/video-parser logic into dedicated modules, an `api/v1/channels.cr` lint pass, trailing-whitespace cleanup, and a sweep of dependency and GitHub Actions bumps.
### New features & important changes
#### For Users
- Searches can be submitted through `POST` requests so queries stay out of URLs, server logs and browser history (#5551)
- Invidious cookies are honoured across alternative configured domains (#5647)
- Embed and "Watch on YouTube" redirects use the correct `t`/`start` parameter and the `www.youtube.com` host consistently (#5660, #5768)
- The `referrerpolicy`/`noreferrer` handling was corrected now that YouTube requires referrers on embeds (#5642)
- The listen button on the title updates its elapsed time, and the deprecated "sort by rating/date" search filter options were removed (#5625, #5629)
#### For instance owners
- Docker builds switched to the 84codes Crystal compiler container image, and OCI images were updated to Crystal 1.20.x with OpenSSL 3.6.2 (#5473, #5692)
- Alpine was bumped to 3.24 in the Docker image (#5778)
- ARM64 and AMD64 Dockerfiles were unified into a single workflow (#5700)
#### For developers
- Constants and functions were encapsulated into dedicated `I18n`, `Helpers`, `Invidious::Videos::Parser` and `Invidious::Videos::Clip` modules (#5637, #5639, #5745)
- `api/v1/channels.cr` received a lint pass and trailing whitespaces were removed from the codebase (#5693, #5634)
- CI bumped the Crystal version matrix and displayed compile progress/stats, and the `crystal-lang/install-crystal` action was updated (#5691, #5696, #5703, #5686)
### Bugs fixed
#### User-side
- Playlists showed no videos because of outdated playlist parsing; this is fixed along with paid course videos breaking the importer (#5774, #5207)
- Private Invidious playlists were reachable through RSS feeds without authentication (#5776)
- Channel videos and playlists failed to load from search, and channel author verification was broken (#5736, #5751)
- A missing `collectionThumbnailViewModel` hash key crashed channel browsing (#5725)
- The `quality=medium` query parameter was appended to videos about to premiere (#5755)
- YouTube/Invidious links did not rewind their timestamp when playback position was rewound (#5601)
- The last character of a comment was lost when the comment contained emoji (#5587)
- Playlist RSS `watch` URLs only joined params with `&` when params were present, and thumbnail paths `/pl_c` and `/tvfilm_banner` are now supported (#5646, #5742)
#### For instance owners
- Docker/OCI builds keep current with Crystal 1.20.1, OpenSSL 3.6.2, Alpine 3.24 and the unified multi-arch Dockerfile (#5703, #5701, #5778, #5700)
#### For developers
- Dependency and GitHub Actions bumps kept CI current: `docker/login-action`, `build-push-action`, `metadata-action`, `setup-buildx-action`, `int128/docker-manifest-create-action` and `crystal-lang/install-crystal` (#5705, #5766, #5721, #5686, #5661, #5662, #5663, #5664)
### Full list of pull requests merged since the last release (newest first)
* fix: fix playlists not showing any videos due to outdated playlist parsing (https://github.com/iv-org/invidious/pull/5774, by @Fijxu)
* chore(deps): bump alpine from 3.23 to 3.24 in /docker (https://github.com/iv-org/invidious/pull/5778, by @dependabot[bot])
* fix: fix private invidious playlists on rss feeds from being fetched without authentication (https://github.com/iv-org/invidious/pull/5776, by @Fijxu)
* Use "www.youtube.com" consistently (https://github.com/iv-org/invidious/pull/5768, by @janmoesen)
* chore(deps): bump int128/docker-manifest-create-action from 2.21.0 to 2.22.0 (https://github.com/iv-org/invidious/pull/5766, by @dependabot[bot])
* Add support for alternative domains for Invidious cookies (https://github.com/iv-org/invidious/pull/5647, by @Fijxu)
* Only include '&' if params are present in `watch` urls for playlist RSS (https://github.com/iv-org/invidious/pull/5646, by @Fijxu)
* Dockerfile: Switch to 84codes crystal compiler container image (https://github.com/iv-org/invidious/pull/5473, by @Fijxu)
* fix: Do not append query params `quality=medium` to videos that are about to premiere (https://github.com/iv-org/invidious/pull/5755, by @Fijxu)
* Fix Youtube and Invidious links not rewinding their time when video playback position is rewound (https://github.com/iv-org/invidious/pull/5601, by @Fijxu)
* feat: Add support for POST requests on searches for privacy (https://github.com/iv-org/invidious/pull/5551, by @Fijxu)
* Fix last character disappearance if emoji are in comment (https://github.com/iv-org/invidious/pull/5587, by @shiny-comic)
* Encapsulate videos parser and clip functions inside it's own `Invidious::Videos::Parser` and `Invidious::Videos::Clip` module (https://github.com/iv-org/invidious/pull/5745, by @Fijxu)
* fix: fix author verification in channels (https://github.com/iv-org/invidious/pull/5751, by @Fijxu)
* Add support for `/pl_c` and `/tvfilm_banner` paths (thumbnails used in some playlists) (https://github.com/iv-org/invidious/pull/5742, by @Fijxu)
* fix: fix channel videos and playlists on searches (https://github.com/iv-org/invidious/pull/5736, by @Fijxu)
* fix: fix `Missing hash key: "collectionThumbnailViewModel"` (https://github.com/iv-org/invidious/pull/5725, by @Fijxu)
* chore(deps): bump int128/docker-manifest-create-action from 2.20.0 to 2.21.0 (https://github.com/iv-org/invidious/pull/5721, by @dependabot[bot])
* chore: update openssl to 3.6.2 in OCI (https://github.com/iv-org/invidious/pull/5701, by @Fijxu)
* Bump int128/docker-manifest-create-action from 2.19.0 to 2.20.0 (https://github.com/iv-org/invidious/pull/5705, by @dependabot[bot])
* CI: Unify ARM64 and AMD64 Dockerfiles (https://github.com/iv-org/invidious/pull/5700, by @Fijxu)
* CI: update Crystal 1.20.0 to 1.20.1 in ci.yml matrix (https://github.com/iv-org/invidious/pull/5703, by @Fijxu)
* CI: display progress and stats when compiling Invidious in ci.yml matrix (https://github.com/iv-org/invidious/pull/5696, by @Fijxu)
* CI: Bump Crystal version matrix (https://github.com/iv-org/invidious/pull/5691, by @Fijxu)
* chore: update Crystal to 1.20.0 in OCI (https://github.com/iv-org/invidious/pull/5692, by @Fijxu)
* player: Use correct time parameter for YouTube embed redirects (https://github.com/iv-org/invidious/pull/5660, by @radmorecameron)
* chore: lint api/v1/channels.cr (https://github.com/iv-org/invidious/pull/5693, by @Fijxu)
* Encapsulate helpers constants and functions inside it's own `Helpers` module (https://github.com/iv-org/invidious/pull/5639, by @Fijxu)
* Encapsulate translation constants and functions inside it's own `I18n` module (https://github.com/iv-org/invidious/pull/5637, by @Fijxu)
* Bump crystal-lang/install-crystal from 1.9.1 to 1.9.2 (https://github.com/iv-org/invidious/pull/5686, by @dependabot[bot])
* Playlists: fix parsing error when some videos are paid for in a course (https://github.com/iv-org/invidious/pull/5207, by @ChunkyProgrammer)
* Bump docker/login-action from 3 to 4 (https://github.com/iv-org/invidious/pull/5661, by @dependabot[bot])
* Bump docker/build-push-action from 6 to 7 (https://github.com/iv-org/invidious/pull/5662, by @dependabot[bot])
* Bump docker/metadata-action from 5 to 6 (https://github.com/iv-org/invidious/pull/5663, by @dependabot[bot])
* Bump docker/setup-buildx-action from 3 to 4 (https://github.com/iv-org/invidious/pull/5664, by @dependabot[bot])
* Remove noreferrer since youtube now requires referrers on embeds (https://github.com/iv-org/invidious/pull/5642, by @ashleyirispuppy143)
* Remove trailing whitespaces from codebase (https://github.com/iv-org/invidious/pull/5634, by @Fijxu)
* Add title listen button time updates (https://github.com/iv-org/invidious/pull/5625, by @JeroenBoersma)
* Remove sort by rating and date in video search filters (https://github.com/iv-org/invidious/pull/5629, by @Fijxu)
## v2.20260207.0 ## v2.20260207.0
### Wrap-up ### Wrap-up
+15 -13
View File
@@ -205,7 +205,6 @@ https_only: false
# path: /tmp/invidious.sock # path: /tmp/invidious.sock
# permissions: 777 # permissions: 777
# ----------------------------- # -----------------------------
# Network (outbound) # Network (outbound)
# ----------------------------- # -----------------------------
@@ -228,7 +227,6 @@ https_only: false
## ##
#pool_size: 100 #pool_size: 100
## ##
## Additional cookies to be sent when requesting the youtube API. ## Additional cookies to be sent when requesting the youtube API.
## ##
@@ -263,7 +261,6 @@ https_only: false
# host: # host:
# port: # port:
## ##
## Use Innertube's transcripts API instead of timedtext for closed captions ## Use Innertube's transcripts API instead of timedtext for closed captions
## ##
@@ -344,7 +341,6 @@ https_only: false
## ##
#statistics_enabled: false #statistics_enabled: false
# ----------------------------- # -----------------------------
# Users and accounts # Users and accounts
# ----------------------------- # -----------------------------
@@ -456,12 +452,25 @@ full_refresh: false
## ##
feed_threads: 1 feed_threads: 1
##
## Setting to disable easy to abuse API endpoints that can
## be spammed and therefore blocking your Invidious instance.
##
## Useful for public instance maintainers.
##
## Notes: The following API endpoints will be disabled:
## - /api/v1/videos
## - /api/v1/clips
## - /api/v1/transcripts
##
## Accepted values: true, false
## Default: false
##
disable_abusable_api: false
jobs: jobs:
## Options for the database cleaning job ## Options for the database cleaning job
clear_expired_items: clear_expired_items:
## Enable/Disable job ## Enable/Disable job
## ##
## Accepted values: true, false ## Accepted values: true, false
@@ -471,7 +480,6 @@ jobs:
## Options for the channels updater job ## Options for the channels updater job
refresh_channels: refresh_channels:
## Enable/Disable job ## Enable/Disable job
## ##
## Accepted values: true, false ## Accepted values: true, false
@@ -481,7 +489,6 @@ jobs:
## Options for the RSS feeds updater job ## Options for the RSS feeds updater job
refresh_feeds: refresh_feeds:
## Enable/Disable job ## Enable/Disable job
## ##
## Accepted values: true, false ## Accepted values: true, false
@@ -489,7 +496,6 @@ jobs:
## ##
enable: true enable: true
# ----------------------------- # -----------------------------
# Miscellaneous # Miscellaneous
# ----------------------------- # -----------------------------
@@ -688,7 +694,6 @@ default_user_preferences:
## ##
#captions: ["", "", ""] #captions: ["", "", ""]
# ----------------------------- # -----------------------------
# Interface # Interface
# ----------------------------- # -----------------------------
@@ -790,7 +795,6 @@ default_user_preferences:
## ##
#related_videos: true #related_videos: true
# ----------------------------- # -----------------------------
# Video player behavior # Video player behavior
# ----------------------------- # -----------------------------
@@ -854,7 +858,6 @@ default_user_preferences:
## ##
#video_loop: false #video_loop: false
# ----------------------------- # -----------------------------
# Video playback settings # Video playback settings
# ----------------------------- # -----------------------------
@@ -966,7 +969,6 @@ default_user_preferences:
## ##
#sort: published #sort: published
# ----------------------------- # -----------------------------
# Miscellaneous # Miscellaneous
# ----------------------------- # -----------------------------
+1 -1
View File
@@ -1,5 +1,5 @@
name: invidious name: invidious
version: 2.20260207.0-dev version: 2.20260626.0-dev
authors: authors:
- Invidious team <contact@invidious.io> - Invidious team <contact@invidious.io>
+1
View File
@@ -217,6 +217,7 @@ end
Kemal.config.powered_by_header = false Kemal.config.powered_by_header = false
add_handler FilteredCompressHandler.new add_handler FilteredCompressHandler.new
add_handler APIHandler.new add_handler APIHandler.new
add_handler DisableAbusableAPIHandler.new
add_handler AuthHandler.new add_handler AuthHandler.new
add_handler DenyFrame.new add_handler DenyFrame.new
+3
View File
@@ -183,6 +183,9 @@ class Config
# Playlist length limit # Playlist length limit
property playlist_length_limit : Int32 = 500 property playlist_length_limit : Int32 = 500
# Disable easy to abuse API endpoints
property disable_abusable_api : Bool = false
def disabled?(option) def disabled?(option)
case disabled = CONFIG.disable_proxy case disabled = CONFIG.disable_proxy
when Bool when Bool
+3 -3
View File
@@ -194,13 +194,13 @@ module Invidious::Database::PlaylistVideos
PG_DB.exec(request, args: video_array) PG_DB.exec(request, args: video_array)
end end
def delete(index) def delete(index, plid : String)
request = <<-SQL request = <<-SQL
DELETE FROM playlist_videos * DELETE FROM playlist_videos *
WHERE index = $1 WHERE index = $1 AND plid = $2
SQL SQL
PG_DB.exec(request, index) PG_DB.exec(request, index, plid)
end end
def delete_by_playlist(plid : String) def delete_by_playlist(plid : String)
+20
View File
@@ -133,6 +133,26 @@ class APIHandler < Kemal::Handler
end end
end end
class DisableAbusableAPIHandler < Kemal::Handler
{% for method in %w(GET HEAD) %}
# This endpoints make a video request to Invidious companion.
{% for endpoint in %w(videos clips transcripts) %}
only ["/api/v1/{{ endpoint.id }}/:id"], {{ method }}
{% end %}
{% end %}
def call(env)
return call_next env unless only_match?(env) && CONFIG.disable_abusable_api
env.response.content_type = "application/json"
env.response.status_code = 403
message = {"error" => "This API endpoint has been disabled by the administrator."}.to_json
env.response.print message
env.response.close
return
end
end
class DenyFrame < Kemal::Handler class DenyFrame < Kemal::Handler
exclude ["/embed/*"] exclude ["/embed/*"]
@@ -36,6 +36,11 @@ class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
LOGGER.trace("RefreshChannelsJob: #{id} fiber : Updating DB") LOGGER.trace("RefreshChannelsJob: #{id} fiber : Updating DB")
Invidious::Database::Channels.update_author(id, channel.author) Invidious::Database::Channels.update_author(id, channel.author)
if backoff > 2.minutes
backoff /= 2
LOGGER.debug("RefreshChannelsJob: #{id} fiber : decreasing backoff to #{backoff}s")
end
rescue ex rescue ex
LOGGER.error("RefreshChannelsJob: #{id} : #{ex.message}") LOGGER.error("RefreshChannelsJob: #{id} : #{ex.message}")
if ex.message == "Deleted or invalid channel" if ex.message == "Deleted or invalid channel"
+42 -15
View File
@@ -454,6 +454,10 @@ def get_playlist_videos(playlist : InvidiousPlaylist | Playlist, offset : Int32,
end end
end end
# TODO (2026-06-24): Migrate this function to use parsers instead, as it uses,
# the same LockupViewModel used in Channel videos and Youtube playlists that
# appears on searches (Invidious /search endpoint).
# Related to https://github.com/iv-org/invidious/pull/5736
def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JSON::Any)) def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JSON::Any))
videos = [] of PlaylistVideo | ProblematicTimelineItem videos = [] of PlaylistVideo | ProblematicTimelineItem
@@ -467,8 +471,7 @@ def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JS
tabs_contents = tabs_renderer.["contents"]? || tabs_renderer.["content"] tabs_contents = tabs_renderer.["contents"]? || tabs_renderer.["content"]
list_renderer = tabs_contents.["sectionListRenderer"]["contents"][0] list_renderer = tabs_contents.["sectionListRenderer"]["contents"][0]
item_renderer = list_renderer.["itemSectionRenderer"]["contents"][0] contents = list_renderer.["itemSectionRenderer"]["contents"].as_a
contents = item_renderer.["playlistVideoListRenderer"]["contents"].as_a
else else
# Continuation data # Continuation data
contents = initial_data["onResponseReceivedActions"][0]? contents = initial_data["onResponseReceivedActions"][0]?
@@ -479,15 +482,39 @@ def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JS
end end
contents.try &.each do |item| contents.try &.each do |item|
if i = item["playlistVideoRenderer"]? if i = item["lockupViewModel"]?
video_id = i.dig?("navigationEndpoint", "watchEndpoint", "videoId").try &.as_s || i.dig("videoId").as_s thumbnail_view_model = i.dig?(
plid = i.dig?("navigationEndpoint", "watchEndpoint", "playlistId").try &.as_s || playlist_id "contentImage", "thumbnailViewModel"
index = i.dig?("navigationEndpoint", "watchEndpoint", "index").try &.as_i64 || i.dig("index", "simpleText").as_s.to_i64 )
watch_endpoint = i.dig?("rendererContext", "commandContext", "onTap", "innertubeCommand", "watchEndpoint")
video_id = watch_endpoint.try &.["videoId"]?.try &.as_s
plid = watch_endpoint.try &.["playlistId"]?.try &.as_s || playlist_id
index = watch_endpoint.try &.["index"]?.try &.as_i64
metadata = i["metadata"]?
lockup_metadata_view_model = metadata.try &.dig?("lockupMetadataViewModel")
title = lockup_metadata_view_model.try &.dig?("title", "content").try &.as_s
lockup_metadata = lockup_metadata_view_model.try &.dig?("metadata")
metadata_rows = lockup_metadata.try &.dig?("contentMetadataViewModel", "metadataRows").try &.as_a
# Find the metadataParts with commandRuns inside, which contains author
# information.
metadata_parts = metadata_rows.try &.find { |row|
parts = row["metadataParts"]?.try &.as_a
parts && parts.any? { |item2| item2.dig?("text", "commandRuns").try &.as_a }
}.try &.["metadataParts"].as_a
if author_info = metadata_parts.try &.find(&.dig?("text", "commandRuns"))
.try &.["text"]
author = author_info["content"].as_s
ucid = author_info.dig?("commandRuns", 0, "onTap", "innertubeCommand", "browseEndpoint", "browseId")
.try &.as_s
end
length = thumbnail_view_model.try &.dig?("overlays", 0, "thumbnailBottomOverlayViewModel", "badges", 0, "thumbnailBadgeViewModel", "text").try &.as_s
length_seconds = decode_length_seconds(length) if length
title = i["title"].try { |t| t["simpleText"]? || t["runs"]?.try &.[0]["text"]? }.try &.as_s || ""
author = i["shortBylineText"]?.try &.["runs"][0]["text"].as_s || ""
ucid = i["shortBylineText"]?.try &.["runs"][0]["navigationEndpoint"]["browseEndpoint"]["browseId"].as_s || ""
length_seconds = i["lengthSeconds"]?.try &.as_s.to_i
live = false live = false
if !length_seconds if !length_seconds
@@ -496,15 +523,15 @@ def extract_playlist_videos(playlist_id : String, initial_data : Hash(String, JS
end end
videos << PlaylistVideo.new({ videos << PlaylistVideo.new({
title: title, title: title || "",
id: video_id, id: video_id || "",
author: author, author: author || "",
ucid: ucid, ucid: ucid || "",
length_seconds: length_seconds, length_seconds: length_seconds,
published: Time.utc, published: Time.utc,
plid: plid, plid: plid,
live_now: live, live_now: live,
index: index, index: index || -1_i64,
}) })
end end
rescue ex rescue ex
+1 -1
View File
@@ -364,7 +364,7 @@ module Invidious::Routes::API::V1::Authenticated
return error_json(404, "Playlist does not contain index") return error_json(404, "Playlist does not contain index")
end end
Invidious::Database::PlaylistVideos.delete(index) Invidious::Database::PlaylistVideos.delete(index, plid)
Invidious::Database::Playlists.update_video_removed(plid, index) Invidious::Database::Playlists.update_video_removed(plid, index)
env.response.status_code = 204 env.response.status_code = 204
+6 -2
View File
@@ -357,8 +357,12 @@ module Invidious::Routes::Playlists
Invidious::Database::PlaylistVideos.insert(playlist_video) Invidious::Database::PlaylistVideos.insert(playlist_video)
Invidious::Database::Playlists.update_video_added(playlist_id, playlist_video.index) Invidious::Database::Playlists.update_video_added(playlist_id, playlist_video.index)
when "remove_video" when "remove_video"
index = env.params.query["set_video_id"] index = env.params.query["set_video_id"].to_i64?
Invidious::Database::PlaylistVideos.delete(index) if index.nil? || !playlist.index.includes? index
return error_json(404, "Playlist does not contain index")
end
Invidious::Database::PlaylistVideos.delete(index, playlist_id)
Invidious::Database::Playlists.update_video_removed(playlist_id, index) Invidious::Database::Playlists.update_video_removed(playlist_id, index)
when "move_video_before" when "move_video_before"
# TODO: Playlist stub # TODO: Playlist stub
+1 -1
View File
@@ -106,7 +106,7 @@ end
def add_yt_headers(request) def add_yt_headers(request)
request.headers.delete("User-Agent") if request.headers["User-Agent"] == "Crystal" request.headers.delete("User-Agent") 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/128.0.0.0 Safari/537.36" request.headers["User-Agent"] ||= "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
request.headers["Accept-Charset"] ||= "ISO-8859-1,utf-8;q=0.7,*;q=0.7" request.headers["Accept-Charset"] ||= "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
request.headers["Accept"] ||= "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" request.headers["Accept"] ||= "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"