Compare commits
8 commits
master
...
external-p
Author | SHA1 | Date | |
---|---|---|---|
e1a1bf0f79 | |||
7ded7455b0 | |||
53f0a5b1e0 | |||
34a11fe1bb | |||
2db9396dea | |||
|
0c3e5baab0 | ||
73ba53a327 | |||
00bcf53d0f |
13 changed files with 130 additions and 13 deletions
|
@ -51,6 +51,9 @@ videojs.Vhs.xhr.beforeRequest = function(options) {
|
||||||
return options;
|
return options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
videojs.Vhs.GOAL_BUFFER_LENGTH = 20;
|
||||||
|
videojs.Vhs.MAX_GOAL_BUFFER_LENGTH = 30;
|
||||||
|
|
||||||
var player = videojs('player', options);
|
var player = videojs('player', options);
|
||||||
|
|
||||||
player.on('error', function () {
|
player.on('error', function () {
|
||||||
|
|
|
@ -222,6 +222,15 @@ https_only: false
|
||||||
##
|
##
|
||||||
#log_level: Info
|
#log_level: Info
|
||||||
|
|
||||||
|
##
|
||||||
|
## Enables colors in logs. Useful for debugging purposes
|
||||||
|
## This is overridden if "-k" or "--colorize"
|
||||||
|
## are passed on the command line.
|
||||||
|
##
|
||||||
|
## Accepted values: true, false
|
||||||
|
## Default: false
|
||||||
|
##
|
||||||
|
#colorize_logs: false
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Features
|
# Features
|
||||||
|
|
|
@ -115,6 +115,9 @@ Kemal.config.extra_options do |parser|
|
||||||
parser.on("-l LEVEL", "--log-level=LEVEL", "Log level, one of #{LogLevel.values} (default: #{CONFIG.log_level})") do |log_level|
|
parser.on("-l LEVEL", "--log-level=LEVEL", "Log level, one of #{LogLevel.values} (default: #{CONFIG.log_level})") do |log_level|
|
||||||
CONFIG.log_level = LogLevel.parse(log_level)
|
CONFIG.log_level = LogLevel.parse(log_level)
|
||||||
end
|
end
|
||||||
|
parser.on("-k", "--colorize", "Colorize logs") do
|
||||||
|
CONFIG.colorize_logs = true
|
||||||
|
end
|
||||||
parser.on("-v", "--version", "Print version") do
|
parser.on("-v", "--version", "Print version") do
|
||||||
puts SOFTWARE.to_pretty_json
|
puts SOFTWARE.to_pretty_json
|
||||||
exit
|
exit
|
||||||
|
@ -131,7 +134,7 @@ if CONFIG.output.upcase != "STDOUT"
|
||||||
FileUtils.mkdir_p(File.dirname(CONFIG.output))
|
FileUtils.mkdir_p(File.dirname(CONFIG.output))
|
||||||
end
|
end
|
||||||
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
|
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
|
||||||
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level)
|
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level, CONFIG.colorize_logs)
|
||||||
|
|
||||||
# Check table integrity
|
# Check table integrity
|
||||||
Invidious::Database.check_integrity(CONFIG)
|
Invidious::Database.check_integrity(CONFIG)
|
||||||
|
|
|
@ -66,6 +66,8 @@ class Config
|
||||||
property output : String = "STDOUT"
|
property output : String = "STDOUT"
|
||||||
# Default log level, valid YAML values are ints and strings, see src/invidious/helpers/logger.cr
|
# Default log level, valid YAML values are ints and strings, see src/invidious/helpers/logger.cr
|
||||||
property log_level : LogLevel = LogLevel::Info
|
property log_level : LogLevel = LogLevel::Info
|
||||||
|
# Enables colors in logs. Useful for debugging purposes
|
||||||
|
property colorize_logs : Bool = false
|
||||||
# Database configuration with separate parameters (username, hostname, etc)
|
# Database configuration with separate parameters (username, hostname, etc)
|
||||||
property db : DBConfig? = nil
|
property db : DBConfig? = nil
|
||||||
|
|
||||||
|
@ -167,6 +169,11 @@ class Config
|
||||||
# The max resolution the Instance can offer
|
# The max resolution the Instance can offer
|
||||||
property max_dash_resolution : Int32?
|
property max_dash_resolution : Int32?
|
||||||
|
|
||||||
|
# List of names of the backends
|
||||||
|
property backends : Array(String) = [] of String
|
||||||
|
|
||||||
|
property external_videoplayback_proxy : String?
|
||||||
|
|
||||||
# Materialious redirects
|
# Materialious redirects
|
||||||
property materialious_domain : String?
|
property materialious_domain : String?
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require "colorize"
|
||||||
|
|
||||||
enum LogLevel
|
enum LogLevel
|
||||||
All = 0
|
All = 0
|
||||||
Trace = 1
|
Trace = 1
|
||||||
|
@ -10,7 +12,7 @@ enum LogLevel
|
||||||
end
|
end
|
||||||
|
|
||||||
class Invidious::LogHandler < Kemal::BaseLogHandler
|
class Invidious::LogHandler < Kemal::BaseLogHandler
|
||||||
def initialize(@io : IO = STDOUT, @level = LogLevel::Debug)
|
def initialize(@io : IO = STDOUT, @level = LogLevel::Debug, @color : Bool = true)
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(context : HTTP::Server::Context)
|
def call(context : HTTP::Server::Context)
|
||||||
|
@ -39,10 +41,23 @@ class Invidious::LogHandler < Kemal::BaseLogHandler
|
||||||
@io.flush
|
@io.flush
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def color(level)
|
||||||
|
case level
|
||||||
|
when LogLevel::Trace then :cyan
|
||||||
|
when LogLevel::Debug then :green
|
||||||
|
when LogLevel::Info then :white
|
||||||
|
when LogLevel::Warn then :yellow
|
||||||
|
when LogLevel::Error then :red
|
||||||
|
when LogLevel::Fatal then :magenta
|
||||||
|
else :default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
{% for level in %w(trace debug info warn error fatal) %}
|
{% for level in %w(trace debug info warn error fatal) %}
|
||||||
def {{level.id}}(message : String)
|
def {{level.id}}(message : String)
|
||||||
if LogLevel::{{level.id.capitalize}} >= @level
|
if LogLevel::{{level.id.capitalize}} >= @level
|
||||||
puts("#{Time.utc} [{{level.id}}] #{message}")
|
puts("#{Time.utc} [{{level.id}}] #{message}".colorize(color(LogLevel::{{level.id.capitalize}})).toggle(@color))
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
|
@ -176,7 +176,9 @@ module Invidious::SigHelper
|
||||||
|
|
||||||
@conn : Connection
|
@conn : Connection
|
||||||
|
|
||||||
def initialize(uri_or_path)
|
@uri_or_path : String
|
||||||
|
|
||||||
|
def initialize(@uri_or_path)
|
||||||
@conn = Connection.new(uri_or_path)
|
@conn = Connection.new(uri_or_path)
|
||||||
listen
|
listen
|
||||||
end
|
end
|
||||||
|
@ -186,10 +188,27 @@ module Invidious::SigHelper
|
||||||
|
|
||||||
LOGGER.debug("SigHelper: Multiplexor listening")
|
LOGGER.debug("SigHelper: Multiplexor listening")
|
||||||
|
|
||||||
# TODO: reopen socket if unexpectedly closed
|
|
||||||
spawn do
|
spawn do
|
||||||
loop do
|
loop do
|
||||||
receive_data
|
begin
|
||||||
|
receive_data
|
||||||
|
# Handle all errors
|
||||||
|
rescue ex
|
||||||
|
LOGGER.info("SigHelper: Connection to helper died with '#{ex.message}' trying to reconnect...")
|
||||||
|
# We close the socket because for some reason is not closed.
|
||||||
|
@conn.close
|
||||||
|
loop do
|
||||||
|
begin
|
||||||
|
@conn = Connection.new(@uri_or_path)
|
||||||
|
LOGGER.info("SigHelper: Reconnected to SigHelper!")
|
||||||
|
rescue ex
|
||||||
|
LOGGER.debug("SigHelper: Reconnection to helper unsuccessful with error '#{ex.message}' retrying")
|
||||||
|
sleep 0.5
|
||||||
|
next
|
||||||
|
end
|
||||||
|
break if !@conn.closed?
|
||||||
|
end
|
||||||
|
end
|
||||||
Fiber.yield
|
Fiber.yield
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -306,6 +325,11 @@ module Invidious::SigHelper
|
||||||
return @socket.closed?
|
return @socket.closed?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remote_address : Socket::IPAddress
|
||||||
|
|
||||||
|
return @socket.remote_address
|
||||||
|
end
|
||||||
|
|
||||||
def close : Nil
|
def close : Nil
|
||||||
@socket.close if !@socket.closed?
|
@socket.close if !@socket.closed?
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,11 @@ module Invidious::Routes::API::Manifest
|
||||||
|
|
||||||
if local
|
if local
|
||||||
uri = URI.parse(url)
|
uri = URI.parse(url)
|
||||||
url = "#{HOST_URL}#{uri.request_target}host/#{uri.host}/"
|
if CONFIG.external_videoplayback_proxy
|
||||||
|
url = "#{CONFIG.external_videoplayback_proxy}#{uri.request_target}host/#{uri.host}/"
|
||||||
|
else
|
||||||
|
url = "#{HOST_URL}#{uri.request_target}host/#{uri.host}/"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
"<BaseURL>#{url}</BaseURL>"
|
"<BaseURL>#{url}</BaseURL>"
|
||||||
|
@ -48,21 +52,24 @@ module Invidious::Routes::API::Manifest
|
||||||
|
|
||||||
if local
|
if local
|
||||||
adaptive_fmts.each do |fmt|
|
adaptive_fmts.each do |fmt|
|
||||||
fmt["url"] = JSON::Any.new("#{HOST_URL}#{URI.parse(fmt["url"].as_s).request_target}")
|
if CONFIG.external_videoplayback_proxy
|
||||||
|
fmt["url"] = JSON::Any.new("#{CONFIG.external_videoplayback_proxy}#{URI.parse(fmt["url"].as_s).request_target}")
|
||||||
|
else
|
||||||
|
fmt["url"] = JSON::Any.new("#{HOST_URL}#{URI.parse(fmt["url"].as_s).request_target}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
audio_streams = video.audio_streams.sort_by { |stream| {stream["bitrate"].as_i} }.reverse!
|
audio_streams = video.audio_streams.sort_by { |stream| {stream["bitrate"].as_i} }.reverse!
|
||||||
video_streams = video.video_streams.sort_by { |stream| {stream["width"].as_i, stream["fps"].as_i} }.reverse!
|
video_streams = video.video_streams.sort_by { |stream| {stream["width"].as_i, stream["fps"].as_i} }.reverse!
|
||||||
|
|
||||||
# Removes all the resolutions with a height higher than CONFIG.max_dash_resolution
|
# Removes all the resolutions with a height higher than CONFIG.max_dash_resolution
|
||||||
if CONFIG.max_dash_resolution
|
if CONFIG.max_dash_resolution
|
||||||
video_streams.reject! do |z|
|
video_streams.reject! do |z|
|
||||||
(z["height"].as_i > CONFIG.max_dash_resolution.not_nil!) if z["height"]?
|
(z["height"].as_i > CONFIG.max_dash_resolution.not_nil!) if z["height"]?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
manifest = XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
manifest = XML.build(indent: " ", encoding: "UTF-8") do |xml|
|
||||||
xml.element("MPD", "xmlns": "urn:mpeg:dash:schema:mpd:2011",
|
xml.element("MPD", "xmlns": "urn:mpeg:dash:schema:mpd:2011",
|
||||||
"profiles": "urn:mpeg:dash:profile:full:2011", minBufferTime: "PT1.5S", type: "static",
|
"profiles": "urn:mpeg:dash:profile:full:2011", minBufferTime: "PT1.5S", type: "static",
|
||||||
|
|
|
@ -28,6 +28,12 @@ module Invidious::Routes::BeforeAll
|
||||||
extra_media_csp = ""
|
extra_media_csp = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if CONFIG.external_videoplayback_proxy
|
||||||
|
external_videoplayback_proxy = " #{CONFIG.external_videoplayback_proxy}"
|
||||||
|
else
|
||||||
|
external_videoplayback_proxy = ""
|
||||||
|
end
|
||||||
|
|
||||||
# Only allow the pages at /embed/* to be embedded
|
# Only allow the pages at /embed/* to be embedded
|
||||||
if env.request.resource.starts_with?("/embed")
|
if env.request.resource.starts_with?("/embed")
|
||||||
frame_ancestors = "'self' file: http: https:"
|
frame_ancestors = "'self' file: http: https:"
|
||||||
|
@ -43,7 +49,7 @@ module Invidious::Routes::BeforeAll
|
||||||
"style-src 'self' 'unsafe-inline'",
|
"style-src 'self' 'unsafe-inline'",
|
||||||
"img-src 'self' data:",
|
"img-src 'self' data:",
|
||||||
"font-src 'self' data:",
|
"font-src 'self' data:",
|
||||||
"connect-src 'self'",
|
"connect-src 'self'" + external_videoplayback_proxy,
|
||||||
"manifest-src 'self'",
|
"manifest-src 'self'",
|
||||||
"media-src 'self' blob:" + extra_media_csp,
|
"media-src 'self' blob:" + extra_media_csp,
|
||||||
"child-src 'self' blob:",
|
"child-src 'self' blob:",
|
||||||
|
|
18
src/invidious/routes/switch_backend.cr
Normal file
18
src/invidious/routes/switch_backend.cr
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{% skip_file if flag?(:api_only) %}
|
||||||
|
|
||||||
|
module Invidious::Routes::BackendSwitcher
|
||||||
|
def self.switch(env)
|
||||||
|
referer = get_referer(env)
|
||||||
|
backend_id = env.params.query["backend_id"]
|
||||||
|
|
||||||
|
# Checks if there is any alternative domain, like a second domain name,
|
||||||
|
# TOR or I2P address
|
||||||
|
if alt = CONFIG.alternative_domains.index(env.request.headers["Host"])
|
||||||
|
env.response.cookies["SERVER_ID"] = Invidious::User::Cookies.server_id(CONFIG.alternative_domains[alt], backend_id)
|
||||||
|
else
|
||||||
|
env.response.cookies["SERVER_ID"] = Invidious::User::Cookies.server_id(CONFIG.domain, backend_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
env.redirect referer
|
||||||
|
end
|
||||||
|
end
|
|
@ -120,7 +120,7 @@ module Invidious::Routes::Watch
|
||||||
fmt_stream = video.fmt_stream
|
fmt_stream = video.fmt_stream
|
||||||
adaptive_fmts = video.adaptive_fmts
|
adaptive_fmts = video.adaptive_fmts
|
||||||
|
|
||||||
# Removes all the resolutions with a height higher than CONFIG.max_dash_resolution
|
# Removes all the resolutions with a height higher than CONFIG.max_dash_resolution
|
||||||
if CONFIG.max_dash_resolution
|
if CONFIG.max_dash_resolution
|
||||||
adaptive_fmts.reject! do |z|
|
adaptive_fmts.reject! do |z|
|
||||||
(z["height"].as_i > CONFIG.max_dash_resolution.not_nil!) if z["height"]?
|
(z["height"].as_i > CONFIG.max_dash_resolution.not_nil!) if z["height"]?
|
||||||
|
@ -135,7 +135,7 @@ module Invidious::Routes::Watch
|
||||||
video_streams = video.video_streams
|
video_streams = video.video_streams
|
||||||
audio_streams = video.audio_streams
|
audio_streams = video.audio_streams
|
||||||
|
|
||||||
# Removes all the resolutions with a height higher than CONFIG.max_dash_resolution
|
# Removes all the resolutions with a height higher than CONFIG.max_dash_resolution
|
||||||
if CONFIG.max_dash_resolution
|
if CONFIG.max_dash_resolution
|
||||||
video_streams.reject! do |z|
|
video_streams.reject! do |z|
|
||||||
(z["height"].as_i > CONFIG.max_dash_resolution.not_nil!) if z["height"]?
|
(z["height"].as_i > CONFIG.max_dash_resolution.not_nil!) if z["height"]?
|
||||||
|
|
|
@ -21,6 +21,7 @@ module Invidious::Routing
|
||||||
get "/privacy", Routes::Misc, :privacy
|
get "/privacy", Routes::Misc, :privacy
|
||||||
get "/licenses", Routes::Misc, :licenses
|
get "/licenses", Routes::Misc, :licenses
|
||||||
get "/redirect", Routes::Misc, :cross_instance_redirect
|
get "/redirect", Routes::Misc, :cross_instance_redirect
|
||||||
|
get "/switchbackend", Routes::BackendSwitcher, :switch
|
||||||
|
|
||||||
self.register_channel_routes
|
self.register_channel_routes
|
||||||
self.register_watch_routes
|
self.register_watch_routes
|
||||||
|
|
|
@ -45,5 +45,18 @@ struct Invidious::User
|
||||||
samesite: HTTP::Cookie::SameSite::Lax
|
samesite: HTTP::Cookie::SameSite::Lax
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Server ID (SERVER_ID) cookie used for Sticky Sessions
|
||||||
|
# Parameter "domain" comes from the global config
|
||||||
|
def server_id(domain : String?, server_id) : HTTP::Cookie
|
||||||
|
return HTTP::Cookie.new(
|
||||||
|
name: "SERVER_ID",
|
||||||
|
domain: domain,
|
||||||
|
value: server_id,
|
||||||
|
secure: false,
|
||||||
|
http_only: true,
|
||||||
|
samesite: HTTP::Cookie::SameSite::Lax
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -104,6 +104,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<% if CONFIG.backends %>
|
||||||
|
<div class="h-box">
|
||||||
|
<b>Switch Backend:</b>
|
||||||
|
<% CONFIG.backends.each do | backend | %>
|
||||||
|
<a href="/switchbackend?backend_id=<%= backend %>">
|
||||||
|
Backend<%= HTML.escape(backend) %>
|
||||||
|
</a> |
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% if CONFIG.banner %>
|
<% if CONFIG.banner %>
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<h3><%= CONFIG.banner %></h3>
|
<h3><%= CONFIG.banner %></h3>
|
||||||
|
|
Loading…
Reference in a new issue