Compare commits

..

8 Commits

Author SHA1 Message Date
dependabot[bot]
6373ac7814
chore(deps): bump 84codes/crystal in /docker (#5797)
Bumps 84codes/crystal from 1.20.2-alpine to 1.20.3-alpine.

---
updated-dependencies:
- dependency-name: 84codes/crystal
  dependency-version: 1.20.3-alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-07 16:39:57 +02:00
TheFrenchGhosty
31ecdd85f9
Create AI_POLICY.md (#5791)
* Create AI_POLICY.md

* Add some more

* Typo

* Update AI_POLICY.md

Co-authored-by: Émilien (perso) <4016501+unixfox@users.noreply.github.com>

* Various minor enhancements

* Various small rewording

* Add a line about it being mostly done by the maintainers

---------

Co-authored-by: Émilien (perso) <4016501+unixfox@users.noreply.github.com>
2026-07-04 08:20:07 +02:00
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
13 changed files with 97 additions and 24 deletions

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

38
AI_POLICY.md Normal file
View File

@ -0,0 +1,38 @@
Invidious is and has always been made by human first and foremost. However, things have changed recently with the rise of AI.
This document is going to explain everything that you need to know if you ever contribute in any way to Invidious using any kind of AI.
This document has been fully written, from scratch, by a Human.
# Motivation
Invidious is written in an obscure language: Crystal.
Because it is obscure the number of people knowing it is really low.
Because the amount of people knowing it is really low, the amount of contributors to Invidious is really low: the vast majority of contributions are done by the Invidious maintainers themselves.
Invidious is the biggest Crystal project that exists, bigger than Crystal itself [(yes, seriously)](https://shards.info/).
The problem of being the biggest software in an obscure language is that you're often effectively the first project to encounter a problem and because it's an obscure language, not a lot of libraries exist to make it easier for you, meaning, you usually have to make everything you need yourself.
This makes it so working on Invidious far harder than working on most open source projects because you are effectively not benefiting and not using any external libraries for the vast majority of things. Almost any time you need anything, you have to make it yourself, which overcomplicates everything.
We are aware that some people wont like this change and we might even end up on one of the "bad people" list, but we try to be reasonable. We ask that you, please, do not fork the project out of spite because of this new policy - let's not split the thin list of people able to contribute even thinner. Contributions are welcome and highly preferred to anything made by AI.
This policy comes from a place of *need* not from a place of *choice*.
# Policy
Now that AIs exists and have become *reasonably good*, we will tolerate people using them with reasons and knowledge, as long those rules are respected:
- **Any one using AI to report bugs or submit code MUST properly disclose it, this includes mentioning the name of the EXACT model used and the tools used to interact with it.**
- The Human using AI MUST properly check the output manually in addition to any automated check that may exist or may have been created, **this includes BOTH codes AND bug reports**.
- Any code submitted by a Human, written even partially by AI, is the responsibility of this Human - If it's malicious, broken, destructive or anything bad, the Human is the sole responsible.
- Any new code touching any of the actual functions of Invidious MUST BE thoroughly tested by the Human MANUALLY.
- Team members using AIs are strongly encouraged to wait for the review of another Human before merging anything.
- At any point [Human-in-the-loop](https://en.wikipedia.org/wiki/Human-in-the-loop) applies.

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
# ----------------------------- # -----------------------------

View File

@ -2,7 +2,7 @@
ARG OPENSSL_VERSION='3.6.2' ARG OPENSSL_VERSION='3.6.2'
ARG OPENSSL_SHA256='aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f' ARG OPENSSL_SHA256='aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f'
FROM 84codes/crystal:1.20.2-alpine AS dependabot-crystal FROM 84codes/crystal:1.20.3-alpine AS dependabot-crystal
# We compile openssl ourselves due to a memory leak in how crystal interacts # We compile openssl ourselves due to a memory leak in how crystal interacts
# with openssl # with openssl

View File

@ -1,5 +1,5 @@
name: invidious name: invidious
version: 2.20260626.0 version: 2.20260626.0-dev
authors: authors:
- Invidious team <contact@invidious.io> - Invidious team <contact@invidious.io>

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

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

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)

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/*"]

View File

@ -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"

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

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

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"