Merge 540b65a332b869dc42b2427ee285554589e3ffd5 into d51a7a44ad91d2fa7d1330970a15a0d8f365f250

This commit is contained in:
Sijawusz Pur Rahnama 2026-01-24 12:53:00 +09:00 committed by GitHub
commit 5a4ba13228
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
109 changed files with 1284 additions and 1314 deletions

View File

@ -2,24 +2,16 @@
# Lint
#
Lint/UnusedArgument:
Excluded:
- "**/*.ecr"
# Exclude assigns for ECR files
Lint/UselessAssign:
Excluded:
- src/invidious.cr
- src/invidious/helpers/errors.cr
- "**/*.ecr"
- src/invidious/routes/**/*.cr
# Ignore false negative (if !db.query_one?...)
Lint/UnreachableCode:
Excluded:
- src/invidious/database/base.cr
# Ignore shadowed variable `key` (it works for now, and that's
# a sensitive part of the code)
Lint/ShadowingOuterLocalVar:
Excluded:
- src/invidious/helpers/tokens.cr
Lint/NotNil:
Enabled: false
@ -27,41 +19,17 @@ Lint/SpecFilename:
Excluded:
- spec/parsers_helper.cr
#
# Style
#
Style/RedundantBegin:
Enabled: false
Style/RedundantReturn:
Enabled: false
Style/RedundantNext:
Enabled: false
Style/ParenthesesAroundCondition:
Enabled: false
# This requires a rewrite of most data structs (and their usage) in Invidious.
Naming/QueryBoolMethods:
Enabled: false
Naming/AccessorMethodName:
Enabled: false
Naming/BlockParameterName:
Enabled: false
# Hides TODO comment warnings.
#
# Call `bin/ameba --only Documentation/DocumentationAdmonition` to
# list them
Documentation/DocumentationAdmonition:
Enabled: false
#
# Metrics
#

19
.github/workflows/ameba.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Ameba
on:
push:
pull_request:
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Download source
uses: actions/checkout@v6
- name: Run Ameba Linter
uses: crystal-ameba/github-action@master

View File

@ -26,7 +26,6 @@ on:
jobs:
build:
runs-on: ubuntu-latest
name: "build - crystal: ${{ matrix.crystal }}, stable: ${{ matrix.stable }}"
@ -122,7 +121,6 @@ jobs:
run: docker compose logs
lint:
runs-on: ubuntu-latest
continue-on-error: true
@ -159,6 +157,3 @@ jobs:
git diff
exit 1
fi
- name: Run Ameba linter
run: bin/ameba

View File

@ -2,7 +2,7 @@ version: 2.0
shards:
ameba:
git: https://github.com/crystal-ameba/ameba.git
version: 1.6.1
version: 1.7.0-dev+git.commit.9dbeb92f89d7a668940029bd7b935bef370f26c1
athena-negotiation:
git: https://github.com/athena-framework/negotiation.git

View File

@ -34,7 +34,7 @@ development_dependencies:
version: ~> 0.10.4
ameba:
github: crystal-ameba/ameba
version: ~> 1.6.1
branch: master
crystal: ">= 1.10.0, < 2.0.0"

View File

@ -14,7 +14,7 @@ require "spectator"
require "../../../src/invidious/http_server/static_assets_handler.cr"
private def get_static_assets_handler
return Invidious::HttpServer::StaticAssetsHandler.new "spec/http_server/handlers/static_assets_handler", directory_listing: false
Invidious::HttpServer::StaticAssetsHandler.new "spec/http_server/handlers/static_assets_handler", directory_listing: false
end
# Slightly modified version of `handle` function from
@ -125,7 +125,7 @@ Spectator.describe StaticAssetsHandler do
gzip.gets_to_end
end
return expect(decompressed)
expect(decompressed)
end
it "For full file requests" do

View File

@ -7,7 +7,7 @@ Spectator.configure do |config|
end
def csv_sample
return <<-CSV
<<-CSV
Kanal-ID,Kanal-URL,Kanaltitel
UC0hHW5Y08ggq-9kbrGgWj0A,http://www.youtube.com/channel/UC0hHW5Y08ggq-9kbrGgWj0A,Matias Marolla
UC0vBXGSyV14uvJ4hECDOl0Q,http://www.youtube.com/channel/UC0vBXGSyV14uvJ4hECDOl0Q,Techquickie

View File

@ -26,7 +26,7 @@ def load_mock(file) : Hash(String, JSON::Any)
file = File.join(__DIR__, "..", "mocks", file + ".json")
content = File.read(file)
return JSON.parse(content).as_h
JSON.parse(content).as_h
end
Spectator.configure do |config|

View File

@ -161,7 +161,7 @@ def get_about_info(ucid, locale) : AboutChannel
sub_count = 0
if (metadata_rows = initdata.dig?("header", "pageHeaderRenderer", "content", "pageHeaderViewModel", "metadata", "contentMetadataViewModel", "metadataRows").try &.as_a)
if metadata_rows = initdata.dig?("header", "pageHeaderRenderer", "content", "pageHeaderViewModel", "metadata", "contentMetadataViewModel", "metadataRows").try &.as_a
metadata_rows.each do |row|
metadata_part = row.dig?("metadataParts").try &.as_a.find { |i| i.dig?("text", "content").try &.as_s.includes?("subscribers") }
if !metadata_part.nil?

View File

@ -26,21 +26,21 @@ struct ChannelVideo
json.object do
json.field "type", "shortVideo"
json.field "title", self.title
json.field "videoId", self.id
json.field "title", title
json.field "videoId", id
json.field "videoThumbnails" do
Invidious::JSONify::APIv1.thumbnails(json, self.id)
Invidious::JSONify::APIv1.thumbnails(json, id)
end
json.field "lengthSeconds", self.length_seconds
json.field "lengthSeconds", length_seconds
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
json.field "published", self.published.to_unix
json.field "publishedText", translate(locale, "`x` ago", recode_date(self.published, locale))
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", "/channel/#{ucid}"
json.field "published", published.to_unix
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
json.field "viewCount", self.views
json.field "viewCount", views
end
end
@ -51,34 +51,34 @@ struct ChannelVideo
end
def to_xml(locale, query_params, xml : XML::Builder)
query_params["v"] = self.id
query_params["v"] = id
xml.element("entry") do
xml.element("id") { xml.text "yt:video:#{self.id}" }
xml.element("yt:videoId") { xml.text self.id }
xml.element("yt:channelId") { xml.text self.ucid }
xml.element("title") { xml.text self.title }
xml.element("id") { xml.text "yt:video:#{id}" }
xml.element("yt:videoId") { xml.text id }
xml.element("yt:channelId") { xml.text ucid }
xml.element("title") { xml.text title }
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?#{query_params}")
xml.element("author") do
xml.element("name") { xml.text self.author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
xml.element("name") { xml.text author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
end
xml.element("content", type: "xhtml") do
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
xml.element("a", href: "#{HOST_URL}/watch?#{query_params}") do
xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
xml.element("img", src: "#{HOST_URL}/vi/#{id}/mqdefault.jpg")
end
end
end
xml.element("published") { xml.text self.published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("updated") { xml.text self.updated.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("published") { xml.text published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("updated") { xml.text updated.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("media:group") do
xml.element("media:title") { xml.text self.title }
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
xml.element("media:title") { xml.text title }
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{id}/mqdefault.jpg",
width: "320", height: "180")
end
end
@ -107,7 +107,7 @@ class ChannelRedirect < Exception
end
def get_batch_channels(channels)
finished_channel = Channel(String | Nil).new
finished_channel = Channel(String?).new
max_threads = 10
spawn do
@ -141,7 +141,7 @@ def get_batch_channels(channels)
end
end
return final
final
end
def get_channel(id) : InvidiousChannel
@ -152,7 +152,7 @@ def get_channel(id) : InvidiousChannel
Invidious::Database::Channels.insert(channel, update_on_conflict: true)
end
return channel
channel
end
def fetch_channel(ucid, pull_all_videos : Bool)
@ -292,5 +292,5 @@ def fetch_channel(ucid, pull_all_videos : Bool)
end
channel.updated = Time.utc
return channel
channel
end

View File

@ -21,7 +21,7 @@ def fetch_channel_community(ucid, cursor, locale, format, thin_mode)
items = container.as_a
end
return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode)
extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode)
end
def decode_ucid_from_post_protobuf(params)
@ -30,7 +30,7 @@ def decode_ucid_from_post_protobuf(params)
.try { |i| IO::Memory.new(i) }
.try { |i| Protodec::Any.parse(i) }
return decoded_protobuf.try(&.["56:0:embedded"]["2:0:string"].as_s)
decoded_protobuf.try(&.["56:0:embedded"]["2:0:string"].as_s)
end
def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode)
@ -53,7 +53,7 @@ def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode)
items << item
end
return extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode, is_single_post: true)
extract_channel_community(items, ucid: ucid, locale: locale, format: format, thin_mode: thin_mode, is_single_post: true)
end
def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_single_post : Bool = false)
@ -294,7 +294,7 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_sing
end
end
return response
response
end
def produce_channel_community_continuation(ucid, cursor)
@ -310,7 +310,7 @@ def produce_channel_community_continuation(ucid, cursor)
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return continuation
continuation
end
def extract_channel_community_cursor(continuation)

View File

@ -24,7 +24,7 @@ def fetch_channel_playlists(ucid, author, continuation, sort_by)
initial_data = YoutubeAPI.browse(ucid, params: params || "")
end
return extract_items(initial_data, author, ucid)
extract_items(initial_data, author, ucid)
end
def fetch_channel_podcasts(ucid, author, continuation)
@ -33,7 +33,7 @@ def fetch_channel_podcasts(ucid, author, continuation)
else
initial_data = YoutubeAPI.browse(ucid, params: "Eghwb2RjYXN0c_IGBQoDugEA")
end
return extract_items(initial_data, author, ucid)
extract_items(initial_data, author, ucid)
end
def fetch_channel_releases(ucid, author, continuation)
@ -42,7 +42,7 @@ def fetch_channel_releases(ucid, author, continuation)
else
initial_data = YoutubeAPI.browse(ucid, params: "EghyZWxlYXNlc_IGBQoDsgEA")
end
return extract_items(initial_data, author, ucid)
extract_items(initial_data, author, ucid)
end
def fetch_channel_courses(ucid, author, continuation)
@ -51,5 +51,5 @@ def fetch_channel_courses(ucid, author, continuation)
else
initial_data = YoutubeAPI.browse(ucid, params: "Egdjb3Vyc2Vz8gYFCgPCAQA%3D")
end
return extract_items(initial_data, author, ucid)
extract_items(initial_data, author, ucid)
end

View File

@ -9,7 +9,7 @@ module Invidious::Channel::Tabs
# an author name and ucid directly (e.g in RSS feeds).
# TODO: figure out how to get rid of that
def get_videos(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
return get_videos(
get_videos(
channel.author, channel.ucid,
continuation: continuation, sort_by: sort_by
)
@ -19,7 +19,7 @@ module Invidious::Channel::Tabs
# an author name and ucid directly (e.g in RSS feeds).
# TODO: figure out how to get rid of that
def get_videos(channel : InvidiousChannel, *, continuation : String? = nil, sort_by = "newest")
return get_videos(
get_videos(
channel.author, channel.id,
continuation: continuation, sort_by: sort_by
)
@ -29,7 +29,7 @@ module Invidious::Channel::Tabs
continuation ||= make_initial_videos_ctoken(ucid, sort_by)
initial_data = YoutubeAPI.browse(continuation: continuation)
return extract_items(initial_data, author, ucid)
extract_items(initial_data, author, ucid)
end
def get_60_videos(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
@ -59,7 +59,7 @@ module Invidious::Channel::Tabs
continuation ||= make_initial_shorts_ctoken(channel.ucid, sort_by)
initial_data = YoutubeAPI.browse(continuation: continuation)
return extract_items(initial_data, channel.author, channel.ucid)
extract_items(initial_data, channel.author, channel.ucid)
end
# -------------------
@ -70,7 +70,7 @@ module Invidious::Channel::Tabs
continuation ||= make_initial_livestreams_ctoken(channel.ucid, sort_by)
initial_data = YoutubeAPI.browse(continuation: continuation)
return extract_items(initial_data, channel.author, channel.ucid)
extract_items(initial_data, channel.author, channel.ucid)
end
def get_60_livestreams(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
@ -98,10 +98,10 @@ module Invidious::Channel::Tabs
private def sort_options_videos_short(sort_by : String)
case sort_by
when "newest" then return 4_i64
when "popular" then return 2_i64
when "oldest" then return 5_i64
else return 4_i64 # Fallback to "newest"
when "newest" then 4_i64
when "popular" then 2_i64
when "oldest" then 5_i64
else 4_i64 # Fallback to "newest"
end
end
@ -118,7 +118,7 @@ module Invidious::Channel::Tabs
},
}
return channel_ctoken_wrap(ucid, object)
channel_ctoken_wrap(ucid, object)
end
# Generate the initial "continuation token" to get the first page of the
@ -134,7 +134,7 @@ module Invidious::Channel::Tabs
},
}
return channel_ctoken_wrap(ucid, object)
channel_ctoken_wrap(ucid, object)
end
# Generate the initial "continuation token" to get the first page of the
@ -158,7 +158,7 @@ module Invidious::Channel::Tabs
},
}
return channel_ctoken_wrap(ucid, object)
channel_ctoken_wrap(ucid, object)
end
# The protobuf structure common between videos/shorts/livestreams
@ -187,6 +187,6 @@ module Invidious::Channel::Tabs
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return continuation
continuation
end
end

View File

@ -37,7 +37,7 @@ def text_to_parsed_content(text : String) : JSON::Any
nodes << (node)
end
end
return JSON.parse({"runs" => nodes}.to_json)
JSON.parse({"runs" => nodes}.to_json)
end
def parse_content(content : JSON::Any, video_id : String? = "") : String
@ -85,5 +85,5 @@ def content_to_comment_html(content, video_id : String? = "")
text
end
return html_array.join("").delete('\ufeff')
html_array.join("").delete('\ufeff')
end

View File

