Compare commits

...

6 commits
master ... test

11 changed files with 132 additions and 19 deletions

View file

@ -51,8 +51,8 @@ videojs.Vhs.xhr.beforeRequest = function(options) {
return options; return options;
}; };
videojs.Vhs.GOAL_BUFFER_LENGTH = 20; videojs.Vhs.GOAL_BUFFER_LENGTH = 40;
videojs.Vhs.MAX_GOAL_BUFFER_LENGTH = 30; videojs.Vhs.MAX_GOAL_BUFFER_LENGTH = 80;
var player = videojs('player', options); var player = videojs('player', options);

View file

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

View file

@ -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)
@ -185,6 +188,10 @@ Invidious::Jobs.register Invidious::Jobs::ClearExpiredItemsJob.new
Invidious::Jobs.register Invidious::Jobs::InstanceListRefreshJob.new Invidious::Jobs.register Invidious::Jobs::InstanceListRefreshJob.new
if CONFIG.external_videoplayback_proxy
Invidious::Jobs.register Invidious::Jobs::CheckExternalProxy.new
end
Invidious::Jobs.start_all Invidious::Jobs.start_all
def popular_videos def popular_videos

View file

@ -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
@ -169,6 +171,11 @@ class Config
# List of names of the backends # List of names of the backends
property backends : Array(String) = [] of String property backends : Array(String) = [] of String
# Character used to separate the backend number from the description/note
# of the backend
property backends_delimiter : String = "|"
property external_videoplayback_proxy : String?
# Materialious redirects # Materialious redirects
property materialious_domain : String? property materialious_domain : String?

View file

@ -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 %}

View file

@ -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,10 @@ 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

View file

@ -0,0 +1,13 @@
class Invidious::Jobs::CheckExternalProxy < Invidious::Jobs::BaseJob
def initialize
end
def begin
loop do
Invidious::Routes::API::Manifest.check_external_proxy
LOGGER.info("CheckExternalProxy: Done, sleeping for 1 minute")
sleep 1.minutes
Fiber.yield
end
end
end

View file

@ -1,4 +1,15 @@
module Invidious::Routes::API::Manifest module Invidious::Routes::API::Manifest
@@proxy_alive : Bool = false
def self.check_external_proxy
begin
response = HTTP::Client.get("#{CONFIG.external_videoplayback_proxy}")
@@proxy_alive = response.status_code == 200
rescue
@@proxy_alive = false
end
end
# /api/manifest/dash/id/:id # /api/manifest/dash/id/:id
def self.get_dash_video_id(env) def self.get_dash_video_id(env)
env.response.headers.add("Access-Control-Allow-Origin", "*") env.response.headers.add("Access-Control-Allow-Origin", "*")
@ -35,7 +46,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 @@proxy_alive
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 +63,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 @@proxy_alive
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",

View file

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

View file

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

View file

@ -1,6 +1,7 @@
<% <%
locale = env.get("preferences").as(Preferences).locale locale = env.get("preferences").as(Preferences).locale
dark_mode = env.get("preferences").as(Preferences).dark_mode dark_mode = env.get("preferences").as(Preferences).dark_mode
current_backend = env.request.cookies["SERVER_ID"]?.try &.value
%> %>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="<%= locale %>"> <html lang="<%= locale %>">
@ -104,13 +105,26 @@
</div> </div>
</div> </div>
<% if CONFIG.backends %> <% if !CONFIG.backends.empty? %>
<div class="h-box"> <div class="h-box">
<b>Switch Backend:</b> <b>Switch Backend:</b>
<% CONFIG.backends.each do | backend | %> <% CONFIG.backends.each do | backend | %>
<a href="/switchbackend?backend_id=<%= backend %>"> <% backend = backend.split(CONFIG.backends_delimiter) %>
Backend<%= HTML.escape(backend) %> <% if current_backend == backend[0] %>
</a> | <a href="/switchbackend?backend_id=<%= backend[0] %>" style="text-decoration-line: underline; display: inline-block;">
Backend<%= HTML.escape(backend[0]) %>
<% if backend.size == 2 %>
<%= HTML.escape(backend[1]) %>
<% end %>
</a> <span> | </span>
<% else %>
<a href="/switchbackend?backend_id=<%= backend[0] %>" style="display: inline-block;">
Backend<%= HTML.escape(backend[0]) %>
<% if backend.size == 2 %>
<%= HTML.escape(backend[1]) %>
<% end %>
</a> <span> | </span>
<% end %>
<% end %> <% end %>
</div> </div>
<% end %> <% end %>
@ -296,6 +310,7 @@
</div> </div>
<hr/> <hr/>
<div class="footer-footer"> <div class="footer-footer">
<div class="box">You are currently using Backend: <%= current_backend %></p>
<span class="left"> <span class="left">
<% if CONFIG.modified_source_code_url %> <% if CONFIG.modified_source_code_url %>
<%= translate(locale, "footer_current_version_modified") %> <%= translate(locale, "footer_current_version_modified") %>