Compare commits
6 commits
Author | SHA1 | Date | |
---|---|---|---|
2ed94a2c08 | |||
69cf7f7715 | |||
7cbdb5077a | |||
ffe2228e6a | |||
ea98539260 | |||
e88a4fac6c |
11 changed files with 132 additions and 19 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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,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
|
||||||
|
|
13
src/invidious/jobs/check_external_proxy.cr
Normal file
13
src/invidious/jobs/check_external_proxy.cr
Normal 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
|
|
@ -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",
|
||||||
|
|
|
@ -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:",
|
||||||
|
|
|
@ -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"]?
|
||||||
|
|
|
@ -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") %>
|
||||||
|
|
Loading…
Reference in a new issue