mirror of https://github.com/iv-org/invidious.git
Merge branch 'iv-org:master' into invidious_migrations
This commit is contained in:
commit
788ca3f4ac
|
@ -301,7 +301,6 @@ Spectator.describe Invidious::Search::Filters do
|
||||||
|
|
||||||
it "Encodes features filter (single)" do
|
it "Encodes features filter (single)" do
|
||||||
Invidious::Search::Filters::Features.each do |value|
|
Invidious::Search::Filters::Features.each do |value|
|
||||||
string = described_class.format_features(value)
|
|
||||||
filters = described_class.new(features: value)
|
filters = described_class.new(features: value)
|
||||||
|
|
||||||
expect("#{filters.to_iv_params}")
|
expect("#{filters.to_iv_params}")
|
||||||
|
|
|
@ -232,7 +232,7 @@ def fetch_channel(ucid, pull_all_videos : Bool)
|
||||||
id: video_id,
|
id: video_id,
|
||||||
title: title,
|
title: title,
|
||||||
published: published,
|
published: published,
|
||||||
updated: Time.utc,
|
updated: updated,
|
||||||
ucid: ucid,
|
ucid: ucid,
|
||||||
author: author,
|
author: author,
|
||||||
length_seconds: length_seconds,
|
length_seconds: length_seconds,
|
||||||
|
|
|
@ -149,12 +149,12 @@ module Invidious::Frontend::Comments
|
||||||
|
|
||||||
if comments["videoId"]?
|
if comments["videoId"]?
|
||||||
html << <<-END_HTML
|
html << <<-END_HTML
|
||||||
<a href="https://www.youtube.com/watch?v=#{comments["videoId"]}&lc=#{child["commentId"]}" title="#{translate(locale, "YouTube comment permalink")}">[YT]</a>
|
<a rel="noreferrer noopener" href="https://www.youtube.com/watch?v=#{comments["videoId"]}&lc=#{child["commentId"]}" title="#{translate(locale, "YouTube comment permalink")}">[YT]</a>
|
||||||
|
|
|
|
||||||
END_HTML
|
END_HTML
|
||||||
elsif comments["authorId"]?
|
elsif comments["authorId"]?
|
||||||
html << <<-END_HTML
|
html << <<-END_HTML
|
||||||
<a href="https://www.youtube.com/channel/#{comments["authorId"]}/community?lb=#{child["commentId"]}" title="#{translate(locale, "YouTube comment permalink")}">[YT]</a>
|
<a rel="noreferrer noopener" href="https://www.youtube.com/channel/#{comments["authorId"]}/community?lb=#{child["commentId"]}" title="#{translate(locale, "YouTube comment permalink")}">[YT]</a>
|
||||||
|
|
|
|
||||||
END_HTML
|
END_HTML
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,9 +6,9 @@ module Invidious::Frontend::Misc
|
||||||
|
|
||||||
if prefs.automatic_instance_redirect
|
if prefs.automatic_instance_redirect
|
||||||
current_page = env.get?("current_page").as(String)
|
current_page = env.get?("current_page").as(String)
|
||||||
redirect_url = "/redirect?referer=#{current_page}"
|
return "/redirect?referer=#{current_page}"
|
||||||
else
|
else
|
||||||
redirect_url = "https://redirect.invidious.io#{env.request.resource}"
|
return "https://redirect.invidious.io#{env.request.resource}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -190,7 +190,7 @@ def error_redirect_helper(env : HTTP::Server::Context)
|
||||||
<a href="/redirect?referer=#{env.get("current_page")}">#{switch_instance}</a>
|
<a href="/redirect?referer=#{env.get("current_page")}">#{switch_instance}</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://youtube.com#{env.request.resource}">#{go_to_youtube}</a>
|
<a rel="noreferrer noopener" href="https://youtube.com#{env.request.resource}">#{go_to_youtube}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
END_HTML
|
END_HTML
|
||||||
|
|
|
@ -97,7 +97,7 @@ class AuthHandler < Kemal::Handler
|
||||||
if token = env.request.headers["Authorization"]?
|
if token = env.request.headers["Authorization"]?
|
||||||
token = JSON.parse(URI.decode_www_form(token.lchop("Bearer ")))
|
token = JSON.parse(URI.decode_www_form(token.lchop("Bearer ")))
|
||||||
session = URI.decode_www_form(token["session"].as_s)
|
session = URI.decode_www_form(token["session"].as_s)
|
||||||
scopes, expire, signature = validate_request(token, session, env.request, HMAC_KEY, nil)
|
scopes, _, _ = validate_request(token, session, env.request, HMAC_KEY, nil)
|
||||||
|
|
||||||
if email = Invidious::Database::SessionIDs.select_email(session)
|
if email = Invidious::Database::SessionIDs.select_email(session)
|
||||||
user = Invidious::Database::Users.select!(email: email)
|
user = Invidious::Database::Users.select!(email: email)
|
||||||
|
|
|
@ -95,7 +95,6 @@ module I18next::Plurals
|
||||||
"hr" => PluralForms::Special_Hungarian_Serbian,
|
"hr" => PluralForms::Special_Hungarian_Serbian,
|
||||||
"it" => PluralForms::Special_Spanish_Italian,
|
"it" => PluralForms::Special_Spanish_Italian,
|
||||||
"pt" => PluralForms::Special_French_Portuguese,
|
"pt" => PluralForms::Special_French_Portuguese,
|
||||||
"pt" => PluralForms::Special_French_Portuguese,
|
|
||||||
"sr" => PluralForms::Special_Hungarian_Serbian,
|
"sr" => PluralForms::Special_Hungarian_Serbian,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +188,7 @@ module I18next::Plurals
|
||||||
|
|
||||||
# Emulate the `rule.numbers.size == 2 && rule.numbers[0] == 1` check
|
# Emulate the `rule.numbers.size == 2 && rule.numbers[0] == 1` check
|
||||||
# from original i18next code
|
# from original i18next code
|
||||||
private def is_simple_plural(form : PluralForms) : Bool
|
private def simple_plural?(form : PluralForms) : Bool
|
||||||
case form
|
case form
|
||||||
when .single_gt_one? then return true
|
when .single_gt_one? then return true
|
||||||
when .single_not_one? then return true
|
when .single_not_one? then return true
|
||||||
|
@ -211,7 +210,7 @@ module I18next::Plurals
|
||||||
idx = SuffixIndex.get_index(plural_form, count)
|
idx = SuffixIndex.get_index(plural_form, count)
|
||||||
|
|
||||||
# Simple plurals are handled differently in all versions (but v4)
|
# Simple plurals are handled differently in all versions (but v4)
|
||||||
if @simplify_plural_suffix && is_simple_plural(plural_form)
|
if @simplify_plural_suffix && simple_plural?(plural_form)
|
||||||
return (idx == 1) ? "_plural" : ""
|
return (idx == 1) ? "_plural" : ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -262,9 +261,9 @@ module I18next::Plurals
|
||||||
when .special_hebrew? then return special_hebrew(count)
|
when .special_hebrew? then return special_hebrew(count)
|
||||||
when .special_odia? then return special_odia(count)
|
when .special_odia? then return special_odia(count)
|
||||||
# Mixed v3/v4 forms
|
# Mixed v3/v4 forms
|
||||||
when .special_spanish_italian? then return special_cldr_Spanish_Italian(count)
|
when .special_spanish_italian? then return special_cldr_spanish_italian(count)
|
||||||
when .special_french_portuguese? then return special_cldr_French_Portuguese(count)
|
when .special_french_portuguese? then return special_cldr_french_portuguese(count)
|
||||||
when .special_hungarian_serbian? then return special_cldr_Hungarian_Serbian(count)
|
when .special_hungarian_serbian? then return special_cldr_hungarian_serbian(count)
|
||||||
else
|
else
|
||||||
# default, if nothing matched above
|
# default, if nothing matched above
|
||||||
return 0_u8
|
return 0_u8
|
||||||
|
@ -535,7 +534,7 @@ module I18next::Plurals
|
||||||
#
|
#
|
||||||
# This rule is mostly compliant to CLDR v42
|
# This rule is mostly compliant to CLDR v42
|
||||||
#
|
#
|
||||||
def self.special_cldr_Spanish_Italian(count : Int) : UInt8
|
def self.special_cldr_spanish_italian(count : Int) : UInt8
|
||||||
return 0_u8 if (count == 1) # one
|
return 0_u8 if (count == 1) # one
|
||||||
return 1_u8 if (count != 0 && count % 1_000_000 == 0) # many
|
return 1_u8 if (count != 0 && count % 1_000_000 == 0) # many
|
||||||
return 2_u8 # other
|
return 2_u8 # other
|
||||||
|
@ -545,7 +544,7 @@ module I18next::Plurals
|
||||||
#
|
#
|
||||||
# This rule is mostly compliant to CLDR v42
|
# This rule is mostly compliant to CLDR v42
|
||||||
#
|
#
|
||||||
def self.special_cldr_French_Portuguese(count : Int) : UInt8
|
def self.special_cldr_french_portuguese(count : Int) : UInt8
|
||||||
return 0_u8 if (count == 0 || count == 1) # one
|
return 0_u8 if (count == 0 || count == 1) # one
|
||||||
return 1_u8 if (count % 1_000_000 == 0) # many
|
return 1_u8 if (count % 1_000_000 == 0) # many
|
||||||
return 2_u8 # other
|
return 2_u8 # other
|
||||||
|
@ -555,7 +554,7 @@ module I18next::Plurals
|
||||||
#
|
#
|
||||||
# This rule is mostly compliant to CLDR v42
|
# This rule is mostly compliant to CLDR v42
|
||||||
#
|
#
|
||||||
def self.special_cldr_Hungarian_Serbian(count : Int) : UInt8
|
def self.special_cldr_hungarian_serbian(count : Int) : UInt8
|
||||||
n_mod_10 = count % 10
|
n_mod_10 = count % 10
|
||||||
n_mod_100 = count % 100
|
n_mod_100 = count % 100
|
||||||
|
|
||||||
|
|
|
@ -34,24 +34,11 @@ class Invidious::LogHandler < Kemal::BaseLogHandler
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
|
||||||
def puts(message : String)
|
|
||||||
@io << message << '\n'
|
|
||||||
@io.flush
|
|
||||||
end
|
|
||||||
|
|
||||||
def write(message : String)
|
def write(message : String)
|
||||||
@io << message
|
@io << message
|
||||||
@io.flush
|
@io.flush
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_log_level(level : String)
|
|
||||||
@level = LogLevel.parse(level)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_log_level(level : LogLevel)
|
|
||||||
@level = level
|
|
||||||
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
|
||||||
|
|
|
@ -53,7 +53,7 @@ module Invidious::Routes::Account
|
||||||
return error_template(401, "Password is a required field")
|
return error_template(401, "Password is a required field")
|
||||||
end
|
end
|
||||||
|
|
||||||
new_passwords = env.params.body.select { |k, v| k.match(/^new_password\[\d+\]$/) }.map { |k, v| v }
|
new_passwords = env.params.body.select { |k, _| k.match(/^new_password\[\d+\]$/) }.map { |_, v| v }
|
||||||
|
|
||||||
if new_passwords.size <= 1 || new_passwords.uniq.size != 1
|
if new_passwords.size <= 1 || new_passwords.uniq.size != 1
|
||||||
return error_template(400, "New passwords must match")
|
return error_template(400, "New passwords must match")
|
||||||
|
@ -240,7 +240,7 @@ module Invidious::Routes::Account
|
||||||
return error_template(400, ex)
|
return error_template(400, ex)
|
||||||
end
|
end
|
||||||
|
|
||||||
scopes = env.params.body.select { |k, v| k.match(/^scopes\[\d+\]$/) }.map { |k, v| v }
|
scopes = env.params.body.select { |k, _| k.match(/^scopes\[\d+\]$/) }.map { |_, v| v }
|
||||||
callback_url = env.params.body["callbackUrl"]?
|
callback_url = env.params.body["callbackUrl"]?
|
||||||
expire = env.params.body["expire"]?.try &.to_i?
|
expire = env.params.body["expire"]?.try &.to_i?
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,7 @@ module Invidious::Routes::API::V1::Videos
|
||||||
|
|
||||||
storyboard[:storyboard_count].times do |i|
|
storyboard[:storyboard_count].times do |i|
|
||||||
url = storyboard[:url]
|
url = storyboard[:url]
|
||||||
authority = /(i\d?).ytimg.com/.match(url).not_nil![1]?
|
authority = /(i\d?).ytimg.com/.match!(url)[1]?
|
||||||
url = url.gsub("$M", i).gsub(%r(https://i\d?.ytimg.com/sb/), "")
|
url = url.gsub("$M", i).gsub(%r(https://i\d?.ytimg.com/sb/), "")
|
||||||
url = "#{HOST_URL}/sb/#{authority}/#{url}"
|
url = "#{HOST_URL}/sb/#{authority}/#{url}"
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ module Invidious::Routes::API::V1::Videos
|
||||||
if CONFIG.cache_annotations && (cached_annotation = Invidious::Database::Annotations.select(id))
|
if CONFIG.cache_annotations && (cached_annotation = Invidious::Database::Annotations.select(id))
|
||||||
annotations = cached_annotation.annotations
|
annotations = cached_annotation.annotations
|
||||||
else
|
else
|
||||||
index = CHARS_SAFE.index(id[0]).not_nil!.to_s.rjust(2, '0')
|
index = CHARS_SAFE.index!(id[0]).to_s.rjust(2, '0')
|
||||||
|
|
||||||
# IA doesn't handle leading hyphens,
|
# IA doesn't handle leading hyphens,
|
||||||
# so we use https://archive.org/details/youtubeannotations_64
|
# so we use https://archive.org/details/youtubeannotations_64
|
||||||
|
|
|
@ -124,7 +124,7 @@ struct Invidious::User
|
||||||
playlist = create_playlist(title, privacy, user)
|
playlist = create_playlist(title, privacy, user)
|
||||||
Invidious::Database::Playlists.update_description(playlist.id, description)
|
Invidious::Database::Playlists.update_description(playlist.id, description)
|
||||||
|
|
||||||
videos = item["videos"]?.try &.as_a?.try &.each_with_index do |video_id, idx|
|
item["videos"]?.try &.as_a?.try &.each_with_index do |video_id, idx|
|
||||||
if idx > CONFIG.playlist_length_limit
|
if idx > CONFIG.playlist_length_limit
|
||||||
raise InfoException.new("Playlist cannot have more than #{CONFIG.playlist_length_limit} videos")
|
raise InfoException.new("Playlist cannot have more than #{CONFIG.playlist_length_limit} videos")
|
||||||
end
|
end
|
||||||
|
@ -182,7 +182,7 @@ struct Invidious::User
|
||||||
if is_opml?(type, extension)
|
if is_opml?(type, extension)
|
||||||
subscriptions = XML.parse(body)
|
subscriptions = XML.parse(body)
|
||||||
user.subscriptions += subscriptions.xpath_nodes(%q(//outline[@type="rss"])).map do |channel|
|
user.subscriptions += subscriptions.xpath_nodes(%q(//outline[@type="rss"])).map do |channel|
|
||||||
channel["xmlUrl"].match(/UC[a-zA-Z0-9_-]{22}/).not_nil![0]
|
channel["xmlUrl"].match!(/UC[a-zA-Z0-9_-]{22}/)[0]
|
||||||
end
|
end
|
||||||
elsif extension == "json" || type == "application/json"
|
elsif extension == "json" || type == "application/json"
|
||||||
subscriptions = JSON.parse(body)
|
subscriptions = JSON.parse(body)
|
||||||
|
|
|
@ -394,10 +394,6 @@ end
|
||||||
def fetch_video(id, region)
|
def fetch_video(id, region)
|
||||||
info = extract_video_info(video_id: id)
|
info = extract_video_info(video_id: id)
|
||||||
|
|
||||||
allowed_regions = info
|
|
||||||
.dig?("microformat", "playerMicroformatRenderer", "availableCountries")
|
|
||||||
.try &.as_a.map &.as_s || [] of String
|
|
||||||
|
|
||||||
if reason = info["reason"]?
|
if reason = info["reason"]?
|
||||||
if reason == "Video unavailable"
|
if reason == "Video unavailable"
|
||||||
raise NotFoundException.new(reason.as_s || "")
|
raise NotFoundException.new(reason.as_s || "")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="flex-right flexible">
|
<div class="flex-right flexible">
|
||||||
<div class="icon-buttons">
|
<div class="icon-buttons">
|
||||||
<a title="<%=translate(locale, "videoinfo_watch_on_youTube")%>" href="https://www.youtube.com/watch<%=endpoint_params%>">
|
<a title="<%=translate(locale, "videoinfo_watch_on_youTube")%>" rel="noreferrer noopener" href="https://www.youtube.com/watch<%=endpoint_params%>">
|
||||||
<i class="icon ion-logo-youtube"></i>
|
<i class="icon ion-logo-youtube"></i>
|
||||||
</a>
|
</a>
|
||||||
<a title="<%=translate(locale, "Audio mode")%>" href="/watch<%=endpoint_params%>&listen=1">
|
<a title="<%=translate(locale, "Audio mode")%>" href="/watch<%=endpoint_params%>&listen=1">
|
||||||
|
|
|
@ -83,7 +83,7 @@
|
||||||
|
|
||||||
<% if !playlist.is_a? InvidiousPlaylist %>
|
<% if !playlist.is_a? InvidiousPlaylist %>
|
||||||
<div class="pure-u-2-3">
|
<div class="pure-u-2-3">
|
||||||
<a href="https://www.youtube.com/playlist?list=<%= playlist.id %>">
|
<a rel="noreferrer noopener" href="https://www.youtube.com/playlist?list=<%= playlist.id %>">
|
||||||
<%= translate(locale, "View playlist on YouTube") %>
|
<%= translate(locale, "View playlist on YouTube") %>
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
|
|
|
@ -123,8 +123,8 @@ we're going to need to do it here in order to allow for translations.
|
||||||
link_yt_embed = IV::HttpServer::Utils.add_params_to_url(link_yt_embed, link_yt_param)
|
link_yt_embed = IV::HttpServer::Utils.add_params_to_url(link_yt_embed, link_yt_param)
|
||||||
end
|
end
|
||||||
-%>
|
-%>
|
||||||
<a id="link-yt-watch" data-base-url="<%= link_yt_watch %>" href="<%= link_yt_watch %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
|
<a id="link-yt-watch" rel="noreferrer noopener" data-base-url="<%= link_yt_watch %>" href="<%= link_yt_watch %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
|
||||||
(<a id="link-yt-embed" data-base-url="<%= link_yt_embed %>" href="<%= link_yt_embed %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
|
(<a id="link-yt-embed" rel="noreferrer noopener" data-base-url="<%= link_yt_embed %>" href="<%= link_yt_embed %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<p id="watch-on-another-invidious-instance">
|
<p id="watch-on-another-invidious-instance">
|
||||||
|
|
|
@ -24,7 +24,7 @@ struct YoutubeConnectionPool
|
||||||
@pool = build_pool()
|
@pool = build_pool()
|
||||||
end
|
end
|
||||||
|
|
||||||
def client(&block)
|
def client(&)
|
||||||
conn = pool.checkout
|
conn = pool.checkout
|
||||||
begin
|
begin
|
||||||
response = yield conn
|
response = yield conn
|
||||||
|
@ -69,7 +69,7 @@ def make_client(url : URI, region = nil, force_resolve : Bool = false)
|
||||||
return client
|
return client
|
||||||
end
|
end
|
||||||
|
|
||||||
def make_client(url : URI, region = nil, force_resolve : Bool = false, &block)
|
def make_client(url : URI, region = nil, force_resolve : Bool = false, &)
|
||||||
client = make_client(url, region, force_resolve)
|
client = make_client(url, region, force_resolve)
|
||||||
begin
|
begin
|
||||||
yield client
|
yield client
|
||||||
|
|
|
@ -109,7 +109,6 @@ private module Parsers
|
||||||
end
|
end
|
||||||
|
|
||||||
live_now = false
|
live_now = false
|
||||||
paid = false
|
|
||||||
premium = false
|
premium = false
|
||||||
|
|
||||||
premiere_timestamp = item_contents.dig?("upcomingEventData", "startTime").try { |t| Time.unix(t.as_s.to_i64) }
|
premiere_timestamp = item_contents.dig?("upcomingEventData", "startTime").try { |t| Time.unix(t.as_s.to_i64) }
|
||||||
|
@ -856,7 +855,7 @@ end
|
||||||
#
|
#
|
||||||
# This function yields the container so that items can be parsed separately.
|
# This function yields the container so that items can be parsed separately.
|
||||||
#
|
#
|
||||||
def extract_items(initial_data : InitialData, &block)
|
def extract_items(initial_data : InitialData, &)
|
||||||
if unpackaged_data = initial_data["contents"]?.try &.as_h
|
if unpackaged_data = initial_data["contents"]?.try &.as_h
|
||||||
elsif unpackaged_data = initial_data["response"]?.try &.as_h
|
elsif unpackaged_data = initial_data["response"]?.try &.as_h
|
||||||
elsif unpackaged_data = initial_data.dig?("onResponseReceivedActions", 1).try &.as_h
|
elsif unpackaged_data = initial_data.dig?("onResponseReceivedActions", 1).try &.as_h
|
||||||
|
|
|
@ -83,5 +83,5 @@ end
|
||||||
|
|
||||||
def extract_selected_tab(tabs)
|
def extract_selected_tab(tabs)
|
||||||
# Extract the selected tab from the array of tabs Youtube returns
|
# Extract the selected tab from the array of tabs Youtube returns
|
||||||
return selected_target = tabs.as_a.select(&.["tabRenderer"]?.try &.["selected"]?.try &.as_bool)[0]["tabRenderer"]
|
return tabs.as_a.select(&.["tabRenderer"]?.try &.["selected"]?.try &.as_bool)[0]["tabRenderer"]
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue