|
|
@ -106,34 +106,30 @@ LOCALES = {
|
|
|
|
|
|
|
|
|
|
|
|
YT_POOL = QUICPool.new(YT_URL, capacity: CONFIG.pool_size, timeout: 2.0)
|
|
|
|
YT_POOL = QUICPool.new(YT_URL, capacity: CONFIG.pool_size, timeout: 2.0)
|
|
|
|
|
|
|
|
|
|
|
|
config = CONFIG
|
|
|
|
# CLI
|
|
|
|
output = STDOUT
|
|
|
|
|
|
|
|
loglvl = LogLevel::Debug
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Kemal.config.extra_options do |parser|
|
|
|
|
Kemal.config.extra_options do |parser|
|
|
|
|
parser.banner = "Usage: invidious [arguments]"
|
|
|
|
parser.banner = "Usage: invidious [arguments]"
|
|
|
|
parser.on("-c THREADS", "--channel-threads=THREADS", "Number of threads for refreshing channels (default: #{config.channel_threads})") do |number|
|
|
|
|
parser.on("-c THREADS", "--channel-threads=THREADS", "Number of threads for refreshing channels (default: #{CONFIG.channel_threads})") do |number|
|
|
|
|
begin
|
|
|
|
begin
|
|
|
|
config.channel_threads = number.to_i
|
|
|
|
CONFIG.channel_threads = number.to_i
|
|
|
|
rescue ex
|
|
|
|
rescue ex
|
|
|
|
puts "THREADS must be integer"
|
|
|
|
puts "THREADS must be integer"
|
|
|
|
exit
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
parser.on("-f THREADS", "--feed-threads=THREADS", "Number of threads for refreshing feeds (default: #{config.feed_threads})") do |number|
|
|
|
|
parser.on("-f THREADS", "--feed-threads=THREADS", "Number of threads for refreshing feeds (default: #{CONFIG.feed_threads})") do |number|
|
|
|
|
begin
|
|
|
|
begin
|
|
|
|
config.feed_threads = number.to_i
|
|
|
|
CONFIG.feed_threads = number.to_i
|
|
|
|
rescue ex
|
|
|
|
rescue ex
|
|
|
|
puts "THREADS must be integer"
|
|
|
|
puts "THREADS must be integer"
|
|
|
|
exit
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
parser.on("-o OUTPUT", "--output=OUTPUT", "Redirect output (default: STDOUT)") do |output_arg|
|
|
|
|
parser.on("-o OUTPUT", "--output=OUTPUT", "Redirect output (default: #{CONFIG.output})") do |output|
|
|
|
|
FileUtils.mkdir_p(File.dirname(output_arg))
|
|
|
|
CONFIG.output = output
|
|
|
|
output = File.open(output_arg, mode: "a")
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
parser.on("-l LEVEL", "--log-level=LEVEL", "Log level, one of #{LogLevel.values} (default: #{loglvl})") do |loglvl_arg|
|
|
|
|
parser.on("-l LEVEL", "--log-level=LEVEL", "Log level, one of #{LogLevel.values} (default: #{CONFIG.log_level})") do |log_level|
|
|
|
|
loglvl = LogLevel.parse(loglvl_arg)
|
|
|
|
CONFIG.log_level = LogLevel.parse(log_level)
|
|
|
|
end
|
|
|
|
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
|
|
|
@ -143,35 +139,41 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
Kemal::CLI.new ARGV
|
|
|
|
Kemal::CLI.new ARGV
|
|
|
|
|
|
|
|
|
|
|
|
logger = Invidious::LogHandler.new(output, loglvl)
|
|
|
|
if CONFIG.output.upcase != "STDOUT"
|
|
|
|
|
|
|
|
FileUtils.mkdir_p(File.dirname(CONFIG.output))
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
|
|
|
|
|
|
|
|
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config = CONFIG
|
|
|
|
|
|
|
|
|
|
|
|
# Check table integrity
|
|
|
|
# Check table integrity
|
|
|
|
if CONFIG.check_tables
|
|
|
|
if CONFIG.check_tables
|
|
|
|
check_enum(PG_DB, logger, "privacy", PlaylistPrivacy)
|
|
|
|
check_enum(PG_DB, "privacy", PlaylistPrivacy)
|
|
|
|
|
|
|
|
|
|
|
|
check_table(PG_DB, logger, "channels", InvidiousChannel)
|
|
|
|
check_table(PG_DB, "channels", InvidiousChannel)
|
|
|
|
check_table(PG_DB, logger, "channel_videos", ChannelVideo)
|
|
|
|
check_table(PG_DB, "channel_videos", ChannelVideo)
|
|
|
|
check_table(PG_DB, logger, "playlists", InvidiousPlaylist)
|
|
|
|
check_table(PG_DB, "playlists", InvidiousPlaylist)
|
|
|
|
check_table(PG_DB, logger, "playlist_videos", PlaylistVideo)
|
|
|
|
check_table(PG_DB, "playlist_videos", PlaylistVideo)
|
|
|
|
check_table(PG_DB, logger, "nonces", Nonce)
|
|
|
|
check_table(PG_DB, "nonces", Nonce)
|
|
|
|
check_table(PG_DB, logger, "session_ids", SessionId)
|
|
|
|
check_table(PG_DB, "session_ids", SessionId)
|
|
|
|
check_table(PG_DB, logger, "users", User)
|
|
|
|
check_table(PG_DB, "users", User)
|
|
|
|
check_table(PG_DB, logger, "videos", Video)
|
|
|
|
check_table(PG_DB, "videos", Video)
|
|
|
|
|
|
|
|
|
|
|
|
if CONFIG.cache_annotations
|
|
|
|
if CONFIG.cache_annotations
|
|
|
|
check_table(PG_DB, logger, "annotations", Annotation)
|
|
|
|
check_table(PG_DB, "annotations", Annotation)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Start jobs
|
|
|
|
# Start jobs
|
|
|
|
|
|
|
|
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::RefreshChannelsJob.new(PG_DB, logger, config)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::RefreshChannelsJob.new(PG_DB, config)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::RefreshFeedsJob.new(PG_DB, logger, config)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::RefreshFeedsJob.new(PG_DB, config)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::SubscribeToFeedsJob.new(PG_DB, logger, config, HMAC_KEY)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::SubscribeToFeedsJob.new(PG_DB, config, HMAC_KEY)
|
|
|
|
|
|
|
|
|
|
|
|
DECRYPT_FUNCTION = DecryptFunction.new(CONFIG.decrypt_polling)
|
|
|
|
DECRYPT_FUNCTION = DecryptFunction.new(CONFIG.decrypt_polling)
|
|
|
|
if config.decrypt_polling
|
|
|
|
if config.decrypt_polling
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::UpdateDecryptFunctionJob.new(logger)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::UpdateDecryptFunctionJob.new
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
if config.statistics_enabled
|
|
|
|
if config.statistics_enabled
|
|
|
@ -183,7 +185,7 @@ if config.popular_enabled
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
if config.captcha_key
|
|
|
|
if config.captcha_key
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::BypassCaptchaJob.new(logger, config)
|
|
|
|
Invidious::Jobs.register Invidious::Jobs::BypassCaptchaJob.new(config)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
connection_channel = Channel({Bool, Channel(PQ::Notification)}).new(32)
|
|
|
|
connection_channel = Channel({Bool, Channel(PQ::Notification)}).new(32)
|
|
|
@ -261,7 +263,7 @@ before_all do |env|
|
|
|
|
headers["Cookie"] = env.request.headers["Cookie"]
|
|
|
|
headers["Cookie"] = env.request.headers["Cookie"]
|
|
|
|
|
|
|
|
|
|
|
|
begin
|
|
|
|
begin
|
|
|
|
user, sid = get_user(sid, headers, PG_DB, logger, false)
|
|
|
|
user, sid = get_user(sid, headers, PG_DB, false)
|
|
|
|
csrf_token = generate_response(sid, {
|
|
|
|
csrf_token = generate_response(sid, {
|
|
|
|
":authorize_token",
|
|
|
|
":authorize_token",
|
|
|
|
":playlist_ajax",
|
|
|
|
":playlist_ajax",
|
|
|
@ -531,7 +533,7 @@ post "/subscription_ajax" do |env|
|
|
|
|
case action
|
|
|
|
case action
|
|
|
|
when "action_create_subscription_to_channel"
|
|
|
|
when "action_create_subscription_to_channel"
|
|
|
|
if !user.subscriptions.includes? channel_id
|
|
|
|
if !user.subscriptions.includes? channel_id
|
|
|
|
get_channel(channel_id, PG_DB, logger, false, false)
|
|
|
|
get_channel(channel_id, PG_DB, false, false)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = array_append(subscriptions, $1) WHERE email = $2", channel_id, email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = array_append(subscriptions, $1) WHERE email = $2", channel_id, email)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
when "action_remove_subscriptions"
|
|
|
|
when "action_remove_subscriptions"
|
|
|
@ -566,7 +568,7 @@ get "/subscription_manager" do |env|
|
|
|
|
headers = HTTP::Headers.new
|
|
|
|
headers = HTTP::Headers.new
|
|
|
|
headers["Cookie"] = env.request.headers["Cookie"]
|
|
|
|
headers["Cookie"] = env.request.headers["Cookie"]
|
|
|
|
|
|
|
|
|
|
|
|
user, sid = get_user(sid, headers, PG_DB, logger)
|
|
|
|
user, sid = get_user(sid, headers, PG_DB)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
action_takeout = env.params.query["action_takeout"]?.try &.to_i?
|
|
|
|
action_takeout = env.params.query["action_takeout"]?.try &.to_i?
|
|
|
@ -690,7 +692,7 @@ post "/data_control" do |env|
|
|
|
|
user.subscriptions += body["subscriptions"].as_a.map { |a| a.as_s }
|
|
|
|
user.subscriptions += body["subscriptions"].as_a.map { |a| a.as_s }
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
|
|
|
|
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, logger, false, false)
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
end
|
|
|
|
end
|
|
|
@ -759,7 +761,7 @@ post "/data_control" do |env|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
|
|
|
|
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, logger, false, false)
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
when "import_freetube"
|
|
|
|
when "import_freetube"
|
|
|
@ -768,7 +770,7 @@ post "/data_control" do |env|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
|
|
|
|
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, logger, false, false)
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
when "import_newpipe_subscriptions"
|
|
|
|
when "import_newpipe_subscriptions"
|
|
|
@ -787,7 +789,7 @@ post "/data_control" do |env|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
|
|
|
|
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, logger, false, false)
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
when "import_newpipe"
|
|
|
|
when "import_newpipe"
|
|
|
@ -806,7 +808,7 @@ post "/data_control" do |env|
|
|
|
|
user.subscriptions += db.query_all("SELECT url FROM subscriptions", as: String).map { |url| url.lchop("https://www.youtube.com/channel/") }
|
|
|
|
user.subscriptions += db.query_all("SELECT url FROM subscriptions", as: String).map { |url| url.lchop("https://www.youtube.com/channel/") }
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
user.subscriptions.uniq!
|
|
|
|
|
|
|
|
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, logger, false, false)
|
|
|
|
user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false)
|
|
|
|
|
|
|
|
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = $1 WHERE email = $2", user.subscriptions, user.email)
|
|
|
|
|
|
|
|
|
|
|
@ -1209,7 +1211,7 @@ get "/feed/subscriptions" do |env|
|
|
|
|
headers["Cookie"] = env.request.headers["Cookie"]
|
|
|
|
headers["Cookie"] = env.request.headers["Cookie"]
|
|
|
|
|
|
|
|
|
|
|
|
if !user.password
|
|
|
|
if !user.password
|
|
|
|
user, sid = get_user(sid, headers, PG_DB, logger)
|
|
|
|
user, sid = get_user(sid, headers, PG_DB)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
max_results = env.params.query["max_results"]?.try &.to_i?.try &.clamp(0, MAX_ITEMS_PER_PAGE)
|
|
|
|
max_results = env.params.query["max_results"]?.try &.to_i?.try &.clamp(0, MAX_ITEMS_PER_PAGE)
|
|
|
@ -1513,7 +1515,7 @@ post "/feed/webhook/:token" do |env|
|
|
|
|
signature = env.request.headers["X-Hub-Signature"].lchop("sha1=")
|
|
|
|
signature = env.request.headers["X-Hub-Signature"].lchop("sha1=")
|
|
|
|
|
|
|
|
|
|
|
|
if signature != OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, body)
|
|
|
|
if signature != OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, body)
|
|
|
|
logger.error("/feed/webhook/#{token} : Invalid signature")
|
|
|
|
LOGGER.error("/feed/webhook/#{token} : Invalid signature")
|
|
|
|
env.response.status_code = 200
|
|
|
|
env.response.status_code = 200
|
|
|
|
next
|
|
|
|
next
|
|
|
|
end
|
|
|
|
end
|
|
|
@ -2831,7 +2833,7 @@ post "/api/v1/auth/subscriptions/:ucid" do |env|
|
|
|
|
ucid = env.params.url["ucid"]
|
|
|
|
ucid = env.params.url["ucid"]
|
|
|
|
|
|
|
|
|
|
|
|
if !user.subscriptions.includes? ucid
|
|
|
|
if !user.subscriptions.includes? ucid
|
|
|
|
get_channel(ucid, PG_DB, logger, false, false)
|
|
|
|
get_channel(ucid, PG_DB, false, false)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = array_append(subscriptions,$1) WHERE email = $2", ucid, user.email)
|
|
|
|
PG_DB.exec("UPDATE users SET feed_needs_update = true, subscriptions = array_append(subscriptions,$1) WHERE email = $2", ucid, user.email)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
@ -3925,7 +3927,7 @@ add_context_storage_type(Array(String))
|
|
|
|
add_context_storage_type(Preferences)
|
|
|
|
add_context_storage_type(Preferences)
|
|
|
|
add_context_storage_type(User)
|
|
|
|
add_context_storage_type(User)
|
|
|
|
|
|
|
|
|
|
|
|
Kemal.config.logger = logger
|
|
|
|
Kemal.config.logger = LOGGER
|
|
|
|
Kemal.config.host_binding = Kemal.config.host_binding != "0.0.0.0" ? Kemal.config.host_binding : CONFIG.host_binding
|
|
|
|
Kemal.config.host_binding = Kemal.config.host_binding != "0.0.0.0" ? Kemal.config.host_binding : CONFIG.host_binding
|
|
|
|
Kemal.config.port = Kemal.config.port != 3000 ? Kemal.config.port : CONFIG.port
|
|
|
|
Kemal.config.port = Kemal.config.port != 3000 ? Kemal.config.port : CONFIG.port
|
|
|
|
Kemal.run
|
|
|
|
Kemal.run
|
|
|
|