@ -45,7 +45,7 @@ module Invidious::Comments
html = node
end
return html.to_xml(options: XML::SaveOptions::NO_DECL)
html.to_xml(options: XML::SaveOptions::NO_DECL)
end
def fill_links(html, scheme, host)
@ -71,6 +71,6 @@ module Invidious::Comments
html = html.xpath_node(%q(//body/p)).not_nil!
end
return html.to_xml(options: XML::SaveOptions::NO_DECL)
html.to_xml(options: XML::SaveOptions::NO_DECL)
end
end

View File

@ -13,7 +13,7 @@ module Invidious::Comments
client_config = YoutubeAPI::ClientConfig.new(region: region)
response = YoutubeAPI.next(continuation: ctoken, client_config: client_config)
return parse_youtube(id, response, format, locale, thin_mode, sort_by)
parse_youtube(id, response, format, locale, thin_mode, sort_by)
end
def fetch_community_post_comments(ucid, post_id, sort_by = "top")
@ -58,7 +58,7 @@ module Invidious::Comments
.try { |i| URI.encode_www_form(i) }
initial_data = YoutubeAPI.browse(continuation: continuation)
return initial_data
initial_data
end
def parse_youtube(id, response, format, locale, thin_mode, sort_by = "top", is_post = false)
@ -320,7 +320,7 @@ module Invidious::Comments
end
end
return response
response
end
def produce_continuation(video_id, cursor = "", sort_by = "top")
@ -364,6 +364,6 @@ module Invidious::Comments
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return continuation
continuation
end
end

View File

@ -183,15 +183,15 @@ class Config
def disabled?(option)
case disabled = CONFIG.disable_proxy
when Bool
return disabled
disabled
when Array
if disabled.includes? option
return true
true
else
return false
false
end
else
return false
false
end
end
@ -318,6 +318,6 @@ class Config
end
end
return config
config
end
end

View File

@ -19,6 +19,6 @@ module Invidious::Database::Annotations
WHERE id = $1
SQL
return PG_DB.query_one?(request, id, as: Annotation)
PG_DB.query_one?(request, id, as: Annotation)
end
end

View File

@ -32,6 +32,7 @@ module Invidious::Database
def check_enum(enum_name, struct_type = nil)
return # TODO
# ameba:disable Lint/UnreachableCode
if !PG_DB.query_one?("SELECT true FROM pg_type WHERE typname = $1", enum_name, as: Bool)
LOGGER.info("check_enum: CREATE TYPE #{enum_name}")
@ -131,6 +132,6 @@ module Invidious::Database
end
end
return column_array
column_array
end
end

View File

@ -72,7 +72,7 @@ module Invidious::Database::Channels
WHERE id = $1
SQL
return PG_DB.query_one?(request, id, as: InvidiousChannel)
PG_DB.query_one?(request, id, as: InvidiousChannel)
end
def select(ids : Array(String)) : Array(InvidiousChannel)?
@ -83,7 +83,7 @@ module Invidious::Database::Channels
WHERE id = ANY($1)
SQL
return PG_DB.query_all(request, ids, as: InvidiousChannel)
PG_DB.query_all(request, ids, as: InvidiousChannel)
end
end
@ -114,7 +114,7 @@ module Invidious::Database::ChannelVideos
RETURNING (xmax=0) AS was_insert
SQL
return PG_DB.query_one(request, *video.to_tuple, as: Bool)
PG_DB.query_one(request, *video.to_tuple, as: Bool)
end
# -------------------
@ -130,7 +130,7 @@ module Invidious::Database::ChannelVideos
ORDER BY published DESC
SQL
return PG_DB.query_all(request, ids, as: ChannelVideo)
PG_DB.query_all(request, ids, as: ChannelVideo)
end
def select_notfications(ucid : String, since : Time) : Array(ChannelVideo)
@ -141,7 +141,7 @@ module Invidious::Database::ChannelVideos
LIMIT 15
SQL
return PG_DB.query_all(request, ucid, since, as: ChannelVideo)
PG_DB.query_all(request, ucid, since, as: ChannelVideo)
end
def select_popular_videos : Array(ChannelVideo)

View File

@ -50,6 +50,6 @@ module Invidious::Database::Nonces
WHERE nonce = $1
SQL
return PG_DB.query_one?(request, nonce, as: {String, Time})
PG_DB.query_one?(request, nonce, as: {String, Time})
end
end

View File

@ -100,7 +100,7 @@ module Invidious::Database::Playlists
WHERE id = $1
SQL
return PG_DB.query_one?(request, id, as: InvidiousPlaylist)
PG_DB.query_one?(request, id, as: InvidiousPlaylist)
end
def select_all(*, author : String) : Array(InvidiousPlaylist)
@ -109,7 +109,7 @@ module Invidious::Database::Playlists
WHERE author = $1
SQL
return PG_DB.query_all(request, author, as: InvidiousPlaylist)
PG_DB.query_all(request, author, as: InvidiousPlaylist)
end
# -------------------
@ -157,7 +157,7 @@ module Invidious::Database::Playlists
WHERE id = $1
SQL
return PG_DB.query_one?(request, id, as: String).nil?
PG_DB.query_one?(request, id, as: String).nil?
end
# Count how many playlist a user has created.
@ -167,7 +167,7 @@ module Invidious::Database::Playlists
WHERE author = $1
SQL
return PG_DB.query_one?(request, author, as: Int64) || 0_i64
PG_DB.query_one?(request, author, as: Int64) || 0_i64
end
end
@ -225,7 +225,7 @@ module Invidious::Database::PlaylistVideos
OFFSET $4
SQL
return PG_DB.query_all(request, plid, index, limit, offset, as: PlaylistVideo)
PG_DB.query_all(request, plid, index, limit, offset, as: PlaylistVideo)
end
def select_index(plid : String, vid : String) : Int64?
@ -235,7 +235,7 @@ module Invidious::Database::PlaylistVideos
LIMIT 1
SQL
return PG_DB.query_one?(request, plid, vid, as: Int64)
PG_DB.query_one?(request, plid, vid, as: Int64)
end
def select_one_id(plid : String, index : VideoIndex) : String?
@ -246,7 +246,7 @@ module Invidious::Database::PlaylistVideos
LIMIT 1
SQL
return PG_DB.query_one?(request, plid, index, as: String)
PG_DB.query_one?(request, plid, index, as: String)
end
def select_ids(plid : String, index : VideoIndex, limit = 500) : Array(String)
@ -257,6 +257,6 @@ module Invidious::Database::PlaylistVideos
LIMIT $3
SQL
return PG_DB.query_all(request, plid, index, limit, as: String)
PG_DB.query_all(request, plid, index, limit, as: String)
end
end

View File

@ -194,7 +194,7 @@ module Invidious::Database::Users
WHERE email = $1
SQL
return PG_DB.query_one?(request, email, as: User)
PG_DB.query_one?(request, email, as: User)
end
# Same as select, but can raise an exception
@ -204,7 +204,7 @@ module Invidious::Database::Users
WHERE email = $1
SQL
return PG_DB.query_one(request, email, as: User)
PG_DB.query_one(request, email, as: User)
end
def select(*, token : String) : User?
@ -213,7 +213,7 @@ module Invidious::Database::Users
WHERE token = $1
SQL
return PG_DB.query_one?(request, token, as: User)
PG_DB.query_one?(request, token, as: User)
end
def select_notifications(user : User) : Array(String)
@ -223,6 +223,6 @@ module Invidious::Database::Users
WHERE email = $1
SQL
return PG_DB.query_one(request, user.email, as: Array(String))
PG_DB.query_one(request, user.email, as: Array(String))
end
end

View File

@ -47,6 +47,6 @@ module Invidious::Database::Videos
WHERE id = $1
SQL
return PG_DB.query_one?(request, id, as: Video)
PG_DB.query_one?(request, id, as: Video)
end
end

View File

@ -23,7 +23,7 @@ class BrokenTubeException < Exception
end
def message
return "Missing JSON element \"#{@element}\""
"Missing JSON element \"#{@element}\""
end
end

View File

@ -14,12 +14,12 @@ module Invidious::Frontend::ChannelPage
end
def generate_tabs_links(locale : String, channel : AboutChannel, selected_tab : TabsAvailable)
return String.build(1500) do |str|
String.build(1500) do |str|
base_url = "/channel/#{channel.ucid}"
TabsAvailable.each do |tab|
# Ignore playlists, as it is not supported for auto-generated channels yet
next if (tab.playlists? && channel.auto_generated)
next if tab.playlists? && channel.auto_generated
tab_name = tab.to_s.downcase

View File

@ -11,24 +11,24 @@ module Invidious::Frontend::Comments
replies_html = ""
if child.replies.is_a?(RedditThing)
replies = child.replies.as(RedditThing)
replies_html = self.template_reddit(replies.data.as(RedditListing).children, locale)
replies_html = template_reddit(replies.data.as(RedditListing).children, locale)
end
if child.depth > 0
html << <<-END_HTML
html << <<-HTML
<div class="pure-g">
<div class="pure-u-1-24">
</div>
<div class="pure-u-23-24">
END_HTML
HTML
else
html << <<-END_HTML
html << <<-HTML
<div class="pure-g">
<div class="pure-u-1">
END_HTML
HTML
end
html << <<-END_HTML
html << <<-HTML
<p>
<a href="javascript:void(0)" data-onclick="toggle_parent">[ ]</a>
<b><a href="https://www.reddit.com/user/#{child.author}">#{child.author}</a></b>
@ -42,7 +42,7 @@ module Invidious::Frontend::Comments
</div>
</div>
</div>
END_HTML
HTML
end
end
end

View File

@ -12,7 +12,7 @@ module Invidious::Frontend::Comments
NumberFormatting::Separator
)
replies_html = <<-END_HTML
replies_html = <<-HTML
<div id="replies" class="pure-g">
<div class="pure-u-1-24"></div>
<div class="pure-u-23-24">
@ -22,7 +22,7 @@ module Invidious::Frontend::Comments
</p>
</div>
</div>
END_HTML
HTML
elsif comments["authorId"]? && !comments["singlePost"]?
# for posts we should display a link to the post
replies_count_text = translate_count(locale,
@ -31,7 +31,7 @@ module Invidious::Frontend::Comments
NumberFormatting::Separator
)
replies_html = <<-END_HTML
replies_html = <<-HTML
<div class="pure-g">
<div class="pure-u-1-24"></div>
<div class="pure-u-23-24">
@ -40,7 +40,7 @@ module Invidious::Frontend::Comments
</p>
</div>
</div>
END_HTML
HTML
end
if !thin_mode
@ -65,7 +65,7 @@ module Invidious::Frontend::Comments
str << %(width="16" height="16" />)
end
end
html << <<-END_HTML
html << <<-HTML
<div class="pure-g" style="width:100%">
<div class="channel-profile pure-u-4-24 pure-u-md-2-24">
<img loading="lazy" style="margin-right:1em;margin-top:1em;width:90%" src="#{author_thumbnail}" alt="" />
@ -77,7 +77,7 @@ module Invidious::Frontend::Comments
</b>
#{sponsor_icon}
<p style="white-space:pre-wrap">#{child["contentHtml"]}</p>
END_HTML
HTML
if child["attachment"]?
attachment = child["attachment"]
@ -86,82 +86,81 @@ module Invidious::Frontend::Comments
when "image"
attachment = attachment["imageThumbnails"][1]
html << <<-END_HTML
html << <<-HTML
<div class="pure-g">
<div class="pure-u-1 pure-u-md-1-2">
<img loading="lazy" style="width:100%" src="/ggpht#{URI.parse(attachment["url"].as_s).request_target}" alt="" />
</div>
</div>
END_HTML
HTML
when "video"
if attachment["error"]?
html << <<-END_HTML
html << <<-HTML
<div class="pure-g video-iframe-wrapper">
<p>#{attachment["error"]}</p>
</div>
END_HTML
HTML
else
html << <<-END_HTML
html << <<-HTML
<div class="pure-g video-iframe-wrapper">
<iframe class="video-iframe" src='/embed/#{attachment["videoId"]?}?autoplay=0'></iframe>
</div>
END_HTML
HTML
end
when "multiImage"
html << <<-END_HTML
html << <<-HTML
<section class="carousel">
<a class="skip-link" href="#skip-#{child["commentId"]}">#{translate(locale, "carousel_skip")}</a>
<div class="slides">
END_HTML
HTML
image_array = attachment["images"].as_a
image_array.each_index do |i|
html << <<-END_HTML
html << <<-HTML
<div class="slides-item slide-#{i + 1}" id="#{child["commentId"]}-slide-#{i + 1}" aria-label="#{translate(locale, "carousel_slide", {"current" => (i + 1).to_s, "total" => image_array.size.to_s})}" tabindex="0">
<img loading="lazy" src="/ggpht#{URI.parse(image_array[i][1]["url"].as_s).request_target}" alt="" />
</div>
END_HTML
HTML
end
html << <<-END_HTML
html << <<-HTML
</div>
<div class="carousel__nav">
END_HTML
HTML
attachment["images"].as_a.each_index do |i|
html << <<-END_HTML
html << <<-HTML
<a class="slider-nav" href="##{child["commentId"]}-slide-#{i + 1}" aria-label="#{translate(locale, "carousel_go_to", (i + 1).to_s)}" tabindex="-1" aria-hidden="true">#{i + 1}</a>
END_HTML
HTML
end
html << <<-END_HTML
html << <<-HTML
</div>
<div id="skip-#{child["commentId"]}"></div>
</section>
END_HTML
else nil # Ignore
HTML
end
end
html << <<-END_HTML
html << <<-HTML
<p>
<span title="#{Time.unix(child["published"].as_i64).to_s(translate(locale, "%A %B %-d, %Y"))}">#{translate(locale, "`x` ago", recode_date(Time.unix(child["published"].as_i64), locale))} #{child["isEdited"] == true ? translate(locale, "(edited)") : ""}</span>
|
END_HTML
HTML
if comments["videoId"]?
html << <<-END_HTML
html << <<-HTML
<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
HTML
elsif comments["authorId"]?
html << <<-END_HTML
html << <<-HTML
<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
HTML
end
html << <<-END_HTML
html << <<-HTML
<i class="icon ion-ios-thumbs-up"></i> #{number_with_separator(child["likeCount"])}
END_HTML
HTML
if child["creatorHeart"]?
if !thin_mode
@ -170,7 +169,7 @@ module Invidious::Frontend::Comments
creator_thumbnail = ""
end
html << <<-END_HTML
html << <<-HTML
&nbsp;
<span class="creator-heart-container" title="#{translate(locale, "`x` marked it with a ❤", child["creatorHeart"]["creatorName"].as_s)}">
<span class="creator-heart">
@ -180,19 +179,19 @@ module Invidious::Frontend::Comments
</span>
</span>
</span>
END_HTML
HTML
end
html << <<-END_HTML
html << <<-HTML
</p>
#{replies_html}
</div>
</div>
END_HTML
HTML
end
if comments["continuation"]?
html << <<-END_HTML
html << <<-HTML
<div class="pure-g">
<div class="pure-u-1">
<p>
@ -201,7 +200,7 @@ module Invidious::Frontend::Comments
</p>
</div>
</div>
END_HTML
HTML
end
end
end

View File

@ -6,9 +6,9 @@ module Invidious::Frontend::Misc
if preferences.automatic_instance_redirect
current_page = env.get?("current_page").as(String)
return "/redirect?referer=#{current_page}"
"/redirect?referer=#{current_page}"
else
return "https://redirect.invidious.io#{env.request.resource}"
"https://redirect.invidious.io#{env.request.resource}"
end
end
end

View File

@ -60,7 +60,7 @@ module Invidious::Frontend::Pagination
end
def nav_numeric(locale : String?, *, base_url : String | URI, current_page : Int, show_next : Bool = true)
return String.build do |str|
String.build do |str|
str << %(<div class="h-box">\n)
str << %(<div class="page-nav-container flexible">\n)
@ -70,7 +70,7 @@ module Invidious::Frontend::Pagination
params_prev = URI::Params{"page" => (current_page - 1).to_s}
url_prev = HttpServer::Utils.add_params_to_url(base_url, params_prev)
self.previous_page(str, locale, url_prev.to_s)
previous_page(str, locale, url_prev.to_s)
end
str << %(</div>\n)
@ -80,7 +80,7 @@ module Invidious::Frontend::Pagination
params_next = URI::Params{"page" => (current_page + 1).to_s}
url_next = HttpServer::Utils.add_params_to_url(base_url, params_next)
self.next_page(str, locale, url_next.to_s)
next_page(str, locale, url_next.to_s)
end
str << %(</div>\n)
@ -91,7 +91,7 @@ module Invidious::Frontend::Pagination
end
def nav_ctoken(locale : String?, *, base_url : String | URI, ctoken : String?, first_page : Bool, params : URI::Params)
return String.build do |str|
String.build do |str|
str << %(<div class="h-box">\n)
str << %(<div class="page-nav-container flexible">\n)
@ -109,7 +109,7 @@ module Invidious::Frontend::Pagination
params["continuation"] = ctoken
url_next = HttpServer::Utils.add_params_to_url(base_url, params)
self.next_page(str, locale, url_next.to_s)
next_page(str, locale, url_next.to_s)
end
str << %(</div>\n)

View File

@ -3,7 +3,7 @@ module Invidious::Frontend::SearchFilters
# Generate the search filters collapsable widget.
def generate(filters : Search::Filters, query : String, page : Int, locale : String) : String
return String.build(8000) do |str|
String.build(8000) do |str|
str << "<div id='filters'>\n"
str << "\t<details id='filters-collapse'>"
str << "\t\t<summary>" << translate(locale, "search_filters_title") << "</summary>\n"

View File

@ -28,12 +28,12 @@ module Invidious::Frontend::WatchPage
end
url = "/download"
if (CONFIG.invidious_companion.present?)
if CONFIG.invidious_companion.present?
invidious_companion = CONFIG.invidious_companion.sample
url = "#{invidious_companion.public_url}/download?check=#{invidious_companion_encrypt(video.id)}"
end
return String.build(4000) do |str|
String.build(4000) do |str|
str << "<form"
str << " class=\"pure-form pure-form-stacked\""
str << " action='#{url}'"

View File

@ -9,7 +9,7 @@ module Invidious::Hashtag
response = YoutubeAPI.browse(continuation: ctoken, client_config: client_config)
items, _ = extract_items(response)
return items
items
end
def generate_continuation(hashtag : String, cursor : Int)
@ -37,6 +37,6 @@ module Invidious::Hashtag
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return continuation
continuation
end
end

View File

@ -15,7 +15,7 @@ def github_details(summary : String, content : String)
details += %(\n```)
details += %(\n</p>)
details += %(\n</details>)
return HTML.escape(details)
HTML.escape(details)
end
def get_issue_template(env : HTTP::Server::Context, exception : Exception) : Tuple(String, String)
@ -61,7 +61,8 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, exce
url_new_issue += "?labels=bug&template=bug_report.md&title="
url_new_issue += URI.encode_www_form("[Bug] " + issue_title)
error_message = <<-END_HTML
# ameba:disable Lint/UselessAssign
error_message = <<-HTML
<div class="error_message">
<h2>#{translate(locale, "crash_page_you_found_a_bug")}</h2>
<br/><br/>
@ -80,13 +81,14 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, exce
<!-- TODO: Add a "copy to clipboard" button -->
<pre class="error-issue-template">#{issue_template}</pre>
</div>
END_HTML
HTML
# Don't show the usual "next steps" widget. The same options are
# proposed above the error message, just worded differently.
# ameba:disable Lint/UselessAssign
next_steps = ""
return templated "error"
templated "error"
end
def error_template_helper(env : HTTP::Server::Context, status_code : Int32, message : String)
@ -95,10 +97,12 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, mess
locale = env.get("preferences").as(Preferences).locale
# ameba:disable Lint/UselessAssign
error_message = translate(locale, message)
# ameba:disable Lint/UselessAssign
next_steps = error_redirect_helper(env)
return templated "error"
templated "error"
end
# -------------------
@ -117,14 +121,14 @@ def error_atom_helper(env : HTTP::Server::Context, status_code : Int32, exceptio
env.response.content_type = "application/atom+xml"
env.response.status_code = status_code
return "<error>#{exception.inspect_with_backtrace}</error>"
"<error>#{exception.inspect_with_backtrace}</error>"
end
def error_atom_helper(env : HTTP::Server::Context, status_code : Int32, message : String)
env.response.content_type = "application/atom+xml"
env.response.status_code = status_code
return "<error>#{message}</error>"
"<error>#{message}</error>"
end
# -------------------
@ -139,7 +143,7 @@ def error_json_helper(
env : HTTP::Server::Context,
status_code : Int32,
exception : Exception,
additional_fields : Hash(String, Object) | Nil = nil,
additional_fields : Hash(String, Object)? = nil,
)
if exception.is_a?(InfoException)
return error_json_helper(env, status_code, exception.message || "", additional_fields)
@ -154,14 +158,14 @@ def error_json_helper(
error_message = error_message.merge(additional_fields)
end
return error_message.to_json
error_message.to_json
end
def error_json_helper(
env : HTTP::Server::Context,
status_code : Int32,
message : String,
additional_fields : Hash(String, Object) | Nil = nil,
additional_fields : Hash(String, Object)? = nil,
)
env.response.content_type = "application/json"
env.response.status_code = status_code
@ -172,7 +176,7 @@ def error_json_helper(
error_message = error_message.merge(additional_fields)
end
return error_message.to_json
error_message.to_json
end
# -------------------
@ -191,7 +195,7 @@ def error_redirect_helper(env : HTTP::Server::Context)
go_to_youtube = translate(locale, "next_steps_error_message_go_to_youtube")
switch_instance = translate(locale, "Switch Invidious Instance")
return <<-END_HTML
<<-HTML
<p style="margin-bottom: 4px;">#{next_steps_text}</p>
<ul>
<li>
@ -204,8 +208,8 @@ def error_redirect_helper(env : HTTP::Server::Context)
<a rel="noreferrer noopener" href="https://youtube.com#{env.request.resource}">#{go_to_youtube}</a>
</li>
</ul>
END_HTML
HTML
else
return ""
""
end
end

View File

@ -20,7 +20,7 @@ module HTTP::Handler
end
class Kemal::RouteHandler
{% for method in %w(GET POST PUT HEAD DELETE PATCH OPTIONS) %}
{% for method in %w[GET POST PUT HEAD DELETE PATCH OPTIONS] %}
exclude ["/api/v1/*"], {{ method }}
{% end %}
@ -44,7 +44,7 @@ class Kemal::RouteHandler
end
class Kemal::ExceptionHandler
{% for method in %w(GET POST PUT HEAD DELETE PATCH OPTIONS) %}
{% for method in %w[GET POST PUT HEAD DELETE PATCH OPTIONS] %}
exclude ["/api/v1/*"], {{ method }}
{% end %}
@ -72,7 +72,7 @@ class FilteredCompressHandler < HTTP::CompressHandler
end
class AuthHandler < Kemal::Handler
{% for method in %w(GET POST PUT HEAD DELETE PATCH OPTIONS) %}
{% for method in %w[GET POST PUT HEAD DELETE PATCH OPTIONS] %}
only ["/api/v1/auth/*"], {{ method }}
{% end %}
@ -121,7 +121,7 @@ class AuthHandler < Kemal::Handler
end
class APIHandler < Kemal::Handler
{% for method in %w(GET POST PUT HEAD DELETE PATCH OPTIONS) %}
{% for method in %w[GET POST PUT HEAD DELETE PATCH OPTIONS] %}
only ["/api/v1/*"], {{ method }}
{% end %}
exclude ["/api/v1/auth/notifications"], "GET"

View File

@ -32,7 +32,7 @@ def html_to_content(description_html : String)
description = XML.parse_html(description).content.strip("\n ")
end
return description
description
end
def cache_annotation(id, annotations)
@ -165,7 +165,7 @@ def create_notification_stream(env, topics, connection_channel)
end
def extract_initial_data(body) : Hash(String, JSON::Any)
return JSON.parse(body.match(/(window\["ytInitialData"\]|var\s*ytInitialData)\s*=\s*(?<info>{.*?});<\/script>/mx).try &.["info"] || "{}").as_h
JSON.parse(body.match(/(window\["ytInitialData"\]|var\s*ytInitialData)\s*=\s*(?<info>{.*?});<\/script>/mx).try &.["info"] || "{}").as_h
end
def proxy_file(response, env)
@ -196,5 +196,5 @@ def get_playback_statistic
Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"] = tracker
end
return tracker.as(Hash(String, Int64 | Float64))
tracker.as(Hash(String, Int64 | Float64))
end

View File

@ -91,10 +91,10 @@ def load_all_locales
locales[name] = JSON.parse(File.read("locales/#{name}.json")).as_h
end
return locales
locales
end
def translate(locale : String?, key : String, text : String | Hash(String, String) | Nil = nil) : String
def translate(locale : String?, key : String, text : String | Hash(String, String)? = nil) : String
# Log a warning if "key" doesn't exist in en-US locale and return
# that key as the text, so this is more or less transparent to the user.
if !LOCALES["en-US"].has_key?(key)
@ -141,7 +141,7 @@ def translate(locale : String?, key : String, text : String | Hash(String, Strin
end
end
return translation
translation
end
def translate_count(locale : String, key : String, count : Int, format = NumberFormatting::None) : String
@ -177,15 +177,15 @@ def translate_count(locale : String, key : String, count : Int, format = NumberF
else count_txt = count.to_s
end
return translation.gsub("{{count}}", count_txt)
translation.gsub("{{count}}", count_txt)
end
def translate_bool(locale : String?, translation : Bool)
case translation
when true
return translate(locale, "Yes")
translate(locale, "Yes")
when false
return translate(locale, "No")
translate(locale, "No")
end
end
@ -195,5 +195,5 @@ def locale_is_rtl?(locale : String?)
# Arabic, Persian, Hebrew
# See https://en.wikipedia.org/wiki/Right-to-left_script#List_of_RTL_scripts
return {"ar", "fa", "he"}.includes? locale
{"ar", "fa", "he"}.includes? locale
end

View File

@ -151,17 +151,17 @@ module I18next::Plurals
@version = version.to_u8
end
self.init_rules
init_rules
end
def init_rules
# Look into sets
PLURAL_SETS.each do |form, langs|
langs.each { |lang| self.forms[lang] = form }
langs.each { |lang| forms[lang] = form }
end
# Add plurals from the "singles" set
self.forms.merge!(PLURAL_SINGLES)
forms.merge!(PLURAL_SINGLES)
end
def get_plural_form(locale : String) : PluralForms
@ -170,12 +170,12 @@ module I18next::Plurals
locale = locale.split('-')[0]
end
return self.forms[locale] if self.forms[locale]?
return forms[locale] if forms[locale]?
# If nothing was found, then use the most common form, i.e
# one singular and one plural, as in english. Not perfect,
# but better than yielding an exception at the user.
return PluralForms::Single_not_one
PluralForms::Single_not_one
end
def get_suffix(locale : String, count : Int) : String
@ -183,19 +183,19 @@ module I18next::Plurals
# determine if comparison should be done on a signed or unsigned integer,
# but this variable is never set, resulting in the comparison always
# being done on absolute numbers.
return get_suffix_retrocompat(locale, count.abs)
get_suffix_retrocompat(locale, count.abs)
end
# Emulate the `rule.numbers.size == 2 && rule.numbers[0] == 1` check
# from original i18next code
private def simple_plural?(form : PluralForms) : Bool
case form
when .single_gt_one? then return true
when .single_not_one? then return true
when .special_icelandic? then return true
when .special_macedonian? then return true
when .single_gt_one? then true
when .single_not_one? then true
when .special_icelandic? then true
when .special_macedonian? then true
else
return false
false
end
end
@ -226,7 +226,7 @@ module I18next::Plurals
# when 2
# return "_#{suffix}"
# else # v3
return "_#{idx}"
"_#{idx}"
# end
end
end
@ -238,35 +238,35 @@ module I18next::Plurals
module SuffixIndex
def self.get_index(plural_form : PluralForms, count : Int) : UInt8
case plural_form
when .single_gt_one? then return (count > 1) ? 1_u8 : 0_u8
when .single_not_one? then return (count != 1) ? 1_u8 : 0_u8
when .none? then return 0_u8
when .dual_slavic? then return dual_slavic(count)
when .special_arabic? then return special_arabic(count)
when .special_czech_slovak? then return special_czech_slovak(count)
when .special_polish_kashubian? then return special_polish_kashubian(count)
when .special_welsh? then return special_welsh(count)
when .special_irish? then return special_irish(count)
when .special_scottish_gaelic? then return special_scottish_gaelic(count)
when .special_icelandic? then return special_icelandic(count)
when .special_javanese? then return special_javanese(count)
when .special_cornish? then return special_cornish(count)
when .special_lithuanian? then return special_lithuanian(count)
when .special_latvian? then return special_latvian(count)
when .special_macedonian? then return special_macedonian(count)
when .special_mandinka? then return special_mandinka(count)
when .special_maltese? then return special_maltese(count)
when .special_romanian? then return special_romanian(count)
when .special_slovenian? then return special_slovenian(count)
when .special_hebrew? then return special_hebrew(count)
when .special_odia? then return special_odia(count)
when .single_gt_one? then (count > 1) ? 1_u8 : 0_u8
when .single_not_one? then (count != 1) ? 1_u8 : 0_u8
when .none? then 0_u8
when .dual_slavic? then dual_slavic(count)
when .special_arabic? then special_arabic(count)
when .special_czech_slovak? then special_czech_slovak(count)
when .special_polish_kashubian? then special_polish_kashubian(count)
when .special_welsh? then special_welsh(count)
when .special_irish? then special_irish(count)
when .special_scottish_gaelic? then special_scottish_gaelic(count)
when .special_icelandic? then special_icelandic(count)
when .special_javanese? then special_javanese(count)
when .special_cornish? then special_cornish(count)
when .special_lithuanian? then special_lithuanian(count)
when .special_latvian? then special_latvian(count)
when .special_macedonian? then special_macedonian(count)
when .special_mandinka? then special_mandinka(count)
when .special_maltese? then special_maltese(count)
when .special_romanian? then special_romanian(count)
when .special_slovenian? then special_slovenian(count)
when .special_hebrew? then special_hebrew(count)
when .special_odia? then special_odia(count)
# Mixed v3/v4 forms
when .special_spanish_italian? then return special_cldr_spanish_italian(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_spanish_italian? then special_cldr_spanish_italian(count)
when .special_french_portuguese? then special_cldr_french_portuguese(count)
when .special_hungarian_serbian? then special_cldr_hungarian_serbian(count)
else
# default, if nothing matched above
return 0_u8
0_u8
end
end
@ -280,11 +280,11 @@ module I18next::Plurals
n_mod_100 = count % 100
if n_mod_10 == 1 && n_mod_100 != 11
return 0_u8
0_u8
elsif n_mod_10 >= 2 && n_mod_10 <= 4 && (n_mod_100 < 10 || n_mod_100 >= 20)
return 1_u8
1_u8
else
return 2_u8
2_u8
end
end
@ -294,13 +294,13 @@ module I18next::Plurals
# Rule: (n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5)
#
def self.special_arabic(count : Int) : UInt8
return count.to_u8 if (count == 0 || count == 1 || count == 2)
return count.to_u8 if count == 0 || count == 1 || count == 2
n_mod_100 = count % 100
return 3_u8 if (n_mod_100 >= 3 && n_mod_100 <= 10)
return 4_u8 if (n_mod_100 >= 11)
return 5_u8
return 3_u8 if n_mod_100 >= 3 && n_mod_100 <= 10
return 4_u8 if n_mod_100 >= 11
5_u8
end
# Plural form for Czech and Slovak languages
@ -309,9 +309,9 @@ module I18next::Plurals
# Rule: ((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2)
#
def self.special_czech_slovak(count : Int) : UInt8
return 0_u8 if (count == 1)
return 1_u8 if (count >= 2 && count <= 4)
return 2_u8
return 0_u8 if count == 1
return 1_u8 if count >= 2 && count <= 4
2_u8
end
# Plural form for Polish and Kashubian languages
@ -320,15 +320,15 @@ module I18next::Plurals
# Rule: (n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)
#
def self.special_polish_kashubian(count : Int) : UInt8
return 0_u8 if (count == 1)
return 0_u8 if count == 1
n_mod_10 = count % 10
n_mod_100 = count % 100
if n_mod_10 >= 2 && n_mod_10 <= 4 && (n_mod_100 < 10 || n_mod_100 >= 20)
return 1_u8
1_u8
else
return 2_u8
2_u8
end
end
@ -338,10 +338,10 @@ module I18next::Plurals
# Rule: ((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3)
#
def self.special_welsh(count : Int) : UInt8
return 0_u8 if (count == 1)
return 1_u8 if (count == 2)
return 2_u8 if (count != 8 && count != 11)
return 3_u8
return 0_u8 if count == 1
return 1_u8 if count == 2
return 2_u8 if count != 8 && count != 11
3_u8
end
# Plural form for Irish language
@ -350,11 +350,11 @@ module I18next::Plurals
# Rule: (n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4)
#
def self.special_irish(count : Int) : UInt8
return 0_u8 if (count == 1)
return 1_u8 if (count == 2)
return 2_u8 if (count < 7)
return 3_u8 if (count < 11)
return 4_u8
return 0_u8 if count == 1
return 1_u8 if count == 2
return 2_u8 if count < 7
return 3_u8 if count < 11
4_u8
end
# Plural form for Gaelic language
@ -363,10 +363,10 @@ module I18next::Plurals
# Rule: ((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3)
#
def self.special_scottish_gaelic(count : Int) : UInt8
return 0_u8 if (count == 1 || count == 11)
return 1_u8 if (count == 2 || count == 12)
return 2_u8 if (count > 2 && count < 20)
return 3_u8
return 0_u8 if count == 1 || count == 11
return 1_u8 if count == 2 || count == 12
return 2_u8 if count > 2 && count < 20
3_u8
end
# Plural form for Icelandic language
@ -376,9 +376,9 @@ module I18next::Plurals
#
def self.special_icelandic(count : Int) : UInt8
if (count % 10) != 1 || (count % 100) == 11
return 1_u8
1_u8
else
return 0_u8
0_u8
end
end
@ -388,7 +388,7 @@ module I18next::Plurals
# Rule: (n !== 0)
#
def self.special_javanese(count : Int) : UInt8
return (count != 0) ? 1_u8 : 0_u8
(count != 0) ? 1_u8 : 0_u8
end
# Plural form for Cornish language
@ -400,7 +400,7 @@ module I18next::Plurals
return 0_u8 if count == 1
return 1_u8 if count == 2
return 2_u8 if count == 3
return 3_u8
3_u8
end
# Plural form for Lithuanian language
@ -413,11 +413,11 @@ module I18next::Plurals
n_mod_100 = count % 100
if n_mod_10 == 1 && n_mod_100 != 11
return 0_u8
0_u8
elsif n_mod_10 >= 2 && (n_mod_100 < 10 || n_mod_100 >= 20)
return 1_u8
1_u8
else
return 2_u8
2_u8
end
end
@ -428,11 +428,11 @@ module I18next::Plurals
#
def self.special_latvian(count : Int) : UInt8
if (count % 10) == 1 && (count % 100) != 11
return 0_u8
0_u8
elsif count != 0
return 1_u8
1_u8
else
return 2_u8
2_u8
end
end
@ -443,9 +443,9 @@ module I18next::Plurals
#
def self.special_macedonian(count : Int) : UInt8
if count == 1 || ((count % 10) == 1 && (count % 100) != 11)
return 0_u8
0_u8
else
return 1_u8
1_u8
end
end
@ -455,7 +455,7 @@ module I18next::Plurals
# Rule: (n==0 ? 0 : n==1 ? 1 : 2)
#
def self.special_mandinka(count : Int) : UInt8
return (count == 0 || count == 1) ? count.to_u8 : 2_u8
(count == 0 || count == 1) ? count.to_u8 : 2_u8
end
# Plural form for Maltese language
@ -468,9 +468,9 @@ module I18next::Plurals
return 1_u8 if count == 0
n_mod_100 = count % 100
return 1_u8 if (n_mod_100 > 1 && n_mod_100 < 11)
return 2_u8 if (n_mod_100 > 10 && n_mod_100 < 20)
return 3_u8
return 1_u8 if n_mod_100 > 1 && n_mod_100 < 11
return 2_u8 if n_mod_100 > 10 && n_mod_100 < 20
3_u8
end
# Plural form for Romanian language
@ -483,8 +483,8 @@ module I18next::Plurals
return 1_u8 if count == 0
n_mod_100 = count % 100
return 1_u8 if (n_mod_100 > 0 && n_mod_100 < 20)
return 2_u8
return 1_u8 if n_mod_100 > 0 && n_mod_100 < 20
2_u8
end
# Plural form for Slovenian language
@ -494,10 +494,10 @@ module I18next::Plurals
#
def self.special_slovenian(count : Int) : UInt8
n_mod_100 = count % 100
return 1_u8 if (n_mod_100 == 1)
return 2_u8 if (n_mod_100 == 2)
return 3_u8 if (n_mod_100 == 3 || n_mod_100 == 4)
return 0_u8
return 1_u8 if n_mod_100 == 1
return 2_u8 if n_mod_100 == 2
return 3_u8 if n_mod_100 == 3 || n_mod_100 == 4
0_u8
end
# Plural form for Hebrew language
@ -506,13 +506,13 @@ module I18next::Plurals
# Rule: (n==1 ? 0 : n==2 ? 1 : (n<0 || n>10) && n%10==0 ? 2 : 3)
#
def self.special_hebrew(count : Int) : UInt8
return 0_u8 if (count == 1)
return 1_u8 if (count == 2)
return 0_u8 if count == 1
return 1_u8 if count == 2
if (count < 0 || count > 10) && (count % 10) == 0
return 2_u8
2_u8
else
return 3_u8
3_u8
end
end
@ -523,7 +523,7 @@ module I18next::Plurals
# special rule for it.
#
def self.special_odia(count : Int) : UInt8
return (count == 1) ? 0_u8 : 1_u8
(count == 1) ? 0_u8 : 1_u8
end
# -------------------
@ -535,9 +535,9 @@ module I18next::Plurals
# This rule is mostly compliant to CLDR v42
#
def self.special_cldr_spanish_italian(count : Int) : UInt8
return 0_u8 if (count == 1) # one
return 1_u8 if (count != 0 && count % 1_000_000 == 0) # many
return 2_u8 # other
return 0_u8 if count == 1 # one
return 1_u8 if count != 0 && count % 1_000_000 == 0 # many
2_u8 # other
end
# Plural form for French and Portuguese
@ -545,9 +545,9 @@ module I18next::Plurals
# This rule is mostly compliant to CLDR v42
#
def self.special_cldr_french_portuguese(count : Int) : UInt8
return 0_u8 if (count == 0 || count == 1) # one
return 1_u8 if (count % 1_000_000 == 0) # many
return 2_u8 # other
return 0_u8 if count == 0 || count == 1 # one
return 1_u8 if count % 1_000_000 == 0 # many
2_u8 # other
end
# Plural form for Hungarian and Serbian
@ -558,9 +558,9 @@ module I18next::Plurals
n_mod_10 = count % 10
n_mod_100 = count % 100
return 0_u8 if (n_mod_10 == 1 && n_mod_100 != 11) # one
return 1_u8 if (2 <= n_mod_10 <= 4 && (n_mod_100 < 12 || 14 < n_mod_100)) # few
return 2_u8 # other
return 0_u8 if n_mod_10 == 1 && n_mod_100 != 11 # one
return 1_u8 if 2 <= n_mod_10 <= 4 && (n_mod_100 < 12 || 14 < n_mod_100) # few
2_u8 # other
end
end
end

View File

@ -55,7 +55,7 @@ class Invidious::LogHandler < Kemal::BaseLogHandler
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)
if LogLevel::{{ level.id.capitalize }} >= @level
puts("#{Time.utc} [{{ level.id }}] #{message}".colorize(color(LogLevel::{{ level.id.capitalize }})))

View File

@ -28,19 +28,19 @@ struct SearchVideo
property badges : VideoBadges
def to_xml(auto_generated, query_params, xml : XML::Builder)
query_params["v"] = self.id
query_params["v"] = id
xml.element("entry") do
xml.element("id") { xml.text "yt:video:#{self.id}" }
xml.element("yt:videoId") { xml.text self.id }
xml.element("yt:channelId") { xml.text self.ucid }
xml.element("title") { xml.text self.title }
xml.element("id") { xml.text "yt:video:#{id}" }
xml.element("yt:videoId") { xml.text id }
xml.element("yt:channelId") { xml.text ucid }
xml.element("title") { xml.text title }
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?#{query_params}")
xml.element("author") do
if auto_generated
xml.element("name") { xml.text self.author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
xml.element("name") { xml.text author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
else
xml.element("name") { xml.text author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
@ -50,24 +50,24 @@ struct SearchVideo
xml.element("content", type: "xhtml") do
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
xml.element("a", href: "#{HOST_URL}/watch?#{query_params}") do
xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
xml.element("img", src: "#{HOST_URL}/vi/#{id}/mqdefault.jpg")
end
xml.element("p", style: "word-break:break-word;white-space:pre-wrap") { xml.text html_to_content(self.description_html) }
xml.element("p", style: "word-break:break-word;white-space:pre-wrap") { xml.text html_to_content(description_html) }
end
end
xml.element("published") { xml.text self.published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("published") { xml.text published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("media:group") do
xml.element("media:title") { xml.text self.title }
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
xml.element("media:title") { xml.text title }
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{id}/mqdefault.jpg",
width: "320", height: "180")
xml.element("media:description") { xml.text html_to_content(self.description_html) }
xml.element("media:description") { xml.text html_to_content(description_html) }
end
xml.element("media:community") do
xml.element("media:statistics", views: self.views)
xml.element("media:statistics", views: views)
end
end
end
@ -81,13 +81,13 @@ struct SearchVideo
def to_json(locale : String?, json : JSON::Builder)
json.object do
json.field "type", "video"
json.field "title", self.title
json.field "videoId", self.id
json.field "title", title
json.field "videoId", id
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
json.field "authorVerified", self.author_verified
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", "/channel/#{ucid}"
json.field "authorVerified", author_verified
author_thumbnail = self.author_thumbnail
@ -108,31 +108,31 @@ struct SearchVideo
end
json.field "videoThumbnails" do
Invidious::JSONify::APIv1.thumbnails(json, self.id)
Invidious::JSONify::APIv1.thumbnails(json, id)
end
json.field "description", html_to_content(self.description_html)
json.field "descriptionHtml", self.description_html
json.field "description", html_to_content(description_html)
json.field "descriptionHtml", description_html
json.field "viewCount", self.views
json.field "viewCountText", translate_count(locale, "generic_views_count", self.views, NumberFormatting::Short)
json.field "published", self.published.to_unix
json.field "publishedText", translate(locale, "`x` ago", recode_date(self.published, locale))
json.field "lengthSeconds", self.length_seconds
json.field "liveNow", self.badges.live_now?
json.field "premium", self.badges.premium?
json.field "isUpcoming", self.upcoming?
json.field "viewCount", views
json.field "viewCountText", translate_count(locale, "generic_views_count", views, NumberFormatting::Short)
json.field "published", published.to_unix
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
json.field "lengthSeconds", length_seconds
json.field "liveNow", badges.live_now?
json.field "premium", badges.premium?
json.field "isUpcoming", upcoming?
if self.premiere_timestamp
json.field "premiereTimestamp", self.premiere_timestamp.try &.to_unix
if premiere_timestamp
json.field "premiereTimestamp", premiere_timestamp.try &.to_unix
end
json.field "isNew", self.badges.new?
json.field "is4k", self.badges.four_k?
json.field "is8k", self.badges.eight_k?
json.field "isVr180", self.badges.vr180?
json.field "isVr360", self.badges.vr360?
json.field "is3d", self.badges.three_d?
json.field "hasCaptions", self.badges.closed_captions?
json.field "isNew", badges.new?
json.field "is4k", badges.four_k?
json.field "is8k", badges.eight_k?
json.field "isVr180", badges.vr180?
json.field "isVr360", badges.vr360?
json.field "is3d", badges.three_d?
json.field "hasCaptions", badges.closed_captions?
end
end
@ -175,20 +175,20 @@ struct SearchPlaylist
def to_json(locale : String?, json : JSON::Builder)
json.object do
json.field "type", "playlist"
json.field "title", self.title
json.field "playlistId", self.id
json.field "playlistThumbnail", self.thumbnail
json.field "title", title
json.field "playlistId", id
json.field "playlistThumbnail", thumbnail
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", "/channel/#{ucid}"
json.field "authorVerified", self.author_verified
json.field "authorVerified", author_verified
json.field "videoCount", self.video_count
json.field "videoCount", video_count
json.field "videos" do
json.array do
self.videos.each do |video|
videos.each do |video|
json.object do
json.field "title", video.title
json.field "videoId", video.id
@ -232,17 +232,17 @@ struct SearchChannel
def to_json(locale : String?, json : JSON::Builder)
json.object do
json.field "type", "channel"
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
json.field "authorVerified", self.author_verified
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", "/channel/#{ucid}"
json.field "authorVerified", author_verified
json.field "authorThumbnails" do
json.array do
qualities = {32, 48, 76, 100, 176, 512}
qualities.each do |quality|
json.object do
json.field "url", self.author_thumbnail.gsub(/=s\d+/, "=s#{quality}")
json.field "url", author_thumbnail.gsub(/=s\d+/, "=s#{quality}")
json.field "width", quality
json.field "height", quality
end
@ -250,13 +250,13 @@ struct SearchChannel
end
end
json.field "autoGenerated", self.auto_generated
json.field "subCount", self.subscriber_count
json.field "videoCount", self.video_count
json.field "channelHandle", self.channel_handle
json.field "autoGenerated", auto_generated
json.field "subCount", subscriber_count
json.field "videoCount", video_count
json.field "channelHandle", channel_handle
json.field "description", html_to_content(self.description_html)
json.field "descriptionHtml", self.description_html
json.field "description", html_to_content(description_html)
json.field "descriptionHtml", description_html
end
end
@ -283,10 +283,10 @@ struct SearchHashtag
def to_json(locale : String?, json : JSON::Builder)
json.object do
json.field "type", "hashtag"
json.field "title", self.title
json.field "url", self.url
json.field "videoCount", self.video_count
json.field "channelCount", self.channel_count
json.field "title", title
json.field "url", url
json.field "videoCount", video_count
json.field "channelCount", channel_count
end
end
end
@ -315,7 +315,7 @@ struct ProblematicTimelineItem
# Provides compatibility with PlaylistVideo
def to_json(json : JSON::Builder, *args, **kwargs)
return to_json("", json)
to_json("", json)
end
def to_xml(env, locale, xml : XML::Builder)
@ -352,10 +352,10 @@ class Category
def to_json(locale : String?, json : JSON::Builder)
json.object do
json.field "type", "category"
json.field "title", self.title
json.field "title", title
json.field "contents" do
json.array do
self.contents.each do |item|
contents.each do |item|
item.to_json(locale, json)
end
end

View File

@ -16,7 +16,7 @@ def generate_token(email, scopes, expire, key)
token["signature"] = sign_token(key, token)
return token.to_json
token.to_json
end
def generate_response(session, scopes, key, expire = 6.hours, use_nonce = false)
@ -36,7 +36,7 @@ def generate_response(session, scopes, key, expire = 6.hours, use_nonce = false)
token["signature"] = sign_token(key, token)
return token.to_json
token.to_json
end
def sign_token(key, hash)
@ -45,6 +45,7 @@ def sign_token(key, hash)
# TODO: figure out which "key" variable is used
# Ameba reports a warning for "Lint/ShadowingOuterLocalVar" on this
# variable, but it's preferable to not touch that (works fine atm).
# ameba:disable Lint/ShadowingOuterLocalVar
hash.each do |key, value|
next if key == "signature"
@ -63,7 +64,7 @@ def sign_token(key, hash)
end
string_to_sign = string_to_sign.sort.join("\n")
return Base64.urlsafe_encode(OpenSSL::HMAC.digest(:sha256, key, string_to_sign)).strip
Base64.urlsafe_encode(OpenSSL::HMAC.digest(:sha256, key, string_to_sign)).strip
end
def validate_request(token, session, request, key, locale = nil)
@ -116,7 +117,7 @@ def scope_includes_scope(scope, subset)
subset_endpoint = subset_endpoint.downcase
if methods.empty?
methods = %w(GET POST PUT HEAD DELETE PATCH OPTIONS)
methods = %w[GET POST PUT HEAD DELETE PATCH OPTIONS]
end
if methods & subset_methods != subset_methods
@ -131,7 +132,7 @@ def scope_includes_scope(scope, subset)
return false
end
return true
true
end
def scopes_include_scope(scopes, subset)
@ -141,5 +142,5 @@ def scopes_include_scope(scopes, subset)
end
end
return false
false
end

View File

@ -8,7 +8,7 @@ def ci_lower_bound(pos, n)
z = 1.96
phat = 1.0*pos/n
return (phat + z*z/(2*n) - z * Math.sqrt((phat*(1 - phat) + z*z/(4*n))/n))/(1 + z*z/n)
(phat + z*z/(2*n) - z * Math.sqrt((phat*(1 - phat) + z*z/(4*n))/n))/(1 + z*z/n)
end
def elapsed_text(elapsed)
@ -31,12 +31,12 @@ def decode_length_seconds(string)
seconds: length_seconds[2]
).total_seconds.to_i32
return length_seconds
length_seconds
end
def recode_length_seconds(time)
if time <= 0
return ""
""
else
time = time.seconds
text = "#{time.minutes.to_s.rjust(2, '0')}:#{time.seconds.to_s.rjust(2, '0')}"
@ -47,7 +47,7 @@ def recode_length_seconds(time)
text = text.lchop('0')
return text
text
end
end
@ -66,7 +66,7 @@ def decode_interval(string : String) : Time::Span
time = Time::Span.new(minutes: raw_minutes)
end
return time
time
end
def decode_time(string)
@ -88,7 +88,7 @@ def decode_time(string)
time = hours * 3600 + minutes * 60 + seconds + millis // 1000
end
return time
time
end
def decode_date(string : String)
@ -108,7 +108,6 @@ def decode_date(string : String)
return Time.utc
when "yesterday"
return Time.utc - 1.day
else nil # Continue
end
# String matches format "20 hours ago", "4 months ago", "20s ago", "15min ago"...
@ -137,26 +136,26 @@ def decode_date(string : String)
raise "Could not parse #{string}"
end
return Time.utc - delta
Time.utc - delta
end
def recode_date(time : Time, locale)
span = Time.utc - time
if span.total_days > 365.0
return translate_count(locale, "generic_count_years", span.total_days.to_i // 365)
translate_count(locale, "generic_count_years", span.total_days.to_i // 365)
elsif span.total_days > 30.0
return translate_count(locale, "generic_count_months", span.total_days.to_i // 30)
translate_count(locale, "generic_count_months", span.total_days.to_i // 30)
elsif span.total_days > 7.0
return translate_count(locale, "generic_count_weeks", span.total_days.to_i // 7)
translate_count(locale, "generic_count_weeks", span.total_days.to_i // 7)
elsif span.total_hours > 24.0
return translate_count(locale, "generic_count_days", span.total_days.to_i)
translate_count(locale, "generic_count_days", span.total_days.to_i)
elsif span.total_minutes > 60.0
return translate_count(locale, "generic_count_hours", span.total_hours.to_i)
translate_count(locale, "generic_count_hours", span.total_hours.to_i)
elsif span.total_seconds > 60.0
return translate_count(locale, "generic_count_minutes", span.total_minutes.to_i)
translate_count(locale, "generic_count_minutes", span.total_minutes.to_i)
else
return translate_count(locale, "generic_count_seconds", span.total_seconds.to_i)
translate_count(locale, "generic_count_seconds", span.total_seconds.to_i)
end
end
@ -174,9 +173,9 @@ def short_text_to_number(short_text : String) : Int64
when "b" then number *= 1_000_000_000
end
return number.to_i64
number.to_i64
rescue ex
return 0_i64
0_i64
end
def number_to_short_text(number)
@ -209,7 +208,7 @@ def arg_array(array, start = 1)
args = args.join(",")
end
return args
args
end
def make_host_url(kemal_config)
@ -235,7 +234,7 @@ def make_host_url(kemal_config)
host = CONFIG.domain.not_nil!.lchop(".")
return "#{scheme}#{host}#{port}"
"#{scheme}#{host}#{port}"
end
def get_referer(env, fallback = "/", unroll = true)
@ -268,13 +267,13 @@ def get_referer(env, fallback = "/", unroll = true)
referer = fallback
end
return referer
referer
end
def sha256(text)
digest = OpenSSL::Digest.new("SHA256")
digest << text
return digest.final.hexstring
digest.final.hexstring
end
def subscribe_pubsub(topic, key)
@ -302,7 +301,7 @@ def subscribe_pubsub(topic, key)
"hub.secret" => key.to_s,
}
return make_client(PUBSUB_URL, &.post("/subscribe", form: body))
make_client(PUBSUB_URL, &.post("/subscribe", form: body))
end
def parse_range(range)
@ -328,7 +327,7 @@ def reduce_uri(uri : URI | String, max_length : Int32 = 50, suffix : String = "
if str.size > max_length
str = "#{str[0, max_length]}#{suffix}"
end
return str
str
end
# Get the html link from a NavigationEndpoint or an innertubeCommand
@ -381,7 +380,7 @@ def parse_link_endpoint(endpoint : JSON::Any, text : String, video_id : String)
text = %(<a href="#{url}">#{reduce_uri(text)}</a>)
end
end
return text
text
end
def encrypt_ecb_without_salt(data, key)
@ -394,11 +393,11 @@ def encrypt_ecb_without_salt(data, key)
io.write(cipher.final)
io.rewind
return io
io
end
def invidious_companion_encrypt(data)
timestamp = Time.utc.to_unix
encrypted_data = encrypt_ecb_without_salt("#{timestamp}|#{data}", CONFIG.invidious_companion_key)
return Base64.urlsafe_encode(encrypted_data)
Base64.urlsafe_encode(encrypted_data)
end

View File

@ -20,7 +20,7 @@ module WebVTT
# Writes an vtt cue with the specified time stamp and contents
def cue(start_time : Time::Span, end_time : Time::Span, text : String)
timestamp(start_time, end_time)
@io << self.escape(text)
@io << escape(text)
@io << "\n\n"
end
@ -40,7 +40,7 @@ module WebVTT
end
private def escape(text : String) : String
return text.gsub(ESCAPE_SUBSTITUTIONS)
text.gsub(ESCAPE_SUBSTITUTIONS)
end
def document(setting_fields : Hash(String, String)? = nil, &)

View File

@ -68,7 +68,7 @@ module Invidious::HttpServer
end
end
return flush_io_to_cache(retrieve_bytes_from, file_path, file_info)
flush_io_to_cache(retrieve_bytes_from, file_path, file_info)
end
# Writes file data to the cache
@ -114,7 +114,7 @@ module Invidious::HttpServer
# This is only used in the specs to clear the cache before each handler test
def self.clear_cache
@@current_cache_size = 0
return @@cached_files.clear
@@cached_files.clear
end
end
end

View File

@ -14,9 +14,9 @@ module Invidious::HttpServer
url.query_params = params
if absolute
return "#{HOST_URL}#{url.request_target}"
"#{HOST_URL}#{url.request_target}"
else
return url.request_target
url.request_target
end
end
@ -35,7 +35,7 @@ module Invidious::HttpServer
str << params
end
return url
url
end
end
end

View File

@ -25,7 +25,7 @@ class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
# - Is it an instance with a good uptime?
# - Is it an updated instance?
private def refresh_instances
raw_instance_list = self.fetch_instances
raw_instance_list = fetch_instances
filtered_instance_list = [] of Tuple(String, String)
raw_instance_list.each do |instance_data|
@ -73,7 +73,7 @@ class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
raw_instance_list = [] of JSON::Any
end
return raw_instance_list
raw_instance_list
end
# Checks if the given target instance is outdated
@ -84,7 +84,7 @@ class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
remote_commit_date = Time.parse(remote_commit_date[0], "%Y.%m.%d", Time::Location::UTC)
local_commit_date = Time.parse(CURRENT_VERSION, "%Y.%m.%d", Time::Location::UTC)
return (remote_commit_date - local_commit_date).abs.days > 30
(remote_commit_date - local_commit_date).abs.days > 30
end
# Checks if the uptime of the target instance is greater than 90% over a 30 day period
@ -92,6 +92,6 @@ class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
return true if !target_instance_health_monitor["down"].as_bool == false
return true if target_instance_health_monitor["uptime"].as_f < 90
return false
false
end
end

View File

@ -13,10 +13,10 @@ module Invidious::JSONify::APIv1
json.field "error", video.info["reason"] if video.info["reason"]?
json.field "videoThumbnails" do
self.thumbnails(json, video.id)
thumbnails(json, video.id)
end
json.field "storyboards" do
self.storyboards(json, video.id, video.storyboards)
storyboards(json, video.id, video.storyboards)
end
json.field "description", video.description
@ -138,7 +138,7 @@ module Invidious::JSONify::APIv1
if fmt_info = Invidious::Videos::Formats.itag_to_metadata?(fmt["itag"])
json.field "container", fmt_info["ext"]
json.field "encoding", fmt_info["vcodec"]? || fmt_info["acodec"]
json.field "encoding", (fmt_info["vcodec"]? || fmt_info["acodec"])
end
# Livestream chunk infos
@ -199,7 +199,7 @@ module Invidious::JSONify::APIv1
if fmt_info = Invidious::Videos::Formats.itag_to_metadata?(fmt["itag"])
json.field "container", fmt_info["ext"]
json.field "encoding", fmt_info["vcodec"]? || fmt_info["acodec"]
json.field "encoding", (fmt_info["vcodec"]? || fmt_info["acodec"])
end
end
end
@ -241,7 +241,7 @@ module Invidious::JSONify::APIv1
json.field "videoId", rv["id"]
json.field "title", rv["title"]
json.field "videoThumbnails" do
self.thumbnails(json, rv["id"])
thumbnails(json, rv["id"])
end
json.field "author", rv["author"]

View File

@ -74,7 +74,7 @@ def fetch_mix(rdid, video_id, cookies = nil, locale = nil)
videos.uniq!(&.id)
videos = videos.first(50)
return Mix.new({
Mix.new({
title: mix_title,
id: rdid,
videos: videos,
@ -82,7 +82,7 @@ def fetch_mix(rdid, video_id, cookies = nil, locale = nil)
end
def template_mix(mix, listen)
html = <<-END_HTML
html = <<-HTML
<h3>
<a href="/mix?list=#{mix["mixId"]}">
#{mix["title"]}
@ -90,10 +90,10 @@ def template_mix(mix, listen)
</h3>
<div class="pure-menu pure-menu-scrollable playlist-restricted">
<ol class="pure-menu-list">
END_HTML
HTML
mix["videos"].as_a.each do |video|
html += <<-END_HTML
html += <<-HTML
<li class="pure-menu-item">
<a href="/watch?v=#{video["videoId"]}&list=#{mix["mixId"]}#{listen ? "&listen=1" : ""}">
<div class="thumbnail">
@ -106,14 +106,14 @@ def template_mix(mix, listen)
</p>
</a>
</li>
END_HTML
HTML
end
html += <<-END_HTML
html += <<-HTML
</ol>
</div>
<hr>
END_HTML
HTML
html
end

View File

@ -13,30 +13,30 @@ struct PlaylistVideo
def to_xml(xml : XML::Builder)
xml.element("entry") do
xml.element("id") { xml.text "yt:video:#{self.id}" }
xml.element("yt:videoId") { xml.text self.id }
xml.element("yt:channelId") { xml.text self.ucid }
xml.element("title") { xml.text self.title }
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?v=#{self.id}")
xml.element("id") { xml.text "yt:video:#{id}" }
xml.element("yt:videoId") { xml.text id }
xml.element("yt:channelId") { xml.text ucid }
xml.element("title") { xml.text title }
xml.element("link", rel: "alternate", href: "#{HOST_URL}/watch?v=#{id}")
xml.element("author") do
xml.element("name") { xml.text self.author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{self.ucid}" }
xml.element("name") { xml.text author }
xml.element("uri") { xml.text "#{HOST_URL}/channel/#{ucid}" }
end
xml.element("content", type: "xhtml") do
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
xml.element("a", href: "#{HOST_URL}/watch?v=#{self.id}") do
xml.element("img", src: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg")
xml.element("a", href: "#{HOST_URL}/watch?v=#{id}") do
xml.element("img", src: "#{HOST_URL}/vi/#{id}/mqdefault.jpg")
end
end
end
xml.element("published") { xml.text self.published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("published") { xml.text published.to_s("%Y-%m-%dT%H:%M:%S%:z") }
xml.element("media:group") do
xml.element("media:title") { xml.text self.title }
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{self.id}/mqdefault.jpg",
xml.element("media:title") { xml.text title }
xml.element("media:thumbnail", url: "#{HOST_URL}/vi/#{id}/mqdefault.jpg",
width: "320", height: "180")
end
end
@ -54,15 +54,15 @@ struct PlaylistVideo
json.object do
json.field "type", "video"
json.field "title", self.title
json.field "videoId", self.id
json.field "title", title
json.field "videoId", id
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", "/channel/#{ucid}"
json.field "videoThumbnails" do
Invidious::JSONify::APIv1.thumbnails(json, self.id)
Invidious::JSONify::APIv1.thumbnails(json, id)
end
if index
@ -72,8 +72,8 @@ struct PlaylistVideo
json.field "index", self.index
end
json.field "lengthSeconds", self.length_seconds
json.field "liveNow", self.live_now
json.field "lengthSeconds", length_seconds
json.field "liveNow", live_now
end
end
@ -101,14 +101,14 @@ struct Playlist
def to_json(offset, json : JSON::Builder, video_id : String? = nil)
json.object do
json.field "type", "playlist"
json.field "title", self.title
json.field "playlistId", self.id
json.field "playlistThumbnail", self.thumbnail
json.field "title", title
json.field "playlistId", id
json.field "playlistThumbnail", thumbnail
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
json.field "subtitle", self.subtitle
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", "/channel/#{ucid}"
json.field "subtitle", subtitle
json.field "authorThumbnails" do
json.array do
@ -116,7 +116,7 @@ struct Playlist
qualities.each do |quality|
json.object do
json.field "url", self.author_thumbnail.not_nil!.gsub(/=\d+/, "=s#{quality}")
json.field "url", author_thumbnail.not_nil!.gsub(/=\d+/, "=s#{quality}")
json.field "width", quality
json.field "height", quality
end
@ -124,13 +124,13 @@ struct Playlist
end
end
json.field "description", self.description
json.field "descriptionHtml", self.description_html
json.field "videoCount", self.video_count
json.field "description", description
json.field "descriptionHtml", description_html
json.field "videoCount", video_count
json.field "viewCount", self.views
json.field "updated", self.updated.to_unix
json.field "isListed", self.privacy.public?
json.field "viewCount", views
json.field "updated", updated.to_unix
json.field "isListed", privacy.public?
json.field "videos" do
json.array do
@ -180,33 +180,33 @@ struct InvidiousPlaylist
module PlaylistPrivacyConverter
def self.from_rs(rs)
return PlaylistPrivacy.parse(String.new(rs.read(Slice(UInt8))))
PlaylistPrivacy.parse(String.new(rs.read(Slice(UInt8))))
end
end
def to_json(offset, json : JSON::Builder, video_id : String? = nil)
json.object do
json.field "type", "invidiousPlaylist"
json.field "title", self.title
json.field "playlistId", self.id
json.field "title", title
json.field "playlistId", id
json.field "author", self.author
json.field "authorId", self.ucid
json.field "author", author
json.field "authorId", ucid
json.field "authorUrl", nil
json.field "authorThumbnails", [] of String
json.field "description", html_to_content(self.description_html)
json.field "descriptionHtml", self.description_html
json.field "videoCount", self.video_count
json.field "description", html_to_content(description_html)
json.field "descriptionHtml", description_html
json.field "videoCount", video_count
json.field "viewCount", self.views
json.field "updated", self.updated.to_unix
json.field "isListed", self.privacy.public?
json.field "viewCount", views
json.field "updated", updated.to_unix
json.field "isListed", privacy.public?
json.field "videos" do
json.array do
if (!offset || offset == 0) && !video_id.nil?
index = Invidious::Database::PlaylistVideos.select_index(self.id, video_id)
index = Invidious::Database::PlaylistVideos.select_index(id, video_id)
offset = self.index.index(index) || 0
end
@ -227,7 +227,7 @@ struct InvidiousPlaylist
def thumbnail
# TODO: Get playlist thumbnail from playlist data rather than first video
@thumbnail_id ||= Invidious::Database::PlaylistVideos.select_one_id(self.id, self.index) || "-----------"
@thumbnail_id ||= Invidious::Database::PlaylistVideos.select_one_id(id, index) || "-----------"
"/vi/#{@thumbnail_id}/mqdefault.jpg"
end
@ -244,7 +244,7 @@ struct InvidiousPlaylist
end
def description_html
HTML.escape(self.description)
HTML.escape(description)
end
end
@ -265,7 +265,7 @@ def create_playlist(title, privacy, user)
Invidious::Database::Playlists.insert(playlist)
return playlist
playlist
end
def subscribe_playlist(user, playlist)
@ -283,7 +283,7 @@ def subscribe_playlist(user, playlist)
Invidious::Database::Playlists.insert(playlist)
return playlist
playlist
end
def produce_playlist_continuation(id, index)
@ -318,18 +318,18 @@ def produce_playlist_continuation(id, index)
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return continuation
continuation
end
def get_playlist(plid : String)
if plid.starts_with? "IV"
if playlist = Invidious::Database::Playlists.select(id: plid)
return playlist
playlist
else
raise NotFoundException.new("Playlist does not exist.")
end
else
return fetch_playlist(plid)
fetch_playlist(plid)
end
end
@ -398,7 +398,7 @@ def fetch_playlist(plid : String)
ucid = author_info.dig?("title", "runs", 0, "navigationEndpoint", "browseEndpoint", "browseId").try &.as_s || ""
end
return Playlist.new({
Playlist.new({
title: title,
id: plid,
author: author,
@ -443,7 +443,7 @@ def get_playlist_videos(playlist : InvidiousPlaylist | Playlist, offset : Int32,
offset += 100
end
return videos
videos
end
end
@ -504,11 +504,11 @@ def extract_playlist_videos(initial_data : Hash(String, JSON::Any))
videos << ProblematicTimelineItem.new(parse_exception: ex)
end
return videos
videos
end
def template_playlist(playlist, listen)
html = <<-END_HTML
html = <<-HTML
<h3>
<a href="/playlist?list=#{playlist["playlistId"]}">
#{playlist["title"]}
@ -516,10 +516,10 @@ def template_playlist(playlist, listen)
</h3>
<div class="pure-menu pure-menu-scrollable playlist-restricted">
<ol class="pure-menu-list">
END_HTML
HTML
playlist["videos"].as_a.each do |video|
html += <<-END_HTML
html += <<-HTML
<li class="pure-menu-item" id="#{video["videoId"]}">
<a href="/watch?v=#{video["videoId"]}&list=#{playlist["playlistId"]}&index=#{video["index"]}#{listen ? "&listen=1" : ""}">
<div class="thumbnail">
@ -532,14 +532,14 @@ def template_playlist(playlist, listen)
</p>
</a>
</li>
END_HTML
HTML
end
html += <<-END_HTML
html += <<-HTML
</ol>
</div>
<hr>
END_HTML
HTML
html
end

View File

@ -337,10 +337,10 @@ module Invidious::Routes::Account
end
if redirect
return env.redirect referer
env.redirect referer
else
env.response.content_type = "application/json"
return "{}"
"{}"
end
end
end

View File

@ -140,7 +140,7 @@ module Invidious::Routes::API::Manifest
end
end
return manifest
manifest
end
# /api/manifest/dash/id/videoplayback

View File

@ -17,6 +17,7 @@ module Invidious::Routes::API::V1::Authenticated
user.preferences.to_json
end
# ameba:disable Naming/AccessorMethodName
def self.set_preferences(env)
env.response.content_type = "application/json"
user = env.get("user").as(User)
@ -35,7 +36,7 @@ module Invidious::Routes::API::V1::Authenticated
env.response.content_type = "application/json"
user = env.get("user").as(User)
return Invidious::User::Export.to_invidious(user)
Invidious::User::Export.to_invidious(user)
end
def self.import_invidious(env)
@ -71,7 +72,7 @@ module Invidious::Routes::API::V1::Authenticated
end
watched ||= [] of String
return watched.to_json
watched.to_json
end
def self.mark_watched(env)
@ -423,7 +424,7 @@ module Invidious::Routes::API::V1::Authenticated
env.response.content_type = "text/html"
csrf_token = generate_response(sid, {":authorize_token"}, HMAC_KEY, use_nonce: true)
return templated "user/authorize_token"
templated "user/authorize_token"
else
env.response.content_type = "application/json"
@ -482,7 +483,7 @@ module Invidious::Routes::API::V1::Authenticated
env.response.content_type = "text/event-stream"
raw_topics = env.params.body["topics"]? || env.params.query["topics"]?
topics = raw_topics.try &.split(",").uniq.first(1000)
topics = raw_topics.try &.split(",").uniq!.first(1000)
topics ||= [] of String
create_notification_stream(env, topics, CONNECTION_CHANNEL)

View File

@ -137,7 +137,7 @@ module Invidious::Routes::API::V1::Channels
env.params.query.delete("sort_by") if env.params.query.has_key?("sort_by")
env.params.query.delete("continuation") if env.params.query.has_key?("continuation")
return self.videos(env)
videos(env)
end
def self.videos(env)
@ -173,7 +173,7 @@ module Invidious::Routes::API::V1::Channels
end
end
return JSON.build do |json|
JSON.build do |json|
json.object do
json.field "videos" do
json.array do
@ -219,7 +219,7 @@ module Invidious::Routes::API::V1::Channels
end
end
return JSON.build do |json|
JSON.build do |json|
json.object do
json.field "videos" do
json.array do
@ -265,7 +265,7 @@ module Invidious::Routes::API::V1::Channels
end
end
return JSON.build do |json|
JSON.build do |json|
json.object do
json.field "videos" do
json.array do
@ -416,7 +416,7 @@ module Invidious::Routes::API::V1::Channels
begin
fetch_channel_community(ucid, continuation, locale, format, thin_mode)
rescue ex
return error_json(500, ex)
error_json(500, ex)
end
end
@ -444,7 +444,7 @@ module Invidious::Routes::API::V1::Channels
begin
fetch_channel_community_post(ucid, id, locale, format, thin_mode)
rescue ex
return error_json(500, ex)
error_json(500, ex)
end
end
@ -472,7 +472,7 @@ module Invidious::Routes::API::V1::Channels
else
comments = YoutubeAPI.browse(continuation: continuation)
end
return Comments.parse_youtube(id, comments, format, locale, thin_mode, is_post: true)
Comments.parse_youtube(id, comments, format, locale, thin_mode, is_post: true)
end
def self.channels(env)

View File

@ -4,10 +4,10 @@ module Invidious::Routes::API::V1::Misc
env.response.content_type = "application/json"
if !CONFIG.statistics_enabled
return {"software" => SOFTWARE}.to_json
{"software" => SOFTWARE}.to_json
else
# Calculate playback success rate
if (tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]?)
if tracker = Invidious::Jobs::StatisticsRefreshJob::STATISTICS["playback"]?
tracker = tracker.as(Hash(String, Int64 | Float64))
if !tracker.empty?
@ -22,7 +22,7 @@ module Invidious::Routes::API::V1::Misc
end
end
return Invidious::Jobs::StatisticsRefreshJob::STATISTICS.to_json
Invidious::Jobs::StatisticsRefreshJob::STATISTICS.to_json
end
end

View File

@ -53,7 +53,7 @@ module Invidious::Routes::API::V1::Search
end
end
rescue ex
return error_json(500, ex)
error_json(500, ex)
end
end

View File

@ -18,7 +18,7 @@ module Invidious::Routes::API::V1::Videos
return error_json(500, ex)
end
return JSON.build do |json|
JSON.build do |json|
Invidious::JSONify::APIv1.video(video, json, locale: locale, proxy: proxy)
end
end
@ -247,7 +247,7 @@ module Invidious::Routes::API::V1::Videos
# videojs-vtt-thumbnails is not compliant to the VTT specification, it
# doesn't unescape the HTML entities, so we have to do it here:
# TODO: remove this when we migrate to VideoJS 8
return HTML.unescape(vtt_file)
HTML.unescape(vtt_file)
end
def self.annotations(env)
@ -352,7 +352,7 @@ module Invidious::Routes::API::V1::Videos
return error_json(500, ex)
end
return comments
comments
elsif source == "reddit"
sort_by ||= "confidence"
@ -418,7 +418,7 @@ module Invidious::Routes::API::V1::Videos
return error_json(500, ex)
end
return JSON.build do |json|
JSON.build do |json|
json.object do
json.field "startTime", start_time
json.field "endTime", end_time
@ -513,6 +513,6 @@ module Invidious::Routes::API::V1::Videos
return error_json(500, ex)
end
return transcript.to_json
transcript.to_json
end
end

View File

@ -4,15 +4,15 @@ module Invidious::Routes::Channels
# Redirection for unsupported routes ("tabs")
def self.redirect_home(env)
ucid = env.params.url["ucid"]
return env.redirect "/channel/#{URI.encode_www_form(ucid)}"
env.redirect "/channel/#{URI.encode_www_form(ucid)}"
end
def self.home(env)
self.videos(env)
videos(env)
end
def self.videos(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -64,7 +64,7 @@ module Invidious::Routes::Channels
end
def self.shorts(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -99,7 +99,7 @@ module Invidious::Routes::Channels
end
def self.streams(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -134,7 +134,7 @@ module Invidious::Routes::Channels
end
def self.playlists(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -158,7 +158,7 @@ module Invidious::Routes::Channels
end
def self.podcasts(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -178,7 +178,7 @@ module Invidious::Routes::Channels
end
def self.releases(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -198,7 +198,7 @@ module Invidious::Routes::Channels
end
def self.courses(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -220,7 +220,7 @@ module Invidious::Routes::Channels
def self.community(env)
return env.redirect env.request.path.sub("posts", "community") if env.request.path.split("/").last == "posts"
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
if !data.is_a?(Tuple)
return data
end
@ -298,7 +298,7 @@ module Invidious::Routes::Channels
end
def self.channels(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
return data if !data.is_a?(Tuple)
locale, user, subscriptions, continuation, ucid, channel = data
@ -318,7 +318,7 @@ module Invidious::Routes::Channels
end
def self.about(env)
data = self.fetch_basic_information(env)
data = fetch_basic_information(env)
if !data.is_a?(Tuple)
return data
end
@ -365,7 +365,7 @@ module Invidious::Routes::Channels
url += "?#{invidious_url_params}" if !invidious_url_params.empty?
return env.redirect url
env.redirect url
end
# Handles redirects for the /profile endpoint
@ -378,7 +378,7 @@ module Invidious::Routes::Channels
user = env.params.query["user"]?
if !user
return error_template(404, "This channel does not exist.")
error_template(404, "This channel does not exist.")
else
env.redirect "/user/#{user}#{uri_params}"
end

View File

@ -9,7 +9,7 @@ module Invidious::Routes::Companion
begin
COMPANION_POOL.client do |wrapper|
wrapper.client.get(url, env.request.headers) do |resp|
return self.proxy_companion(env, resp)
return proxy_companion(env, resp)
end
end
rescue ex
@ -26,7 +26,7 @@ module Invidious::Routes::Companion
begin
COMPANION_POOL.client do |wrapper|
wrapper.client.post(url, env.request.headers, env.request.body) do |resp|
return self.proxy_companion(env, resp)
return proxy_companion(env, resp)
end
end
rescue ex
@ -42,7 +42,7 @@ module Invidious::Routes::Companion
begin
COMPANION_POOL.client do |wrapper|
wrapper.client.options(url, env.request.headers) do |resp|
return self.proxy_companion(env, resp)
return proxy_companion(env, resp)
end
end
rescue ex
@ -55,6 +55,6 @@ module Invidious::Routes::Companion
env.response.headers[key] = value
end
return IO.copy response.body_io, env.response
IO.copy response.body_io, env.response
end
end

View File

@ -119,7 +119,6 @@ module Invidious::Routes::Embed
end
return env.redirect url
else nil # Continue
end
params = process_video_params(env.params.query, preferences)
@ -182,14 +181,14 @@ module Invidious::Routes::Embed
captions = video.captions
preferred_captions = captions.select { |caption|
preferred_captions = captions.select do |caption|
params.preferred_captions.includes?(caption.name) ||
params.preferred_captions.includes?(caption.language_code.split("-")[0])
}
preferred_captions.sort_by! { |caption|
end
preferred_captions.sort_by! do |caption|
(params.preferred_captions.index(caption.name) ||
params.preferred_captions.index(caption.language_code.split("-")[0])).not_nil!
}
end
captions = captions - preferred_captions
aspect_ratio = nil

View File

@ -322,7 +322,6 @@ module Invidious::Routes::Feeds
request_target = URI.parse(node[attribute.name]).request_target
query_string_opt = request_target.starts_with?("/watch?v=") ? "&#{params}" : ""
node[attribute.name] = "#{HOST_URL}#{request_target}#{query_string_opt}"
else nil # Skip
end
end
end

View File

@ -13,7 +13,7 @@ module Invidious::Routes::Images
begin
GGPHT_POOL.client &.get(url, headers) do |resp|
return self.proxy_image(env, resp)
return proxy_image(env, resp)
end
rescue ex
end
@ -44,7 +44,7 @@ module Invidious::Routes::Images
begin
get_ytimg_pool(authority).client &.get(url, headers) do |resp|
env.response.headers["Connection"] = "close"
return self.proxy_image(env, resp)
return proxy_image(env, resp)
end
rescue ex
end
@ -66,7 +66,7 @@ module Invidious::Routes::Images
begin
get_ytimg_pool("i9").client &.get(url, headers) do |resp|
return self.proxy_image(env, resp)
return proxy_image(env, resp)
end
rescue ex
end
@ -128,7 +128,7 @@ module Invidious::Routes::Images
begin
get_ytimg_pool("i").client &.get(url, headers) do |resp|
return self.proxy_image(env, resp)
return proxy_image(env, resp)
end
rescue ex
end
@ -148,6 +148,6 @@ module Invidious::Routes::Images
return env.response.headers.delete("Transfer-Encoding")
end
return proxy_file(response, env)
proxy_file(response, env)
end
end

View File

@ -347,7 +347,6 @@ module Invidious::Routes::PreferencesRoute
response: error_template(415, "Uploaded file is too large")
)
end
else nil # Ignore
end
end
end

View File

@ -241,7 +241,7 @@ module Invidious::Routes::VideoPlayback
query_params = HTTP::Params.new(raw_params)
env.response.headers["Access-Control-Allow-Origin"] = "*"
return env.redirect "/videoplayback?#{query_params}"
env.redirect "/videoplayback?#{query_params}"
end
# /videoplayback/* && /videoplayback/*
@ -307,6 +307,6 @@ module Invidious::Routes::VideoPlayback
url += "&title=#{URI.encode_www_form(title, space_to_plus: false)}" if title
end
return env.redirect url
env.redirect url
end
end

View File

@ -145,14 +145,14 @@ module Invidious::Routes::Watch
captions = video.captions
preferred_captions = captions.select { |caption|
preferred_captions = captions.select do |caption|
params.preferred_captions.includes?(caption.name) ||
params.preferred_captions.includes?(caption.language_code.split("-")[0])
}
preferred_captions.sort_by! { |caption|
end
preferred_captions.sort_by! do |caption|
(params.preferred_captions.index(caption.name) ||
params.preferred_captions.index(caption.language_code.split("-")[0])).not_nil!
}
end
captions = captions - preferred_captions
aspect_ratio = "16:9"
@ -215,7 +215,7 @@ module Invidious::Routes::Watch
url += "&#{env.params.query}"
end
return env.redirect url
env.redirect url
end
def self.mark_watched(env)
@ -289,9 +289,9 @@ module Invidious::Routes::Watch
env.params.query["end"] = end_time.to_s if end_time != nil
end
return env.redirect "/watch?v=#{video_id}&#{env.params.query}"
env.redirect "/watch?v=#{video_id}&#{env.params.query}"
else
return error_template(404, "The requested clip doesn't exist")
error_template(404, "The requested clip doesn't exist")
end
end
@ -330,16 +330,16 @@ module Invidious::Routes::Watch
env.params.query["title"] = filename
env.params.query["label"] = URI.decode_www_form(label.as_s)
return Invidious::Routes::API::V1::Videos.captions(env)
Invidious::Routes::API::V1::Videos.captions(env)
elsif itag = download_widget["itag"]?.try &.as_i.to_s
# URL params specific to /latest_version
env.params.query["id"] = video_id
env.params.query["title"] = filename
env.params.query["local"] = "true"
return Invidious::Routes::VideoPlayback.latest_version(env)
Invidious::Routes::VideoPlayback.latest_version(env)
else
return error_template(400, "Invalid label or itag")
error_template(400, "Invalid label or itag")
end
end
end

View File

@ -42,11 +42,11 @@ module Invidious::Routing
end
{% end %}
self.register_image_routes
self.register_api_v1_routes
self.register_api_manifest_routes
self.register_video_playback_routes
self.register_companion_routes
register_image_routes
register_api_v1_routes
register_api_manifest_routes
register_video_playback_routes
register_companion_routes
end
# -------------------

View File

@ -28,5 +28,5 @@ def produce_channel_search_continuation(ucid, query, page)
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return continuation
continuation
end

View File

@ -80,7 +80,7 @@ module Invidious::Search
end
def default? : Bool
return @date.none? && @type.all? && @duration.none? && \
@date.none? && @type.all? && @duration.none? && \
@features.none? && @sort.relevance?
end
@ -110,7 +110,7 @@ module Invidious::Search
end
end
return features
features
end
def self.format_features(features : Features) : String
@ -132,7 +132,7 @@ module Invidious::Search
str << "location" if features.location?
str << "purchased" if features.purchased?
return str.join(',')
str.join(',')
end
def self.from_legacy_filters(str : String) : {Filters, String, String, Bool}
@ -230,7 +230,7 @@ module Invidious::Search
params.delete("sort")
end
return filters
filters
end
def to_iv_params : HTTP::Params
@ -249,7 +249,7 @@ module Invidious::Search
raw_params["features"] = [Filters.format_features(@features)]
end
return HTTP::Params.new(raw_params)
HTTP::Params.new(raw_params)
end
# -------------------
@ -304,7 +304,7 @@ module Invidious::Search
# See https://github.com/iv-org/invidious/issues/4398
object["30:varint"] = 1.to_i64
return object
object
.try { |i| Protodec::Any.cast_json(i) }
.try { |i| Protodec::Any.from_json(i) }
.try { |i| Base64.urlsafe_encode(i) }
@ -370,7 +370,7 @@ module Invidious::Search
# Remove URL parameter and return result
params.delete("sp")
return filters
filters
end
end
end

View File

@ -10,7 +10,7 @@ module Invidious::Search
initial_data = YoutubeAPI.search(query.text, search_params, client_config: client_config)
items, _ = extract_items(initial_data)
return items.reject!(Category)
items.reject!(Category)
end
# Search a youtube channel
@ -32,14 +32,14 @@ module Invidious::Search
response_json = YoutubeAPI.browse(continuation)
items, _ = extract_items(response_json, "", ucid)
return items.reject!(Category)
items.reject!(Category)
end
# Search inside of user subscriptions
def subscriptions(query : Query, user : Invidious::User) : Array(ChannelVideo)
view_name = "subscriptions_#{sha256(user.email)}"
return PG_DB.query_all("
PG_DB.query_all(<<-SQL, query.text, (query.page - 1) * 20, as: ChannelVideo)
SELECT id,title,published,updated,ucid,author,length_seconds
FROM (
SELECT *,
@ -47,10 +47,8 @@ module Invidious::Search
to_tsvector(#{view_name}.author)
as document
FROM #{view_name}
) v_search WHERE v_search.document @@ plainto_tsquery($1) LIMIT 20 OFFSET $2;",
query.text, (query.page - 1) * 20,
as: ChannelVideo
)
) v_search WHERE v_search.document @@ plainto_tsquery($1) LIMIT 20 OFFSET $2;
SQL
end
end
end

View File

@ -25,19 +25,19 @@ module Invidious::Search
# Return true if @raw_query is either `nil` or empty
private def empty_raw_query?
return @raw_query.empty?
@raw_query.empty?
end
# Same as `empty_raw_query?`, but named for external use
def empty?
return self.empty_raw_query?
empty_raw_query?
end
# Getter for the query string.
# It is named `text` to reduce confusion (`search_query.text` makes more
# sense than `search_query.query`)
def text
return @query
@query
end
# Initialize a new search query.
@ -70,7 +70,7 @@ module Invidious::Search
# Stop here if raw query is empty
# NOTE: maybe raise in the future?
return if self.empty_raw_query?
return if empty_raw_query?
# Specific handling
case @type
@ -120,7 +120,7 @@ module Invidious::Search
items = [] of SearchItem
# Don't bother going further if search query is empty
return items if self.empty_raw_query?
return items if empty_raw_query?
case @type
when .regular?, .playlist?
@ -135,7 +135,7 @@ module Invidious::Search
end
end
return items
items
end
# Return the HTTP::Params corresponding to this Query (invidious format)
@ -145,7 +145,7 @@ module Invidious::Search
params["q"] = @query
params["channel"] = @channel if !@channel.empty?
return params
params
end
# Checks if the query is a standalone URL
@ -160,7 +160,7 @@ module Invidious::Search
return false if !@filters.default?
# Simple heuristics: domain name
return @raw_query.starts_with?(
@raw_query.starts_with?(
/(https?:\/\/)?(www\.)?(m\.)?youtu(\.be|be\.com)\//
)
end

View File

@ -35,7 +35,7 @@ def fetch_trending(trending_type, region, locale)
# Ignore the smaller categories, as they generally contain a sponsored
# channel, which brings a lot of noise on the trending page.
# See: https://github.com/iv-org/invidious/issues/2989
next if (itm.contents.size < 24 && deduplicate)
next if itm.contents.size < 24 && deduplicate
extracted.concat itm.contents.select(SearchItem)
else

View File

@ -19,7 +19,7 @@ struct Invidious::User
hour = 12
end
clock_svg = <<-END_SVG
clock_svg = <<-SVG
<svg viewBox="0 0 100 100" width="200px" height="200px">
<circle cx="50" cy="50" r="45" fill="#eee" stroke="black" stroke-width="2"></circle>
@ -41,7 +41,7 @@ struct Invidious::User
<line id="minute" transform="rotate(#{minute_angle}, 50, 50)" x1="50" y1="50" x2="50" y2="16" fill="black" stroke="black" stroke-width="2"></line>
<line id="hour" transform="rotate(#{hour_angle}, 50, 50)" x1="50" y1="50" x2="50" y2="24" fill="black" stroke="black" stroke-width="2"></line>
</svg>
END_SVG
SVG
image = "data:image/png;base64,"
image += Process.run(%(rsvg-convert -w 400 -h 400 -b none -f png), shell: true,
@ -53,7 +53,7 @@ struct Invidious::User
answer = "#{hour}:#{minute.to_s.rjust(2, '0')}:#{second.to_s.rjust(2, '0')}"
answer = OpenSSL::HMAC.hexdigest(:sha256, key, answer)
return {
{
question: image,
tokens: {generate_response(answer, {":login"}, key, use_nonce: true)},
}

View File

@ -11,7 +11,7 @@ struct Invidious::User
# Session ID (SID) cookie
# Parameter "domain" comes from the global config
def sid(domain : String?, sid) : HTTP::Cookie
return HTTP::Cookie.new(
HTTP::Cookie.new(
name: "SID",
domain: domain,
value: sid,
@ -25,7 +25,7 @@ struct Invidious::User
# Preferences (PREFS) cookie
# Parameter "domain" comes from the global config
def prefs(domain : String?, preferences : Preferences) : HTTP::Cookie
return HTTP::Cookie.new(
HTTP::Cookie.new(
name: "PREFS",
domain: domain,
value: URI.encode_www_form(preferences.to_json),

View File

@ -5,7 +5,7 @@ struct Invidious::User
def to_invidious(user : User)
playlists = Invidious::Database::Playlists.select_like_iv(user.email)
return JSON.build do |json|
JSON.build do |json|
json.object do
json.field "subscriptions", user.subscriptions
json.field "watch_history", user.watched

View File

@ -27,7 +27,7 @@ struct Invidious::User
subscriptions << channel_id
end
return subscriptions
subscriptions
end
def parse_playlist_export_csv(user : User, raw_input : String)
@ -81,7 +81,7 @@ struct Invidious::User
end
end
return playlist
playlist
end
# -------------------
@ -171,7 +171,7 @@ struct Invidious::User
opml_extensions = ["xml", "opml"]
return opml_mimetypes.any?(&.== mimetype) || opml_extensions.any?(&.== extension)
opml_mimetypes.any?(&.== mimetype) || opml_extensions.any?(&.== extension)
end
# Import subscribed channels from Youtube
@ -200,7 +200,7 @@ struct Invidious::User
user.subscriptions = get_batch_channels(user.subscriptions)
Invidious::Database::Users.update_subscriptions(user)
return true
true
end
def from_youtube_pl(user : User, body : String, filename : String, type : String) : Bool
@ -209,12 +209,12 @@ struct Invidious::User
if extension == "csv" || type == "text/csv"
playlist = parse_playlist_export_csv(user, body)
if playlist
return true
true
else
return false
false
end
else
return false
false
end
end
@ -232,9 +232,9 @@ struct Invidious::User
user.watched += watched
user.watched.uniq!
Invidious::Database::Users.update_watch_history(user)
return true
true
else
return false
false
end
end
@ -328,7 +328,7 @@ struct Invidious::User
end
# Success!
return true
true
end
end # module
end

View File

@ -64,7 +64,6 @@ struct Preferences
end
def self.from_json(value : JSON::PullParser) : String
begin
result = value.read_string
if result.empty?
@ -79,7 +78,6 @@ struct Preferences
"light"
end
end
end
def self.to_yaml(value : String, yaml : YAML::Nodes::Builder)
yaml.scalar value
@ -262,12 +260,12 @@ struct Preferences
module TimeSpanConverter
def self.to_yaml(value : Time::Span, yaml : YAML::Nodes::Builder)
return yaml.scalar value.total_minutes.to_i32
yaml.scalar value.total_minutes.to_i32
end
def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Time::Span
if node.is_a?(YAML::Nodes::Scalar)
return decode_interval(node.value)
decode_interval(node.value)
else
node.raise "Expected scalar, not #{node.class}"
end

View File

@ -17,11 +17,9 @@ struct Invidious::User
module PreferencesConverter
def self.from_rs(rs)
begin
Preferences.from_json(rs.read(String))
rescue ex
Preferences.from_json("{}")
end
end
end
end

View File

@ -45,7 +45,6 @@ def get_subscription_feed(user, max_results = 40, page = 1)
notifications.sort_by!(&.author)
when "channel name - reverse"
notifications.sort_by!(&.author).reverse!
else nil # Ignore
end
else
if user.preferences.latest_only
@ -94,7 +93,6 @@ def get_subscription_feed(user, max_results = 40, page = 1)
videos.sort_by!(&.author)
when "channel name - reverse"
videos.sort_by!(&.author).reverse!
else nil # Ignore
end
notifications = Invidious::Database::Users.select_notifications(user)

View File

@ -48,7 +48,7 @@ struct Video
end
end
def to_json(json : JSON::Builder | Nil = nil)
def to_json(json : JSON::Builder? = nil)
to_json(nil, json)
end
@ -56,15 +56,15 @@ struct Video
def video_type : VideoType
video_type = info["videoType"]?.try &.as_s || "video"
return VideoType.parse?(video_type) || VideoType::Video
VideoType.parse?(video_type) || VideoType::Video
end
def schema_version : Int
return info["version"]?.try &.as_i || 1
info["version"]?.try &.as_i || 1
end
def published : Time
return info["published"]?
info["published"]?
.try { |t| Time.parse(t.as_s, "%Y-%m-%d", Time::Location::UTC) } || Time.utc
end
@ -73,11 +73,11 @@ struct Video
end
def live_now
return (self.video_type == VideoType::Livestream)
(video_type == VideoType::Livestream)
end
def post_live_dvr
return info["isPostLiveDvr"].as_bool
info["isPostLiveDvr"].as_bool
end
def premiere_timestamp : Time?
@ -94,21 +94,21 @@ struct Video
def fmt_stream : Array(Hash(String, JSON::Any))
if formats = info.dig?("streamingData", "formats")
return formats
formats
.as_a.map(&.as_h)
.sort_by! { |f| f["width"]?.try &.as_i || 0 }
else
return [] of Hash(String, JSON::Any)
[] of Hash(String, JSON::Any)
end
end
def adaptive_fmts : Array(Hash(String, JSON::Any))
if formats = info.dig?("streamingData", "adaptiveFormats")
return formats
formats
.as_a.map(&.as_h)
.sort_by! { |f| f["width"]?.try &.as_i || f["audioTrack"]?.try { |a| a["audioIsDefault"]?.try { |v| v.as_bool ? -1 : 0 } } || 0 }
else
return [] of Hash(String, JSON::Any)
[] of Hash(String, JSON::Any)
end
end
@ -124,11 +124,11 @@ struct Video
def storyboards
container = info.dig?("storyboards") || JSON::Any.new("{}")
return IV::Videos::Storyboard.from_yt_json(container, self.length_seconds)
IV::Videos::Storyboard.from_yt_json(container, length_seconds)
end
def paid
return (self.reason || "").includes? "requires payment"
(reason || "").includes? "requires payment"
end
def premium
@ -140,7 +140,7 @@ struct Video
@captions = Invidious::Videos::Captions::Metadata.from_yt_json(info["captions"])
end
return @captions
@captions
end
def hls_manifest_url : String?
@ -149,7 +149,7 @@ struct Video
def dash_manifest_url : String?
raw_dash_url = info.dig?("streamingData", "dashManifestUrl").try &.as_s
return nil if raw_dash_url.nil?
return if raw_dash_url.nil?
# Use manifest v5 parameter to reduce file size
# See https://github.com/iv-org/invidious/issues/4186
@ -162,7 +162,7 @@ struct Video
dash_url.query = "#{dash_query}&mpd_version=5"
end
return dash_url.to_s
dash_url.to_s
end
def genre_url : String?
@ -170,11 +170,11 @@ struct Video
end
def vr? : Bool?
return {"EQUIRECTANGULAR", "MESH"}.includes? self.projection_type
{"EQUIRECTANGULAR", "MESH"}.includes? projection_type
end
def projection_type : String?
return info.dig?("streamingData", "adaptiveFormats", 0, "projectionType").try &.as_s
info.dig?("streamingData", "adaptiveFormats", 0, "projectionType").try &.as_s
end
def reason : String?
@ -182,14 +182,14 @@ struct Video
end
def music : Array(VideoMusic)
info["music"].as_a.map { |music_json|
info["music"].as_a.map do |music_json|
VideoMusic.new(
music_json["song"].as_s,
music_json["album"].as_s,
music_json["artist"].as_s,
music_json["license"].as_s
)
}
end
end
# Macros defining getters/setters for various types of data
@ -197,7 +197,7 @@ struct Video
private macro getset_string(name)
# Return {{ name.stringify }} from `info`
def {{ name.id.underscore }} : String
return info[{{name.stringify}}]?.try &.as_s || ""
info[{{ name.stringify }}]?.try &.as_s || ""
end
# Update {{ name.stringify }} into `info`
@ -211,7 +211,7 @@ struct Video
private macro getset_string_array(name)
# Return {{ name.stringify }} from `info`
def {{ name.id.underscore }} : Array(String)
return info[{{name.stringify}}]?.try &.as_a.map &.as_s || [] of String
info[{{ name.stringify }}]?.try &.as_a.map &.as_s || [] of String
end
# Update {{ name.stringify }} into `info`
@ -225,7 +225,7 @@ struct Video
{% for op, type in {i32: Int32, i64: Int64} %}
private macro getset_{{ op }}(name)
def \{{name.id.underscore}} : {{ type }}
return info[\{{name.stringify}}]?.try &.as_i64.to_{{op}} || 0_{{op}}
info[\{{name.stringify}}]?.try &.as_i64.to_{{ op }} || 0_{{ op }}
end
def \{{name.id.underscore}}=(value : Int)
@ -239,7 +239,7 @@ struct Video
private macro getset_bool(name)
# Return {{ name.stringify }} from `info`
def {{ name.id.underscore }} : Bool
return info[{{name.stringify}}]?.try &.as_bool || false
info[{{ name.stringify }}]?.try &.as_bool || false
end
# Update {{ name.stringify }} into `info`
@ -254,7 +254,7 @@ struct Video
private macro predicate_bool(method_name, name)
# Return {{ name.stringify }} from `info`
def {{ method_name.id.underscore }}? : Bool
return info[{{name.stringify}}]?.try &.as_bool || false
info[{{ name.stringify }}]?.try &.as_bool || false
end
# Update {{ name.stringify }} into `info`
@ -316,11 +316,11 @@ def get_video(id, refresh = true, region = nil, force_refresh = false)
Invidious::Database::Videos.insert(video) if !region
end
return video
video
rescue DB::Error
# Avoid common `DB::PoolRetryAttemptsExceeded` error and friends
# Note: All DB errors inherit from `DB::Error`
return fetch_video(id, region)
fetch_video(id, region)
end
def fetch_video(id, region)
@ -350,7 +350,7 @@ def fetch_video(id, region)
updated: Time.utc,
})
return video
video
end
def process_continuation(query, plid, id)

View File

@ -33,7 +33,7 @@ module Invidious::Videos
captions_list << Captions::Metadata.new(name, language_code, base_url, auto_generated)
end
return captions_list
captions_list
end
def timedtext_to_vtt(timedtext : String, tlang = nil) : String
@ -82,7 +82,7 @@ module Invidious::Videos
end
end
return result
result
end
end

View File

@ -26,7 +26,7 @@ private def copy_string(str : String::Builder, iter : Iterator, count : Int) : I
copied += 1
end
return copied
copied
end
def parse_description(desc, video_id : String) : String?
@ -52,7 +52,7 @@ def parse_description(desc, video_id : String) : String?
index = 0
return String.build do |str|
String.build do |str|
commands.each do |command|
cmd_start = command["startIndex"].as_i
cmd_length = command["length"].as_i

View File

@ -1,6 +1,6 @@
module Invidious::Videos::Formats
def self.itag_to_metadata?(itag : JSON::Any)
return FORMATS[itag.to_s]?
FORMATS[itag.to_s]?
end
# See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L380-#L476

View File

@ -7,7 +7,7 @@ require "json"
# TODO: "compactRadioRenderer" (Mix) and
# TODO: Use a proper struct/class instead of a hacky JSON object
def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
return nil if !related["videoId"]?
return if !related["videoId"]?
# The compact renderer has video length in seconds, where the end
# screen rendered has a full text version ("42:40")
@ -40,7 +40,7 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
# TODO: when refactoring video types, make a struct for related videos
# or reuse an existing type, if that fits.
return {
{
"id" => related["videoId"],
"title" => related["title"]["simpleText"],
"author" => author || JSON::Any.new(""),
@ -57,7 +57,7 @@ def extract_video_info(video_id : String)
player_response = YoutubeAPI.player(video_id: video_id)
if player_response.nil?
return nil
return
end
playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s
@ -128,7 +128,7 @@ def extract_video_info(video_id : String)
# Data structure version, for cache control
params["version"] = JSON::Any.new(Video::SCHEMA_VERSION.to_i64)
return params
params
end
def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig) : Hash(String, JSON::Any)?
@ -145,9 +145,9 @@ def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConf
"The video returned by YouTube isn't the requested one. (#{client_config.client_type} client)"
)
elsif playability_status == "OK"
return response
response
else
return nil
return
end
end
@ -440,7 +440,7 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
"subCountText" => JSON::Any.new(subs_text || "-"),
}
return params
params
end
private def convert_url(fmt)
@ -457,9 +457,9 @@ private def convert_url(fmt)
url.query_params = params
LOGGER.trace("convert_url: new url is '#{url}'")
return url.to_s
url.to_s
rescue ex
LOGGER.debug("convert_url: Error when parsing video URL")
LOGGER.trace(ex.inspect_with_backtrace)
return ""
""
end

View File

@ -62,7 +62,7 @@ module Invidious::Videos
# The base URL is the first chunk
base_url = URI.parse(storyboards.shift)
return storyboards.map_with_index do |sb, i|
storyboards.map_with_index do |sb, i|
# Separate the different storyboard parameters:
# width/height: respective dimensions, in pixels, of a single thumbnail
# count: how many thumbnails are displayed across the full video

View File

@ -45,7 +45,7 @@ module Invidious::Videos
.try { |i| Base64.urlsafe_encode(i) }
.try { |i| URI.encode_www_form(i) }
return params
params
end
# Constructs a Transcripts struct from the initial YouTube response
@ -92,7 +92,7 @@ module Invidious::Videos
lines << line_type.new(start_ms, end_ms, text)
end
return Transcript.new(
Transcript.new(
lines: lines,
language_code: language_code,
auto_generated: auto_generated,
@ -120,7 +120,7 @@ module Invidious::Videos
end
end
return vtt
vtt
end
def to_json(json : JSON::Builder)

View File

@ -158,5 +158,5 @@ def process_video_params(query, preferences)
save_player_pos: save_player_pos,
})
return params
params
end

View File

@ -16,7 +16,7 @@
best_m4a_stream_bitrate = 0
audio_streams.each_with_index do |fmt, i|
bandwidth = fmt["bitrate"].as_i
if (fmt["mimeType"].as_s.starts_with?("audio/mp4") && bandwidth > best_m4a_stream_bitrate)
if fmt["mimeType"].as_s.starts_with?("audio/mp4") && bandwidth > best_m4a_stream_bitrate
best_m4a_stream_bitrate = bandwidth
best_m4a_stream_index = i
end
@ -26,7 +26,7 @@
src_url = "/latest_version?id=#{video.id}&itag=#{fmt["itag"]}"
src_url += "&local=true" if params.local
src_url = invidious_companion.public_url.to_s + src_url +
"&check=#{invidious_companion_check_id}" if (invidious_companion)
"&check=#{invidious_companion_check_id}" if invidious_companion
bitrate = fmt["bitrate"]
mimetype = HTML.escape(fmt["mimeType"].as_s)
@ -42,7 +42,7 @@
<% if params.quality == "dash"
src_url = "/api/manifest/dash/id/" + video.id + "?local=true&unique_res=1"
src_url = invidious_companion.public_url.to_s + src_url +
"&check=#{invidious_companion_check_id}" if (invidious_companion)
"&check=#{invidious_companion_check_id}" if invidious_companion
%>
<source src="<%= src_url %>" type='application/dash+xml' label="dash">
<% end %>
@ -54,7 +54,7 @@
src_url = "/latest_version?id=#{video.id}&itag=#{fmt["itag"]}"
src_url += "&local=true" if params.local
src_url = invidious_companion.public_url.to_s + src_url +
"&check=#{invidious_companion_check_id}" if (invidious_companion)
"&check=#{invidious_companion_check_id}" if invidious_companion
quality = fmt["quality"]
mimetype = HTML.escape(fmt["mimeType"].as_s)
@ -70,7 +70,7 @@
<% preferred_captions.each do |caption|
api_captions_url = "/api/v1/captions/"
api_captions_url = invidious_companion.public_url.to_s + api_captions_url if (invidious_companion)
api_captions_url = invidious_companion.public_url.to_s + api_captions_url if invidious_companion
api_captions_check_id = "&check=#{invidious_companion_check_id}"
%>
<track kind="captions" src="<%= api_captions_url %><%= video.id %>?label=<%= caption.name %><%= api_captions_check_id %>" label="<%= caption.name %>">
@ -78,7 +78,7 @@
<% captions.each do |caption|
api_captions_url = "/api/v1/captions/"
api_captions_url = invidious_companion.public_url.to_s + api_captions_url if (invidious_companion)
api_captions_url = invidious_companion.public_url.to_s + api_captions_url if invidious_companion
api_captions_check_id = "&check=#{invidious_companion_check_id}"
%>
<track kind="captions" src="<%= api_captions_url %><%= video.id %>?label=<%= caption.name %><%= api_captions_check_id %>" label="<%= caption.name %>">

View File

@ -25,7 +25,6 @@
<% end %>
<% if captcha %>
<% captcha = captcha.not_nil! %>
<img style="width:50%" src='<%= captcha[:question] %>'/>
<% captcha[:tokens].each_with_index do |token, i| %>
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">

View File

@ -41,7 +41,7 @@ struct YoutubeConnectionPool
)
DB::Pool(HTTP::Client).new(options) do
next make_client(url, force_resolve: true)
make_client(url, force_resolve: true)
end
end
end
@ -133,7 +133,7 @@ def make_client(url : URI, region = nil, force_resolve : Bool = false, force_you
client.read_timeout = 10.seconds
client.connect_timeout = 10.seconds
return client
client
end
def make_client(url : URI, region = nil, force_resolve : Bool = false, use_http_proxy : Bool = true, &)
@ -149,7 +149,7 @@ def make_configured_http_proxy_client
# This method is only called when configuration for an HTTP proxy are set
config_proxy = CONFIG.http_proxy.not_nil!
return HTTP::Proxy::Client.new(
HTTP::Proxy::Client.new(
config_proxy.host,
config_proxy.port,
@ -163,12 +163,12 @@ end
# Creates a new one when the specified pool for the subdomain does not exist
def get_ytimg_pool(subdomain)
if pool = YTIMG_POOLS[subdomain]?
return pool
pool
else
LOGGER.info("ytimg_pool: Creating a new HTTP pool for \"https://#{subdomain}.ytimg.com\"")
pool = YoutubeConnectionPool.new(URI.parse("https://#{subdomain}.ytimg.com"), capacity: CONFIG.pool_size)
YTIMG_POOLS[subdomain] = pool
return pool
pool
end
end

View File

@ -37,8 +37,7 @@ record AuthorFallback, name : String, id : String
private module Parsers
module BaseParser
def parse(*args)
begin
return parse_internal(*args)
parse_internal(*args)
rescue ex
LOGGER.debug("#{{{ @type.name }}}: Failed to render item.")
LOGGER.debug("#{{{ @type.name }}}: Got exception: #{ex.message}")
@ -47,7 +46,6 @@ private module Parsers
)
end
end
end
# Parses a InnerTube videoRenderer into a SearchVideo. Returns nil when the given object isn't a videoRenderer
#
@ -64,7 +62,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = (item["videoRenderer"]? || item["gridVideoRenderer"]?)
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -152,7 +150,6 @@ private module Parsers
when "Premium"
# TODO: Potentially available as item_contents["topStandaloneBadge"]["metadataBadgeRenderer"]
badges |= VideoBadges::Premium
else nil # Ignore
end
end
@ -173,7 +170,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -192,7 +189,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = (item["channelRenderer"]? || item["gridChannelRenderer"]?)
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -207,7 +204,7 @@ private module Parsers
# TODO change default value to nil
subscriber_count = item_contents.dig?("subscriberCountText", "simpleText").try &.as_s
channel_handle = subscriber_count if (subscriber_count.try &.starts_with? "@")
channel_handle = subscriber_count if subscriber_count.try &.starts_with? "@"
# Since youtube added channel handles, `VideoCountText` holds the number of
# subscribers and `subscriberCountText` holds the handle, except when the
@ -240,7 +237,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -255,7 +252,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["hashtagTileRenderer"]?
return self.parse(item_contents)
return parse(item_contents)
end
end
@ -280,7 +277,7 @@ private module Parsers
end
end
return SearchHashtag.new({
SearchHashtag.new({
title: title,
url: url,
video_count: short_text_to_number(video_count_txt || ""),
@ -289,7 +286,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -308,7 +305,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["gridPlaylistRenderer"]?
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -334,7 +331,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -352,7 +349,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["playlistRenderer"]?
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -395,7 +392,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -415,7 +412,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["shelfRenderer"]?
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -467,7 +464,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -484,7 +481,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item.dig?("itemSectionRenderer", "contents", 0)
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -492,11 +489,11 @@ private module Parsers
child = VideoRendererParser.process(item_contents, author_fallback)
child ||= PlaylistRendererParser.process(item_contents, author_fallback)
return child
child
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -513,7 +510,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item.dig?("richItemRenderer", "content")
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -523,11 +520,11 @@ private module Parsers
child ||= PlaylistRendererParser.process(item_contents, author_fallback)
child ||= LockupViewModelParser.process(item_contents, author_fallback)
child ||= ShortsLockupViewModelParser.process(item_contents, author_fallback)
return child
child
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -546,7 +543,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["reelItemRenderer"]?
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -626,7 +623,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -643,7 +640,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["lockupViewModel"]?
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -674,9 +671,9 @@ private module Parsers
video_count = thumbnail_view_model.dig("overlays").as_a
.compact_map(&.dig?("thumbnailOverlayBadgeViewModel", "thumbnailBadges").try &.as_a)
.flatten
.find(nil, &.dig?("thumbnailBadgeViewModel", "text").try { |node|
.find(nil, &.dig?("thumbnailBadgeViewModel", "text").try do |node|
{"episodes", "videos"}.any? { |str| node.as_s.ends_with?(str) }
})
end)
.try &.dig("thumbnailBadgeViewModel", "text").as_s.to_i(strict: false)
metadata = item_contents.dig("metadata", "lockupMetadataViewModel")
@ -691,7 +688,7 @@ private module Parsers
# item_contents.dig("rendererContext", "commandContext", "onTap", "innertubeCommand", "watchEndpoint")
# Available fields: "videoId", "playlistId", "params"
return SearchPlaylist.new({
SearchPlaylist.new({
title: title,
id: playlist_id,
author: author_fallback.name,
@ -704,7 +701,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -721,7 +718,7 @@ private module Parsers
def process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["shortsLockupViewModel"]?
return self.parse(item_contents, author_fallback)
return parse(item_contents, author_fallback)
end
end
@ -762,7 +759,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -778,7 +775,7 @@ private module Parsers
module ContinuationItemRendererParser
def self.process(item : JSON::Any, author_fallback : AuthorFallback)
if item_contents = item["continuationItemRenderer"]?
return self.parse(item_contents)
return parse(item_contents)
end
end
@ -791,7 +788,7 @@ private module Parsers
end
def self.parser_name
return {{@type.name}}
{{ @type.name }}
end
end
end
@ -831,7 +828,7 @@ private module Extractors
module YouTubeTabs
def self.process(initial_data : InitialData)
if target = initial_data["twoColumnBrowseResultsRenderer"]?
self.extract(target)
extract(target)
end
end
@ -845,7 +842,7 @@ private module Extractors
raw_items = rich_grid_contents.as_a
end
return raw_items
raw_items
end
private def self.unpack_section_list(contents)
@ -853,13 +850,13 @@ private module Extractors
contents.as_a.each do |item|
if item_section_content = item.dig?("itemSectionRenderer", "contents")
raw_items += self.unpack_item_section(item_section_content)
raw_items += unpack_item_section(item_section_content)
else
raw_items << item
end
end
return raw_items
raw_items
end
private def self.unpack_item_section(contents)
@ -874,11 +871,11 @@ private module Extractors
end
end
return raw_items
raw_items
end
def self.extractor_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -902,7 +899,7 @@ private module Extractors
module SearchResults
def self.process(initial_data : InitialData)
if target = initial_data["twoColumnSearchResultsRenderer"]?
self.extract(target)
extract(target)
end
end
@ -915,11 +912,11 @@ private module Extractors
end
end
return raw_items.flatten
raw_items.flatten
end
def self.extractor_name
return {{@type.name}}
{{ @type.name }}
end
end
@ -936,11 +933,11 @@ private module Extractors
module ContinuationContent
def self.process(initial_data : InitialData)
if target = initial_data["continuationContents"]?
self.extract(target)
extract(target)
elsif target = initial_data["appendContinuationItemsAction"]?
self.extract(target)
extract(target)
elsif target = initial_data["reloadContinuationItemsCommand"]?
self.extract(target)
extract(target)
end
end
@ -949,11 +946,11 @@ private module Extractors
content ||= target.dig?("gridContinuation", "items")
content ||= target.dig?("richGridContinuation", "contents")
return content.nil? ? [] of JSON::Any : content.as_a
content.nil? ? [] of JSON::Any : content.as_a
end
def self.extractor_name
return {{@type.name}}
{{ @type.name }}
end
end
end
@ -969,14 +966,14 @@ module HelperExtractors
def self.get_video_count(container : JSON::Any) : Int32
if box = container["videoCountText"]?
if (extracted_text = extract_text(box)) && !extracted_text.includes? " subscriber"
return extracted_text.gsub(/\D/, "").to_i
extracted_text.gsub(/\D/, "").to_i
else
return 0
0
end
elsif box = container["videoCount"]?
return box.as_s.to_i
box.as_s.to_i
else
return 0
0
end
end
@ -990,7 +987,7 @@ module HelperExtractors
# Simpletext: "4M views"
# runs: {"text": "1.1K"},{"text":" watching"}
return box["simpleText"]?.try &.as_s.sub(" views", "") ||
box["simpleText"]?.try &.as_s.sub(" views", "") ||
box.dig?("runs", 0, "text").try &.as_s || "0"
end
@ -1000,7 +997,7 @@ module HelperExtractors
#
# Raises when it's unable to parse from the given JSON data.
def self.get_thumbnails(container : JSON::Any) : String
return container.dig("thumbnail", "thumbnails", 0, "url").as_s
container.dig("thumbnail", "thumbnails", 0, "url").as_s
end
# ditto
@ -1008,13 +1005,13 @@ module HelperExtractors
# YouTube sometimes sends the thumbnail as:
# {"thumbnails": [{"thumbnails": [{"url": "example.com"}, ...]}]}
def self.get_thumbnails_plural(container : JSON::Any) : String
return container.dig("thumbnails", 0, "thumbnails", 0, "url").as_s
container.dig("thumbnails", 0, "thumbnails", 0, "url").as_s
end
# Retrieves the ID required for querying the InnerTube browse endpoint.
# Returns an empty string when it's unable to do so
def self.get_browse_id(container)
return container.dig?("navigationEndpoint", "browseEndpoint", "browseId").try &.as_s || ""
container.dig?("navigationEndpoint", "browseEndpoint", "browseId").try &.as_s || ""
end
end

View File

@ -17,15 +17,13 @@
# another nil will be returned.
def extract_text(item : JSON::Any?) : String?
if item.nil?
return nil
return
end
if text_container = item["simpleText"]?
return text_container.as_s
text_container.as_s
elsif text_container = item["runs"]?
return text_container.as_a.map(&.["text"].as_s).join("")
else
nil
end
end
@ -60,18 +58,18 @@ def has_verified_badge?(badges : JSON::Any?)
return true if style == "BADGE_STYLE_TYPE_VERIFIED_ARTIST"
end
return false
false
rescue ex
LOGGER.debug("Unable to parse owner badges. Got exception: #{ex.message}")
LOGGER.trace("Owner badges data: #{badges.to_json}")
return false
false
end
# This function extracts SearchVideo items from a Category.
# Categories are commonly returned in search results and trending pages.
def extract_category(category : Category) : Array(SearchVideo)
return category.contents.select(SearchVideo)
category.contents.select(SearchVideo)
end
# :ditto:
@ -83,5 +81,5 @@ end
def extract_selected_tab(tabs)
# Extract the selected tab from the array of tabs Youtube returns
return tabs.as_a.select(&.["tabRenderer"]?.try &.["selected"]?.try &.as_bool)[0]["tabRenderer"]
tabs.as_a.select(&.["tabRenderer"]?.try &.["selected"]?.try &.as_bool)[0]["tabRenderer"]
end

View File

@ -30,7 +30,7 @@ module UrlSanitizer
return false
end
return true
true
end
# Return which kind of parameters are allowed based on the
@ -38,15 +38,15 @@ module UrlSanitizer
private def determine_allowed(path_root : String)
case path_root
when "watch", "w", "v", "embed", "e", "shorts", "clip"
return :watch
:watch
when .starts_with?("@"), "c", "channel", "user", "profile", "attribution_link"
return :channel
:channel
when "playlist", "mix"
return :playlist
:playlist
when "results", "search"
return :search
:search
else # hashtag, post, trending, brand URLs, etc..
return nil
return
end
end
@ -61,7 +61,7 @@ module UrlSanitizer
end
end
return new_params
new_params
end
# Transform any user-supplied youtube URL into something we can trust
@ -78,7 +78,7 @@ module UrlSanitizer
new_uri = URI.new(path: "/")
# Redirect to homepage for bogus URLs
return new_uri if (unsafe_host.nil? || unsafe_path.nil?)
return new_uri if unsafe_host.nil? || unsafe_path.nil?
breadcrumbs = unsafe_path
.split('/', remove_empty: true)
@ -116,6 +116,6 @@ module UrlSanitizer
new_uri.query_params = new_params
end
return new_uri
new_uri
end
end

View File

@ -207,7 +207,7 @@ module YoutubeAPI
# Region to provide to youtube, e.g to alter search results
# (this is passed as the `gl` parameter).
property region : String | Nil
property region : String?
# Initialization function
def initialize(
@ -267,8 +267,8 @@ module YoutubeAPI
# Convert to string, for logging purposes
def to_s
return {
client_type: self.name,
{
client_type: name,
region: @region,
}.to_s
end
@ -283,7 +283,7 @@ module YoutubeAPI
# Return, as a Hash, the "context" data required to request the
# youtube API endpoints.
#
private def make_context(client_config : ClientConfig | Nil, video_id = "dQw4w9WgXcQ") : Hash
private def make_context(client_config : ClientConfig?, video_id = "dQw4w9WgXcQ") : Hash
# Use the default client config if nil is passed
client_config ||= DEFAULT_CLIENT_CONFIG
@ -331,7 +331,7 @@ module YoutubeAPI
client_context["client"]["platform"] = platform
end
return client_context
client_context
end
####################################################################
@ -353,14 +353,14 @@ module YoutubeAPI
#
# - A playlist ID (parameters MUST be an empty string)
#
def browse(continuation : String, client_config : ClientConfig | Nil = nil)
def browse(continuation : String, client_config : ClientConfig? = nil)
# JSON Request data, required by the API
data = {
"context" => self.make_context(client_config),
"context" => make_context(client_config),
"continuation" => continuation,
}
return self._post_json("/youtubei/v1/browse", data, client_config)
_post_json("/youtubei/v1/browse", data, client_config)
end
# :ditto:
@ -368,12 +368,12 @@ module YoutubeAPI
browse_id : String,
*, # Force the following parameters to be passed by name
params : String,
client_config : ClientConfig | Nil = nil,
client_config : ClientConfig? = nil,
)
# JSON Request data, required by the API
data = {
"browseId" => browse_id,
"context" => self.make_context(client_config),
"context" => make_context(client_config),
}
# Append the additional parameters if those were provided
@ -382,7 +382,7 @@ module YoutubeAPI
data["params"] = params
end
return self._post_json("/youtubei/v1/browse", data, client_config)
_post_json("/youtubei/v1/browse", data, client_config)
end
####################################################################
@ -421,29 +421,29 @@ module YoutubeAPI
# })
# ```
#
def next(continuation : String, *, client_config : ClientConfig | Nil = nil)
def next(continuation : String, *, client_config : ClientConfig? = nil)
# JSON Request data, required by the API
data = {
"context" => self.make_context(client_config),
"context" => make_context(client_config),
"continuation" => continuation,
}
return self._post_json("/youtubei/v1/next", data, client_config)
_post_json("/youtubei/v1/next", data, client_config)
end
# :ditto:
def next(data : Hash, *, client_config : ClientConfig | Nil = nil)
def next(data : Hash, *, client_config : ClientConfig? = nil)
# JSON Request data, required by the API
data2 = data.merge({
"context" => self.make_context(client_config),
"context" => make_context(client_config),
})
return self._post_json("/youtubei/v1/next", data2, client_config)
_post_json("/youtubei/v1/next", data2, client_config)
end
# Allow a NamedTuple to be passed, too.
def next(data : NamedTuple, *, client_config : ClientConfig | Nil = nil)
return self.next(data.to_h, client_config: client_config)
def next(data : NamedTuple, *, client_config : ClientConfig? = nil)
self.next(data.to_h, client_config: client_config)
end
####################################################################
@ -461,9 +461,9 @@ module YoutubeAPI
}
if CONFIG.invidious_companion.present?
return self._post_invidious_companion("/youtubei/v1/player", data)
_post_invidious_companion("/youtubei/v1/player", data)
else
return nil
return
end
end
@ -495,13 +495,13 @@ module YoutubeAPI
# channel_b = YoutubeAPI.resolve_url("https://youtube.com/c/invalid")
# ```
#
def resolve_url(url : String, client_config : ClientConfig | Nil = nil)
def resolve_url(url : String, client_config : ClientConfig? = nil)
data = {
"context" => self.make_context(nil),
"context" => make_context(nil),
"url" => url,
}
return self._post_json("/youtubei/v1/navigation/resolve_url", data, client_config)
_post_json("/youtubei/v1/navigation/resolve_url", data, client_config)
end
####################################################################
@ -521,16 +521,16 @@ module YoutubeAPI
def search(
search_query : String,
params : String,
client_config : ClientConfig | Nil = nil,
client_config : ClientConfig? = nil,
)
# JSON Request data, required by the API
data = {
"query" => search_query,
"context" => self.make_context(client_config),
"context" => make_context(client_config),
"params" => params,
}
return self._post_json("/youtubei/v1/search", data, client_config)
_post_json("/youtubei/v1/search", data, client_config)
end
####################################################################
@ -547,14 +547,14 @@ module YoutubeAPI
def get_transcript(
params : String,
client_config : ClientConfig | Nil = nil,
client_config : ClientConfig? = nil,
) : Hash(String, JSON::Any)
data = {
"context" => self.make_context(client_config),
"context" => make_context(client_config),
"params" => params,
}
return self._post_json("/youtubei/v1/get_transcript", data, client_config)
_post_json("/youtubei/v1/get_transcript", data, client_config)
end
####################################################################
@ -569,7 +569,7 @@ module YoutubeAPI
def _post_json(
endpoint : String,
data : Hash,
client_config : ClientConfig | Nil,
client_config : ClientConfig?,
) : Hash(String, JSON::Any)
# Use the default client config if nil is passed
client_config ||= DEFAULT_CLIENT_CONFIG
@ -602,7 +602,7 @@ module YoutubeAPI
status code #{response.status_code}. See <a href=\"https://docs.invidious.io/youtube-errors-explained/\"> \
https://docs.invidious.io/youtube-errors-explained/</a> for troubleshooting.")
end
self._decompress(response.body_io, response.headers["Content-Encoding"]?)
_decompress(response.body_io, response.headers["Content-Encoding"]?)
end
end
@ -623,7 +623,7 @@ module YoutubeAPI
error #{code} with message:<br>\"#{message}\"")
end
return initial_data
initial_data
end
####################################################################
@ -661,7 +661,7 @@ module YoutubeAPI
end
end
return response_body
response_body
rescue ex
raise InfoException.new("Error while communicating with Invidious companion: " + (ex.message || "no extra info found"))
end
@ -685,7 +685,7 @@ module YoutubeAPI
# Multiple encodings can be combined, and are listed in the order
# in which they were applied. E.g: "deflate, gzip" means that the
# content must be first "gunzipped", then "defated".
encodings.split(',').reverse.each do |enc|
encodings.split(',').reverse!.each do |enc|
case enc.strip(' ')
when "gzip"
body_io = Compress::Gzip::Reader.new(body_io, sync_close: true)
@ -695,6 +695,6 @@ module YoutubeAPI
end
end
return body_io.gets_to_end
body_io.gets_to_end
end
end # End of module