Merge branch 'master' into theme-styles

This commit is contained in:
rockerBOO 2025-05-04 23:16:51 -04:00
commit 82b943dc9b
No known key found for this signature in database
GPG Key ID: 0D4EAF00DCABC97B
8 changed files with 47 additions and 41 deletions

View File

@ -2,6 +2,12 @@
## vX.Y.0 (future) ## vX.Y.0 (future)
## v2.20250504.0
Small release with quick workaround fix for issue #4251 (Nil assertion failed).
PR: https://github.com/iv-org/invidious/issues/5263
## v2.20250314.0 ## v2.20250314.0
### Wrap-up ### Wrap-up

View File

@ -81,9 +81,9 @@
- [Available in many languages](locales/), thanks to [our translators](#contribute) - [Available in many languages](locales/), thanks to [our translators](#contribute)
**Data import/export** **Data import/export**
- Import subscriptions from YouTube, NewPipe and Freetube - Import subscriptions from YouTube, NewPipe and FreeTube
- Import watch history from YouTube and NewPipe - Import watch history from YouTube and NewPipe
- Export subscriptions to NewPipe and Freetube - Export subscriptions to NewPipe and FreeTube
- Import/Export Invidious user data - Import/Export Invidious user data
**Technical features** **Technical features**
@ -95,11 +95,11 @@
## Quick start ## Quick start
**Using invidious:** **Using Invidious:**
- [Select a public instance from the list](https://instances.invidious.io) and start watching videos right now! - [Select a public instance from the list](https://instances.invidious.io) and start watching videos right now!
**Hosting invidious:** **Hosting Invidious:**
- [Follow the installation instructions](https://docs.invidious.io/installation/) - [Follow the installation instructions](https://docs.invidious.io/installation/)
@ -114,8 +114,8 @@ https://github.com/iv-org/documentation
### Extensions ### Extensions
We highly recommend the use of [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect#get), We highly recommend the use of [Privacy Redirect](https://github.com/SimonBrazell/privacy-redirect#get),
a browser extension that automatically redirects Youtube URLs to any Invidious instance and replaces a browser extension that automatically redirects YouTube URLs to any Invidious instance and replaces
embedded youtube videos on other websites with invidious. embedded YouTube videos on other websites with Invidious.
The documentation contains a list of browser extensions that we recommended to use along with Invidious. The documentation contains a list of browser extensions that we recommended to use along with Invidious.
@ -140,7 +140,7 @@ We use [Weblate](https://weblate.org) to manage Invidious translations.
You can suggest new translations and/or correction here: https://hosted.weblate.org/engage/invidious/. You can suggest new translations and/or correction here: https://hosted.weblate.org/engage/invidious/.
Creating an account is not required, but recommended, especially if you want to contribute regularly. Creating an account is not required, but recommended, especially if you want to contribute regularly.
Weblate also allows you to log-in with major SSO providers like Github, Gitlab, BitBucket, Google, ... Weblate also allows you to log-in with major SSO providers like GitHub, GitLab, BitBucket, Google, ...
## Projects using Invidious ## Projects using Invidious

View File

@ -90,14 +90,14 @@ db:
## ##
## API key for Invidious companion, used for securing the communication ## API key for Invidious companion, used for securing the communication
## between Invidious and Invidious companion. ## between Invidious and Invidious companion.
## The size of the key needs to be more or equal to 16. ## The key needs to be exactly 16 characters long.
## ##
## Note: This parameter is mandatory when Invidious companion is enabled ## Note: This parameter is mandatory when Invidious companion is enabled
## and should be a random string. ## and should be a random string.
## Such random string can be generated on linux with the following ## Such random string can be generated on linux with the following
## command: `pwgen 16 1` ## command: `pwgen 16 1`
## ##
## Accepted values: a string ## Accepted values: a string (of length 16)
## Default: <none> ## Default: <none>
## ##
#invidious_companion_key: "CHANGE_ME!!" #invidious_companion_key: "CHANGE_ME!!"

View File

@ -4,6 +4,8 @@
# If you want to use Invidious in production, see the docker-compose.yml file provided # If you want to use Invidious in production, see the docker-compose.yml file provided
# in the installation documentation: https://docs.invidious.io/installation/ # in the installation documentation: https://docs.invidious.io/installation/
---
version: "3" version: "3"
services: services:
@ -11,9 +13,10 @@ services:
build: build:
context: . context: .
dockerfile: docker/Dockerfile dockerfile: docker/Dockerfile
image: rockerboo/invidious
restart: unless-stopped restart: unless-stopped
ports: ports:
- "127.0.0.1:3000:3000" - "127.0.0.1:9999:3000"
environment: environment:
# Please read the following file for a comprehensive list of all available # Please read the following file for a comprehensive list of all available
# configuration options and their associated syntax: # configuration options and their associated syntax:

View File

@ -23,10 +23,16 @@ module Invidious::Frontend::WatchPage
return "<p id=\"download\">#{translate(locale, "Download is disabled")}</p>" return "<p id=\"download\">#{translate(locale, "Download is disabled")}</p>"
end end
url = "/download"
if (CONFIG.invidious_companion.present?)
invidious_companion = CONFIG.invidious_companion.sample
url = "#{invidious_companion.public_url}/download?check=#{invidious_companion_encrypt(video.id)}"
end
return String.build(4000) do |str| return String.build(4000) do |str|
str << "<form" str << "<form"
str << " class=\"watch-action-group\"" str << " class=\"watch-action-group\""
str << " action='/download'" str << " action='#{url}'"
str << " method='post'" str << " method='post'"
str << " rel='noopener'" str << " rel='noopener'"
str << " target='_blank'>" str << " target='_blank'>"

View File

@ -21,7 +21,7 @@ module Invidious::Routes::VideoPlayback
end end
# Sanity check, to avoid being used as an open proxy # Sanity check, to avoid being used as an open proxy
if !host.matches?(/[\w-]+.googlevideo.com/) if !host.matches?(/[\w-]+\.(?:googlevideo|c\.youtube)\.com/)
return error_template(400, "Invalid \"host\" parameter.") return error_template(400, "Invalid \"host\" parameter.")
end end
@ -37,7 +37,8 @@ module Invidious::Routes::VideoPlayback
# See: https://github.com/iv-org/invidious/issues/3302 # See: https://github.com/iv-org/invidious/issues/3302
range_header = env.request.headers["Range"]? range_header = env.request.headers["Range"]?
if range_header.nil? sq = query_params["sq"]?
if range_header.nil? && sq.nil?
range_for_head = query_params["range"]? || "0-640" range_for_head = query_params["range"]? || "0-640"
headers["Range"] = "bytes=#{range_for_head}" headers["Range"] = "bytes=#{range_for_head}"
end end

View File

@ -293,6 +293,9 @@ module Invidious::Routes::Watch
if CONFIG.disabled?("downloads") if CONFIG.disabled?("downloads")
return error_template(403, "Administrator has disabled this endpoint.") return error_template(403, "Administrator has disabled this endpoint.")
end end
if CONFIG.invidious_companion.present?
return error_template(403, "Downloads should be routed through Companion when present")
end
title = env.params.body["title"]? || "" title = env.params.body["title"]? || ""
video_id = env.params.body["id"]? || "" video_id = env.params.body["id"]? || ""
@ -328,13 +331,7 @@ module Invidious::Routes::Watch
env.params.query["title"] = filename env.params.query["title"] = filename
env.params.query["local"] = "true" env.params.query["local"] = "true"
if (CONFIG.invidious_companion.present?) return Invidious::Routes::VideoPlayback.latest_version(env)
video = get_video(video_id)
invidious_companion = CONFIG.invidious_companion.sample
return env.redirect "#{invidious_companion.public_url}/latest_version?#{env.params.query}"
else
return Invidious::Routes::VideoPlayback.latest_version(env)
end
else else
return error_template(400, "Invalid label or itag") return error_template(400, "Invalid label or itag")
end end

View File

@ -109,27 +109,20 @@ def extract_video_info(video_id : String)
params["reason"] = JSON::Any.new(reason) if reason params["reason"] = JSON::Any.new(reason) if reason
if !CONFIG.invidious_companion.present? if !CONFIG.invidious_companion.present?
new_player_response = nil if player_response["streamingData"]? && player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil?
LOGGER.warn("Missing URLs for adaptive formats, falling back to other YT clients.")
# Don't use Android test suite client if po_token is passed because po_token doesn't players_fallback = [YoutubeAPI::ClientType::WebMobile, YoutubeAPI::ClientType::TvHtml5]
# work for Android test suite client. players_fallback.each do |player_fallback|
if reason.nil? && CONFIG.po_token.nil? client_config.client_type = player_fallback
# Fetch the video streams using an Android client in order to get the player_fallback_response = try_fetch_streaming_data(video_id, client_config)
# decrypted URLs and maybe fix throttling issues (#2194). See the if player_fallback_response && player_fallback_response["streamingData"]? &&
# following issue for an explanation about decrypted URLs: player_fallback_response.dig?("streamingData", "adaptiveFormats", 0, "url")
# https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 streaming_data = player_response["streamingData"].as_h
client_config.client_type = YoutubeAPI::ClientType::AndroidTestSuite streaming_data["adaptiveFormats"] = player_fallback_response["streamingData"]["adaptiveFormats"]
new_player_response = try_fetch_streaming_data(video_id, client_config) player_response["streamingData"] = JSON::Any.new(streaming_data)
end break
end
# Replace player response and reset reason end
if !new_player_response.nil?
# Preserve captions & storyboard data before replacement
new_player_response["storyboards"] = player_response["storyboards"] if player_response["storyboards"]?
new_player_response["captions"] = player_response["captions"] if player_response["captions"]?
player_response = new_player_response
params.delete("reason")
end end
end end