mirror of
https://github.com/iv-org/invidious.git
synced 2025-08-28 07:28:31 +00:00
Migrate to stdlib Log
This commit is contained in:
parent
1ae0f45b0e
commit
3e847b8412
@ -289,7 +289,7 @@ https_only: false
|
|||||||
## Logging Verbosity. This is overridden if "-l LEVEL" or
|
## Logging Verbosity. This is overridden if "-l LEVEL" or
|
||||||
## "--log-level=LEVEL" are passed on the command line.
|
## "--log-level=LEVEL" are passed on the command line.
|
||||||
##
|
##
|
||||||
## Accepted values: All, Trace, Debug, Info, Warn, Error, Fatal, Off
|
## Accepted values: Trace, Debug, Info, Notice, Warn, Error, Fatal, None
|
||||||
## Default: Info
|
## Default: Info
|
||||||
##
|
##
|
||||||
#log_level: Info
|
#log_level: Info
|
||||||
|
@ -24,7 +24,7 @@ def create_licence_tr(path, file_name, licence_name, licence_link, source_locati
|
|||||||
"<tr>
|
"<tr>
|
||||||
<td><a href=\\"/#{path}\\">#{file_name}</a></td>
|
<td><a href=\\"/#{path}\\">#{file_name}</a></td>
|
||||||
<td><a href=\\"#{licence_link}\\">#{licence_name}</a></td>
|
<td><a href=\\"#{licence_link}\\">#{licence_name}</a></td>
|
||||||
<td><a href=\\"#{source_location}\\">\#{translate(locale, "source")}</a></td>
|
<td><a href=\\"#{source_location}\\">\#{I18n.translate(locale, "source")}</a></td>
|
||||||
</tr>"
|
</tr>"
|
||||||
HTML
|
HTML
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ shards:
|
|||||||
|
|
||||||
backtracer:
|
backtracer:
|
||||||
git: https://github.com/sija/backtracer.cr.git
|
git: https://github.com/sija/backtracer.cr.git
|
||||||
version: 1.2.2
|
version: 1.2.4
|
||||||
|
|
||||||
db:
|
db:
|
||||||
git: https://github.com/crystal-lang/crystal-db.git
|
git: https://github.com/crystal-lang/crystal-db.git
|
||||||
@ -18,7 +18,7 @@ shards:
|
|||||||
|
|
||||||
exception_page:
|
exception_page:
|
||||||
git: https://github.com/crystal-loot/exception_page.git
|
git: https://github.com/crystal-loot/exception_page.git
|
||||||
version: 0.4.1
|
version: 0.5.0
|
||||||
|
|
||||||
http_proxy:
|
http_proxy:
|
||||||
git: https://github.com/mamantoha/http_proxy.git
|
git: https://github.com/mamantoha/http_proxy.git
|
||||||
@ -26,7 +26,7 @@ shards:
|
|||||||
|
|
||||||
kemal:
|
kemal:
|
||||||
git: https://github.com/kemalcr/kemal.git
|
git: https://github.com/kemalcr/kemal.git
|
||||||
version: 1.6.0
|
version: 1.7.2
|
||||||
|
|
||||||
pg:
|
pg:
|
||||||
git: https://github.com/will/crystal-pg.git
|
git: https://github.com/will/crystal-pg.git
|
||||||
|
@ -17,7 +17,7 @@ dependencies:
|
|||||||
version: ~> 0.21.0
|
version: ~> 0.21.0
|
||||||
kemal:
|
kemal:
|
||||||
github: kemalcr/kemal
|
github: kemalcr/kemal
|
||||||
version: ~> 1.6.0
|
version: ~> 1.7.2
|
||||||
protodec:
|
protodec:
|
||||||
github: iv-org/protodec
|
github: iv-org/protodec
|
||||||
version: ~> 0.1.5
|
version: ~> 0.1.5
|
||||||
|
@ -4,7 +4,7 @@ Spectator.describe Invidious::Hashtag do
|
|||||||
it "parses richItemRenderer containers (test 1)" do
|
it "parses richItemRenderer containers (test 1)" do
|
||||||
# Enable mock
|
# Enable mock
|
||||||
test_content = load_mock("hashtag/martingarrix_page1")
|
test_content = load_mock("hashtag/martingarrix_page1")
|
||||||
videos, _ = extract_items(test_content)
|
videos, _ = YoutubeJSONParser.extract_items(test_content)
|
||||||
|
|
||||||
expect(typeof(videos)).to eq(Array(SearchItem))
|
expect(typeof(videos)).to eq(Array(SearchItem))
|
||||||
expect(videos.size).to eq(60)
|
expect(videos.size).to eq(60)
|
||||||
@ -57,7 +57,7 @@ Spectator.describe Invidious::Hashtag do
|
|||||||
it "parses richItemRenderer containers (test 2)" do
|
it "parses richItemRenderer containers (test 2)" do
|
||||||
# Enable mock
|
# Enable mock
|
||||||
test_content = load_mock("hashtag/martingarrix_page2")
|
test_content = load_mock("hashtag/martingarrix_page2")
|
||||||
videos, _ = extract_items(test_content)
|
videos, _ = YoutubeJSONParser.extract_items(test_content)
|
||||||
|
|
||||||
expect(typeof(videos)).to eq(Array(SearchItem))
|
expect(typeof(videos)).to eq(Array(SearchItem))
|
||||||
expect(videos.size).to eq(60)
|
expect(videos.size).to eq(60)
|
||||||
|
@ -7,7 +7,7 @@ Spectator.describe "parse_video_info" do
|
|||||||
_next = load_mock("video/regular_mrbeast.next")
|
_next = load_mock("video/regular_mrbeast.next")
|
||||||
|
|
||||||
raw_data = _player.merge!(_next)
|
raw_data = _player.merge!(_next)
|
||||||
info = parse_video_info("2isYuQZMbdU", raw_data)
|
info = Parser.parse_video_info("2isYuQZMbdU", raw_data)
|
||||||
|
|
||||||
# Some basic verifications
|
# Some basic verifications
|
||||||
expect(typeof(info)).to eq(Hash(String, JSON::Any))
|
expect(typeof(info)).to eq(Hash(String, JSON::Any))
|
||||||
@ -89,7 +89,7 @@ Spectator.describe "parse_video_info" do
|
|||||||
_next = load_mock("video/regular_no-description.next")
|
_next = load_mock("video/regular_no-description.next")
|
||||||
|
|
||||||
raw_data = _player.merge!(_next)
|
raw_data = _player.merge!(_next)
|
||||||
info = parse_video_info("iuevw6218F0", raw_data)
|
info = Parser.parse_video_info("iuevw6218F0", raw_data)
|
||||||
|
|
||||||
# Some basic verifications
|
# Some basic verifications
|
||||||
expect(typeof(info)).to eq(Hash(String, JSON::Any))
|
expect(typeof(info)).to eq(Hash(String, JSON::Any))
|
||||||
|
@ -7,7 +7,7 @@ Spectator.describe "parse_video_info" do
|
|||||||
_next = load_mock("video/scheduled_live_PBD-Podcast.next")
|
_next = load_mock("video/scheduled_live_PBD-Podcast.next")
|
||||||
|
|
||||||
raw_data = _player.merge!(_next)
|
raw_data = _player.merge!(_next)
|
||||||
info = parse_video_info("N-yVic7BbY0", raw_data)
|
info = Parser.parse_video_info("N-yVic7BbY0", raw_data)
|
||||||
|
|
||||||
# Some basic verifications
|
# Some basic verifications
|
||||||
expect(typeof(info)).to eq(Hash(String, JSON::Any))
|
expect(typeof(info)).to eq(Hash(String, JSON::Any))
|
||||||
|
@ -8,7 +8,6 @@ require "spectator"
|
|||||||
|
|
||||||
require "../src/invidious/exceptions"
|
require "../src/invidious/exceptions"
|
||||||
require "../src/invidious/helpers/macros"
|
require "../src/invidious/helpers/macros"
|
||||||
require "../src/invidious/helpers/logger"
|
|
||||||
require "../src/invidious/helpers/utils"
|
require "../src/invidious/helpers/utils"
|
||||||
|
|
||||||
require "../src/invidious/videos"
|
require "../src/invidious/videos"
|
||||||
@ -19,9 +18,6 @@ require "../src/invidious/helpers/serialized_yt_data"
|
|||||||
require "../src/invidious/yt_backend/extractors"
|
require "../src/invidious/yt_backend/extractors"
|
||||||
require "../src/invidious/yt_backend/extractors_utils"
|
require "../src/invidious/yt_backend/extractors_utils"
|
||||||
|
|
||||||
OUTPUT = File.open(File::NULL, "w")
|
|
||||||
LOGGER = Invidious::LogHandler.new(OUTPUT, LogLevel::Off)
|
|
||||||
|
|
||||||
def load_mock(file) : Hash(String, JSON::Any)
|
def load_mock(file) : Hash(String, JSON::Any)
|
||||||
file = File.join(__DIR__, "..", "mocks", file + ".json")
|
file = File.join(__DIR__, "..", "mocks", file + ".json")
|
||||||
content = File.read(file)
|
content = File.read(file)
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
require "digest/md5"
|
require "digest/md5"
|
||||||
require "file_utils"
|
|
||||||
|
|
||||||
# Require kemal, then our own overrides
|
# Require kemal, then our own overrides
|
||||||
require "kemal"
|
require "kemal"
|
||||||
@ -30,6 +29,7 @@ require "xml"
|
|||||||
require "yaml"
|
require "yaml"
|
||||||
require "compress/zip"
|
require "compress/zip"
|
||||||
require "protodec/utils"
|
require "protodec/utils"
|
||||||
|
require "log"
|
||||||
|
|
||||||
require "./invidious/database/*"
|
require "./invidious/database/*"
|
||||||
require "./invidious/database/migrations/*"
|
require "./invidious/database/migrations/*"
|
||||||
@ -122,8 +122,8 @@ Kemal.config.extra_options do |parser|
|
|||||||
parser.on("-o OUTPUT", "--output=OUTPUT", "Redirect output (default: #{CONFIG.output})") do |output|
|
parser.on("-o OUTPUT", "--output=OUTPUT", "Redirect output (default: #{CONFIG.output})") do |output|
|
||||||
CONFIG.output = output
|
CONFIG.output = output
|
||||||
end
|
end
|
||||||
parser.on("-l LEVEL", "--log-level=LEVEL", "Log level, one of #{LogLevel.values} (default: #{CONFIG.log_level})") do |log_level|
|
parser.on("-l LEVEL", "--log-level=LEVEL", "Log level, one of #{Log::Severity.values} (default: #{CONFIG.log_level})") do |log_level|
|
||||||
CONFIG.log_level = LogLevel.parse(log_level)
|
CONFIG.log_level = Log::Severity.parse(log_level)
|
||||||
end
|
end
|
||||||
parser.on("-k", "--colorize", "Colorize logs") do
|
parser.on("-k", "--colorize", "Colorize logs") do
|
||||||
CONFIG.colorize_logs = true
|
CONFIG.colorize_logs = true
|
||||||
@ -140,11 +140,23 @@ end
|
|||||||
|
|
||||||
Kemal::CLI.new ARGV
|
Kemal::CLI.new ARGV
|
||||||
|
|
||||||
if CONFIG.output.upcase != "STDOUT"
|
Log.setup do |c|
|
||||||
FileUtils.mkdir_p(File.dirname(CONFIG.output))
|
colorize = CONFIG.colorize_logs
|
||||||
|
output = CONFIG.output
|
||||||
|
|
||||||
|
backend = Log::IOBackend.new(formatter: Invidious::Logger.formatter(colorize))
|
||||||
|
if output.upcase != "STDOUT"
|
||||||
|
Dir.mkdir_p(File.dirname(output))
|
||||||
|
io = File.open(output, "wb")
|
||||||
|
colorize = false
|
||||||
|
backend = Log::IOBackend.new(io, formatter: Invidious::Logger.formatter(colorize))
|
||||||
|
puts("File output enabled in config, logs will being saved in '#{output}'")
|
||||||
|
end
|
||||||
|
|
||||||
|
c.bind "*", CONFIG.log_level, backend
|
||||||
|
c.bind "db.*", :none, backend
|
||||||
|
c.bind "http.*", :none, backend
|
||||||
end
|
end
|
||||||
OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mode: "a")
|
|
||||||
LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level, CONFIG.colorize_logs)
|
|
||||||
|
|
||||||
# Check table integrity
|
# Check table integrity
|
||||||
Invidious::Database.check_integrity(CONFIG)
|
Invidious::Database.check_integrity(CONFIG)
|
||||||
@ -242,7 +254,6 @@ add_context_storage_type(Array(String))
|
|||||||
add_context_storage_type(Preferences)
|
add_context_storage_type(Preferences)
|
||||||
add_context_storage_type(Invidious::User)
|
add_context_storage_type(Invidious::User)
|
||||||
|
|
||||||
Kemal.config.logger = LOGGER
|
|
||||||
Kemal.config.app_name = "Invidious"
|
Kemal.config.app_name = "Invidious"
|
||||||
|
|
||||||
# Use in kemal's production mode.
|
# Use in kemal's production mode.
|
||||||
|
@ -200,7 +200,7 @@ def fetch_related_channels(about_channel : AboutChannel, continuation : String?
|
|||||||
initial_data = YoutubeAPI.browse(continuation)
|
initial_data = YoutubeAPI.browse(continuation)
|
||||||
end
|
end
|
||||||
|
|
||||||
items, continuation = extract_items(initial_data)
|
items, continuation = YoutubeJSONParser.extract_items(initial_data)
|
||||||
|
|
||||||
return items.select(SearchChannel), continuation
|
return items.select(SearchChannel), continuation
|
||||||
end
|
end
|
||||||
|
@ -38,7 +38,7 @@ struct ChannelVideo
|
|||||||
json.field "authorId", self.ucid
|
json.field "authorId", self.ucid
|
||||||
json.field "authorUrl", "/channel/#{self.ucid}"
|
json.field "authorUrl", "/channel/#{self.ucid}"
|
||||||
json.field "published", self.published.to_unix
|
json.field "published", self.published.to_unix
|
||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(self.published, locale))
|
json.field "publishedText", I18n.translate(locale, "`x` ago", recode_date(self.published, locale))
|
||||||
|
|
||||||
json.field "viewCount", self.views
|
json.field "viewCount", self.views
|
||||||
end
|
end
|
||||||
@ -156,8 +156,8 @@ def get_channel(id) : InvidiousChannel
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fetch_channel(ucid, pull_all_videos : Bool)
|
def fetch_channel(ucid, pull_all_videos : Bool)
|
||||||
LOGGER.debug("fetch_channel: #{ucid}")
|
::Log.forf.debug { "#{ucid}" }
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : pull_all_videos = #{pull_all_videos}")
|
::Log.forf.trace { "#{ucid} : pull_all_videos = #{pull_all_videos}" }
|
||||||
|
|
||||||
namespaces = {
|
namespaces = {
|
||||||
"yt" => "http://www.youtube.com/xml/schemas/2015",
|
"yt" => "http://www.youtube.com/xml/schemas/2015",
|
||||||
@ -165,9 +165,9 @@ def fetch_channel(ucid, pull_all_videos : Bool)
|
|||||||
"default" => "http://www.w3.org/2005/Atom",
|
"default" => "http://www.w3.org/2005/Atom",
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : Downloading RSS feed")
|
::Log.forf.trace { "#{ucid} : Downloading RSS feed" }
|
||||||
rss = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{ucid}").body
|
rss = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{ucid}").body
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : Parsing RSS feed")
|
::Log.forf.trace { "#{ucid} : Parsing RSS feed" }
|
||||||
rss = XML.parse(rss)
|
rss = XML.parse(rss)
|
||||||
|
|
||||||
author = rss.xpath_node("//default:feed/default:title", namespaces)
|
author = rss.xpath_node("//default:feed/default:title", namespaces)
|
||||||
@ -184,7 +184,7 @@ def fetch_channel(ucid, pull_all_videos : Bool)
|
|||||||
auto_generated = true
|
auto_generated = true
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : author = #{author}, auto_generated = #{auto_generated}")
|
::Log.forf.trace { "#{ucid} : author = #{author}, auto_generated = #{auto_generated}" }
|
||||||
|
|
||||||
channel = InvidiousChannel.new({
|
channel = InvidiousChannel.new({
|
||||||
id: ucid,
|
id: ucid,
|
||||||
@ -194,10 +194,10 @@ def fetch_channel(ucid, pull_all_videos : Bool)
|
|||||||
subscribed: nil,
|
subscribed: nil,
|
||||||
})
|
})
|
||||||
|
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : Downloading channel videos page")
|
::Log.forf.trace { "#{ucid} : Downloading channel videos page" }
|
||||||
videos, continuation = IV::Channel::Tabs.get_videos(channel)
|
videos, continuation = IV::Channel::Tabs.get_videos(channel)
|
||||||
|
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : Extracting videos from channel RSS feed")
|
::Log.forf.trace { "#{ucid} : Extracting videos from channel RSS feed" }
|
||||||
rss.xpath_nodes("//default:feed/default:entry", namespaces).each do |entry|
|
rss.xpath_nodes("//default:feed/default:entry", namespaces).each do |entry|
|
||||||
video_id = entry.xpath_node("yt:videoId", namespaces).not_nil!.content
|
video_id = entry.xpath_node("yt:videoId", namespaces).not_nil!.content
|
||||||
title = entry.xpath_node("default:title", namespaces).not_nil!.content
|
title = entry.xpath_node("default:title", namespaces).not_nil!.content
|
||||||
@ -241,17 +241,17 @@ def fetch_channel(ucid, pull_all_videos : Bool)
|
|||||||
views: views,
|
views: views,
|
||||||
})
|
})
|
||||||
|
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : video #{video_id} : Updating or inserting video")
|
::Log.forf.trace { "#{ucid} : video #{video_id} : Updating or inserting video" }
|
||||||
|
|
||||||
# We don't include the 'premiere_timestamp' here because channel pages don't include them,
|
# We don't include the 'premiere_timestamp' here because channel pages don't include them,
|
||||||
# meaning the above timestamp is always null
|
# meaning the above timestamp is always null
|
||||||
was_insert = Invidious::Database::ChannelVideos.insert(video)
|
was_insert = Invidious::Database::ChannelVideos.insert(video)
|
||||||
|
|
||||||
if was_insert
|
if was_insert
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : video #{video_id} : Inserted, updating subscriptions")
|
::Log.forf.trace { "#{ucid} : video #{video_id} : Inserted, updating subscriptions" }
|
||||||
NOTIFICATION_CHANNEL.send(VideoNotification.from_video(video))
|
NOTIFICATION_CHANNEL.send(VideoNotification.from_video(video))
|
||||||
else
|
else
|
||||||
LOGGER.trace("fetch_channel: #{ucid} : video #{video_id} : Updated")
|
::Log.forf.trace { "#{ucid} : video #{video_id} : Updated" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ def fetch_channel_community(ucid, cursor, locale, format, thin_mode)
|
|||||||
initial_data = YoutubeAPI.browse(ucid, params: "Egljb21tdW5pdHk%3D")
|
initial_data = YoutubeAPI.browse(ucid, params: "Egljb21tdW5pdHk%3D")
|
||||||
|
|
||||||
items = [] of JSON::Any
|
items = [] of JSON::Any
|
||||||
extract_items(initial_data) do |item|
|
YoutubeJSONParser.extract_items(initial_data) do |item|
|
||||||
items << item
|
items << item
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -43,7 +43,7 @@ def fetch_channel_community_post(ucid, post_id, locale, format, thin_mode)
|
|||||||
initial_data = YoutubeAPI.browse(ucid, params: params)
|
initial_data = YoutubeAPI.browse(ucid, params: params)
|
||||||
|
|
||||||
items = [] of JSON::Any
|
items = [] of JSON::Any
|
||||||
extract_items(initial_data) do |item|
|
YoutubeJSONParser.extract_items(initial_data) do |item|
|
||||||
items << item
|
items << item
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_sing
|
|||||||
json.field "contentHtml", content_html
|
json.field "contentHtml", content_html
|
||||||
|
|
||||||
json.field "published", published.to_unix
|
json.field "published", published.to_unix
|
||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
|
json.field "publishedText", I18n.translate(locale, "`x` ago", recode_date(published, locale))
|
||||||
|
|
||||||
json.field "likeCount", like_count
|
json.field "likeCount", like_count
|
||||||
json.field "replyCount", reply_count
|
json.field "replyCount", reply_count
|
||||||
@ -136,7 +136,7 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_sing
|
|||||||
json.field "attachment" do
|
json.field "attachment" do
|
||||||
case attachment.as_h
|
case attachment.as_h
|
||||||
when .has_key?("videoRenderer")
|
when .has_key?("videoRenderer")
|
||||||
parse_item(attachment)
|
YoutubeJSONParser.parse_item(attachment)
|
||||||
.as(SearchVideo)
|
.as(SearchVideo)
|
||||||
.to_json(locale, json)
|
.to_json(locale, json)
|
||||||
when .has_key?("backstageImageRenderer")
|
when .has_key?("backstageImageRenderer")
|
||||||
@ -224,7 +224,7 @@ def extract_channel_community(items, *, ucid, locale, format, thin_mode, is_sing
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
when .has_key?("playlistRenderer")
|
when .has_key?("playlistRenderer")
|
||||||
parse_item(attachment)
|
YoutubeJSONParser.parse_item(attachment)
|
||||||
.as(SearchPlaylist)
|
.as(SearchPlaylist)
|
||||||
.to_json(locale, json)
|
.to_json(locale, json)
|
||||||
when .has_key?("quizRenderer")
|
when .has_key?("quizRenderer")
|
||||||
|
@ -24,7 +24,7 @@ def fetch_channel_playlists(ucid, author, continuation, sort_by)
|
|||||||
initial_data = YoutubeAPI.browse(ucid, params: params || "")
|
initial_data = YoutubeAPI.browse(ucid, params: params || "")
|
||||||
end
|
end
|
||||||
|
|
||||||
return extract_items(initial_data, author, ucid)
|
return YoutubeJSONParser.extract_items(initial_data, author, ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_channel_podcasts(ucid, author, continuation)
|
def fetch_channel_podcasts(ucid, author, continuation)
|
||||||
@ -33,7 +33,7 @@ def fetch_channel_podcasts(ucid, author, continuation)
|
|||||||
else
|
else
|
||||||
initial_data = YoutubeAPI.browse(ucid, params: "Eghwb2RjYXN0c_IGBQoDugEA")
|
initial_data = YoutubeAPI.browse(ucid, params: "Eghwb2RjYXN0c_IGBQoDugEA")
|
||||||
end
|
end
|
||||||
return extract_items(initial_data, author, ucid)
|
return YoutubeJSONParser.extract_items(initial_data, author, ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_channel_releases(ucid, author, continuation)
|
def fetch_channel_releases(ucid, author, continuation)
|
||||||
@ -42,7 +42,7 @@ def fetch_channel_releases(ucid, author, continuation)
|
|||||||
else
|
else
|
||||||
initial_data = YoutubeAPI.browse(ucid, params: "EghyZWxlYXNlc_IGBQoDsgEA")
|
initial_data = YoutubeAPI.browse(ucid, params: "EghyZWxlYXNlc_IGBQoDsgEA")
|
||||||
end
|
end
|
||||||
return extract_items(initial_data, author, ucid)
|
return YoutubeJSONParser.extract_items(initial_data, author, ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_channel_courses(ucid, author, continuation)
|
def fetch_channel_courses(ucid, author, continuation)
|
||||||
@ -51,5 +51,5 @@ def fetch_channel_courses(ucid, author, continuation)
|
|||||||
else
|
else
|
||||||
initial_data = YoutubeAPI.browse(ucid, params: "Egdjb3Vyc2Vz8gYFCgPCAQA%3D")
|
initial_data = YoutubeAPI.browse(ucid, params: "Egdjb3Vyc2Vz8gYFCgPCAQA%3D")
|
||||||
end
|
end
|
||||||
return extract_items(initial_data, author, ucid)
|
return YoutubeJSONParser.extract_items(initial_data, author, ucid)
|
||||||
end
|
end
|
||||||
|
@ -29,7 +29,7 @@ module Invidious::Channel::Tabs
|
|||||||
continuation ||= make_initial_videos_ctoken(ucid, sort_by)
|
continuation ||= make_initial_videos_ctoken(ucid, sort_by)
|
||||||
initial_data = YoutubeAPI.browse(continuation: continuation)
|
initial_data = YoutubeAPI.browse(continuation: continuation)
|
||||||
|
|
||||||
return extract_items(initial_data, author, ucid)
|
return YoutubeJSONParser.extract_items(initial_data, author, ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_60_videos(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
|
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)
|
continuation ||= make_initial_shorts_ctoken(channel.ucid, sort_by)
|
||||||
initial_data = YoutubeAPI.browse(continuation: continuation)
|
initial_data = YoutubeAPI.browse(continuation: continuation)
|
||||||
|
|
||||||
return extract_items(initial_data, channel.author, channel.ucid)
|
return YoutubeJSONParser.extract_items(initial_data, channel.author, channel.ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
# -------------------
|
# -------------------
|
||||||
@ -70,7 +70,7 @@ module Invidious::Channel::Tabs
|
|||||||
continuation ||= make_initial_livestreams_ctoken(channel.ucid, sort_by)
|
continuation ||= make_initial_livestreams_ctoken(channel.ucid, sort_by)
|
||||||
initial_data = YoutubeAPI.browse(continuation: continuation)
|
initial_data = YoutubeAPI.browse(continuation: continuation)
|
||||||
|
|
||||||
return extract_items(initial_data, channel.author, channel.ucid)
|
return YoutubeJSONParser.extract_items(initial_data, channel.author, channel.ucid)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_60_livestreams(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
|
def get_60_livestreams(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
|
||||||
|
@ -264,7 +264,7 @@ module Invidious::Comments
|
|||||||
end
|
end
|
||||||
|
|
||||||
json.field "published", published.to_unix
|
json.field "published", published.to_unix
|
||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
|
json.field "publishedText", I18n.translate(locale, "`x` ago", recode_date(published, locale))
|
||||||
end
|
end
|
||||||
|
|
||||||
if node_replies && !response["commentRepliesContinuation"]?
|
if node_replies && !response["commentRepliesContinuation"]?
|
||||||
|
@ -73,6 +73,7 @@ end
|
|||||||
|
|
||||||
class Config
|
class Config
|
||||||
include YAML::Serializable
|
include YAML::Serializable
|
||||||
|
CLog = ::Log.for(self)
|
||||||
|
|
||||||
class CompanionConfig
|
class CompanionConfig
|
||||||
include YAML::Serializable
|
include YAML::Serializable
|
||||||
@ -93,8 +94,8 @@ class Config
|
|||||||
property feed_threads : Int32 = 1
|
property feed_threads : Int32 = 1
|
||||||
# Log file path or STDOUT
|
# Log file path or STDOUT
|
||||||
property output : String = "STDOUT"
|
property output : String = "STDOUT"
|
||||||
# Default log level, valid YAML values are ints and strings, see src/invidious/helpers/logger.cr
|
# Default log level, valid YAML values are ints and strings, see https://crystal-lang.org/api/master/CLog.Severity.html#enum-members
|
||||||
property log_level : LogLevel = LogLevel::Info
|
property log_level : Log::Severity = Log::Severity::Info
|
||||||
# Enables colors in logs. Useful for debugging purposes
|
# Enables colors in logs. Useful for debugging purposes
|
||||||
property colorize_logs : Bool = false
|
property colorize_logs : Bool = false
|
||||||
# Database configuration with separate parameters (username, hostname, etc)
|
# Database configuration with separate parameters (username, hostname, etc)
|
||||||
@ -244,14 +245,14 @@ class Config
|
|||||||
|
|
||||||
# Exit on fail
|
# Exit on fail
|
||||||
if !success
|
if !success
|
||||||
puts %(Config.{{ivar.id}} failed to parse #{env_value} as {{ivar.type}})
|
CLog.fatal { %({{ivar.id}} failed to parse #{env_value} as {{ivar.type}}) }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Warn when any config attribute is set to "CHANGE_ME!!"
|
# Warn when any config attribute is set to "CHANGE_ME!!"
|
||||||
if config.{{ivar.id}} == "CHANGE_ME!!"
|
if config.{{ivar.id}} == "CHANGE_ME!!"
|
||||||
puts "Config: The value of '#{ {{ivar.stringify}} }' needs to be changed!!"
|
CLog.fatal { "The value of '#{ {{ivar.stringify}} }' needs to be changed!!" }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
{% end %}
|
{% end %}
|
||||||
@ -259,28 +260,28 @@ class Config
|
|||||||
if config.invidious_companion.present?
|
if config.invidious_companion.present?
|
||||||
# invidious_companion and signature_server can't work together
|
# invidious_companion and signature_server can't work together
|
||||||
if config.signature_server
|
if config.signature_server
|
||||||
puts "Config: You can not run inv_sig_helper and invidious_companion at the same time."
|
CLog.fatal { "You can not run inv_sig_helper and invidious_companion at the same time." }
|
||||||
exit(1)
|
exit(1)
|
||||||
elsif config.invidious_companion_key.empty?
|
elsif config.invidious_companion_key.empty?
|
||||||
puts "Config: Please configure a key if you are using invidious companion."
|
CLog.fatal { "Please configure a key if you are using invidious companion." }
|
||||||
exit(1)
|
exit(1)
|
||||||
elsif config.invidious_companion_key == "CHANGE_ME!!"
|
elsif config.invidious_companion_key == "CHANGE_ME!!"
|
||||||
puts "Config: The value of 'invidious_companion_key' needs to be changed!!"
|
CLog.fatal { "The value of 'invidious_companion_key' needs to be changed!!" }
|
||||||
exit(1)
|
exit(1)
|
||||||
elsif config.invidious_companion_key.size != 16
|
elsif config.invidious_companion_key.size != 16
|
||||||
puts "Config: The value of 'invidious_companion_key' needs to be a size of 16 characters."
|
CLog.fatal { "The value of 'invidious_companion_key' needs to be a size of 16 characters." }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
elsif config.signature_server
|
elsif config.signature_server
|
||||||
puts("WARNING: inv-sig-helper is deprecated. Please switch to Invidious companion: https://docs.invidious.io/companion-installation/")
|
CLog.warn { "inv-sig-helper is deprecated. Please switch to Invidious companion: https://docs.invidious.io/companion-installation/" }
|
||||||
else
|
else
|
||||||
puts("WARNING: Invidious companion is required to view and playback videos. For more information see https://docs.invidious.io/companion-installation/")
|
CLog.warn { "Invidious companion is required to view and playback videos. For more information see https://docs.invidious.io/companion-installation/" }
|
||||||
end
|
end
|
||||||
|
|
||||||
# HMAC_key is mandatory
|
# HMAC_key is mandatory
|
||||||
# See: https://github.com/iv-org/invidious/issues/3854
|
# See: https://github.com/iv-org/invidious/issues/3854
|
||||||
if config.hmac_key.empty?
|
if config.hmac_key.empty?
|
||||||
puts "Config: 'hmac_key' is required/can't be empty"
|
CLog.fatal { "'hmac_key' is required/can't be empty" }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -296,7 +297,7 @@ class Config
|
|||||||
path: db.dbname,
|
path: db.dbname,
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
puts "Config: Either database_url or db.* is required"
|
CLog.fatal { "Either database_url or db.* is required" }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -304,17 +305,17 @@ class Config
|
|||||||
# Check if the socket configuration is valid
|
# Check if the socket configuration is valid
|
||||||
if sb = config.socket_binding
|
if sb = config.socket_binding
|
||||||
if sb.path.ends_with?("/") || File.directory?(sb.path)
|
if sb.path.ends_with?("/") || File.directory?(sb.path)
|
||||||
puts "Config: The socket path " + sb.path + " must not be a directory!"
|
CLog.fatal { "The socket path " + sb.path + " must not be a directory!" }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
d = File.dirname(sb.path)
|
d = File.dirname(sb.path)
|
||||||
if !File.directory?(d)
|
if !File.directory?(d)
|
||||||
puts "Config: Socket directory " + sb.path + " does not exist or is not a directory!"
|
CLog.fatal { "Socket directory " + sb.path + " does not exist or is not a directory!" }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
p = sb.permissions.to_i?(base: 8)
|
p = sb.permissions.to_i?(base: 8)
|
||||||
if !p || p < 0 || p > 0o777
|
if !p || p < 0 || p > 0o777
|
||||||
puts "Config: Socket permissions must be an octal between 0 and 777!"
|
CLog.fatal { "Socket permissions must be an octal between 0 and 777!" }
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,6 +2,7 @@ require "pg"
|
|||||||
|
|
||||||
module Invidious::Database
|
module Invidious::Database
|
||||||
extend self
|
extend self
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
# Checks table integrity
|
# Checks table integrity
|
||||||
#
|
#
|
||||||
@ -33,7 +34,7 @@ module Invidious::Database
|
|||||||
return # TODO
|
return # TODO
|
||||||
|
|
||||||
if !PG_DB.query_one?("SELECT true FROM pg_type WHERE typname = $1", enum_name, as: Bool)
|
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}")
|
::Log.forf.info { "CREATE TYPE #{enum_name}" }
|
||||||
|
|
||||||
PG_DB.using_connection do |conn|
|
PG_DB.using_connection do |conn|
|
||||||
conn.as(PG::Connection).exec_all(File.read("config/sql/#{enum_name}.sql"))
|
conn.as(PG::Connection).exec_all(File.read("config/sql/#{enum_name}.sql"))
|
||||||
@ -46,7 +47,7 @@ module Invidious::Database
|
|||||||
begin
|
begin
|
||||||
PG_DB.exec("SELECT * FROM #{table_name} LIMIT 0")
|
PG_DB.exec("SELECT * FROM #{table_name} LIMIT 0")
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.info("check_table: check_table: CREATE TABLE #{table_name}")
|
::Log.forf.info { "CREATE TABLE #{table_name}" }
|
||||||
|
|
||||||
PG_DB.using_connection do |conn|
|
PG_DB.using_connection do |conn|
|
||||||
conn.as(PG::Connection).exec_all(File.read("config/sql/#{table_name}.sql"))
|
conn.as(PG::Connection).exec_all(File.read("config/sql/#{table_name}.sql"))
|
||||||
@ -66,7 +67,7 @@ module Invidious::Database
|
|||||||
if name != column_array[i]?
|
if name != column_array[i]?
|
||||||
if !column_array[i]?
|
if !column_array[i]?
|
||||||
new_column = column_types.select(&.starts_with?(name))[0]
|
new_column = column_types.select(&.starts_with?(name))[0]
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} ADD COLUMN #{new_column}")
|
::Log.forf.info { "ALTER TABLE #{table_name} ADD COLUMN #{new_column}" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}")
|
PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}")
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
@ -84,29 +85,29 @@ module Invidious::Database
|
|||||||
|
|
||||||
# There's a column we didn't expect
|
# There's a column we didn't expect
|
||||||
if !new_column
|
if !new_column
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]}")
|
::Log.forf.info { "ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]}" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
||||||
|
|
||||||
column_array = get_column_array(PG_DB, table_name)
|
column_array = get_column_array(PG_DB, table_name)
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} ADD COLUMN #{new_column}")
|
::Log.forf.info { "ALTER TABLE #{table_name} ADD COLUMN #{new_column}" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}")
|
PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}")
|
||||||
|
|
||||||
LOGGER.info("check_table: UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}")
|
::Log.forf.info { "UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}" }
|
||||||
PG_DB.exec("UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}")
|
PG_DB.exec("UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}")
|
||||||
|
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
::Log.forf.info { "ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
||||||
|
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}")
|
::Log.forf.info { "ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}")
|
PG_DB.exec("ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}")
|
||||||
|
|
||||||
column_array = get_column_array(PG_DB, table_name)
|
column_array = get_column_array(PG_DB, table_name)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
::Log.forf.info { "ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -116,7 +117,7 @@ module Invidious::Database
|
|||||||
|
|
||||||
column_array.each do |column|
|
column_array.each do |column|
|
||||||
if !struct_array.includes? column
|
if !struct_array.includes? column
|
||||||
LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE")
|
::Log.forf.info { "ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE" }
|
||||||
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE")
|
PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,12 +14,12 @@ class Invidious::Database::Migrator
|
|||||||
.each do |migration|
|
.each do |migration|
|
||||||
next if versions.includes?(migration.version)
|
next if versions.includes?(migration.version)
|
||||||
|
|
||||||
puts "Running migration: #{migration.class.name}"
|
Log.info { "Running migration: #{migration.class.name}" }
|
||||||
migration.migrate
|
migration.migrate
|
||||||
ran_migration = true
|
ran_migration = true
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "No migrations to run." unless ran_migration
|
Log.info { "No migrations to run." } unless ran_migration
|
||||||
end
|
end
|
||||||
|
|
||||||
def pending_migrations? : Bool
|
def pending_migrations? : Bool
|
||||||
|
@ -28,14 +28,14 @@ module Invidious::Frontend::ChannelPage
|
|||||||
|
|
||||||
if tab == selected_tab
|
if tab == selected_tab
|
||||||
str << "\t<b>"
|
str << "\t<b>"
|
||||||
str << translate(locale, "channel_tab_#{tab_name}_label")
|
str << I18n.translate(locale, "channel_tab_#{tab_name}_label")
|
||||||
str << "</b>\n"
|
str << "</b>\n"
|
||||||
else
|
else
|
||||||
# Video tab doesn't have the last path component
|
# Video tab doesn't have the last path component
|
||||||
url = tab.videos? ? base_url : "#{base_url}/#{tab_name}"
|
url = tab.videos? ? base_url : "#{base_url}/#{tab_name}"
|
||||||
|
|
||||||
str << %(\t<a href=") << url << %(">)
|
str << %(\t<a href=") << url << %(">)
|
||||||
str << translate(locale, "channel_tab_#{tab_name}_label")
|
str << I18n.translate(locale, "channel_tab_#{tab_name}_label")
|
||||||
str << "</a>\n"
|
str << "</a>\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,9 +32,9 @@ module Invidious::Frontend::Comments
|
|||||||
<p>
|
<p>
|
||||||
<a href="javascript:void(0)" data-onclick="toggle_parent">[ − ]</a>
|
<a href="javascript:void(0)" data-onclick="toggle_parent">[ − ]</a>
|
||||||
<b><a href="https://www.reddit.com/user/#{child.author}">#{child.author}</a></b>
|
<b><a href="https://www.reddit.com/user/#{child.author}">#{child.author}</a></b>
|
||||||
#{translate_count(locale, "comments_points_count", child.score, NumberFormatting::Separator)}
|
#{I18n.translate_count(locale, "comments_points_count", child.score, I18n::NumberFormatting::Separator)}
|
||||||
<span title="#{child.created_utc.to_s("%a %B %-d %T %Y UTC")}">#{translate(locale, "`x` ago", recode_date(child.created_utc, locale))}</span>
|
<span title="#{child.created_utc.to_s("%a %B %-d %T %Y UTC")}">#{I18n.translate(locale, "`x` ago", recode_date(child.created_utc, locale))}</span>
|
||||||
<a href="https://www.reddit.com#{child.permalink}" title="#{translate(locale, "permalink")}">#{translate(locale, "permalink")}</a>
|
<a href="https://www.reddit.com#{child.permalink}" title="#{I18n.translate(locale, "permalink")}">#{I18n.translate(locale, "permalink")}</a>
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
#{body_html}
|
#{body_html}
|
||||||
|
@ -6,10 +6,10 @@ module Invidious::Frontend::Comments
|
|||||||
root = comments["comments"].as_a
|
root = comments["comments"].as_a
|
||||||
root.each do |child|
|
root.each do |child|
|
||||||
if child["replies"]?
|
if child["replies"]?
|
||||||
replies_count_text = translate_count(locale,
|
replies_count_text = I18n.translate_count(locale,
|
||||||
"comments_view_x_replies",
|
"comments_view_x_replies",
|
||||||
child["replies"]["replyCount"].as_i64 || 0,
|
child["replies"]["replyCount"].as_i64 || 0,
|
||||||
NumberFormatting::Separator
|
I18n::NumberFormatting::Separator
|
||||||
)
|
)
|
||||||
|
|
||||||
replies_html = <<-END_HTML
|
replies_html = <<-END_HTML
|
||||||
@ -25,10 +25,10 @@ module Invidious::Frontend::Comments
|
|||||||
END_HTML
|
END_HTML
|
||||||
elsif comments["authorId"]? && !comments["singlePost"]?
|
elsif comments["authorId"]? && !comments["singlePost"]?
|
||||||
# for posts we should display a link to the post
|
# for posts we should display a link to the post
|
||||||
replies_count_text = translate_count(locale,
|
replies_count_text = I18n.translate_count(locale,
|
||||||
"comments_view_x_replies",
|
"comments_view_x_replies",
|
||||||
child["replyCount"].as_i64 || 0,
|
child["replyCount"].as_i64 || 0,
|
||||||
NumberFormatting::Separator
|
I18n::NumberFormatting::Separator
|
||||||
)
|
)
|
||||||
|
|
||||||
replies_html = <<-END_HTML
|
replies_html = <<-END_HTML
|
||||||
@ -61,7 +61,7 @@ module Invidious::Frontend::Comments
|
|||||||
sponsor_icon = String.build do |str|
|
sponsor_icon = String.build do |str|
|
||||||
str << %(<img alt="" )
|
str << %(<img alt="" )
|
||||||
str << %(src="/ggpht) << URI.parse(child["sponsorIconUrl"].as_s).request_target << "\" "
|
str << %(src="/ggpht) << URI.parse(child["sponsorIconUrl"].as_s).request_target << "\" "
|
||||||
str << %(title=") << translate(locale, "Channel Sponsor") << "\" "
|
str << %(title=") << I18n.translate(locale, "Channel Sponsor") << "\" "
|
||||||
str << %(width="16" height="16" />)
|
str << %(width="16" height="16" />)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -110,14 +110,14 @@ module Invidious::Frontend::Comments
|
|||||||
when "multiImage"
|
when "multiImage"
|
||||||
html << <<-END_HTML
|
html << <<-END_HTML
|
||||||
<section class="carousel">
|
<section class="carousel">
|
||||||
<a class="skip-link" href="#skip-#{child["commentId"]}">#{translate(locale, "carousel_skip")}</a>
|
<a class="skip-link" href="#skip-#{child["commentId"]}">#{I18n.translate(locale, "carousel_skip")}</a>
|
||||||
<div class="slides">
|
<div class="slides">
|
||||||
END_HTML
|
END_HTML
|
||||||
image_array = attachment["images"].as_a
|
image_array = attachment["images"].as_a
|
||||||
|
|
||||||
image_array.each_index do |i|
|
image_array.each_index do |i|
|
||||||
html << <<-END_HTML
|
html << <<-END_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">
|
<div class="slides-item slide-#{i + 1}" id="#{child["commentId"]}-slide-#{i + 1}" aria-label="#{I18n.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="" />
|
<img loading="lazy" src="/ggpht#{URI.parse(image_array[i][1]["url"].as_s).request_target}" alt="" />
|
||||||
</div>
|
</div>
|
||||||
END_HTML
|
END_HTML
|
||||||
@ -129,7 +129,7 @@ module Invidious::Frontend::Comments
|
|||||||
END_HTML
|
END_HTML
|
||||||
attachment["images"].as_a.each_index do |i|
|
attachment["images"].as_a.each_index do |i|
|
||||||
html << <<-END_HTML
|
html << <<-END_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>
|
<a class="slider-nav" href="##{child["commentId"]}-slide-#{i + 1}" aria-label="#{I18n.translate(locale, "carousel_go_to", (i + 1).to_s)}" tabindex="-1" aria-hidden="true">#{i + 1}</a>
|
||||||
END_HTML
|
END_HTML
|
||||||
end
|
end
|
||||||
html << <<-END_HTML
|
html << <<-END_HTML
|
||||||
@ -143,18 +143,18 @@ module Invidious::Frontend::Comments
|
|||||||
|
|
||||||
html << <<-END_HTML
|
html << <<-END_HTML
|
||||||
<p>
|
<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>
|
<span title="#{Time.unix(child["published"].as_i64).to_s(I18n.translate(locale, "%A %B %-d, %Y"))}">#{I18n.translate(locale, "`x` ago", recode_date(Time.unix(child["published"].as_i64), locale))} #{child["isEdited"] == true ? I18n.translate(locale, "(edited)") : ""}</span>
|
||||||
|
|
|
|
||||||
END_HTML
|
END_HTML
|
||||||
|
|
||||||
if comments["videoId"]?
|
if comments["videoId"]?
|
||||||
html << <<-END_HTML
|
html << <<-END_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>
|
<a rel="noreferrer noopener" href="https://www.youtube.com/watch?v=#{comments["videoId"]}&lc=#{child["commentId"]}" title="#{I18n.translate(locale, "YouTube comment permalink")}">[YT]</a>
|
||||||
|
|
|
|
||||||
END_HTML
|
END_HTML
|
||||||
elsif comments["authorId"]?
|
elsif comments["authorId"]?
|
||||||
html << <<-END_HTML
|
html << <<-END_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>
|
<a rel="noreferrer noopener" href="https://www.youtube.com/channel/#{comments["authorId"]}/community?lb=#{child["commentId"]}" title="#{I18n.translate(locale, "YouTube comment permalink")}">[YT]</a>
|
||||||
|
|
|
|
||||||
END_HTML
|
END_HTML
|
||||||
end
|
end
|
||||||
@ -172,7 +172,7 @@ module Invidious::Frontend::Comments
|
|||||||
|
|
||||||
html << <<-END_HTML
|
html << <<-END_HTML
|
||||||
|
|
||||||
<span class="creator-heart-container" title="#{translate(locale, "`x` marked it with a ❤", child["creatorHeart"]["creatorName"].as_s)}">
|
<span class="creator-heart-container" title="#{I18n.translate(locale, "`x` marked it with a ❤", child["creatorHeart"]["creatorName"].as_s)}">
|
||||||
<span class="creator-heart">
|
<span class="creator-heart">
|
||||||
<img loading="lazy" class="creator-heart-background-hearted" src="#{creator_thumbnail}" alt="" />
|
<img loading="lazy" class="creator-heart-background-hearted" src="#{creator_thumbnail}" alt="" />
|
||||||
<span class="creator-heart-small-hearted">
|
<span class="creator-heart-small-hearted">
|
||||||
@ -197,7 +197,7 @@ module Invidious::Frontend::Comments
|
|||||||
<div class="pure-u-1">
|
<div class="pure-u-1">
|
||||||
<p>
|
<p>
|
||||||
<a href="javascript:void(0)" data-continuation="#{comments["continuation"]}"
|
<a href="javascript:void(0)" data-continuation="#{comments["continuation"]}"
|
||||||
data-onclick="get_youtube_replies" data-load-more #{"data-load-replies" if is_replies}>#{translate(locale, "Load more")}</a>
|
data-onclick="get_youtube_replies" data-load-more #{"data-load-replies" if is_replies}>#{I18n.translate(locale, "Load more")}</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,16 +6,16 @@ module Invidious::Frontend::Pagination
|
|||||||
private def first_page(str : String::Builder, locale : String?, url : String)
|
private def first_page(str : String::Builder, locale : String?, url : String)
|
||||||
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
||||||
|
|
||||||
if locale_is_rtl?(locale)
|
if I18n.locale_is_rtl?(locale)
|
||||||
# Inverted arrow ("first" points to the right)
|
# Inverted arrow ("first" points to the right)
|
||||||
str << translate(locale, "First page")
|
str << I18n.translate(locale, "First page")
|
||||||
str << " "
|
str << " "
|
||||||
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
||||||
else
|
else
|
||||||
# Regular arrow ("first" points to the left)
|
# Regular arrow ("first" points to the left)
|
||||||
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
||||||
str << " "
|
str << " "
|
||||||
str << translate(locale, "First page")
|
str << I18n.translate(locale, "First page")
|
||||||
end
|
end
|
||||||
|
|
||||||
str << "</a>"
|
str << "</a>"
|
||||||
@ -25,16 +25,16 @@ module Invidious::Frontend::Pagination
|
|||||||
# Link
|
# Link
|
||||||
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
||||||
|
|
||||||
if locale_is_rtl?(locale)
|
if I18n.locale_is_rtl?(locale)
|
||||||
# Inverted arrow ("previous" points to the right)
|
# Inverted arrow ("previous" points to the right)
|
||||||
str << translate(locale, "Previous page")
|
str << I18n.translate(locale, "Previous page")
|
||||||
str << " "
|
str << " "
|
||||||
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
||||||
else
|
else
|
||||||
# Regular arrow ("previous" points to the left)
|
# Regular arrow ("previous" points to the left)
|
||||||
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
||||||
str << " "
|
str << " "
|
||||||
str << translate(locale, "Previous page")
|
str << I18n.translate(locale, "Previous page")
|
||||||
end
|
end
|
||||||
|
|
||||||
str << "</a>"
|
str << "</a>"
|
||||||
@ -44,14 +44,14 @@ module Invidious::Frontend::Pagination
|
|||||||
# Link
|
# Link
|
||||||
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
str << %(<a href=") << url << %(" class="pure-button pure-button-secondary">)
|
||||||
|
|
||||||
if locale_is_rtl?(locale)
|
if I18n.locale_is_rtl?(locale)
|
||||||
# Inverted arrow ("next" points to the left)
|
# Inverted arrow ("next" points to the left)
|
||||||
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
str << %(<i class="icon ion-ios-arrow-back"></i>)
|
||||||
str << " "
|
str << " "
|
||||||
str << translate(locale, "Next page")
|
str << I18n.translate(locale, "Next page")
|
||||||
else
|
else
|
||||||
# Regular arrow ("next" points to the right)
|
# Regular arrow ("next" points to the right)
|
||||||
str << translate(locale, "Next page")
|
str << I18n.translate(locale, "Next page")
|
||||||
str << " "
|
str << " "
|
||||||
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
str << %(<i class="icon ion-ios-arrow-forward"></i>)
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
return String.build(8000) do |str|
|
return String.build(8000) do |str|
|
||||||
str << "<div id='filters'>\n"
|
str << "<div id='filters'>\n"
|
||||||
str << "\t<details id='filters-collapse'>"
|
str << "\t<details id='filters-collapse'>"
|
||||||
str << "\t\t<summary>" << translate(locale, "search_filters_title") << "</summary>\n"
|
str << "\t\t<summary>" << I18n.translate(locale, "search_filters_title") << "</summary>\n"
|
||||||
|
|
||||||
str << "\t\t<div id='filters-box'><form action='/search' method='get'>\n"
|
str << "\t\t<div id='filters-box'><form action='/search' method='get'>\n"
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
|
|
||||||
str << "\t\t\t<div id='filters-apply'>"
|
str << "\t\t\t<div id='filters-apply'>"
|
||||||
str << "<button type='submit' class=\"pure-button pure-button-primary\">"
|
str << "<button type='submit' class=\"pure-button pure-button-primary\">"
|
||||||
str << translate(locale, "search_filters_apply_button")
|
str << I18n.translate(locale, "search_filters_apply_button")
|
||||||
str << "</button></div>\n"
|
str << "</button></div>\n"
|
||||||
|
|
||||||
str << "\t\t</form></div>\n"
|
str << "\t\t</form></div>\n"
|
||||||
@ -41,7 +41,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
str << "\t\t\t\t<div class=\"filter-column\"><fieldset>\n"
|
str << "\t\t\t\t<div class=\"filter-column\"><fieldset>\n"
|
||||||
|
|
||||||
str << "\t\t\t\t\t<legend><div class=\"filter-name underlined\">"
|
str << "\t\t\t\t\t<legend><div class=\"filter-name underlined\">"
|
||||||
str << translate(locale, "search_filters_{{name}}_label")
|
str << I18n.translate(locale, "search_filters_{{name}}_label")
|
||||||
str << "</div></legend>\n"
|
str << "</div></legend>\n"
|
||||||
|
|
||||||
str << "\t\t\t\t\t<div class=\"filter-options\">\n"
|
str << "\t\t\t\t\t<div class=\"filter-options\">\n"
|
||||||
@ -62,7 +62,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
str << '>'
|
str << '>'
|
||||||
|
|
||||||
str << "<label for='filter-date-{{date}}'>"
|
str << "<label for='filter-date-{{date}}'>"
|
||||||
str << translate(locale, "search_filters_date_option_{{date}}")
|
str << I18n.translate(locale, "search_filters_date_option_{{date}}")
|
||||||
str << "</label></div>\n"
|
str << "</label></div>\n"
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
@ -78,7 +78,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
str << '>'
|
str << '>'
|
||||||
|
|
||||||
str << "<label for='filter-type-{{type}}'>"
|
str << "<label for='filter-type-{{type}}'>"
|
||||||
str << translate(locale, "search_filters_type_option_{{type}}")
|
str << I18n.translate(locale, "search_filters_type_option_{{type}}")
|
||||||
str << "</label></div>\n"
|
str << "</label></div>\n"
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
@ -94,7 +94,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
str << '>'
|
str << '>'
|
||||||
|
|
||||||
str << "<label for='filter-duration-{{duration}}'>"
|
str << "<label for='filter-duration-{{duration}}'>"
|
||||||
str << translate(locale, "search_filters_duration_option_{{duration}}")
|
str << I18n.translate(locale, "search_filters_duration_option_{{duration}}")
|
||||||
str << "</label></div>\n"
|
str << "</label></div>\n"
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
@ -111,7 +111,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
str << '>'
|
str << '>'
|
||||||
|
|
||||||
str << "<label for='filter-feature-{{feature}}'>"
|
str << "<label for='filter-feature-{{feature}}'>"
|
||||||
str << translate(locale, "search_filters_features_option_{{feature}}")
|
str << I18n.translate(locale, "search_filters_features_option_{{feature}}")
|
||||||
str << "</label></div>\n"
|
str << "</label></div>\n"
|
||||||
{% end %}
|
{% end %}
|
||||||
{% end %}
|
{% end %}
|
||||||
@ -128,7 +128,7 @@ module Invidious::Frontend::SearchFilters
|
|||||||
str << '>'
|
str << '>'
|
||||||
|
|
||||||
str << "<label for='filter-sort-{{sort}}'>"
|
str << "<label for='filter-sort-{{sort}}'>"
|
||||||
str << translate(locale, "search_filters_sort_option_{{sort}}")
|
str << I18n.translate(locale, "search_filters_sort_option_{{sort}}")
|
||||||
str << "</label></div>\n"
|
str << "</label></div>\n"
|
||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
@ -20,7 +20,7 @@ module Invidious::Frontend::WatchPage
|
|||||||
|
|
||||||
def download_widget(locale : String, video : Video, video_assets : VideoAssets) : String
|
def download_widget(locale : String, video : Video, video_assets : VideoAssets) : String
|
||||||
if CONFIG.disabled?("downloads")
|
if CONFIG.disabled?("downloads")
|
||||||
return "<p id=\"download\">#{translate(locale, "Download is disabled")}</p>"
|
return "<p id=\"download\">#{I18n.translate(locale, "Download is disabled")}</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
url = "/download"
|
url = "/download"
|
||||||
@ -45,7 +45,7 @@ module Invidious::Frontend::WatchPage
|
|||||||
str << "\t<div class=\"pure-control-group\">\n"
|
str << "\t<div class=\"pure-control-group\">\n"
|
||||||
|
|
||||||
str << "\t\t<label for='download_widget'>"
|
str << "\t\t<label for='download_widget'>"
|
||||||
str << translate(locale, "Download as: ")
|
str << I18n.translate(locale, "Download as: ")
|
||||||
str << "</label>\n"
|
str << "</label>\n"
|
||||||
|
|
||||||
str << "\t\t<select name='download_widget' id='download_widget'>\n"
|
str << "\t\t<select name='download_widget' id='download_widget'>\n"
|
||||||
@ -94,7 +94,7 @@ module Invidious::Frontend::WatchPage
|
|||||||
value = {"label": caption.name, "ext": "#{caption.language_code}.vtt"}.to_json
|
value = {"label": caption.name, "ext": "#{caption.language_code}.vtt"}.to_json
|
||||||
|
|
||||||
str << "\t\t\t<option value='" << value << "'>"
|
str << "\t\t\t<option value='" << value << "'>"
|
||||||
str << translate(locale, "download_subtitles", translate(locale, caption.name))
|
str << I18n.translate(locale, "download_subtitles", I18n.translate(locale, caption.name))
|
||||||
str << "</option>\n"
|
str << "</option>\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ module Invidious::Frontend::WatchPage
|
|||||||
str << "\t</div>\n"
|
str << "\t</div>\n"
|
||||||
|
|
||||||
str << "\t<button type=\"submit\" class=\"pure-button pure-button-primary\">\n"
|
str << "\t<button type=\"submit\" class=\"pure-button pure-button-primary\">\n"
|
||||||
str << "\t\t<b>" << translate(locale, "Download") << "</b>\n"
|
str << "\t\t<b>" << I18n.translate(locale, "Download") << "</b>\n"
|
||||||
str << "\t</button>\n"
|
str << "\t</button>\n"
|
||||||
|
|
||||||
str << "</form>\n"
|
str << "</form>\n"
|
||||||
|
@ -8,7 +8,7 @@ module Invidious::Hashtag
|
|||||||
client_config = YoutubeAPI::ClientConfig.new(region: region)
|
client_config = YoutubeAPI::ClientConfig.new(region: region)
|
||||||
response = YoutubeAPI.browse(continuation: ctoken, client_config: client_config)
|
response = YoutubeAPI.browse(continuation: ctoken, client_config: client_config)
|
||||||
|
|
||||||
items, _ = extract_items(response)
|
items, _ = YoutubeJSONParser.extract_items(response)
|
||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,19 +63,19 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, exce
|
|||||||
|
|
||||||
error_message = <<-END_HTML
|
error_message = <<-END_HTML
|
||||||
<div class="error_message">
|
<div class="error_message">
|
||||||
<h2>#{translate(locale, "crash_page_you_found_a_bug")}</h2>
|
<h2>#{I18n.translate(locale, "crash_page_you_found_a_bug")}</h2>
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
|
|
||||||
<p><b>#{translate(locale, "crash_page_before_reporting")}</b></p>
|
<p><b>#{I18n.translate(locale, "crash_page_before_reporting")}</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>#{translate(locale, "crash_page_refresh", env.request.resource)}</li>
|
<li>#{I18n.translate(locale, "crash_page_refresh", env.request.resource)}</li>
|
||||||
<li>#{translate(locale, "crash_page_switch_instance", url_switch)}</li>
|
<li>#{I18n.translate(locale, "crash_page_switch_instance", url_switch)}</li>
|
||||||
<li>#{translate(locale, "crash_page_read_the_faq", url_faq)}</li>
|
<li>#{I18n.translate(locale, "crash_page_read_the_faq", url_faq)}</li>
|
||||||
<li>#{translate(locale, "crash_page_search_issue", url_search_issues)}</li>
|
<li>#{I18n.translate(locale, "crash_page_search_issue", url_search_issues)}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<p>#{translate(locale, "crash_page_report_issue", url_new_issue)}</p>
|
<p>#{I18n.translate(locale, "crash_page_report_issue", url_new_issue)}</p>
|
||||||
|
|
||||||
<!-- TODO: Add a "copy to clipboard" button -->
|
<!-- TODO: Add a "copy to clipboard" button -->
|
||||||
<pre class="error-issue-template">#{issue_template}</pre>
|
<pre class="error-issue-template">#{issue_template}</pre>
|
||||||
@ -95,7 +95,7 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, mess
|
|||||||
|
|
||||||
locale = env.get("preferences").as(Preferences).locale
|
locale = env.get("preferences").as(Preferences).locale
|
||||||
|
|
||||||
error_message = translate(locale, message)
|
error_message = I18n.translate(locale, message)
|
||||||
next_steps = error_redirect_helper(env)
|
next_steps = error_redirect_helper(env)
|
||||||
|
|
||||||
return templated "error"
|
return templated "error"
|
||||||
@ -186,10 +186,10 @@ def error_redirect_helper(env : HTTP::Server::Context)
|
|||||||
|
|
||||||
if request_path.starts_with?("/search") || request_path.starts_with?("/watch") ||
|
if request_path.starts_with?("/search") || request_path.starts_with?("/watch") ||
|
||||||
request_path.starts_with?("/channel") || request_path.starts_with?("/playlist?list=PL")
|
request_path.starts_with?("/channel") || request_path.starts_with?("/playlist?list=PL")
|
||||||
next_steps_text = translate(locale, "next_steps_error_message")
|
next_steps_text = I18n.translate(locale, "next_steps_error_message")
|
||||||
refresh = translate(locale, "next_steps_error_message_refresh")
|
refresh = I18n.translate(locale, "next_steps_error_message_refresh")
|
||||||
go_to_youtube = translate(locale, "next_steps_error_message_go_to_youtube")
|
go_to_youtube = I18n.translate(locale, "next_steps_error_message_go_to_youtube")
|
||||||
switch_instance = translate(locale, "Switch Invidious Instance")
|
switch_instance = I18n.translate(locale, "Switch Invidious Instance")
|
||||||
|
|
||||||
return <<-END_HTML
|
return <<-END_HTML
|
||||||
<p style="margin-bottom: 4px;">#{next_steps_text}</p>
|
<p style="margin-bottom: 4px;">#{next_steps_text}</p>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
module I18n
|
||||||
|
extend self
|
||||||
# Languages requiring a better level of translation (at least 20%)
|
# Languages requiring a better level of translation (at least 20%)
|
||||||
# to be added to the list below:
|
# to be added to the list below:
|
||||||
#
|
#
|
||||||
@ -98,7 +100,7 @@ def translate(locale : String?, key : String, text : String | Hash(String, Strin
|
|||||||
# Log a warning if "key" doesn't exist in en-US locale and return
|
# 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.
|
# that key as the text, so this is more or less transparent to the user.
|
||||||
if !LOCALES["en-US"].has_key?(key)
|
if !LOCALES["en-US"].has_key?(key)
|
||||||
LOGGER.warn("i18n: Missing translation key \"#{key}\"")
|
Log.warn { "Missing translation key \"#{key}\"" }
|
||||||
return key
|
return key
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -165,7 +167,7 @@ def translate_count(locale : String, key : String, count : Int, format = NumberF
|
|||||||
translation = translate_count("en-US", key, count)
|
translation = translate_count("en-US", key, count)
|
||||||
else
|
else
|
||||||
# Return key if we're already in english, as the translation is missing
|
# Return key if we're already in english, as the translation is missing
|
||||||
LOGGER.warn("i18n: Missing translation key \"#{key}\"")
|
Log.warn { "Missing translation key \"#{key}\"" }
|
||||||
return key
|
return key
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -197,3 +199,4 @@ def locale_is_rtl?(locale : String?)
|
|||||||
# See https://en.wikipedia.org/wiki/Right-to-left_script#List_of_RTL_scripts
|
# See https://en.wikipedia.org/wiki/Right-to-left_script#List_of_RTL_scripts
|
||||||
return {"ar", "fa", "he"}.includes? locale
|
return {"ar", "fa", "he"}.includes? locale
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
@ -145,7 +145,7 @@ module I18next::Plurals
|
|||||||
if version > 4 || version == 0
|
if version > 4 || version == 0
|
||||||
raise "Invalid i18next version: v#{version}."
|
raise "Invalid i18next version: v#{version}."
|
||||||
elsif version == 4
|
elsif version == 4
|
||||||
# Logger.error("Unsupported i18next version: v4. Falling back to v3")
|
# Log.error { "Unsupported i18next version: v4. Falling back to v3" }
|
||||||
@version = 3_u8
|
@version = 3_u8
|
||||||
else
|
else
|
||||||
@version = version.to_u8
|
@version = version.to_u8
|
||||||
|
@ -1,72 +1,46 @@
|
|||||||
require "colorize"
|
require "colorize"
|
||||||
|
|
||||||
enum LogLevel
|
module Invidious::Logger
|
||||||
All = 0
|
extend self
|
||||||
Trace = 1
|
|
||||||
Debug = 2
|
|
||||||
Info = 3
|
|
||||||
Warn = 4
|
|
||||||
Error = 5
|
|
||||||
Fatal = 6
|
|
||||||
Off = 7
|
|
||||||
end
|
|
||||||
|
|
||||||
class Invidious::LogHandler < Kemal::BaseLogHandler
|
def formatter(use_color : Bool = true)
|
||||||
def initialize(@io : IO = STDOUT, @level = LogLevel::Debug, use_color : Bool = true)
|
|
||||||
Colorize.enabled = use_color
|
Colorize.enabled = use_color
|
||||||
Colorize.on_tty_only!
|
Colorize.on_tty_only!
|
||||||
|
|
||||||
|
formatter = ::Log::Formatter.new do |entry, io|
|
||||||
|
message = entry.message
|
||||||
|
severity = entry.severity
|
||||||
|
data = entry.data
|
||||||
|
source = entry.source
|
||||||
|
timestamp = entry.timestamp
|
||||||
|
|
||||||
|
io << (use_color ? timestamp.colorize(:dark_gray) : timestamp) << " "
|
||||||
|
io << (use_color ? colorize_severity(severity) : severity.label) << " "
|
||||||
|
io << (use_color ? source.colorize(:dark_gray) : source) << ": " if !source.empty?
|
||||||
|
io << message
|
||||||
|
if !data.empty?
|
||||||
|
io << " "
|
||||||
|
data.each do |dat|
|
||||||
|
io << (use_color ? dat[0].to_s.colorize(:light_cyan) : dat[0].to_s)
|
||||||
|
io << "="
|
||||||
|
io << dat[1].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(context : HTTP::Server::Context)
|
|
||||||
elapsed_time = Time.measure { call_next(context) }
|
|
||||||
elapsed_text = elapsed_text(elapsed_time)
|
|
||||||
|
|
||||||
# Default: full path with parameters
|
|
||||||
requested_url = context.request.resource
|
|
||||||
|
|
||||||
# Try not to log search queries passed as GET parameters during normal use
|
|
||||||
# (They will still be logged if log level is 'Debug' or 'Trace')
|
|
||||||
if @level > LogLevel::Debug && (
|
|
||||||
requested_url.downcase.includes?("search") || requested_url.downcase.includes?("q=")
|
|
||||||
)
|
|
||||||
# Log only the path
|
|
||||||
requested_url = context.request.path
|
|
||||||
end
|
|
||||||
|
|
||||||
info("#{context.response.status_code} #{context.request.method} #{requested_url} #{elapsed_text}")
|
|
||||||
|
|
||||||
context
|
|
||||||
end
|
|
||||||
|
|
||||||
def write(message : String)
|
|
||||||
@io << message
|
|
||||||
@io.flush
|
|
||||||
end
|
|
||||||
|
|
||||||
def color(level)
|
|
||||||
case level
|
|
||||||
when LogLevel::Trace then :cyan
|
|
||||||
when LogLevel::Debug then :green
|
|
||||||
when LogLevel::Info then :white
|
|
||||||
when LogLevel::Warn then :yellow
|
|
||||||
when LogLevel::Error then :red
|
|
||||||
when LogLevel::Fatal then :magenta
|
|
||||||
else :default
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
{% for level in %w(trace debug info warn error fatal) %}
|
return formatter
|
||||||
def {{level.id}}(message : String)
|
|
||||||
if LogLevel::{{level.id.capitalize}} >= @level
|
|
||||||
puts("#{Time.utc} [{{level.id}}] #{message}".colorize(color(LogLevel::{{level.id.capitalize}})))
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
|
|
||||||
private def elapsed_text(elapsed)
|
private def colorize_severity(severity : Log::Severity)
|
||||||
millis = elapsed.total_milliseconds
|
case severity
|
||||||
return "#{millis.round(2)}ms" if millis >= 1
|
in Log::Severity::Trace then severity.label.colorize(:cyan)
|
||||||
|
in Log::Severity::Info then severity.label.colorize(:green)
|
||||||
"#{(millis * 1000).round(2)}µs"
|
in Log::Severity::Notice then severity.label.colorize(:light_yellow)
|
||||||
|
in Log::Severity::Warn then severity.label.colorize(:yellow)
|
||||||
|
in Log::Severity::Error then severity.label.colorize(:red)
|
||||||
|
in Log::Severity::Fatal then severity.label.colorize(:red)
|
||||||
|
in Log::Severity::Debug then severity.label
|
||||||
|
in Log::Severity::None then severity.label
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -70,3 +70,9 @@ macro haltf(env, status_code = 200, response = "")
|
|||||||
{{env}}.response.close
|
{{env}}.response.close
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Log
|
||||||
|
macro forf
|
||||||
|
Log.for({{@def.name.stringify}})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@ -115,9 +115,9 @@ struct SearchVideo
|
|||||||
json.field "descriptionHtml", self.description_html
|
json.field "descriptionHtml", self.description_html
|
||||||
|
|
||||||
json.field "viewCount", self.views
|
json.field "viewCount", self.views
|
||||||
json.field "viewCountText", translate_count(locale, "generic_views_count", self.views, NumberFormatting::Short)
|
json.field "viewCountText", I18n.translate_count(locale, "generic_views_count", self.views, I18n::NumberFormatting::Short)
|
||||||
json.field "published", self.published.to_unix
|
json.field "published", self.published.to_unix
|
||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(self.published, locale))
|
json.field "publishedText", I18n.translate(locale, "`x` ago", recode_date(self.published, locale))
|
||||||
json.field "lengthSeconds", self.length_seconds
|
json.field "lengthSeconds", self.length_seconds
|
||||||
json.field "liveNow", self.badges.live_now?
|
json.field "liveNow", self.badges.live_now?
|
||||||
json.field "premium", self.badges.premium?
|
json.field "premium", self.badges.premium?
|
||||||
@ -327,8 +327,8 @@ struct ProblematicTimelineItem
|
|||||||
xml.element("content", type: "xhtml") do
|
xml.element("content", type: "xhtml") do
|
||||||
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
|
xml.element("div", xmlns: "http://www.w3.org/1999/xhtml") do
|
||||||
xml.element("div") do
|
xml.element("div") do
|
||||||
xml.element("h4") { translate(locale, "timeline_parse_error_placeholder_heading") }
|
xml.element("h4") { I18n.translate(locale, "timeline_parse_error_placeholder_heading") }
|
||||||
xml.element("p") { translate(locale, "timeline_parse_error_placeholder_message") }
|
xml.element("p") { I18n.translate(locale, "timeline_parse_error_placeholder_message") }
|
||||||
end
|
end
|
||||||
|
|
||||||
xml.element("pre") do
|
xml.element("pre") do
|
||||||
|
@ -73,6 +73,8 @@ module Invidious::SigHelper
|
|||||||
# ----------------------
|
# ----------------------
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
@mux : Multiplexor
|
@mux : Multiplexor
|
||||||
|
|
||||||
def initialize(uri_or_path)
|
def initialize(uri_or_path)
|
||||||
@ -156,8 +158,8 @@ module Invidious::SigHelper
|
|||||||
slice = channel.receive
|
slice = channel.receive
|
||||||
return yield slice
|
return yield slice
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug("SigHelper: Error when sending a request")
|
Log.debug { "Error when sending a request" }
|
||||||
LOGGER.trace(ex.inspect_with_backtrace)
|
Log.trace { ex.inspect_with_backtrace }
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -167,6 +169,8 @@ module Invidious::SigHelper
|
|||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
class Multiplexor
|
class Multiplexor
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
alias TransactionID = UInt32
|
alias TransactionID = UInt32
|
||||||
record Transaction, channel = ::Channel(Bytes).new
|
record Transaction, channel = ::Channel(Bytes).new
|
||||||
|
|
||||||
@ -185,22 +189,22 @@ module Invidious::SigHelper
|
|||||||
def listen : Nil
|
def listen : Nil
|
||||||
raise "Socket is closed" if @conn.closed?
|
raise "Socket is closed" if @conn.closed?
|
||||||
|
|
||||||
LOGGER.debug("SigHelper: Multiplexor listening")
|
Log.debug { "Multiplexor listening" }
|
||||||
|
|
||||||
spawn do
|
spawn do
|
||||||
loop do
|
loop do
|
||||||
begin
|
begin
|
||||||
receive_data
|
receive_data
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.info("SigHelper: Connection to helper died with '#{ex.message}' trying to reconnect...")
|
Log.info { "Connection to helper died with '#{ex.message}' trying to reconnect..." }
|
||||||
# We close the socket because for some reason is not closed.
|
# We close the socket because for some reason is not closed.
|
||||||
@conn.close
|
@conn.close
|
||||||
loop do
|
loop do
|
||||||
begin
|
begin
|
||||||
@conn = Connection.new(@uri_or_path)
|
@conn = Connection.new(@uri_or_path)
|
||||||
LOGGER.info("SigHelper: Reconnected to SigHelper!")
|
Log.info { "Reconnected to SigHelper!" }
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug("SigHelper: Reconnection to helper unsuccessful with error '#{ex.message}'. Retrying")
|
Log.debug { "Reconnection to helper unsuccessful with error '#{ex.message}'. Retrying" }
|
||||||
sleep 500.milliseconds
|
sleep 500.milliseconds
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
@ -238,7 +242,7 @@ module Invidious::SigHelper
|
|||||||
if transaction = @queue.delete(transaction_id)
|
if transaction = @queue.delete(transaction_id)
|
||||||
# Remove transaction from queue and send data to the channel
|
# Remove transaction from queue and send data to the channel
|
||||||
transaction.channel.send(slice)
|
transaction.channel.send(slice)
|
||||||
LOGGER.trace("SigHelper: Transaction unqueued and data sent to channel")
|
Log.trace { "Transaction unqueued and data sent to channel" }
|
||||||
else
|
else
|
||||||
raise Exception.new("SigHelper: Received transaction was not in queue")
|
raise Exception.new("SigHelper: Received transaction was not in queue")
|
||||||
end
|
end
|
||||||
@ -251,7 +255,7 @@ module Invidious::SigHelper
|
|||||||
transaction_id = @conn.read_bytes(UInt32, NetworkEndian)
|
transaction_id = @conn.read_bytes(UInt32, NetworkEndian)
|
||||||
length = @conn.read_bytes(UInt32, NetworkEndian)
|
length = @conn.read_bytes(UInt32, NetworkEndian)
|
||||||
|
|
||||||
LOGGER.trace("SigHelper: Recv transaction 0x#{transaction_id.to_s(base: 16)} / length #{length}")
|
Log.trace { "Recv transaction 0x#{transaction_id.to_s(base: 16)} / length #{length}" }
|
||||||
|
|
||||||
if length > 67_000
|
if length > 67_000
|
||||||
raise Exception.new("SigHelper: Packet longer than expected (#{length})")
|
raise Exception.new("SigHelper: Packet longer than expected (#{length})")
|
||||||
@ -261,15 +265,15 @@ module Invidious::SigHelper
|
|||||||
slice = Bytes.new(length)
|
slice = Bytes.new(length)
|
||||||
@conn.read(slice) if length > 0
|
@conn.read(slice) if length > 0
|
||||||
|
|
||||||
LOGGER.trace("SigHelper: payload = #{slice}")
|
Log.trace { "payload = #{slice}" }
|
||||||
LOGGER.trace("SigHelper: Recv transaction 0x#{transaction_id.to_s(base: 16)} - Done")
|
Log.trace { "Recv transaction 0x#{transaction_id.to_s(base: 16)} - Done" }
|
||||||
|
|
||||||
return transaction_id, slice
|
return transaction_id, slice
|
||||||
end
|
end
|
||||||
|
|
||||||
# Write a single packet to the socket
|
# Write a single packet to the socket
|
||||||
private def write_packet(transaction_id : TransactionID, request : Request)
|
private def write_packet(transaction_id : TransactionID, request : Request)
|
||||||
LOGGER.trace("SigHelper: Send transaction 0x#{transaction_id.to_s(base: 16)} / opcode #{request.opcode}")
|
Log.trace { "Send transaction 0x#{transaction_id.to_s(base: 16)} / opcode #{request.opcode}" }
|
||||||
|
|
||||||
io = IO::Memory.new(1024)
|
io = IO::Memory.new(1024)
|
||||||
io.write_bytes(request.opcode.to_u8, NetworkEndian)
|
io.write_bytes(request.opcode.to_u8, NetworkEndian)
|
||||||
@ -282,11 +286,13 @@ module Invidious::SigHelper
|
|||||||
@conn.send(io)
|
@conn.send(io)
|
||||||
@conn.flush
|
@conn.flush
|
||||||
|
|
||||||
LOGGER.trace("SigHelper: Send transaction 0x#{transaction_id.to_s(base: 16)} - Done")
|
Log.trace { "Send transaction 0x#{transaction_id.to_s(base: 16)} - Done" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Connection
|
class Connection
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
@socket : UNIXSocket | TCPSocket
|
@socket : UNIXSocket | TCPSocket
|
||||||
|
|
||||||
{% if flag?(:advanced_debug) %}
|
{% if flag?(:advanced_debug) %}
|
||||||
@ -309,7 +315,7 @@ module Invidious::SigHelper
|
|||||||
uri = URI.parse("tcp://#{host_or_path}")
|
uri = URI.parse("tcp://#{host_or_path}")
|
||||||
@socket = TCPSocket.new(uri.host.not_nil!, uri.port.not_nil!)
|
@socket = TCPSocket.new(uri.host.not_nil!, uri.port.not_nil!)
|
||||||
end
|
end
|
||||||
LOGGER.info("SigHelper: Using helper at '#{host_or_path}'")
|
Log.info { "Using helper at '#{host_or_path}'" }
|
||||||
|
|
||||||
{% if flag?(:advanced_debug) %}
|
{% if flag?(:advanced_debug) %}
|
||||||
@io = IO::Hexdump.new(@socket, output: STDERR, read: true, write: true)
|
@io = IO::Hexdump.new(@socket, output: STDERR, read: true, write: true)
|
||||||
|
@ -2,6 +2,8 @@ require "http/params"
|
|||||||
require "./sig_helper"
|
require "./sig_helper"
|
||||||
|
|
||||||
class Invidious::DecryptFunction
|
class Invidious::DecryptFunction
|
||||||
|
Log = ::Log.for(self).for("Signature")
|
||||||
|
|
||||||
@last_update : Time = Time.utc - 42.days
|
@last_update : Time = Time.utc - 42.days
|
||||||
|
|
||||||
def initialize(uri_or_path)
|
def initialize(uri_or_path)
|
||||||
@ -18,7 +20,7 @@ class Invidious::DecryptFunction
|
|||||||
update_time_elapsed = (@client.get_player_timestamp || 301).seconds
|
update_time_elapsed = (@client.get_player_timestamp || 301).seconds
|
||||||
|
|
||||||
if update_time_elapsed > 5.minutes
|
if update_time_elapsed > 5.minutes
|
||||||
LOGGER.debug("Signature: Player might be outdated, updating")
|
Log.debug { "Player might be outdated, updating" }
|
||||||
@client.force_update
|
@client.force_update
|
||||||
@last_update = Time.utc
|
@last_update = Time.utc
|
||||||
end
|
end
|
||||||
@ -28,8 +30,8 @@ class Invidious::DecryptFunction
|
|||||||
self.check_update
|
self.check_update
|
||||||
return @client.decrypt_n_param(n)
|
return @client.decrypt_n_param(n)
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug(ex.message || "Signature: Unknown error")
|
Log.debug { ex.message || "Unknown error" }
|
||||||
LOGGER.trace(ex.inspect_with_backtrace)
|
Log.trace { ex.inspect_with_backtrace }
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -37,8 +39,8 @@ class Invidious::DecryptFunction
|
|||||||
self.check_update
|
self.check_update
|
||||||
return @client.decrypt_sig(str)
|
return @client.decrypt_sig(str)
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug(ex.message || "Signature: Unknown error")
|
Log.debug { ex.message || "Unknown error" }
|
||||||
LOGGER.trace(ex.inspect_with_backtrace)
|
Log.trace { ex.inspect_with_backtrace }
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -46,8 +48,8 @@ class Invidious::DecryptFunction
|
|||||||
self.check_update
|
self.check_update
|
||||||
return @client.get_signature_timestamp
|
return @client.get_signature_timestamp
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug(ex.message || "Signature: Unknown error")
|
Log.debug { ex.message || "Unknown error" }
|
||||||
LOGGER.trace(ex.inspect_with_backtrace)
|
Log.trace { ex.inspect_with_backtrace }
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -144,19 +144,19 @@ def recode_date(time : Time, locale)
|
|||||||
span = Time.utc - time
|
span = Time.utc - time
|
||||||
|
|
||||||
if span.total_days > 365.0
|
if span.total_days > 365.0
|
||||||
return translate_count(locale, "generic_count_years", span.total_days.to_i // 365)
|
return I18n.translate_count(locale, "generic_count_years", span.total_days.to_i // 365)
|
||||||
elsif span.total_days > 30.0
|
elsif span.total_days > 30.0
|
||||||
return translate_count(locale, "generic_count_months", span.total_days.to_i // 30)
|
return I18n.translate_count(locale, "generic_count_months", span.total_days.to_i // 30)
|
||||||
elsif span.total_days > 7.0
|
elsif span.total_days > 7.0
|
||||||
return translate_count(locale, "generic_count_weeks", span.total_days.to_i // 7)
|
return I18n.translate_count(locale, "generic_count_weeks", span.total_days.to_i // 7)
|
||||||
elsif span.total_hours > 24.0
|
elsif span.total_hours > 24.0
|
||||||
return translate_count(locale, "generic_count_days", span.total_days.to_i)
|
return I18n.translate_count(locale, "generic_count_days", span.total_days.to_i)
|
||||||
elsif span.total_minutes > 60.0
|
elsif span.total_minutes > 60.0
|
||||||
return translate_count(locale, "generic_count_hours", span.total_hours.to_i)
|
return I18n.translate_count(locale, "generic_count_hours", span.total_hours.to_i)
|
||||||
elsif span.total_seconds > 60.0
|
elsif span.total_seconds > 60.0
|
||||||
return translate_count(locale, "generic_count_minutes", span.total_minutes.to_i)
|
return I18n.translate_count(locale, "generic_count_minutes", span.total_minutes.to_i)
|
||||||
else
|
else
|
||||||
return translate_count(locale, "generic_count_seconds", span.total_seconds.to_i)
|
return I18n.translate_count(locale, "generic_count_seconds", span.total_seconds.to_i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
class Invidious::Jobs::ClearExpiredItemsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::ClearExpiredItemsJob < Invidious::Jobs::BaseJob
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
# Remove items (videos, nonces, etc..) whose cache is outdated every hour.
|
# Remove items (videos, nonces, etc..) whose cache is outdated every hour.
|
||||||
# Removes the need for a cron job.
|
# Removes the need for a cron job.
|
||||||
def begin
|
def begin
|
||||||
loop do
|
loop do
|
||||||
failed = false
|
failed = false
|
||||||
|
|
||||||
LOGGER.info("jobs: running ClearExpiredItems job")
|
Log.info { "running ClearExpiredItemsJob job" }
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Invidious::Database::Videos.delete_expired
|
Invidious::Database::Videos.delete_expired
|
||||||
@ -16,10 +18,10 @@ class Invidious::Jobs::ClearExpiredItemsJob < Invidious::Jobs::BaseJob
|
|||||||
|
|
||||||
# Retry earlier than scheduled on DB error
|
# Retry earlier than scheduled on DB error
|
||||||
if failed
|
if failed
|
||||||
LOGGER.info("jobs: ClearExpiredItems failed. Retrying in 10 minutes.")
|
Log.info { "ClearExpiredItems failed. Retrying in 10 minutes." }
|
||||||
sleep 10.minutes
|
sleep 10.minutes
|
||||||
else
|
else
|
||||||
LOGGER.info("jobs: ClearExpiredItems done.")
|
Log.info { "ClearExpiredItems done." }
|
||||||
sleep 1.hour
|
sleep 1.hour
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
# We update the internals of a constant as so it can be accessed from anywhere
|
# We update the internals of a constant as so it can be accessed from anywhere
|
||||||
# within the codebase
|
# within the codebase
|
||||||
#
|
#
|
||||||
@ -12,7 +14,7 @@ class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
|
|||||||
def begin
|
def begin
|
||||||
loop do
|
loop do
|
||||||
refresh_instances
|
refresh_instances
|
||||||
LOGGER.info("InstanceListRefreshJob: Done, sleeping for 30 minutes")
|
Log.info { "Done, sleeping for 30 minutes" }
|
||||||
sleep 30.minute
|
sleep 30.minute
|
||||||
Fiber.yield
|
Fiber.yield
|
||||||
end
|
end
|
||||||
@ -43,9 +45,9 @@ class Invidious::Jobs::InstanceListRefreshJob < Invidious::Jobs::BaseJob
|
|||||||
filtered_instance_list << {info["region"].as_s, domain.as_s}
|
filtered_instance_list << {info["region"].as_s, domain.as_s}
|
||||||
rescue ex
|
rescue ex
|
||||||
if domain
|
if domain
|
||||||
LOGGER.info("InstanceListRefreshJob: failed to parse information from '#{domain}' because \"#{ex}\"\n\"#{ex.backtrace.join('\n')}\" ")
|
Log.info { "failed to parse information from '#{domain}' because \"#{ex}\"\n\"#{ex.backtrace.join('\n')}\" " }
|
||||||
else
|
else
|
||||||
LOGGER.info("InstanceListRefreshJob: failed to parse information from an instance because \"#{ex}\"\n\"#{ex.backtrace.join('\n')}\" ")
|
Log.info { "failed to parse information from an instance because \"#{ex}\"\n\"#{ex.backtrace.join('\n')}\" " }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -22,6 +22,8 @@ struct VideoNotification
|
|||||||
end
|
end
|
||||||
|
|
||||||
class Invidious::Jobs::NotificationJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::NotificationJob < Invidious::Jobs::BaseJob
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
private getter notification_channel : ::Channel(VideoNotification)
|
private getter notification_channel : ::Channel(VideoNotification)
|
||||||
private getter connection_channel : ::Channel({Bool, ::Channel(PQ::Notification)})
|
private getter connection_channel : ::Channel({Bool, ::Channel(PQ::Notification)})
|
||||||
private getter pg_url : URI
|
private getter pg_url : URI
|
||||||
@ -57,7 +59,7 @@ class Invidious::Jobs::NotificationJob < Invidious::Jobs::BaseJob
|
|||||||
spawn do
|
spawn do
|
||||||
loop do
|
loop do
|
||||||
begin
|
begin
|
||||||
LOGGER.debug("NotificationJob: waking up")
|
Log.debug { "waking up" }
|
||||||
cloned = {} of String => Set(VideoNotification)
|
cloned = {} of String => Set(VideoNotification)
|
||||||
notify_mutex.synchronize do
|
notify_mutex.synchronize do
|
||||||
cloned = to_notify.clone
|
cloned = to_notify.clone
|
||||||
@ -69,7 +71,7 @@ class Invidious::Jobs::NotificationJob < Invidious::Jobs::BaseJob
|
|||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.info("NotificationJob: updating channel #{channel_id} with #{notifications.size} notifications")
|
Log.info { "updating channel #{channel_id} with #{notifications.size} notifications" }
|
||||||
if CONFIG.enable_user_notifications
|
if CONFIG.enable_user_notifications
|
||||||
video_ids = notifications.map(&.video_id)
|
video_ids = notifications.map(&.video_id)
|
||||||
Invidious::Database::Users.add_multiple_notifications(channel_id, video_ids)
|
Invidious::Database::Users.add_multiple_notifications(channel_id, video_ids)
|
||||||
@ -89,9 +91,9 @@ class Invidious::Jobs::NotificationJob < Invidious::Jobs::BaseJob
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.trace("NotificationJob: Done, sleeping")
|
Log.trace { "Done, sleeping" }
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.error("NotificationJob: #{ex.message}")
|
Log.error { ex.message }
|
||||||
end
|
end
|
||||||
sleep 1.minute
|
sleep 1.minute
|
||||||
Fiber.yield
|
Fiber.yield
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
|
|
||||||
def initialize(@db)
|
def initialize(@db)
|
||||||
@ -12,37 +14,37 @@ class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
|||||||
backoff = 2.minutes
|
backoff = 2.minutes
|
||||||
|
|
||||||
loop do
|
loop do
|
||||||
LOGGER.debug("RefreshChannelsJob: Refreshing all channels")
|
Log.debug { "Refreshing all channels" }
|
||||||
PG_DB.query("SELECT id FROM channels ORDER BY updated") do |rs|
|
PG_DB.query("SELECT id FROM channels ORDER BY updated") do |rs|
|
||||||
rs.each do
|
rs.each do
|
||||||
id = rs.read(String)
|
id = rs.read(String)
|
||||||
|
|
||||||
if active_fibers >= lim_fibers
|
if active_fibers >= lim_fibers
|
||||||
LOGGER.trace("RefreshChannelsJob: Fiber limit reached, waiting...")
|
Log.trace { "Fiber limit reached, waiting..." }
|
||||||
if active_channel.receive
|
if active_channel.receive
|
||||||
LOGGER.trace("RefreshChannelsJob: Fiber limit ok, continuing")
|
Log.trace { "Fiber limit ok, continuing" }
|
||||||
active_fibers -= 1
|
active_fibers -= 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.debug("RefreshChannelsJob: #{id} : Spawning fiber")
|
Log.debug { "#{id} : Spawning fiber" }
|
||||||
active_fibers += 1
|
active_fibers += 1
|
||||||
spawn do
|
spawn do
|
||||||
begin
|
begin
|
||||||
LOGGER.trace("RefreshChannelsJob: #{id} fiber : Fetching channel")
|
Log.trace { "#{id} fiber : Fetching channel" }
|
||||||
channel = fetch_channel(id, pull_all_videos: CONFIG.full_refresh)
|
channel = fetch_channel(id, pull_all_videos: CONFIG.full_refresh)
|
||||||
|
|
||||||
lim_fibers = max_fibers
|
lim_fibers = max_fibers
|
||||||
|
|
||||||
LOGGER.trace("RefreshChannelsJob: #{id} fiber : Updating DB")
|
Log.trace { "#{id} fiber : Updating DB" }
|
||||||
Invidious::Database::Channels.update_author(id, channel.author)
|
Invidious::Database::Channels.update_author(id, channel.author)
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.error("RefreshChannelsJob: #{id} : #{ex.message}")
|
Log.error { "#{id} : #{ex.message}" }
|
||||||
if ex.message == "Deleted or invalid channel"
|
if ex.message == "Deleted or invalid channel"
|
||||||
Invidious::Database::Channels.update_mark_deleted(id)
|
Invidious::Database::Channels.update_mark_deleted(id)
|
||||||
else
|
else
|
||||||
lim_fibers = 1
|
lim_fibers = 1
|
||||||
LOGGER.error("RefreshChannelsJob: #{id} fiber : backing off for #{backoff}s")
|
Log.error { "#{id} fiber : backing off for #{backoff}s" }
|
||||||
sleep backoff
|
sleep backoff
|
||||||
if backoff < 1.days
|
if backoff < 1.days
|
||||||
backoff += backoff
|
backoff += backoff
|
||||||
@ -51,14 +53,14 @@ class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
LOGGER.debug("RefreshChannelsJob: #{id} fiber : Done")
|
Log.debug { "#{id} fiber : Done" }
|
||||||
active_channel.send(true)
|
active_channel.send(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.debug("RefreshChannelsJob: Done, sleeping for #{CONFIG.channel_refresh_interval}")
|
Log.debug { "Done, sleeping for #{CONFIG.channel_refresh_interval}" }
|
||||||
sleep CONFIG.channel_refresh_interval
|
sleep CONFIG.channel_refresh_interval
|
||||||
Fiber.yield
|
Fiber.yield
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
class Invidious::Jobs::RefreshFeedsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::RefreshFeedsJob < Invidious::Jobs::BaseJob
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
|
|
||||||
def initialize(@db)
|
def initialize(@db)
|
||||||
@ -28,14 +30,14 @@ class Invidious::Jobs::RefreshFeedsJob < Invidious::Jobs::BaseJob
|
|||||||
column_array = Invidious::Database.get_column_array(db, view_name)
|
column_array = Invidious::Database.get_column_array(db, view_name)
|
||||||
ChannelVideo.type_array.each_with_index do |name, i|
|
ChannelVideo.type_array.each_with_index do |name, i|
|
||||||
if name != column_array[i]?
|
if name != column_array[i]?
|
||||||
LOGGER.info("RefreshFeedsJob: DROP MATERIALIZED VIEW #{view_name}")
|
Log.info { "DROP MATERIALIZED VIEW #{view_name}" }
|
||||||
db.exec("DROP MATERIALIZED VIEW #{view_name}")
|
db.exec("DROP MATERIALIZED VIEW #{view_name}")
|
||||||
raise "view does not exist"
|
raise "view does not exist"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if !db.query_one("SELECT pg_get_viewdef('#{view_name}')", as: String).includes? "WHERE ((cv.ucid = ANY (u.subscriptions))"
|
if !db.query_one("SELECT pg_get_viewdef('#{view_name}')", as: String).includes? "WHERE ((cv.ucid = ANY (u.subscriptions))"
|
||||||
LOGGER.info("RefreshFeedsJob: Materialized view #{view_name} is out-of-date, recreating...")
|
Log.info { "Materialized view #{view_name} is out-of-date, recreating..." }
|
||||||
db.exec("DROP MATERIALIZED VIEW #{view_name}")
|
db.exec("DROP MATERIALIZED VIEW #{view_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -47,18 +49,18 @@ class Invidious::Jobs::RefreshFeedsJob < Invidious::Jobs::BaseJob
|
|||||||
legacy_view_name = "subscriptions_#{sha256(email)[0..7]}"
|
legacy_view_name = "subscriptions_#{sha256(email)[0..7]}"
|
||||||
|
|
||||||
db.exec("SELECT * FROM #{legacy_view_name} LIMIT 0")
|
db.exec("SELECT * FROM #{legacy_view_name} LIMIT 0")
|
||||||
LOGGER.info("RefreshFeedsJob: RENAME MATERIALIZED VIEW #{legacy_view_name}")
|
Log.info { "RENAME MATERIALIZED VIEW #{legacy_view_name}" }
|
||||||
db.exec("ALTER MATERIALIZED VIEW #{legacy_view_name} RENAME TO #{view_name}")
|
db.exec("ALTER MATERIALIZED VIEW #{legacy_view_name} RENAME TO #{view_name}")
|
||||||
rescue ex
|
rescue ex
|
||||||
begin
|
begin
|
||||||
# While iterating through, we may have an email stored from a deleted account
|
# While iterating through, we may have an email stored from a deleted account
|
||||||
if db.query_one?("SELECT true FROM users WHERE email = $1", email, as: Bool)
|
if db.query_one?("SELECT true FROM users WHERE email = $1", email, as: Bool)
|
||||||
LOGGER.info("RefreshFeedsJob: CREATE #{view_name}")
|
Log.info { "CREATE #{view_name}" }
|
||||||
db.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(email)}")
|
db.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(email)}")
|
||||||
db.exec("UPDATE users SET feed_needs_update = false WHERE email = $1", email)
|
db.exec("UPDATE users SET feed_needs_update = false WHERE email = $1", email)
|
||||||
end
|
end
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.error("RefreshFeedJobs: REFRESH #{email} : #{ex.message}")
|
Log.error { "REFRESH #{email} : #{ex.message}" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
class Invidious::Jobs::SubscribeToFeedsJob < Invidious::Jobs::BaseJob
|
class Invidious::Jobs::SubscribeToFeedsJob < Invidious::Jobs::BaseJob
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
private getter db : DB::Database
|
private getter db : DB::Database
|
||||||
private getter hmac_key : String
|
private getter hmac_key : String
|
||||||
|
|
||||||
@ -32,10 +34,10 @@ class Invidious::Jobs::SubscribeToFeedsJob < Invidious::Jobs::BaseJob
|
|||||||
response = subscribe_pubsub(ucid, hmac_key)
|
response = subscribe_pubsub(ucid, hmac_key)
|
||||||
|
|
||||||
if response.status_code >= 400
|
if response.status_code >= 400
|
||||||
LOGGER.error("SubscribeToFeedsJob: #{ucid} : #{response.body}")
|
Log.error { "#{ucid} : #{response.body}" }
|
||||||
end
|
end
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.error("SubscribeToFeedsJob: #{ucid} : #{ex.message}")
|
Log.error { "#{ucid} : #{ex.message}" }
|
||||||
end
|
end
|
||||||
|
|
||||||
active_channel.send(true)
|
active_channel.send(true)
|
||||||
|
@ -22,7 +22,7 @@ module Invidious::JSONify::APIv1
|
|||||||
json.field "description", video.description
|
json.field "description", video.description
|
||||||
json.field "descriptionHtml", video.description_html
|
json.field "descriptionHtml", video.description_html
|
||||||
json.field "published", video.published.to_unix
|
json.field "published", video.published.to_unix
|
||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(video.published, locale))
|
json.field "publishedText", I18n.translate(locale, "`x` ago", recode_date(video.published, locale))
|
||||||
json.field "keywords", video.keywords
|
json.field "keywords", video.keywords
|
||||||
|
|
||||||
json.field "viewCount", video.views
|
json.field "viewCount", video.views
|
||||||
@ -269,7 +269,7 @@ module Invidious::JSONify::APIv1
|
|||||||
json.field "viewCount", rv["view_count"]?.try &.empty? ? nil : rv["view_count"].to_i64
|
json.field "viewCount", rv["view_count"]?.try &.empty? ? nil : rv["view_count"].to_i64
|
||||||
json.field "published", rv["published"]?
|
json.field "published", rv["published"]?
|
||||||
if rv["published"]?.try &.presence
|
if rv["published"]?.try &.presence
|
||||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(Time.parse_rfc3339(rv["published"].to_s), locale))
|
json.field "publishedText", I18n.translate(locale, "`x` ago", recode_date(Time.parse_rfc3339(rv["published"].to_s), locale))
|
||||||
else
|
else
|
||||||
json.field "publishedText", ""
|
json.field "publishedText", ""
|
||||||
end
|
end
|
||||||
|
@ -7,7 +7,7 @@ module Invidious::Routes::BeforeAll
|
|||||||
preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value))
|
preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value))
|
||||||
else
|
else
|
||||||
if language_header = env.request.headers["Accept-Language"]?
|
if language_header = env.request.headers["Accept-Language"]?
|
||||||
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
if language = ANG.language_negotiator.best(language_header, I18n::LOCALES.keys)
|
||||||
preferences.locale = language.header
|
preferences.locale = language.header
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -352,7 +352,7 @@ module Invidious::Routes::Channels
|
|||||||
resolved_url = YoutubeAPI.resolve_url("https://youtube.com#{env.request.path}#{yt_url_params.size > 0 ? "?#{yt_url_params}" : ""}")
|
resolved_url = YoutubeAPI.resolve_url("https://youtube.com#{env.request.path}#{yt_url_params.size > 0 ? "?#{yt_url_params}" : ""}")
|
||||||
ucid = resolved_url["endpoint"]["browseEndpoint"]["browseId"]
|
ucid = resolved_url["endpoint"]["browseEndpoint"]["browseId"]
|
||||||
rescue ex : InfoException | KeyError
|
rescue ex : InfoException | KeyError
|
||||||
return error_template(404, translate(locale, "This channel does not exist."))
|
return error_template(404, I18n.translate(locale, "This channel does not exist."))
|
||||||
end
|
end
|
||||||
|
|
||||||
selected_tab = env.params.url["tab"]?
|
selected_tab = env.params.url["tab"]?
|
||||||
|
@ -10,7 +10,7 @@ module Invidious::Routes::Embed
|
|||||||
videos = get_playlist_videos(playlist, offset: offset)
|
videos = get_playlist_videos(playlist, offset: offset)
|
||||||
if videos.empty?
|
if videos.empty?
|
||||||
url = "/playlist?list=#{plid}"
|
url = "/playlist?list=#{plid}"
|
||||||
raise NotFoundException.new(translate(locale, "error_video_not_in_playlist", url))
|
raise NotFoundException.new(I18n.translate(locale, "error_video_not_in_playlist", url))
|
||||||
end
|
end
|
||||||
|
|
||||||
first_playlist_video = videos[0].as(PlaylistVideo)
|
first_playlist_video = videos[0].as(PlaylistVideo)
|
||||||
@ -72,7 +72,7 @@ module Invidious::Routes::Embed
|
|||||||
videos = get_playlist_videos(playlist, offset: offset)
|
videos = get_playlist_videos(playlist, offset: offset)
|
||||||
if videos.empty?
|
if videos.empty?
|
||||||
url = "/playlist?list=#{plid}"
|
url = "/playlist?list=#{plid}"
|
||||||
raise NotFoundException.new(translate(locale, "error_video_not_in_playlist", url))
|
raise NotFoundException.new(I18n.translate(locale, "error_video_not_in_playlist", url))
|
||||||
end
|
end
|
||||||
|
|
||||||
first_playlist_video = videos[0].as(PlaylistVideo)
|
first_playlist_video = videos[0].as(PlaylistVideo)
|
||||||
|
@ -37,7 +37,7 @@ module Invidious::Routes::Feeds
|
|||||||
if CONFIG.popular_enabled
|
if CONFIG.popular_enabled
|
||||||
templated "feeds/popular"
|
templated "feeds/popular"
|
||||||
else
|
else
|
||||||
message = translate(locale, "The Popular feed has been disabled by the administrator.")
|
message = I18n.translate(locale, "The Popular feed has been disabled by the administrator.")
|
||||||
templated "message"
|
templated "message"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -258,7 +258,7 @@ module Invidious::Routes::Feeds
|
|||||||
xml.element("link", "type": "text/html", rel: "alternate", href: "#{HOST_URL}/feed/subscriptions")
|
xml.element("link", "type": "text/html", rel: "alternate", href: "#{HOST_URL}/feed/subscriptions")
|
||||||
xml.element("link", "type": "application/atom+xml", rel: "self",
|
xml.element("link", "type": "application/atom+xml", rel: "self",
|
||||||
href: "#{HOST_URL}#{env.request.resource}")
|
href: "#{HOST_URL}#{env.request.resource}")
|
||||||
xml.element("title") { xml.text translate(locale, "Invidious Private Feed for `x`", user.email) }
|
xml.element("title") { xml.text I18n.translate(locale, "Invidious Private Feed for `x`", user.email) }
|
||||||
|
|
||||||
(notifications + videos).each do |video|
|
(notifications + videos).each do |video|
|
||||||
video.to_xml(locale, params, xml)
|
video.to_xml(locale, params, xml)
|
||||||
@ -403,7 +403,7 @@ module Invidious::Routes::Feeds
|
|||||||
signature = env.request.headers["X-Hub-Signature"].lchop("sha1=")
|
signature = env.request.headers["X-Hub-Signature"].lchop("sha1=")
|
||||||
|
|
||||||
if signature != OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, body)
|
if signature != OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, body)
|
||||||
LOGGER.error("/feed/webhook/#{token} : Invalid signature")
|
Log.error { "/feed/webhook/#{token} : Invalid signature" }
|
||||||
haltf env, status_code: 200
|
haltf env, status_code: 200
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ module Invidious::Routes::Login
|
|||||||
user, sid = create_user(sid, email, password)
|
user, sid = create_user(sid, email, password)
|
||||||
|
|
||||||
if language_header = env.request.headers["Accept-Language"]?
|
if language_header = env.request.headers["Accept-Language"]?
|
||||||
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
|
if language = ANG.language_negotiator.best(language_header, I18n::LOCALES.keys)
|
||||||
user.preferences.locale = language.header
|
user.preferences.locale = language.header
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -54,10 +54,10 @@ module Invidious::Routes::Watch
|
|||||||
begin
|
begin
|
||||||
video = get_video(id, region: params.region)
|
video = get_video(id, region: params.region)
|
||||||
rescue ex : NotFoundException
|
rescue ex : NotFoundException
|
||||||
LOGGER.error("get_video not found: #{id} : #{ex.message}")
|
Log.error { "get_video not found: #{id} : #{ex.message}" }
|
||||||
return error_template(404, ex)
|
return error_template(404, ex)
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.error("get_video: #{id} : #{ex.message}")
|
Log.error { "get_video: #{id} : #{ex.message}" }
|
||||||
return error_template(500, ex)
|
return error_template(500, ex)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ module Invidious::Search
|
|||||||
client_config = YoutubeAPI::ClientConfig.new(region: query.region)
|
client_config = YoutubeAPI::ClientConfig.new(region: query.region)
|
||||||
initial_data = YoutubeAPI.search(query.text, search_params, client_config: client_config)
|
initial_data = YoutubeAPI.search(query.text, search_params, client_config: client_config)
|
||||||
|
|
||||||
items, _ = extract_items(initial_data)
|
items, _ = YoutubeJSONParser.extract_items(initial_data)
|
||||||
return items.reject!(Category)
|
return items.reject!(Category)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ module Invidious::Search
|
|||||||
continuation = produce_channel_search_continuation(ucid, query.text, query.page)
|
continuation = produce_channel_search_continuation(ucid, query.text, query.page)
|
||||||
response_json = YoutubeAPI.browse(continuation)
|
response_json = YoutubeAPI.browse(continuation)
|
||||||
|
|
||||||
items, _ = extract_items(response_json, "", ucid)
|
items, _ = YoutubeJSONParser.extract_items(response_json, "", ucid)
|
||||||
return items.reject!(Category)
|
return items.reject!(Category)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ def fetch_trending(trending_type, region, locale)
|
|||||||
client_config = YoutubeAPI::ClientConfig.new(region: region)
|
client_config = YoutubeAPI::ClientConfig.new(region: region)
|
||||||
initial_data = YoutubeAPI.browse("FEtrending", params: params, client_config: client_config)
|
initial_data = YoutubeAPI.browse("FEtrending", params: params, client_config: client_config)
|
||||||
|
|
||||||
items, _ = extract_items(initial_data)
|
items, _ = YoutubeJSONParser.extract_items(initial_data)
|
||||||
|
|
||||||
extracted = [] of SearchItem
|
extracted = [] of SearchItem
|
||||||
|
|
||||||
|
@ -324,7 +324,7 @@ rescue DB::Error
|
|||||||
end
|
end
|
||||||
|
|
||||||
def fetch_video(id, region)
|
def fetch_video(id, region)
|
||||||
info = extract_video_info(video_id: id)
|
info = Parser.extract_video_info(video_id: id)
|
||||||
|
|
||||||
if reason = info["reason"]?
|
if reason = info["reason"]?
|
||||||
if reason == "Video unavailable"
|
if reason == "Video unavailable"
|
||||||
|
@ -6,7 +6,11 @@ require "json"
|
|||||||
#
|
#
|
||||||
# TODO: "compactRadioRenderer" (Mix) and
|
# TODO: "compactRadioRenderer" (Mix) and
|
||||||
# TODO: Use a proper struct/class instead of a hacky JSON object
|
# TODO: Use a proper struct/class instead of a hacky JSON object
|
||||||
def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
|
module Parser
|
||||||
|
extend self
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
|
private def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
|
||||||
return nil if !related["videoId"]?
|
return nil if !related["videoId"]?
|
||||||
|
|
||||||
# The compact renderer has video length in seconds, where the end
|
# The compact renderer has video length in seconds, where the end
|
||||||
@ -34,7 +38,7 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
|
|||||||
HelperExtractors.get_short_view_count(r).to_s
|
HelperExtractors.get_short_view_count(r).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
LOGGER.trace("parse_related_video: Found \"watchNextEndScreenRenderer\" container")
|
::Log.forf.trace { "Found \"watchNextEndScreenRenderer\" container" }
|
||||||
|
|
||||||
if published_time_text = related["publishedTimeText"]?
|
if published_time_text = related["publishedTimeText"]?
|
||||||
decoded_time = decode_date(published_time_text["simpleText"].to_s)
|
decoded_time = decode_date(published_time_text["simpleText"].to_s)
|
||||||
@ -110,7 +114,7 @@ def extract_video_info(video_id : String)
|
|||||||
|
|
||||||
if !CONFIG.invidious_companion.present?
|
if !CONFIG.invidious_companion.present?
|
||||||
if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil?
|
if player_response.dig?("streamingData", "adaptiveFormats", 0, "url").nil?
|
||||||
LOGGER.warn("Missing URLs for adaptive formats, falling back to other YT clients.")
|
Log.warn { "Missing URLs for adaptive formats, falling back to other YT clients." }
|
||||||
players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::WebMobile}
|
players_fallback = {YoutubeAPI::ClientType::TvHtml5, YoutubeAPI::ClientType::WebMobile}
|
||||||
|
|
||||||
players_fallback.each do |player_fallback|
|
players_fallback.each do |player_fallback|
|
||||||
@ -125,7 +129,7 @@ def extract_video_info(video_id : String)
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
rescue InfoException
|
rescue InfoException
|
||||||
next LOGGER.warn("Failed to fetch streams with #{player_fallback}")
|
next Log.warn { "Failed to fetch streams with #{player_fallback}" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -160,11 +164,11 @@ def extract_video_info(video_id : String)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig) : Hash(String, JSON::Any)?
|
def try_fetch_streaming_data(id : String, client_config : YoutubeAPI::ClientConfig) : Hash(String, JSON::Any)?
|
||||||
LOGGER.debug("try_fetch_streaming_data: [#{id}] Using #{client_config.client_type} client.")
|
::Log.forf.debug { "[#{id}] Using #{client_config.client_type} client." }
|
||||||
response = YoutubeAPI.player(video_id: id, params: "2AMB", client_config: client_config)
|
response = YoutubeAPI.player(video_id: id, params: "2AMB", client_config: client_config)
|
||||||
|
|
||||||
playability_status = response["playabilityStatus"]["status"]
|
playability_status = response["playabilityStatus"]["status"]
|
||||||
LOGGER.debug("try_fetch_streaming_data: [#{id}] Got playabilityStatus == #{playability_status}.")
|
::Log.forf.debug { "[#{id}] Got playabilityStatus == #{playability_status}." }
|
||||||
|
|
||||||
if id != response.dig?("videoDetails", "videoId")
|
if id != response.dig?("videoDetails", "videoId")
|
||||||
# YouTube may return a different video player response than expected.
|
# YouTube may return a different video player response than expected.
|
||||||
@ -261,7 +265,7 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
|
|||||||
|
|
||||||
# Related videos
|
# Related videos
|
||||||
|
|
||||||
LOGGER.debug("extract_video_info: parsing related videos...")
|
::Log.forf.debug { "parsing related videos..." }
|
||||||
|
|
||||||
related = [] of JSON::Any
|
related = [] of JSON::Any
|
||||||
|
|
||||||
@ -328,8 +332,8 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
|
|||||||
.try &.dig?("accessibility", "accessibilityData", "label")
|
.try &.dig?("accessibility", "accessibilityData", "label")
|
||||||
likes = likes_txt.as_s.gsub(/\D/, "").to_i64? if likes_txt
|
likes = likes_txt.as_s.gsub(/\D/, "").to_i64? if likes_txt
|
||||||
|
|
||||||
LOGGER.trace("extract_video_info: Found \"likes\" button. Button text is \"#{likes_txt}\"")
|
::Log.forf.trace { "Found \"likes\" button. Button text is \"#{likes_txt}\"" }
|
||||||
LOGGER.debug("extract_video_info: Likes count is #{likes}") if likes
|
::Log.forf.debug { "Likes count is #{likes}" } if likes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -477,7 +481,7 @@ private def convert_url(fmt)
|
|||||||
url = URI.parse(cfr["url"])
|
url = URI.parse(cfr["url"])
|
||||||
params = url.query_params
|
params = url.query_params
|
||||||
|
|
||||||
LOGGER.debug("convert_url: Decoding '#{cfr}'")
|
::Log.forf.debug { "Decoding '#{cfr}'" }
|
||||||
|
|
||||||
unsig = DECRYPT_FUNCTION.try &.decrypt_signature(cfr["s"])
|
unsig = DECRYPT_FUNCTION.try &.decrypt_signature(cfr["s"])
|
||||||
params[sp] = unsig if unsig
|
params[sp] = unsig if unsig
|
||||||
@ -494,11 +498,12 @@ private def convert_url(fmt)
|
|||||||
end
|
end
|
||||||
|
|
||||||
url.query_params = params
|
url.query_params = params
|
||||||
LOGGER.trace("convert_url: new url is '#{url}'")
|
::Log.forf.trace { "new url is '#{url}'" }
|
||||||
|
|
||||||
return url.to_s
|
return url.to_s
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug("convert_url: Error when parsing video URL")
|
::Log.forf.debug { "Error when parsing video URL" }
|
||||||
LOGGER.trace(ex.inspect_with_backtrace)
|
::Log.forf.trace { ex.inspect_with_backtrace }
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
<div class="pure-u-1 pure-u-lg-3-5">
|
<div class="pure-u-1 pure-u-lg-3-5">
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/add_playlist_items" method="get">
|
<form class="pure-form pure-form-aligned" action="/add_playlist_items" method="get">
|
||||||
<legend><a href="/playlist?list=<%= playlist.id %>"><%= translate(locale, "Editing playlist `x`", %|"#{HTML.escape(playlist.title)}"|) %></a></legend>
|
<legend><a href="/playlist?list=<%= playlist.id %>"><%= I18n.translate(locale, "Editing playlist `x`", %|"#{HTML.escape(playlist.title)}"|) %></a></legend>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<input class="pure-input-1" type="search" name="q"
|
<input class="pure-input-1" type="search" name="q"
|
||||||
<% if query %>value="<%= HTML.escape(query.text) %>"<% end %>
|
<% if query %>value="<%= HTML.escape(query.text) %>"<% end %>
|
||||||
placeholder="<%= translate(locale, "Search for videos") %>">
|
placeholder="<%= I18n.translate(locale, "Search for videos") %>">
|
||||||
<input type="hidden" name="list" value="<%= plid %>">
|
<input type="hidden" name="list" value="<%= plid %>">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
@ -35,10 +35,10 @@
|
|||||||
<%=
|
<%=
|
||||||
{
|
{
|
||||||
"ucid" => ucid,
|
"ucid" => ucid,
|
||||||
"youtube_comments_text" => HTML.escape(translate(locale, "View YouTube comments")),
|
"youtube_comments_text" => HTML.escape(I18n.translate(locale, "View YouTube comments")),
|
||||||
"comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")),
|
"comments_text" => HTML.escape(I18n.translate(locale, "View `x` comments", "{commentCount}")),
|
||||||
"hide_replies_text" => HTML.escape(translate(locale, "Hide replies")),
|
"hide_replies_text" => HTML.escape(I18n.translate(locale, "Hide replies")),
|
||||||
"show_replies_text" => HTML.escape(translate(locale, "Show replies")),
|
"show_replies_text" => HTML.escape(I18n.translate(locale, "Show replies")),
|
||||||
"preferences" => env.get("preferences").as(Preferences)
|
"preferences" => env.get("preferences").as(Preferences)
|
||||||
}.to_pretty_json
|
}.to_pretty_json
|
||||||
%>
|
%>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary" dir="auto" href="/feed/channel/<%= ucid %>">
|
<a class="pure-button pure-button-secondary" dir="auto" href="/feed/channel/<%= ucid %>">
|
||||||
<i class="icon ion-logo-rss"></i> <%= translate(locale, "generic_button_rss") %>
|
<i class="icon ion-logo-rss"></i> <%= I18n.translate(locale, "generic_button_rss") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -37,10 +37,10 @@
|
|||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<div class="pure-u-1 pure-md-1-3">
|
||||||
<a href="<%= youtube_url %>"><%= translate(locale, "View channel on YouTube") %></a>
|
<a href="<%= youtube_url %>"><%= I18n.translate(locale, "View channel on YouTube") %></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<div class="pure-u-1 pure-md-1-3">
|
||||||
<a href="<%= redirect_url %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
<a href="<%= redirect_url %>"><%= I18n.translate(locale, "Switch Invidious Instance") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= Invidious::Frontend::ChannelPage.generate_tabs_links(locale, channel, selected_tab) %>
|
<%= Invidious::Frontend::ChannelPage.generate_tabs_links(locale, channel, selected_tab) %>
|
||||||
@ -50,9 +50,9 @@
|
|||||||
<% sort_options.each do |sort| %>
|
<% sort_options.each do |sort| %>
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<div class="pure-u-1 pure-md-1-3">
|
||||||
<% if sort_by == sort %>
|
<% if sort_by == sort %>
|
||||||
<b><%= translate(locale, sort) %></b>
|
<b><%= I18n.translate(locale, sort) %></b>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="<%= relative_url %>?sort_by=<%= sort %>"><%= translate(locale, sort) %></a>
|
<a href="<%= relative_url %>?sort_by=<%= sort %>"><%= I18n.translate(locale, sort) %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% feed_menu.each do |feed| %>
|
<% feed_menu.each do |feed| %>
|
||||||
<a href="/feed/<%= feed.downcase %>" class="feed-menu-item pure-menu-heading">
|
<a href="/feed/<%= feed.downcase %>" class="feed-menu-item pure-menu-heading">
|
||||||
<%= translate(locale, feed) %>
|
<%= I18n.translate(locale, feed) %>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if !item.channel_handle.nil? %><p class="channel-name" dir="auto"><%= item.channel_handle %></p><% end %>
|
<% if !item.channel_handle.nil? %><p class="channel-name" dir="auto"><%= item.channel_handle %></p><% end %>
|
||||||
<p><%= translate_count(locale, "generic_subscribers_count", item.subscriber_count, NumberFormatting::Separator) %></p>
|
<p><%= I18n.translate_count(locale, "generic_subscribers_count", item.subscriber_count, I18n::NumberFormatting::Separator) %></p>
|
||||||
<% if !item.auto_generated && item.channel_handle.nil? %><p><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p><% end %>
|
<% if !item.auto_generated && item.channel_handle.nil? %><p><%= I18n.translate_count(locale, "generic_videos_count", item.video_count, I18n::NumberFormatting::Separator) %></p><% end %>
|
||||||
<h5><%= item.description_html %></h5>
|
<h5><%= item.description_html %></h5>
|
||||||
<% when SearchHashtag %>
|
<% when SearchHashtag %>
|
||||||
<% if !thin_mode %>
|
<% if !thin_mode %>
|
||||||
@ -45,13 +45,13 @@
|
|||||||
|
|
||||||
<div class="video-card-row">
|
<div class="video-card-row">
|
||||||
<%- if item.video_count != 0 -%>
|
<%- if item.video_count != 0 -%>
|
||||||
<p><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p>
|
<p><%= I18n.translate_count(locale, "generic_videos_count", item.video_count, I18n::NumberFormatting::Separator) %></p>
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="video-card-row">
|
<div class="video-card-row">
|
||||||
<%- if item.channel_count != 0 -%>
|
<%- if item.channel_count != 0 -%>
|
||||||
<p><%= translate_count(locale, "generic_channels_count", item.channel_count, NumberFormatting::Separator) %></p>
|
<p><%= I18n.translate_count(locale, "generic_channels_count", item.channel_count, I18n::NumberFormatting::Separator) %></p>
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
</div>
|
</div>
|
||||||
<% when SearchPlaylist, InvidiousPlaylist %>
|
<% when SearchPlaylist, InvidiousPlaylist %>
|
||||||
@ -73,7 +73,7 @@
|
|||||||
<%- end -%>
|
<%- end -%>
|
||||||
|
|
||||||
<div class="bottom-right-overlay">
|
<div class="bottom-right-overlay">
|
||||||
<p class="length"><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p>
|
<p class="length"><%= I18n.translate_count(locale, "generic_videos_count", item.video_count, I18n::NumberFormatting::Separator) %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -101,11 +101,11 @@
|
|||||||
<div class="error-card">
|
<div class="error-card">
|
||||||
<div class="explanation">
|
<div class="explanation">
|
||||||
<i class="icon ion-ios-alert"></i>
|
<i class="icon ion-ios-alert"></i>
|
||||||
<h4><%=translate(locale, "timeline_parse_error_placeholder_heading")%></h4>
|
<h4><%=I18n.translate(locale, "timeline_parse_error_placeholder_heading")%></h4>
|
||||||
<p><%=translate(locale, "timeline_parse_error_placeholder_message")%></p>
|
<p><%=I18n.translate(locale, "timeline_parse_error_placeholder_message")%></p>
|
||||||
</div>
|
</div>
|
||||||
<details>
|
<details>
|
||||||
<summary class="pure-button pure-button-secondary"><%=translate(locale, "timeline_parse_error_show_technical_details")%></summary>
|
<summary class="pure-button pure-button-secondary"><%=I18n.translate(locale, "timeline_parse_error_show_technical_details")%></summary>
|
||||||
<pre class="error-issue-template"><%=get_issue_template(env, item.parse_exception)[1]%></pre>
|
<pre class="error-issue-template"><%=get_issue_template(env, item.parse_exception)[1]%></pre>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
@ -168,7 +168,7 @@
|
|||||||
|
|
||||||
<div class="bottom-right-overlay">
|
<div class="bottom-right-overlay">
|
||||||
<%- if item.responds_to?(:live_now) && item.live_now -%>
|
<%- if item.responds_to?(:live_now) && item.live_now -%>
|
||||||
<p class="length" dir="auto"><i class="icon ion-ios-play-circle"></i> <%= translate(locale, "LIVE") %></p>
|
<p class="length" dir="auto"><i class="icon ion-ios-play-circle"></i> <%= I18n.translate(locale, "LIVE") %></p>
|
||||||
<%- elsif item.length_seconds != 0 -%>
|
<%- elsif item.length_seconds != 0 -%>
|
||||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
@ -200,15 +200,15 @@
|
|||||||
<div class="video-card-row flexible">
|
<div class="video-card-row flexible">
|
||||||
<div class="flex-left">
|
<div class="flex-left">
|
||||||
<% if item.responds_to?(:premiere_timestamp) && item.premiere_timestamp.try &.> Time.utc %>
|
<% if item.responds_to?(:premiere_timestamp) && item.premiere_timestamp.try &.> Time.utc %>
|
||||||
<p class="video-data" dir="auto"><%= translate(locale, "Premieres in `x`", recode_date((item.premiere_timestamp.as(Time) - Time.utc).ago, locale)) %></p>
|
<p class="video-data" dir="auto"><%= I18n.translate(locale, "Premieres in `x`", recode_date((item.premiere_timestamp.as(Time) - Time.utc).ago, locale)) %></p>
|
||||||
<% elsif item.responds_to?(:published) && (Time.utc - item.published) > 1.minute %>
|
<% elsif item.responds_to?(:published) && (Time.utc - item.published) > 1.minute %>
|
||||||
<p class="video-data" dir="auto"><%= translate(locale, "Shared `x` ago", recode_date(item.published, locale)) %></p>
|
<p class="video-data" dir="auto"><%= I18n.translate(locale, "Shared `x` ago", recode_date(item.published, locale)) %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if item.responds_to?(:views) && item.views %>
|
<% if item.responds_to?(:views) && item.views %>
|
||||||
<div class="flex-right">
|
<div class="flex-right">
|
||||||
<p class="video-data" dir="auto"><%= translate_count(locale, "generic_views_count", item.views || 0, NumberFormatting::Short) %></p>
|
<p class="video-data" dir="auto"><%= I18n.translate_count(locale, "generic_views_count", item.views || 0, I18n::NumberFormatting::Short) %></p>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
<script id="pagination-data" type="application/json">
|
<script id="pagination-data" type="application/json">
|
||||||
<%=
|
<%=
|
||||||
{
|
{
|
||||||
"next_page" => translate(locale, "Next page"),
|
"next_page" => I18n.translate(locale, "Next page"),
|
||||||
"prev_page" => translate(locale, "Previous page"),
|
"prev_page" => I18n.translate(locale, "Previous page"),
|
||||||
"is_rtl" => locale_is_rtl?(locale)
|
"is_rtl" => I18n.locale_is_rtl?(locale)
|
||||||
}.to_pretty_json
|
}.to_pretty_json
|
||||||
%>
|
%>
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<input type="search" id="searchbox" autocorrect="off"
|
<input type="search" id="searchbox" autocorrect="off"
|
||||||
autocapitalize="none" spellcheck="false" <% if autofocus %>autofocus<% end %>
|
autocapitalize="none" spellcheck="false" <% if autofocus %>autofocus<% end %>
|
||||||
name="q" placeholder="<%= translate(locale, "search") %>"
|
name="q" placeholder="<%= I18n.translate(locale, "search") %>"
|
||||||
title="<%= translate(locale, "search") %>"
|
title="<%= I18n.translate(locale, "search") %>"
|
||||||
value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } %>">
|
value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } %>">
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<button type="submit" id="searchbutton" aria-label="<%= translate(locale, "search") %>">
|
<button type="submit" id="searchbutton" aria-label="<%= I18n.translate(locale, "search") %>">
|
||||||
<i class="icon ion-ios-search"></i>
|
<i class="icon ion-ios-search"></i>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
<form action="/subscription_ajax?action=remove_subscriptions&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post">
|
<form action="/subscription_ajax?action=remove_subscriptions&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
||||||
<button data-type="unsubscribe" id="subscribe" class="pure-button pure-button-primary">
|
<button data-type="unsubscribe" id="subscribe" class="pure-button pure-button-primary">
|
||||||
<b><input style="all:unset" type="submit" value="<%= translate(locale, "Unsubscribe") %> | <%= sub_count_text %>"></b>
|
<b><input style="all:unset" type="submit" value="<%= I18n.translate(locale, "Unsubscribe") %> | <%= sub_count_text %>"></b>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<% else %>
|
<% else %>
|
||||||
<form action="/subscription_ajax?action=create_subscription_to_channel&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post">
|
<form action="/subscription_ajax?action=create_subscription_to_channel&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
||||||
<button data-type="subscribe" id="subscribe" class="pure-button pure-button-primary">
|
<button data-type="subscribe" id="subscribe" class="pure-button pure-button-primary">
|
||||||
<b><input style="all:unset" type="submit" value="<%= translate(locale, "Subscribe") %> | <%= sub_count_text %>"></b>
|
<b><input style="all:unset" type="submit" value="<%= I18n.translate(locale, "Subscribe") %> | <%= sub_count_text %>"></b>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -22,8 +22,8 @@
|
|||||||
"author" => HTML.escape(author),
|
"author" => HTML.escape(author),
|
||||||
"sub_count_text" => HTML.escape(sub_count_text),
|
"sub_count_text" => HTML.escape(sub_count_text),
|
||||||
"csrf_token" => URI.encode_www_form(env.get?("csrf_token").try &.as(String) || ""),
|
"csrf_token" => URI.encode_www_form(env.get?("csrf_token").try &.as(String) || ""),
|
||||||
"subscribe_text" => HTML.escape(translate(locale, "Subscribe")),
|
"subscribe_text" => HTML.escape(I18n.translate(locale, "Subscribe")),
|
||||||
"unsubscribe_text" => HTML.escape(translate(locale, "Unsubscribe"))
|
"unsubscribe_text" => HTML.escape(I18n.translate(locale, "Unsubscribe"))
|
||||||
}.to_pretty_json
|
}.to_pretty_json
|
||||||
%>
|
%>
|
||||||
</script>
|
</script>
|
||||||
@ -31,6 +31,6 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
<a id="subscribe" class="pure-button pure-button-primary"
|
<a id="subscribe" class="pure-button pure-button-primary"
|
||||||
href="/login?referer=<%= env.get("current_page") %>">
|
href="/login?referer=<%= env.get("current_page") %>">
|
||||||
<b><%= translate(locale, "Subscribe") %> | <%= sub_count_text %></b>
|
<b><%= I18n.translate(locale, "Subscribe") %> | <%= sub_count_text %></b>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
<div class="flex-right flexible">
|
<div class="flex-right flexible">
|
||||||
<div class="icon-buttons">
|
<div class="icon-buttons">
|
||||||
<a title="<%=translate(locale, "videoinfo_watch_on_youTube")%>" rel="noreferrer noopener" href="https://www.youtube.com/watch<%=endpoint_params%>">
|
<a title="<%=I18n.translate(locale, "videoinfo_watch_on_youTube")%>" rel="noreferrer noopener" href="https://www.youtube.com/watch<%=endpoint_params%>">
|
||||||
<i class="icon ion-logo-youtube"></i>
|
<i class="icon ion-logo-youtube"></i>
|
||||||
</a>
|
</a>
|
||||||
<a title="<%=translate(locale, "Audio mode")%>" href="/watch<%=endpoint_params%>&listen=1">
|
<a title="<%=I18n.translate(locale, "Audio mode")%>" href="/watch<%=endpoint_params%>&listen=1">
|
||||||
<i class="icon ion-md-headset"></i>
|
<i class="icon ion-md-headset"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<% if env.get("preferences").as(Preferences).automatic_instance_redirect%>
|
<% if env.get("preferences").as(Preferences).automatic_instance_redirect%>
|
||||||
<a title="<%=translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=%2Fwatch<%=URI.encode_www_form(endpoint_params)%>">
|
<a title="<%=I18n.translate(locale, "Switch Invidious Instance")%>" href="/redirect?referer=%2Fwatch<%=URI.encode_www_form(endpoint_params)%>">
|
||||||
<i class="icon ion-md-jet"></i>
|
<i class="icon ion-md-jet"></i>
|
||||||
</a>
|
</a>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a title="<%=translate(locale, "Switch Invidious Instance")%>" href="https://redirect.invidious.io/watch<%=endpoint_params%>">
|
<a title="<%=I18n.translate(locale, "Switch Invidious Instance")%>" href="https://redirect.invidious.io/watch<%=endpoint_params%>">
|
||||||
<i class="icon ion-md-jet"></i>
|
<i class="icon ion-md-jet"></i>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Create playlist") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Create playlist") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
@ -8,25 +8,25 @@
|
|||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/create_playlist?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" action="/create_playlist?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend><%= translate(locale, "Create playlist") %></legend>
|
<legend><%= I18n.translate(locale, "Create playlist") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="title"><%= translate(locale, "Title") %> :</label>
|
<label for="title"><%= I18n.translate(locale, "Title") %> :</label>
|
||||||
<input required name="title" type="text" placeholder="<%= translate(locale, "Title") %>">
|
<input required name="title" type="text" placeholder="<%= I18n.translate(locale, "Title") %>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="privacy"><%= translate(locale, "Playlist privacy") %> :</label>
|
<label for="privacy"><%= I18n.translate(locale, "Playlist privacy") %> :</label>
|
||||||
<select name="privacy" id="privacy">
|
<select name="privacy" id="privacy">
|
||||||
<% PlaylistPrivacy.names.each do |option| %>
|
<% PlaylistPrivacy.names.each do |option| %>
|
||||||
<option value="<%= option %>" <% if option == "Public" %> selected <% end %>><%= translate(locale, option) %></option>
|
<option value="<%= option %>" <% if option == "Public" %> selected <% end %>><%= I18n.translate(locale, option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-controls">
|
<div class="pure-controls">
|
||||||
<button type="submit" name="action" value="create_playlist" class="pure-button pure-button-primary">
|
<button type="submit" name="action" value="create_playlist" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Create playlist") %>
|
<%= I18n.translate(locale, "Create playlist") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Delete playlist") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Delete playlist") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/delete_playlist?list=<%= plid %>&referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" action="/delete_playlist?list=<%= plid %>&referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<legend><%= translate(locale, "Delete playlist `x`?", %|"#{HTML.escape(playlist.title)}"|) %></legend>
|
<legend><%= I18n.translate(locale, "Delete playlist `x`?", %|"#{HTML.escape(playlist.title)}"|) %></legend>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<button type="submit" name="submit" value="delete_playlist" class="pure-button pure-button-primary">
|
<button type="submit" name="submit" value="delete_playlist" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Yes") %>
|
<%= I18n.translate(locale, "Yes") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<a class="pure-button" href="/playlist?list=<%= plid %>">
|
<a class="pure-button" href="/playlist?list=<%= plid %>">
|
||||||
<%= translate(locale, "No") %>
|
<%= I18n.translate(locale, "No") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,17 +10,17 @@
|
|||||||
<div class="flex-right button-container">
|
<div class="flex-right button-container">
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/playlist?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/playlist?list=<%= plid %>">
|
||||||
<i class="icon ion-md-close"></i> <%= translate(locale, "generic_button_cancel") %>
|
<i class="icon ion-md-close"></i> <%= I18n.translate(locale, "generic_button_cancel") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<button class="pure-button pure-button-secondary low-profile" dir="auto" type="submit">
|
<button class="pure-button pure-button-secondary low-profile" dir="auto" type="submit">
|
||||||
<i class="icon ion-md-save"></i> <%= translate(locale, "generic_button_save") %>
|
<i class="icon ion-md-save"></i> <%= I18n.translate(locale, "generic_button_save") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/delete_playlist?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/delete_playlist?list=<%= plid %>">
|
||||||
<i class="icon ion-md-trash"></i> <%= translate(locale, "generic_button_delete") %>
|
<i class="icon ion-md-trash"></i> <%= I18n.translate(locale, "generic_button_delete") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,11 +36,11 @@
|
|||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<b>
|
<b>
|
||||||
<%= HTML.escape(playlist.author) %> |
|
<%= HTML.escape(playlist.author) %> |
|
||||||
<%= translate_count(locale, "generic_videos_count", playlist.video_count) %> |
|
<%= I18n.translate_count(locale, "generic_videos_count", playlist.video_count) %> |
|
||||||
</b>
|
</b>
|
||||||
<select name="privacy">
|
<select name="privacy">
|
||||||
<%- {"Public", "Unlisted", "Private"}.each do |option| -%>
|
<%- {"Public", "Unlisted", "Private"}.each do |option| -%>
|
||||||
<option value="<%= option %>" <% if option == playlist.privacy.to_s %>selected<% end %>><%= translate(locale, option) %></option>
|
<option value="<%= option %>" <% if option == playlist.privacy.to_s %>selected<% end %>><%= I18n.translate(locale, option) %></option>
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "History") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "History") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3><%= translate_count(locale, "generic_videos_count", user.watched.size, NumberFormatting::HtmlSpan) %></h3>
|
<h3><%= I18n.translate_count(locale, "generic_videos_count", user.watched.size, I18n::NumberFormatting::HtmlSpan) %></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:center">
|
<h3 style="text-align:center">
|
||||||
<a href="/feed/subscriptions"><%= translate_count(locale, "generic_subscriptions_count", user.subscriptions.size, NumberFormatting::HtmlSpan) %></a>
|
<a href="/feed/subscriptions"><%= I18n.translate_count(locale, "generic_subscriptions_count", user.subscriptions.size, I18n::NumberFormatting::HtmlSpan) %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:right">
|
<h3 style="text-align:right">
|
||||||
<a href="/clear_watch_history"><%= translate(locale, "Clear watch history") %></a>
|
<a href="/clear_watch_history"><%= I18n.translate(locale, "Clear watch history") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Playlists") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Playlists") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= rendered "components/feed_menu" %>
|
<%= rendered "components/feed_menu" %>
|
||||||
|
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3><%= translate(locale, "user_created_playlists", %(<span id="count">#{items_created.size}</span>)) %></h3>
|
<h3><%= I18n.translate(locale, "user_created_playlists", %(<span id="count">#{items_created.size}</span>)) %></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:center">
|
<h3 style="text-align:center">
|
||||||
<a href="/create_playlist?referer=<%= URI.encode_www_form("/feed/playlists") %>"><%= translate(locale, "Create playlist") %></a>
|
<a href="/create_playlist?referer=<%= URI.encode_www_form("/feed/playlists") %>"><%= I18n.translate(locale, "Create playlist") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:right">
|
<h3 style="text-align:right">
|
||||||
<a href="/data_control?referer=<%= URI.encode_www_form("/feed/playlists") %>">
|
<a href="/data_control?referer=<%= URI.encode_www_form("/feed/playlists") %>">
|
||||||
<%= translate(locale, "Import/export") %>
|
<%= I18n.translate(locale, "Import/export") %>
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1">
|
<div class="pure-u-1">
|
||||||
<h3><%= translate(locale, "user_saved_playlists", %(<span id="count">#{items_saved.size}</span>)) %></h3>
|
<h3><%= I18n.translate(locale, "user_saved_playlists", %(<span id="count">#{items_saved.size}</span>)) %></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
|
<meta name="description" content="<%= I18n.translate(locale, "An alternative front-end to YouTube") %>">
|
||||||
<title>
|
<title>
|
||||||
<% if env.get("preferences").as(Preferences).default_home != "Popular" %>
|
<% if env.get("preferences").as(Preferences).default_home != "Popular" %>
|
||||||
<%= translate(locale, "Popular") %> - Invidious
|
<%= I18n.translate(locale, "Popular") %> - Invidious
|
||||||
<% else %>
|
<% else %>
|
||||||
Invidious
|
Invidious
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Subscriptions") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Subscriptions") %> - Invidious</title>
|
||||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/private?token=<%= token %>" />
|
<link rel="alternate" type="application/rss+xml" title="RSS" href="/feed/private?token=<%= token %>" />
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
@ -8,12 +8,12 @@
|
|||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3>
|
<h3>
|
||||||
<a href="/subscription_manager"><%= translate(locale, "Manage subscriptions") %></a>
|
<a href="/subscription_manager"><%= I18n.translate(locale, "Manage subscriptions") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:center">
|
<h3 style="text-align:center">
|
||||||
<a href="/feed/history"><%= translate(locale, "Watch history") %></a>
|
<a href="/feed/history"><%= I18n.translate(locale, "Watch history") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
@ -26,7 +26,7 @@
|
|||||||
<% if CONFIG.enable_user_notifications %>
|
<% if CONFIG.enable_user_notifications %>
|
||||||
|
|
||||||
<center>
|
<center>
|
||||||
<%= translate_count(locale, "subscriptions_unseen_notifs_count", notifications.size) %>
|
<%= I18n.translate_count(locale, "subscriptions_unseen_notifs_count", notifications.size) %>
|
||||||
</center>
|
</center>
|
||||||
|
|
||||||
<% if !notifications.empty? %>
|
<% if !notifications.empty? %>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
|
<meta name="description" content="<%= I18n.translate(locale, "An alternative front-end to YouTube") %>">
|
||||||
<title>
|
<title>
|
||||||
<% if env.get("preferences").as(Preferences).default_home != "Trending" %>
|
<% if env.get("preferences").as(Preferences).default_home != "Trending" %>
|
||||||
<%= translate(locale, "Trending") %> - Invidious
|
<%= I18n.translate(locale, "Trending") %> - Invidious
|
||||||
<% else %>
|
<% else %>
|
||||||
Invidious
|
Invidious
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
<div style="align-self:flex-end" class="pure-u-2-3">
|
<div style="align-self:flex-end" class="pure-u-2-3">
|
||||||
<% if plid %>
|
<% if plid %>
|
||||||
<a href="/playlist?list=<%= plid %>">
|
<a href="/playlist?list=<%= plid %>">
|
||||||
<%= translate(locale, "View as playlist") %>
|
<%= I18n.translate(locale, "View as playlist") %>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@ -24,10 +24,10 @@
|
|||||||
<% {"Default", "Music", "Gaming", "Movies"}.each do |option| %>
|
<% {"Default", "Music", "Gaming", "Movies"}.each do |option| %>
|
||||||
<div class="pure-u-1 pure-md-1-3">
|
<div class="pure-u-1 pure-md-1-3">
|
||||||
<% if trending_type == option %>
|
<% if trending_type == option %>
|
||||||
<b><%= translate(locale, option) %></b>
|
<b><%= I18n.translate(locale, option) %></b>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="/feed/trending?type=<%= option %>®ion=<%= region %>">
|
<a href="/feed/trending?type=<%= option %>®ion=<%= region %>">
|
||||||
<%= translate(locale, option) %>
|
<%= I18n.translate(locale, option) %>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1><%= translate(locale, "JavaScript license information") %></h1>
|
<h1><%= I18n.translate(locale, "JavaScript license information") %></h1>
|
||||||
<table id="jslicense-labels1">
|
<table id="jslicense-labels1">
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/iv-org/videojs-quality-selector"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/iv-org/videojs-quality-selector"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -33,7 +33,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/mpetazzoni/sse.js"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/mpetazzoni/sse.js"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -47,7 +47,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/videojs/videojs-contrib-quality-levels"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/videojs/videojs-contrib-quality-levels"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -61,7 +61,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/jfujita/videojs-http-source-selector"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/jfujita/videojs-http-source-selector"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -75,7 +75,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/mister-ben/videojs-mobile-ui"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/mister-ben/videojs-mobile-ui"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -89,7 +89,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/spchuang/videojs-markers"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/spchuang/videojs-markers"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -103,7 +103,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/brightcove/videojs-overlay"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/brightcove/videojs-overlay"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -117,7 +117,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/mkhazov/videojs-share"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/mkhazov/videojs-share"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -131,7 +131,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/chrisboustead/videojs-vtt-thumbnails"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/chrisboustead/videojs-vtt-thumbnails"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -145,7 +145,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/afrmtbl/videojs-youtube-annotations"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/afrmtbl/videojs-youtube-annotations"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -159,7 +159,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/videojs/videojs-vr"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/videojs/videojs-vr"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -173,7 +173,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
<a href="https://github.com/videojs/video.js"><%= translate(locale, "source") %></a>
|
<a href="https://github.com/videojs/video.js"><%= I18n.translate(locale, "source") %></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
|
<meta name="description" content="<%= I18n.translate(locale, "An alternative front-end to YouTube") %>">
|
||||||
<title>
|
<title>
|
||||||
Invidious
|
Invidious
|
||||||
</title>
|
</title>
|
||||||
|
@ -13,28 +13,28 @@
|
|||||||
<%- if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email -%>
|
<%- if playlist.is_a?(InvidiousPlaylist) && playlist.author == user.try &.email -%>
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/add_playlist_items?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/add_playlist_items?list=<%= plid %>">
|
||||||
<i class="icon ion-md-add"></i> <%= translate(locale, "playlist_button_add_items") %>
|
<i class="icon ion-md-add"></i> <%= I18n.translate(locale, "playlist_button_add_items") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/edit_playlist?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/edit_playlist?list=<%= plid %>">
|
||||||
<i class="icon ion-md-create"></i> <%= translate(locale, "generic_button_edit") %>
|
<i class="icon ion-md-create"></i> <%= I18n.translate(locale, "generic_button_edit") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/delete_playlist?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/delete_playlist?list=<%= plid %>">
|
||||||
<i class="icon ion-md-trash"></i> <%= translate(locale, "generic_button_delete") %>
|
<i class="icon ion-md-trash"></i> <%= I18n.translate(locale, "generic_button_delete") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<%- else -%>
|
<%- else -%>
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<%- if IV::Database::Playlists.exists?(playlist.id) -%>
|
<%- if IV::Database::Playlists.exists?(playlist.id) -%>
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/subscribe_playlist?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/subscribe_playlist?list=<%= plid %>">
|
||||||
<i class="icon ion-md-add"></i> <%= translate(locale, "Subscribe") %>
|
<i class="icon ion-md-add"></i> <%= I18n.translate(locale, "Subscribe") %>
|
||||||
</a>
|
</a>
|
||||||
<%- else -%>
|
<%- else -%>
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/delete_playlist?list=<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/delete_playlist?list=<%= plid %>">
|
||||||
<i class="icon ion-md-trash"></i> <%= translate(locale, "Unsubscribe") %>
|
<i class="icon ion-md-trash"></i> <%= I18n.translate(locale, "Unsubscribe") %>
|
||||||
</a>
|
</a>
|
||||||
<%- end -%>
|
<%- end -%>
|
||||||
</div>
|
</div>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
<div class="pure-u">
|
<div class="pure-u">
|
||||||
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/feed/playlist/<%= plid %>">
|
<a class="pure-button pure-button-secondary low-profile" dir="auto" href="/feed/playlist/<%= plid %>">
|
||||||
<i class="icon ion-logo-rss"></i> <%= translate(locale, "generic_button_rss") %>
|
<i class="icon ion-logo-rss"></i> <%= I18n.translate(locale, "generic_button_rss") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -57,15 +57,15 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
<%= author %> |
|
<%= author %> |
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= translate_count(locale, "generic_videos_count", playlist.video_count) %> |
|
<%= I18n.translate_count(locale, "generic_videos_count", playlist.video_count) %> |
|
||||||
<%= translate(locale, "Updated `x` ago", recode_date(playlist.updated, locale)) %> |
|
<%= I18n.translate(locale, "Updated `x` ago", recode_date(playlist.updated, locale)) %> |
|
||||||
<% case playlist.as(InvidiousPlaylist).privacy when %>
|
<% case playlist.as(InvidiousPlaylist).privacy when %>
|
||||||
<% when PlaylistPrivacy::Public %>
|
<% when PlaylistPrivacy::Public %>
|
||||||
<i class="icon ion-md-globe"></i> <%= translate(locale, "Public") %>
|
<i class="icon ion-md-globe"></i> <%= I18n.translate(locale, "Public") %>
|
||||||
<% when PlaylistPrivacy::Unlisted %>
|
<% when PlaylistPrivacy::Unlisted %>
|
||||||
<i class="icon ion-ios-unlock"></i> <%= translate(locale, "Unlisted") %>
|
<i class="icon ion-ios-unlock"></i> <%= I18n.translate(locale, "Unlisted") %>
|
||||||
<% when PlaylistPrivacy::Private %>
|
<% when PlaylistPrivacy::Private %>
|
||||||
<i class="icon ion-ios-lock"></i> <%= translate(locale, "Private") %>
|
<i class="icon ion-ios-lock"></i> <%= I18n.translate(locale, "Private") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</b>
|
</b>
|
||||||
<% else %>
|
<% else %>
|
||||||
@ -76,25 +76,25 @@
|
|||||||
<% subtitle = playlist.subtitle || "" %>
|
<% subtitle = playlist.subtitle || "" %>
|
||||||
<span><%= HTML.escape(subtitle[0..subtitle.rindex(" • ") || subtitle.size]) %></span> |
|
<span><%= HTML.escape(subtitle[0..subtitle.rindex(" • ") || subtitle.size]) %></span> |
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= translate_count(locale, "generic_videos_count", playlist.video_count) %> |
|
<%= I18n.translate_count(locale, "generic_videos_count", playlist.video_count) %> |
|
||||||
<%= translate(locale, "Updated `x` ago", recode_date(playlist.updated, locale)) %>
|
<%= I18n.translate(locale, "Updated `x` ago", recode_date(playlist.updated, locale)) %>
|
||||||
</b>
|
</b>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if !playlist.is_a? InvidiousPlaylist %>
|
<% if !playlist.is_a? InvidiousPlaylist %>
|
||||||
<div class="pure-u-2-3">
|
<div class="pure-u-2-3">
|
||||||
<a rel="noreferrer noopener" href="https://www.youtube.com/playlist?list=<%= playlist.id %>">
|
<a rel="noreferrer noopener" href="https://www.youtube.com/playlist?list=<%= playlist.id %>">
|
||||||
<%= translate(locale, "View playlist on YouTube") %>
|
<%= I18n.translate(locale, "View playlist on YouTube") %>
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
|
|
||||||
<% if env.get("preferences").as(Preferences).automatic_instance_redirect%>
|
<% if env.get("preferences").as(Preferences).automatic_instance_redirect%>
|
||||||
<a href="/redirect?referer=<%= env.get?("current_page") %>">
|
<a href="/redirect?referer=<%= env.get?("current_page") %>">
|
||||||
<%= translate(locale, "Switch Invidious Instance") %>
|
<%= I18n.translate(locale, "Switch Invidious Instance") %>
|
||||||
</a>
|
</a>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="https://redirect.invidious.io/playlist?list=<%= playlist.id %>">
|
<a href="https://redirect.invidious.io/playlist?list=<%= playlist.id %>">
|
||||||
<%= translate(locale, "Switch Invidious Instance") %>
|
<%= I18n.translate(locale, "Switch Invidious Instance") %>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
<noscript>
|
<noscript>
|
||||||
<a href="/post/<%= id %>?ucid=<%= ucid %>&nojs=1">
|
<a href="/post/<%= id %>?ucid=<%= ucid %>&nojs=1">
|
||||||
<%= translate(locale, "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.") %>
|
<%= I18n.translate(locale, "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.") %>
|
||||||
</a>
|
</a>
|
||||||
</noscript>
|
</noscript>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -29,12 +29,12 @@
|
|||||||
<%=
|
<%=
|
||||||
{
|
{
|
||||||
"id" => id,
|
"id" => id,
|
||||||
"youtube_comments_text" => HTML.escape(translate(locale, "View YouTube comments")),
|
"youtube_comments_text" => HTML.escape(I18n.translate(locale, "View YouTube comments")),
|
||||||
"reddit_comments_text" => "",
|
"reddit_comments_text" => "",
|
||||||
"reddit_permalink_text" => "",
|
"reddit_permalink_text" => "",
|
||||||
"comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")),
|
"comments_text" => HTML.escape(I18n.translate(locale, "View `x` comments", "{commentCount}")),
|
||||||
"hide_replies_text" => HTML.escape(translate(locale, "Hide replies")),
|
"hide_replies_text" => HTML.escape(I18n.translate(locale, "Hide replies")),
|
||||||
"show_replies_text" => HTML.escape(translate(locale, "Show replies")),
|
"show_replies_text" => HTML.escape(I18n.translate(locale, "Show replies")),
|
||||||
"params" => {
|
"params" => {
|
||||||
"comments": ["youtube"]
|
"comments": ["youtube"]
|
||||||
},
|
},
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
<%- if items.empty? -%>
|
<%- if items.empty? -%>
|
||||||
<div class="h-box no-results-error">
|
<div class="h-box no-results-error">
|
||||||
<div>
|
<div>
|
||||||
<%= translate(locale, "search_message_no_results") %><br/><br/>
|
<%= I18n.translate(locale, "search_message_no_results") %><br/><br/>
|
||||||
<%= translate(locale, "search_message_change_filters_or_query") %><br/><br/>
|
<%= I18n.translate(locale, "search_message_change_filters_or_query") %><br/><br/>
|
||||||
<%= translate(locale, "search_message_use_another_instance", redirect_url) %>
|
<%= I18n.translate(locale, "search_message_use_another_instance", redirect_url) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%- else -%>
|
<%- else -%>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
|
<meta name="description" content="<%= I18n.translate(locale, "An alternative front-end to YouTube") %>">
|
||||||
<title>
|
<title>
|
||||||
Invidious - <%= translate(locale, "search") %>
|
Invidious - <%= I18n.translate(locale, "search") %>
|
||||||
</title>
|
</title>
|
||||||
<link rel="stylesheet" href="/css/empty.css?v=<%= ASSET_COMMIT %>">
|
<link rel="stylesheet" href="/css/empty.css?v=<%= ASSET_COMMIT %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
<div class="pure-u-1 pure-u-md-8-24 user-field">
|
<div class="pure-u-1 pure-u-md-8-24 user-field">
|
||||||
<% if env.get? "user" %>
|
<% if env.get? "user" %>
|
||||||
<div class="pure-u-1-4">
|
<div class="pure-u-1-4">
|
||||||
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading" title="<%= translate(locale, "toggle_theme") %>">
|
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading" title="<%= I18n.translate(locale, "toggle_theme") %>">
|
||||||
<% if dark_mode == "dark" %>
|
<% if dark_mode == "dark" %>
|
||||||
<i class="icon ion-ios-sunny"></i>
|
<i class="icon ion-ios-sunny"></i>
|
||||||
<% else %>
|
<% else %>
|
||||||
@ -51,7 +51,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-4">
|
<div class="pure-u-1-4">
|
||||||
<a id="notification_ticker" title="<%= translate(locale, "Subscriptions") %>" href="/feed/subscriptions" class="pure-menu-heading">
|
<a id="notification_ticker" title="<%= I18n.translate(locale, "Subscriptions") %>" href="/feed/subscriptions" class="pure-menu-heading">
|
||||||
<% notification_count = env.get("user").as(Invidious::User).notifications.size %>
|
<% notification_count = env.get("user").as(Invidious::User).notifications.size %>
|
||||||
<% if CONFIG.enable_user_notifications && notification_count > 0 %>
|
<% if CONFIG.enable_user_notifications && notification_count > 0 %>
|
||||||
<span id="notification_count"><%= notification_count %></span> <i class="icon ion-ios-notifications"></i>
|
<span id="notification_count"><%= notification_count %></span> <i class="icon ion-ios-notifications"></i>
|
||||||
@ -61,7 +61,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-4">
|
<div class="pure-u-1-4">
|
||||||
<a title="<%= translate(locale, "Preferences") %>" href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
<a title="<%= I18n.translate(locale, "Preferences") %>" href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
||||||
<i class="icon ion-ios-cog"></i>
|
<i class="icon ion-ios-cog"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -74,13 +74,13 @@
|
|||||||
<form action="/signout?referer=<%= env.get?("current_page") %>" method="post">
|
<form action="/signout?referer=<%= env.get?("current_page") %>" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
||||||
<a class="pure-menu-heading" href="#">
|
<a class="pure-menu-heading" href="#">
|
||||||
<input style="all:unset" type="submit" value="<%= translate(locale, "Log out") %>">
|
<input style="all:unset" type="submit" value="<%= I18n.translate(locale, "Log out") %>">
|
||||||
</a>
|
</a>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading" title="<%= translate(locale, "toggle_theme") %>">
|
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading" title="<%= I18n.translate(locale, "toggle_theme") %>">
|
||||||
<% if dark_mode == "dark" %>
|
<% if dark_mode == "dark" %>
|
||||||
<i class="icon ion-ios-sunny"></i>
|
<i class="icon ion-ios-sunny"></i>
|
||||||
<% else %>
|
<% else %>
|
||||||
@ -89,14 +89,14 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<a title="<%= translate(locale, "Preferences") %>" href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
<a title="<%= I18n.translate(locale, "Preferences") %>" href="/preferences?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
||||||
<i class="icon ion-ios-cog"></i>
|
<i class="icon ion-ios-cog"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<% if CONFIG.login_enabled %>
|
<% if CONFIG.login_enabled %>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
<a href="/login?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
||||||
<%= translate(locale, "Log in") %>
|
<%= I18n.translate(locale, "Log in") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -118,38 +118,38 @@
|
|||||||
<span>
|
<span>
|
||||||
<i class="icon ion-logo-github"></i>
|
<i class="icon ion-logo-github"></i>
|
||||||
<% if CONFIG.modified_source_code_url %>
|
<% if CONFIG.modified_source_code_url %>
|
||||||
<a href="https://github.com/iv-org/invidious"><%= translate(locale, "footer_original_source_code") %></a> /
|
<a href="https://github.com/iv-org/invidious"><%= I18n.translate(locale, "footer_original_source_code") %></a> /
|
||||||
<a href="<%= CONFIG.modified_source_code_url %>"><%= translate(locale, "footer_modfied_source_code") %></a>
|
<a href="<%= CONFIG.modified_source_code_url %>"><%= I18n.translate(locale, "footer_modfied_source_code") %></a>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="https://github.com/iv-org/invidious"><%= translate(locale, "footer_source_code") %></a>
|
<a href="https://github.com/iv-org/invidious"><%= I18n.translate(locale, "footer_source_code") %></a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<i class="icon ion-ios-paper"></i>
|
<i class="icon ion-ios-paper"></i>
|
||||||
<a href="https://github.com/iv-org/documentation"><%= translate(locale, "footer_documentation") %></a>
|
<a href="https://github.com/iv-org/documentation"><%= I18n.translate(locale, "footer_documentation") %></a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
<span>
|
<span>
|
||||||
<a href="https://github.com/iv-org/invidious/blob/master/LICENSE"><%= translate(locale, "Released under the AGPLv3 on Github.") %></a>
|
<a href="https://github.com/iv-org/invidious/blob/master/LICENSE"><%= I18n.translate(locale, "Released under the AGPLv3 on Github.") %></a>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<i class="icon ion-logo-javascript"></i>
|
<i class="icon ion-logo-javascript"></i>
|
||||||
<a rel="jslicense" href="/licenses"><%= translate(locale, "View JavaScript license information.") %></a>
|
<a rel="jslicense" href="/licenses"><%= I18n.translate(locale, "View JavaScript license information.") %></a>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<i class="icon ion-ios-paper"></i>
|
<i class="icon ion-ios-paper"></i>
|
||||||
<a href="/privacy"><%= translate(locale, "View privacy policy.") %></a>
|
<a href="/privacy"><%= I18n.translate(locale, "View privacy policy.") %></a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1 pure-u-md-1-3">
|
<div class="pure-u-1 pure-u-md-1-3">
|
||||||
<span>
|
<span>
|
||||||
<i class="icon ion-ios-wallet"></i>
|
<i class="icon ion-ios-wallet"></i>
|
||||||
<a href="https://invidious.io/donate/"><%= translate(locale, "footer_donate_page") %></a>
|
<a href="https://invidious.io/donate/"><%= I18n.translate(locale, "footer_donate_page") %></a>
|
||||||
</span>
|
</span>
|
||||||
<span><%= translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> @ <%= CURRENT_BRANCH %></span>
|
<span><%= I18n.translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> @ <%= CURRENT_BRANCH %></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@ -163,8 +163,8 @@
|
|||||||
<script id="notification_data" type="application/json">
|
<script id="notification_data" type="application/json">
|
||||||
<%=
|
<%=
|
||||||
{
|
{
|
||||||
"upload_text" => HTML.escape(translate(locale, "`x` uploaded a video")),
|
"upload_text" => HTML.escape(I18n.translate(locale, "`x` uploaded a video")),
|
||||||
"live_upload_text" => HTML.escape(translate(locale, "`x` is live"))
|
"live_upload_text" => HTML.escape(I18n.translate(locale, "`x` is live"))
|
||||||
}.to_pretty_json
|
}.to_pretty_json
|
||||||
%>
|
%>
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Token") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Token") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if env.get? "access_token" %>
|
<% if env.get? "access_token" %>
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3>
|
<h3>
|
||||||
<%= translate(locale, "Token") %>
|
<%= I18n.translate(locale, "Token") %>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:center">
|
<h3 style="text-align:center">
|
||||||
<a href="/token_manager"><%= translate(locale, "Token manager") %></a>
|
<a href="/token_manager"><%= I18n.translate(locale, "Token manager") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:right">
|
<h3 style="text-align:right">
|
||||||
<a href="/preferences"><%= translate(locale, "Preferences") %></a>
|
<a href="/preferences"><%= I18n.translate(locale, "Preferences") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -30,9 +30,9 @@
|
|||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/authorize_token" method="post">
|
<form class="pure-form pure-form-aligned" action="/authorize_token" method="post">
|
||||||
<% if callback_url %>
|
<% if callback_url %>
|
||||||
<legend><%= translate(locale, "Authorize token for `x`?", "#{callback_url.scheme}://#{callback_url.host}") %></legend>
|
<legend><%= I18n.translate(locale, "Authorize token for `x`?", "#{callback_url.scheme}://#{callback_url.host}") %></legend>
|
||||||
<% else %>
|
<% else %>
|
||||||
<legend><%= translate(locale, "Authorize token?") %></legend>
|
<legend><%= I18n.translate(locale, "Authorize token?") %></legend>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
@ -48,7 +48,7 @@
|
|||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<button type="submit" name="submit" value="clear_watch_history" class="pure-button pure-button-primary">
|
<button type="submit" name="submit" value="clear_watch_history" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Yes") %>
|
<%= I18n.translate(locale, "Yes") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
@ -57,7 +57,7 @@
|
|||||||
<% else %>
|
<% else %>
|
||||||
<a class="pure-button" href="/">
|
<a class="pure-button" href="/">
|
||||||
<% end %>
|
<% end %>
|
||||||
<%= translate(locale, "No") %>
|
<%= I18n.translate(locale, "No") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Change password") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Change password") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
@ -7,20 +7,20 @@
|
|||||||
<div class="pure-u-1 pure-u-lg-3-5">
|
<div class="pure-u-1 pure-u-lg-3-5">
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/change_password?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" action="/change_password?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<legend><%= translate(locale, "Change password") %></legend>
|
<legend><%= I18n.translate(locale, "Change password") %></legend>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="password"><%= translate(locale, "Password") %> :</label>
|
<label for="password"><%= I18n.translate(locale, "Password") %> :</label>
|
||||||
<input required class="pure-input-1" name="password" type="password" placeholder="<%= translate(locale, "Password") %>">
|
<input required class="pure-input-1" name="password" type="password" placeholder="<%= I18n.translate(locale, "Password") %>">
|
||||||
|
|
||||||
<label for="new_password[0]"><%= translate(locale, "New password") %> :</label>
|
<label for="new_password[0]"><%= I18n.translate(locale, "New password") %> :</label>
|
||||||
<input required class="pure-input-1" name="new_password[0]" type="password" placeholder="<%= translate(locale, "New password") %>">
|
<input required class="pure-input-1" name="new_password[0]" type="password" placeholder="<%= I18n.translate(locale, "New password") %>">
|
||||||
|
|
||||||
<label for="new_password[1]"><%= translate(locale, "New password") %> :</label>
|
<label for="new_password[1]"><%= I18n.translate(locale, "New password") %> :</label>
|
||||||
<input required class="pure-input-1" name="new_password[1]" type="password" placeholder="<%= translate(locale, "New password") %>">
|
<input required class="pure-input-1" name="new_password[1]" type="password" placeholder="<%= I18n.translate(locale, "New password") %>">
|
||||||
|
|
||||||
<button type="submit" name="action" value="change_password" class="pure-button pure-button-primary">
|
<button type="submit" name="action" value="change_password" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Change password") %>
|
<%= I18n.translate(locale, "Change password") %>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(csrf_token) %>">
|
<input type="hidden" name="csrf_token" value="<%= HTML.escape(csrf_token) %>">
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Clear watch history") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Clear watch history") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/clear_watch_history?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" action="/clear_watch_history?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<legend><%= translate(locale, "Clear watch history?") %></legend>
|
<legend><%= I18n.translate(locale, "Clear watch history?") %></legend>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<button type="submit" name="submit" value="clear_watch_history" class="pure-button pure-button-primary">
|
<button type="submit" name="submit" value="clear_watch_history" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Yes") %>
|
<%= I18n.translate(locale, "Yes") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<a class="pure-button" href="<%= URI.encode_www_form(referer) %>">
|
<a class="pure-button" href="<%= URI.encode_www_form(referer) %>">
|
||||||
<%= translate(locale, "No") %>
|
<%= I18n.translate(locale, "No") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,67 +1,67 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Import and Export Data") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Import and Export Data") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" enctype="multipart/form-data" action="/data_control?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" enctype="multipart/form-data" action="/data_control?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend><%= translate(locale, "Import") %></legend>
|
<legend><%= I18n.translate(locale, "Import") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_invidious"><%= translate(locale, "Import Invidious data") %></label>
|
<label for="import_invidious"><%= I18n.translate(locale, "Import Invidious data") %></label>
|
||||||
<input type="file" id="import_invidious" name="import_invidious">
|
<input type="file" id="import_invidious" name="import_invidious">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_youtube">
|
<label for="import_youtube">
|
||||||
<a rel="noopener" target="_blank" href="https://github.com/iv-org/documentation/blob/master/docs/export-youtube-subscriptions.md">
|
<a rel="noopener" target="_blank" href="https://github.com/iv-org/documentation/blob/master/docs/export-youtube-subscriptions.md">
|
||||||
<%= translate(locale, "Import YouTube subscriptions") %>
|
<%= I18n.translate(locale, "Import YouTube subscriptions") %>
|
||||||
</a>
|
</a>
|
||||||
</label>
|
</label>
|
||||||
<input type="file" id="import_youtube" name="import_youtube">
|
<input type="file" id="import_youtube" name="import_youtube">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_youtube_pl"><%= translate(locale, "Import YouTube playlist (.csv)") %></label>
|
<label for="import_youtube_pl"><%= I18n.translate(locale, "Import YouTube playlist (.csv)") %></label>
|
||||||
<input type="file" id="import_youtube_pl" name="import_youtube_pl">
|
<input type="file" id="import_youtube_pl" name="import_youtube_pl">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_youtube_wh"><%= translate(locale, "Import YouTube watch history (.json)") %></label>
|
<label for="import_youtube_wh"><%= I18n.translate(locale, "Import YouTube watch history (.json)") %></label>
|
||||||
<input type="file" id="import_youtube_wh" name="import_youtube_wh">
|
<input type="file" id="import_youtube_wh" name="import_youtube_wh">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_freetube"><%= translate(locale, "Import FreeTube subscriptions (.db)") %></label>
|
<label for="import_freetube"><%= I18n.translate(locale, "Import FreeTube subscriptions (.db)") %></label>
|
||||||
<input type="file" id="import_freetube" name="import_freetube">
|
<input type="file" id="import_freetube" name="import_freetube">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_newpipe_subscriptions"><%= translate(locale, "Import NewPipe subscriptions (.json)") %></label>
|
<label for="import_newpipe_subscriptions"><%= I18n.translate(locale, "Import NewPipe subscriptions (.json)") %></label>
|
||||||
<input type="file" id="import_newpipe_subscriptions" name="import_newpipe_subscriptions">
|
<input type="file" id="import_newpipe_subscriptions" name="import_newpipe_subscriptions">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="import_newpipe"><%= translate(locale, "Import NewPipe data (.zip)") %></label>
|
<label for="import_newpipe"><%= I18n.translate(locale, "Import NewPipe data (.zip)") %></label>
|
||||||
<input type="file" id="import_newpipe" name="import_newpipe">
|
<input type="file" id="import_newpipe" name="import_newpipe">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-controls">
|
<div class="pure-controls">
|
||||||
<button type="submit" class="pure-button pure-button-primary"><%= translate(locale, "Import") %></button>
|
<button type="submit" class="pure-button pure-button-primary"><%= I18n.translate(locale, "Import") %></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<legend><%= translate(locale, "Export") %></legend>
|
<legend><%= I18n.translate(locale, "Export") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/subscription_manager?action_takeout=1"><%= translate(locale, "Export subscriptions as OPML") %></a>
|
<a href="/subscription_manager?action_takeout=1"><%= I18n.translate(locale, "Export subscriptions as OPML") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/subscription_manager?action_takeout=1&format=newpipe"><%= translate(locale, "Export subscriptions as OPML (for NewPipe & FreeTube)") %></a>
|
<a href="/subscription_manager?action_takeout=1&format=newpipe"><%= I18n.translate(locale, "Export subscriptions as OPML (for NewPipe & FreeTube)") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/subscription_manager?action_takeout=1&format=json"><%= translate(locale, "Export data as JSON") %></a>
|
<a href="/subscription_manager?action_takeout=1&format=json"><%= I18n.translate(locale, "Export data as JSON") %></a>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Delete account") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Delete account") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/delete_account?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" action="/delete_account?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<legend><%= translate(locale, "Delete account?") %></legend>
|
<legend><%= I18n.translate(locale, "Delete account?") %></legend>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<button type="submit" name="submit" value="delete_account" class="pure-button pure-button-primary">
|
<button type="submit" name="submit" value="delete_account" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Yes") %>
|
<%= I18n.translate(locale, "Yes") %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-2">
|
<div class="pure-u-1-2">
|
||||||
<a class="pure-button" href="<%= URI.encode_www_form(referer) %>">
|
<a class="pure-button" href="<%= URI.encode_www_form(referer) %>">
|
||||||
<%= translate(locale, "No") %>
|
<%= I18n.translate(locale, "No") %>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Log in") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Log in") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
@ -13,15 +13,15 @@
|
|||||||
<% if email %>
|
<% if email %>
|
||||||
<input name="email" type="hidden" value="<%= HTML.escape(email) %>">
|
<input name="email" type="hidden" value="<%= HTML.escape(email) %>">
|
||||||
<% else %>
|
<% else %>
|
||||||
<label for="email"><%= translate(locale, "User ID") %> :</label>
|
<label for="email"><%= I18n.translate(locale, "User ID") %> :</label>
|
||||||
<input required class="pure-input-1" name="email" type="text" placeholder="<%= translate(locale, "User ID") %>">
|
<input required class="pure-input-1" name="email" type="text" placeholder="<%= I18n.translate(locale, "User ID") %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if password %>
|
<% if password %>
|
||||||
<input name="password" type="hidden" value="<%= HTML.escape(password) %>">
|
<input name="password" type="hidden" value="<%= HTML.escape(password) %>">
|
||||||
<% else %>
|
<% else %>
|
||||||
<label for="password"><%= translate(locale, "Password") %> :</label>
|
<label for="password"><%= I18n.translate(locale, "Password") %> :</label>
|
||||||
<input required class="pure-input-1" name="password" type="password" placeholder="<%= translate(locale, "Password") %>">
|
<input required class="pure-input-1" name="password" type="password" placeholder="<%= I18n.translate(locale, "Password") %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if captcha %>
|
<% if captcha %>
|
||||||
@ -30,15 +30,15 @@
|
|||||||
<% captcha[:tokens].each_with_index do |token, i| %>
|
<% captcha[:tokens].each_with_index do |token, i| %>
|
||||||
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
<input type="hidden" name="token[<%= i %>]" value="<%= HTML.escape(token) %>">
|
||||||
<% end %>
|
<% end %>
|
||||||
<label for="answer"><%= translate(locale, "Time (h:mm:ss):") %></label>
|
<label for="answer"><%= I18n.translate(locale, "Time (h:mm:ss):") %></label>
|
||||||
<input type="text" name="answer" type="text" placeholder="h:mm:ss">
|
<input type="text" name="answer" type="text" placeholder="h:mm:ss">
|
||||||
|
|
||||||
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Register") %>
|
<%= I18n.translate(locale, "Register") %>
|
||||||
</button>
|
</button>
|
||||||
<% else %>
|
<% else %>
|
||||||
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
<button type="submit" name="action" value="signin" class="pure-button pure-button-primary">
|
||||||
<%= translate(locale, "Sign In") %>/<%= translate(locale, "Register") %>
|
<%= I18n.translate(locale, "Sign In") %>/<%= I18n.translate(locale, "Register") %>
|
||||||
</button>
|
</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -1,49 +1,49 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Preferences") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Preferences") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<form class="pure-form pure-form-aligned" action="/preferences?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
<form class="pure-form pure-form-aligned" action="/preferences?referer=<%= URI.encode_www_form(referer) %>" method="post">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend><%= translate(locale, "preferences_category_player") %></legend>
|
<legend><%= I18n.translate(locale, "preferences_category_player") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="video_loop"><%= translate(locale, "preferences_video_loop_label") %></label>
|
<label for="video_loop"><%= I18n.translate(locale, "preferences_video_loop_label") %></label>
|
||||||
<input name="video_loop" id="video_loop" type="checkbox" <% if preferences.video_loop %>checked<% end %>>
|
<input name="video_loop" id="video_loop" type="checkbox" <% if preferences.video_loop %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="preload"><%= translate(locale, "preferences_preload_label") %></label>
|
<label for="preload"><%= I18n.translate(locale, "preferences_preload_label") %></label>
|
||||||
<input name="preload" id="preload" type="checkbox" <% if preferences.preload %>checked<% end %>>
|
<input name="preload" id="preload" type="checkbox" <% if preferences.preload %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="autoplay"><%= translate(locale, "preferences_autoplay_label") %></label>
|
<label for="autoplay"><%= I18n.translate(locale, "preferences_autoplay_label") %></label>
|
||||||
<input name="autoplay" id="autoplay" type="checkbox" <% if preferences.autoplay %>checked<% end %>>
|
<input name="autoplay" id="autoplay" type="checkbox" <% if preferences.autoplay %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="continue"><%= translate(locale, "preferences_continue_label") %></label>
|
<label for="continue"><%= I18n.translate(locale, "preferences_continue_label") %></label>
|
||||||
<input name="continue" id="continue" type="checkbox" <% if preferences.continue %>checked<% end %>>
|
<input name="continue" id="continue" type="checkbox" <% if preferences.continue %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="continue_autoplay"><%= translate(locale, "preferences_continue_autoplay_label") %></label>
|
<label for="continue_autoplay"><%= I18n.translate(locale, "preferences_continue_autoplay_label") %></label>
|
||||||
<input name="continue_autoplay" id="continue_autoplay" type="checkbox" <% if preferences.continue_autoplay %>checked<% end %>>
|
<input name="continue_autoplay" id="continue_autoplay" type="checkbox" <% if preferences.continue_autoplay %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="local"><%= translate(locale, "preferences_local_label") %></label>
|
<label for="local"><%= I18n.translate(locale, "preferences_local_label") %></label>
|
||||||
<input name="local" id="local" type="checkbox" <% if preferences.local && !CONFIG.disabled?("local") %>checked<% end %> <% if CONFIG.disabled?("local") %>disabled<% end %>>
|
<input name="local" id="local" type="checkbox" <% if preferences.local && !CONFIG.disabled?("local") %>checked<% end %> <% if CONFIG.disabled?("local") %>disabled<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="listen"><%= translate(locale, "preferences_listen_label") %></label>
|
<label for="listen"><%= I18n.translate(locale, "preferences_listen_label") %></label>
|
||||||
<input name="listen" id="listen" type="checkbox" <% if preferences.listen %>checked<% end %>>
|
<input name="listen" id="listen" type="checkbox" <% if preferences.listen %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="speed"><%= translate(locale, "preferences_speed_label") %></label>
|
<label for="speed"><%= I18n.translate(locale, "preferences_speed_label") %></label>
|
||||||
<select name="speed" id="speed">
|
<select name="speed" id="speed">
|
||||||
<% {2.0, 1.75, 1.5, 1.25, 1.0, 0.75, 0.5, 0.25}.each do |option| %>
|
<% {2.0, 1.75, 1.5, 1.25, 1.0, 0.75, 0.5, 0.25}.each do |option| %>
|
||||||
<option <% if preferences.speed == option %> selected <% end %>><%= option %></option>
|
<option <% if preferences.speed == option %> selected <% end %>><%= option %></option>
|
||||||
@ -52,11 +52,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="quality"><%= translate(locale, "preferences_quality_label") %></label>
|
<label for="quality"><%= I18n.translate(locale, "preferences_quality_label") %></label>
|
||||||
<select name="quality" id="quality">
|
<select name="quality" id="quality">
|
||||||
<% {"dash", "hd720", "medium", "small"}.each do |option| %>
|
<% {"dash", "hd720", "medium", "small"}.each do |option| %>
|
||||||
<% if !(option == "dash" && CONFIG.disabled?("dash")) %>
|
<% if !(option == "dash" && CONFIG.disabled?("dash")) %>
|
||||||
<option value="<%= option %>" <% if preferences.quality == option %> selected <% end %>><%= translate(locale, "preferences_quality_option_" + option) %></option>
|
<option value="<%= option %>" <% if preferences.quality == option %> selected <% end %>><%= I18n.translate(locale, "preferences_quality_option_" + option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
@ -64,108 +64,108 @@
|
|||||||
|
|
||||||
<% if !CONFIG.disabled?("dash") %>
|
<% if !CONFIG.disabled?("dash") %>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="quality_dash"><%= translate(locale, "preferences_quality_dash_label") %></label>
|
<label for="quality_dash"><%= I18n.translate(locale, "preferences_quality_dash_label") %></label>
|
||||||
<select name="quality_dash" id="quality_dash">
|
<select name="quality_dash" id="quality_dash">
|
||||||
<% {"auto", "best", "4320p", "2160p", "1440p", "1080p", "720p", "480p", "360p", "240p", "144p", "worst"}.each do |option| %>
|
<% {"auto", "best", "4320p", "2160p", "1440p", "1080p", "720p", "480p", "360p", "240p", "144p", "worst"}.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.quality_dash == option %> selected <% end %>><%= translate(locale, "preferences_quality_dash_option_" + option) %></option>
|
<option value="<%= option %>" <% if preferences.quality_dash == option %> selected <% end %>><%= I18n.translate(locale, "preferences_quality_dash_option_" + option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="volume"><%= translate(locale, "preferences_volume_label") %></label>
|
<label for="volume"><%= I18n.translate(locale, "preferences_volume_label") %></label>
|
||||||
<input name="volume" id="volume" data-onrange="update_volume_value" type="range" min="0" max="100" step="5" value="<%= preferences.volume %>">
|
<input name="volume" id="volume" data-onrange="update_volume_value" type="range" min="0" max="100" step="5" value="<%= preferences.volume %>">
|
||||||
<span class="pure-form-message-inline" id="volume-value"><%= preferences.volume %></span>
|
<span class="pure-form-message-inline" id="volume-value"><%= preferences.volume %></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="comments[0]"><%= translate(locale, "preferences_comments_label") %></label>
|
<label for="comments[0]"><%= I18n.translate(locale, "preferences_comments_label") %></label>
|
||||||
<% preferences.comments.each_with_index do |comments, index| %>
|
<% preferences.comments.each_with_index do |comments, index| %>
|
||||||
<select name="comments[<%= index %>]" id="comments[<%= index %>]">
|
<select name="comments[<%= index %>]" id="comments[<%= index %>]">
|
||||||
<% {"", "youtube", "reddit"}.each do |option| %>
|
<% {"", "youtube", "reddit"}.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.comments[index] == option %> selected <% end %>><%= translate(locale, option.blank? ? "none" : option) %></option>
|
<option value="<%= option %>" <% if preferences.comments[index] == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "none" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="captions[0]"><%= translate(locale, "preferences_captions_label") %></label>
|
<label for="captions[0]"><%= I18n.translate(locale, "preferences_captions_label") %></label>
|
||||||
<% preferences.captions.each_with_index do |caption, index| %>
|
<% preferences.captions.each_with_index do |caption, index| %>
|
||||||
<select class="pure-u-1-6" name="captions[<%= index %>]" id="captions[<%= index %>]">
|
<select class="pure-u-1-6" name="captions[<%= index %>]" id="captions[<%= index %>]">
|
||||||
<% Invidious::Videos::Captions::LANGUAGES.each do |option| %>
|
<% Invidious::Videos::Captions::LANGUAGES.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.captions[index] == option %> selected <% end %>><%= translate(locale, option.blank? ? "none" : option) %></option>
|
<option value="<%= option %>" <% if preferences.captions[index] == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "none" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="related_videos"><%= translate(locale, "preferences_related_videos_label") %></label>
|
<label for="related_videos"><%= I18n.translate(locale, "preferences_related_videos_label") %></label>
|
||||||
<input name="related_videos" id="related_videos" type="checkbox" <% if preferences.related_videos %>checked<% end %>>
|
<input name="related_videos" id="related_videos" type="checkbox" <% if preferences.related_videos %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="annotations"><%= translate(locale, "preferences_annotations_label") %></label>
|
<label for="annotations"><%= I18n.translate(locale, "preferences_annotations_label") %></label>
|
||||||
<input name="annotations" id="annotations" type="checkbox" <% if preferences.annotations %>checked<% end %>>
|
<input name="annotations" id="annotations" type="checkbox" <% if preferences.annotations %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="extend_desc"><%= translate(locale, "preferences_extend_desc_label") %></label>
|
<label for="extend_desc"><%= I18n.translate(locale, "preferences_extend_desc_label") %></label>
|
||||||
<input name="extend_desc" id="extend_desc" type="checkbox" <% if preferences.extend_desc %>checked<% end %>>
|
<input name="extend_desc" id="extend_desc" type="checkbox" <% if preferences.extend_desc %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="vr_mode"><%= translate(locale, "preferences_vr_mode_label") %></label>
|
<label for="vr_mode"><%= I18n.translate(locale, "preferences_vr_mode_label") %></label>
|
||||||
<input name="vr_mode" id="vr_mode" type="checkbox" <% if preferences.vr_mode %>checked<% end %>>
|
<input name="vr_mode" id="vr_mode" type="checkbox" <% if preferences.vr_mode %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="save_player_pos"><%= translate(locale, "preferences_save_player_pos_label") %></label>
|
<label for="save_player_pos"><%= I18n.translate(locale, "preferences_save_player_pos_label") %></label>
|
||||||
<input name="save_player_pos" id="save_player_pos" type="checkbox" <% if preferences.save_player_pos %>checked<% end %>>
|
<input name="save_player_pos" id="save_player_pos" type="checkbox" <% if preferences.save_player_pos %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<legend><%= translate(locale, "preferences_category_visual") %></legend>
|
<legend><%= I18n.translate(locale, "preferences_category_visual") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="locale"><%= translate(locale, "preferences_locale_label") %></label>
|
<label for="locale"><%= I18n.translate(locale, "preferences_locale_label") %></label>
|
||||||
<select name="locale" id="locale">
|
<select name="locale" id="locale">
|
||||||
<% LOCALES_LIST.each do |iso_name, full_name| %>
|
<% I18n::LOCALES_LIST.each do |iso_name, full_name| %>
|
||||||
<option value="<%= iso_name %>" <% if preferences.locale == iso_name %> selected <% end %>><%= HTML.escape(full_name) %></option>
|
<option value="<%= iso_name %>" <% if preferences.locale == iso_name %> selected <% end %>><%= HTML.escape(full_name) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="region"><%= translate(locale, "preferences_region_label") %></label>
|
<label for="region"><%= I18n.translate(locale, "preferences_region_label") %></label>
|
||||||
<select name="region" id="region">
|
<select name="region" id="region">
|
||||||
<% CONTENT_REGIONS.each do |option| %>
|
<% I18n::CONTENT_REGIONS.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.region == option %> selected <% end %>><%= option %></option>
|
<option value="<%= option %>" <% if preferences.region == option %> selected <% end %>><%= option %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="player_style"><%= translate(locale, "preferences_player_style_label") %></label>
|
<label for="player_style"><%= I18n.translate(locale, "preferences_player_style_label") %></label>
|
||||||
<select name="player_style" id="player_style">
|
<select name="player_style" id="player_style">
|
||||||
<% {"invidious", "youtube"}.each do |option| %>
|
<% {"invidious", "youtube"}.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.player_style == option %> selected <% end %>><%= translate(locale, option) %></option>
|
<option value="<%= option %>" <% if preferences.player_style == option %> selected <% end %>><%= I18n.translate(locale, option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="dark_mode"><%= translate(locale, "preferences_dark_mode_label") %></label>
|
<label for="dark_mode"><%= I18n.translate(locale, "preferences_dark_mode_label") %></label>
|
||||||
<select name="dark_mode" id="dark_mode">
|
<select name="dark_mode" id="dark_mode">
|
||||||
<% {"", "light", "dark"}.each do |option| %>
|
<% {"", "light", "dark"}.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.dark_mode == option %> selected <% end %>><%= translate(locale, option.blank? ? "auto" : option) %></option>
|
<option value="<%= option %>" <% if preferences.dark_mode == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "auto" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="thin_mode"><%= translate(locale, "preferences_thin_mode_label") %></label>
|
<label for="thin_mode"><%= I18n.translate(locale, "preferences_thin_mode_label") %></label>
|
||||||
<input name="thin_mode" id="thin_mode" type="checkbox" <% if preferences.thin_mode %>checked<% end %>>
|
<input name="thin_mode" id="thin_mode" type="checkbox" <% if preferences.thin_mode %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -176,187 +176,187 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="default_home"><%= translate(locale, "preferences_default_home_label") %></label>
|
<label for="default_home"><%= I18n.translate(locale, "preferences_default_home_label") %></label>
|
||||||
<select name="default_home" id="default_home">
|
<select name="default_home" id="default_home">
|
||||||
<% feed_options.each do |option| %>
|
<% feed_options.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.default_home == option %> selected <% end %>><%= translate(locale, option.blank? ? "Search" : option) %></option>
|
<option value="<%= option %>" <% if preferences.default_home == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "Search" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="feed_menu"><%= translate(locale, "preferences_feed_menu_label") %></label>
|
<label for="feed_menu"><%= I18n.translate(locale, "preferences_feed_menu_label") %></label>
|
||||||
<% (feed_options.size - 1).times do |index| %>
|
<% (feed_options.size - 1).times do |index| %>
|
||||||
<select name="feed_menu[<%= index %>]" id="feed_menu[<%= index %>]">
|
<select name="feed_menu[<%= index %>]" id="feed_menu[<%= index %>]">
|
||||||
<% feed_options.each do |option| %>
|
<% feed_options.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.feed_menu[index]? == option %> selected <% end %>><%= translate(locale, option.blank? ? "Search" : option) %></option>
|
<option value="<%= option %>" <% if preferences.feed_menu[index]? == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "Search" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% if env.get? "user" %>
|
<% if env.get? "user" %>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="show_nick"><%= translate(locale, "preferences_show_nick_label") %></label>
|
<label for="show_nick"><%= I18n.translate(locale, "preferences_show_nick_label") %></label>
|
||||||
<input name="show_nick" id="show_nick" type="checkbox" <% if preferences.show_nick %>checked<% end %>>
|
<input name="show_nick" id="show_nick" type="checkbox" <% if preferences.show_nick %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<legend><%= translate(locale, "preferences_category_misc") %></legend>
|
<legend><%= I18n.translate(locale, "preferences_category_misc") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="automatic_instance_redirect"><%= translate(locale, "preferences_automatic_instance_redirect_label") %></label>
|
<label for="automatic_instance_redirect"><%= I18n.translate(locale, "preferences_automatic_instance_redirect_label") %></label>
|
||||||
<input name="automatic_instance_redirect" id="automatic_instance_redirect" type="checkbox" <% if preferences.automatic_instance_redirect %>checked<% end %>>
|
<input name="automatic_instance_redirect" id="automatic_instance_redirect" type="checkbox" <% if preferences.automatic_instance_redirect %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if env.get? "user" %>
|
<% if env.get? "user" %>
|
||||||
<legend><%= translate(locale, "preferences_category_subscription") %></legend>
|
<legend><%= I18n.translate(locale, "preferences_category_subscription") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="watch_history"><%= translate(locale, "preferences_watch_history_label") %></label>
|
<label for="watch_history"><%= I18n.translate(locale, "preferences_watch_history_label") %></label>
|
||||||
<input name="watch_history" id="watch_history" type="checkbox" <% if preferences.watch_history %>checked<% end %>>
|
<input name="watch_history" id="watch_history" type="checkbox" <% if preferences.watch_history %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="annotations_subscribed"><%= translate(locale, "preferences_annotations_subscribed_label") %></label>
|
<label for="annotations_subscribed"><%= I18n.translate(locale, "preferences_annotations_subscribed_label") %></label>
|
||||||
<input name="annotations_subscribed" id="annotations_subscribed" type="checkbox" <% if preferences.annotations_subscribed %>checked<% end %>>
|
<input name="annotations_subscribed" id="annotations_subscribed" type="checkbox" <% if preferences.annotations_subscribed %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="max_results"><%= translate(locale, "preferences_max_results_label") %></label>
|
<label for="max_results"><%= I18n.translate(locale, "preferences_max_results_label") %></label>
|
||||||
<input name="max_results" id="max_results" type="number" value="<%= preferences.max_results %>">
|
<input name="max_results" id="max_results" type="number" value="<%= preferences.max_results %>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="sort"><%= translate(locale, "preferences_sort_label") %></label>
|
<label for="sort"><%= I18n.translate(locale, "preferences_sort_label") %></label>
|
||||||
<select name="sort" id="sort">
|
<select name="sort" id="sort">
|
||||||
<% {"published", "published - reverse", "alphabetically", "alphabetically - reverse", "channel name", "channel name - reverse"}.each do |option| %>
|
<% {"published", "published - reverse", "alphabetically", "alphabetically - reverse", "channel name", "channel name - reverse"}.each do |option| %>
|
||||||
<option value="<%= option %>" <% if preferences.sort == option %> selected <% end %>><%= translate(locale, option) %></option>
|
<option value="<%= option %>" <% if preferences.sort == option %> selected <% end %>><%= I18n.translate(locale, option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<% if preferences.unseen_only %>
|
<% if preferences.unseen_only %>
|
||||||
<label for="latest_only"><%= translate(locale, "Only show latest unwatched video from channel: ") %></label>
|
<label for="latest_only"><%= I18n.translate(locale, "Only show latest unwatched video from channel: ") %></label>
|
||||||
<% else %>
|
<% else %>
|
||||||
<label for="latest_only"><%= translate(locale, "Only show latest video from channel: ") %></label>
|
<label for="latest_only"><%= I18n.translate(locale, "Only show latest video from channel: ") %></label>
|
||||||
<% end %>
|
<% end %>
|
||||||
<input name="latest_only" id="latest_only" type="checkbox" <% if preferences.latest_only %>checked<% end %>>
|
<input name="latest_only" id="latest_only" type="checkbox" <% if preferences.latest_only %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="unseen_only"><%= translate(locale, "preferences_unseen_only_label") %></label>
|
<label for="unseen_only"><%= I18n.translate(locale, "preferences_unseen_only_label") %></label>
|
||||||
<input name="unseen_only" id="unseen_only" type="checkbox" <% if preferences.unseen_only %>checked<% end %>>
|
<input name="unseen_only" id="unseen_only" type="checkbox" <% if preferences.unseen_only %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if CONFIG.enable_user_notifications %>
|
<% if CONFIG.enable_user_notifications %>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="notifications_only"><%= translate(locale, "preferences_notifications_only_label") %></label>
|
<label for="notifications_only"><%= I18n.translate(locale, "preferences_notifications_only_label") %></label>
|
||||||
<input name="notifications_only" id="notifications_only" type="checkbox" <% if preferences.notifications_only %>checked<% end %>>
|
<input name="notifications_only" id="notifications_only" type="checkbox" <% if preferences.notifications_only %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% # Web notifications are only supported over HTTPS %>
|
<% # Web notifications are only supported over HTTPS %>
|
||||||
<% if Kemal.config.ssl || CONFIG.https_only %>
|
<% if Kemal.config.ssl || CONFIG.https_only %>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="#" data-onclick="notification_requestPermission"><%= translate(locale, "Enable web notifications") %></a>
|
<a href="#" data-onclick="notification_requestPermission"><%= I18n.translate(locale, "Enable web notifications") %></a>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if env.get?("user") && CONFIG.admins.includes? env.get?("user").as(Invidious::User).email %>
|
<% if env.get?("user") && CONFIG.admins.includes? env.get?("user").as(Invidious::User).email %>
|
||||||
<legend><%= translate(locale, "preferences_category_admin") %></legend>
|
<legend><%= I18n.translate(locale, "preferences_category_admin") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="admin_default_home"><%= translate(locale, "preferences_default_home_label") %></label>
|
<label for="admin_default_home"><%= I18n.translate(locale, "preferences_default_home_label") %></label>
|
||||||
<select name="admin_default_home" id="admin_default_home">
|
<select name="admin_default_home" id="admin_default_home">
|
||||||
<% feed_options.each do |option| %>
|
<% feed_options.each do |option| %>
|
||||||
<option value="<%= option %>" <% if CONFIG.default_user_preferences.default_home == option %> selected <% end %>><%= translate(locale, option.blank? ? "none" : option) %></option>
|
<option value="<%= option %>" <% if CONFIG.default_user_preferences.default_home == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "none" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="admin_feed_menu"><%= translate(locale, "preferences_feed_menu_label") %></label>
|
<label for="admin_feed_menu"><%= I18n.translate(locale, "preferences_feed_menu_label") %></label>
|
||||||
<% (feed_options.size - 1).times do |index| %>
|
<% (feed_options.size - 1).times do |index| %>
|
||||||
<select name="admin_feed_menu[<%= index %>]" id="admin_feed_menu[<%= index %>]">
|
<select name="admin_feed_menu[<%= index %>]" id="admin_feed_menu[<%= index %>]">
|
||||||
<% feed_options.each do |option| %>
|
<% feed_options.each do |option| %>
|
||||||
<option value="<%= option %>" <% if CONFIG.default_user_preferences.feed_menu[index]? == option %> selected <% end %>><%= translate(locale, option.blank? ? "none" : option) %></option>
|
<option value="<%= option %>" <% if CONFIG.default_user_preferences.feed_menu[index]? == option %> selected <% end %>><%= I18n.translate(locale, option.blank? ? "none" : option) %></option>
|
||||||
<% end %>
|
<% end %>
|
||||||
</select>
|
</select>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="popular_enabled"><%= translate(locale, "Popular enabled: ") %></label>
|
<label for="popular_enabled"><%= I18n.translate(locale, "Popular enabled: ") %></label>
|
||||||
<input name="popular_enabled" id="popular_enabled" type="checkbox" <% if CONFIG.popular_enabled %>checked<% end %>>
|
<input name="popular_enabled" id="popular_enabled" type="checkbox" <% if CONFIG.popular_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="captcha_enabled"><%= translate(locale, "CAPTCHA enabled: ") %></label>
|
<label for="captcha_enabled"><%= I18n.translate(locale, "CAPTCHA enabled: ") %></label>
|
||||||
<input name="captcha_enabled" id="captcha_enabled" type="checkbox" <% if CONFIG.captcha_enabled %>checked<% end %>>
|
<input name="captcha_enabled" id="captcha_enabled" type="checkbox" <% if CONFIG.captcha_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="login_enabled"><%= translate(locale, "Login enabled: ") %></label>
|
<label for="login_enabled"><%= I18n.translate(locale, "Login enabled: ") %></label>
|
||||||
<input name="login_enabled" id="login_enabled" type="checkbox" <% if CONFIG.login_enabled %>checked<% end %>>
|
<input name="login_enabled" id="login_enabled" type="checkbox" <% if CONFIG.login_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="registration_enabled"><%= translate(locale, "Registration enabled: ") %></label>
|
<label for="registration_enabled"><%= I18n.translate(locale, "Registration enabled: ") %></label>
|
||||||
<input name="registration_enabled" id="registration_enabled" type="checkbox" <% if CONFIG.registration_enabled %>checked<% end %>>
|
<input name="registration_enabled" id="registration_enabled" type="checkbox" <% if CONFIG.registration_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="statistics_enabled"><%= translate(locale, "Report statistics: ") %></label>
|
<label for="statistics_enabled"><%= I18n.translate(locale, "Report statistics: ") %></label>
|
||||||
<input name="statistics_enabled" id="statistics_enabled" type="checkbox" <% if CONFIG.statistics_enabled %>checked<% end %>>
|
<input name="statistics_enabled" id="statistics_enabled" type="checkbox" <% if CONFIG.statistics_enabled %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="modified_source_code_url"><%= translate(locale, "adminprefs_modified_source_code_url_label") %></label>
|
<label for="modified_source_code_url"><%= I18n.translate(locale, "adminprefs_modified_source_code_url_label") %></label>
|
||||||
<input name="modified_source_code_url" id="modified_source_code_url" type="url" value="<%= CONFIG.modified_source_code_url %>">
|
<input name="modified_source_code_url" id="modified_source_code_url" type="url" value="<%= CONFIG.modified_source_code_url %>">
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if env.get? "user" %>
|
<% if env.get? "user" %>
|
||||||
<legend><%= translate(locale, "preferences_category_data") %></legend>
|
<legend><%= I18n.translate(locale, "preferences_category_data") %></legend>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/clear_watch_history?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Clear watch history") %></a>
|
<a href="/clear_watch_history?referer=<%= URI.encode_www_form(referer) %>"><%= I18n.translate(locale, "Clear watch history") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/change_password?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Change password") %></a>
|
<a href="/change_password?referer=<%= URI.encode_www_form(referer) %>"><%= I18n.translate(locale, "Change password") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/data_control?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Import/export data") %></a>
|
<a href="/data_control?referer=<%= URI.encode_www_form(referer) %>"><%= I18n.translate(locale, "Import/export data") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/subscription_manager"><%= translate(locale, "Manage subscriptions") %></a>
|
<a href="/subscription_manager"><%= I18n.translate(locale, "Manage subscriptions") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/token_manager"><%= translate(locale, "Manage tokens") %></a>
|
<a href="/token_manager"><%= I18n.translate(locale, "Manage tokens") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/feed/playlists"><%= translate(locale, "View all playlists") %></a>
|
<a href="/feed/playlists"><%= I18n.translate(locale, "View all playlists") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/feed/history"><%= translate(locale, "Watch history") %></a>
|
<a href="/feed/history"><%= I18n.translate(locale, "Watch history") %></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<a href="/delete_account?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Delete account") %></a>
|
<a href="/delete_account?referer=<%= URI.encode_www_form(referer) %>"><%= I18n.translate(locale, "Delete account") %></a>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-controls">
|
<div class="pure-controls">
|
||||||
<button type="submit" class="pure-button pure-button-primary"><%= translate(locale, "Save preferences") %></button>
|
<button type="submit" class="pure-button pure-button-primary"><%= I18n.translate(locale, "Save preferences") %></button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Subscription manager") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Subscription manager") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3>
|
<h3>
|
||||||
<a href="/feed/subscriptions">
|
<a href="/feed/subscriptions">
|
||||||
<%= translate_count(locale, "generic_subscriptions_count", subscriptions.size, NumberFormatting::HtmlSpan) %>
|
<%= I18n.translate_count(locale, "generic_subscriptions_count", subscriptions.size, I18n::NumberFormatting::HtmlSpan) %>
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:center">
|
<h3 style="text-align:center">
|
||||||
<a href="/feed/history">
|
<a href="/feed/history">
|
||||||
<%= translate(locale, "Watch history") %>
|
<%= I18n.translate(locale, "Watch history") %>
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3 style="text-align:right">
|
<h3 style="text-align:right">
|
||||||
<a href="/data_control?referer=<%= URI.encode_www_form(referer) %>">
|
<a href="/data_control?referer=<%= URI.encode_www_form(referer) %>">
|
||||||
<%= translate(locale, "Import/export") %>
|
<%= I18n.translate(locale, "Import/export") %>
|
||||||
</a>
|
</a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
<h3 style="padding-right:0.5em">
|
<h3 style="padding-right:0.5em">
|
||||||
<form data-onsubmit="return_false" action="/subscription_ajax?action=remove_subscriptions&c=<%= channel.id %>&referer=<%= env.get("current_page") %>" method="post">
|
<form data-onsubmit="return_false" action="/subscription_ajax?action=remove_subscriptions&c=<%= channel.id %>&referer=<%= env.get("current_page") %>" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
||||||
<input style="all:unset" type="submit" data-onclick="remove_subscription" data-ucid="<%= channel.id %>" value="<%= translate(locale, "unsubscribe") %>">
|
<input style="all:unset" type="submit" data-onclick="remove_subscription" data-ucid="<%= channel.id %>" value="<%= I18n.translate(locale, "unsubscribe") %>">
|
||||||
</form>
|
</form>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<% content_for "header" do %>
|
<% content_for "header" do %>
|
||||||
<title><%= translate(locale, "Token manager") %> - Invidious</title>
|
<title><%= I18n.translate(locale, "Token manager") %> - Invidious</title>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="pure-g h-box">
|
<div class="pure-g h-box">
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<h3>
|
<h3>
|
||||||
<%= translate_count(locale, "tokens_count", tokens.size, NumberFormatting::HtmlSpan) %>
|
<%= I18n.translate_count(locale, "tokens_count", tokens.size, I18n::NumberFormatting::HtmlSpan) %>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-3"></div>
|
<div class="pure-u-1-3"></div>
|
||||||
<div class="pure-u-1-3" style="text-align:right">
|
<div class="pure-u-1-3" style="text-align:right">
|
||||||
<h3>
|
<h3>
|
||||||
<a href="/preferences?referer=<%= URI.encode_www_form(referer) %>"><%= translate(locale, "Preferences") %></a>
|
<a href="/preferences?referer=<%= URI.encode_www_form(referer) %>"><%= I18n.translate(locale, "Preferences") %></a>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -25,13 +25,13 @@
|
|||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-5" style="text-align:center">
|
<div class="pure-u-1-5" style="text-align:center">
|
||||||
<h4><%= translate(locale, "`x` ago", recode_date(token[:issued], locale)) %></h4>
|
<h4><%= I18n.translate(locale, "`x` ago", recode_date(token[:issued], locale)) %></h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1-5" style="text-align:right">
|
<div class="pure-u-1-5" style="text-align:right">
|
||||||
<h3 style="padding-right:0.5em">
|
<h3 style="padding-right:0.5em">
|
||||||
<form data-onsubmit="return_false" action="/token_ajax?action=revoke_token&session=<%= token[:session] %>&referer=<%= env.get("current_page") %>" method="post">
|
<form data-onsubmit="return_false" action="/token_ajax?action=revoke_token&session=<%= token[:session] %>&referer=<%= env.get("current_page") %>" method="post">
|
||||||
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
|
||||||
<input style="all:unset" type="submit" data-onclick="revoke_token" data-session="<%= token[:session] %>" value="<%= translate(locale, "revoke") %>">
|
<input style="all:unset" type="submit" data-onclick="revoke_token" data-session="<%= token[:session] %>" value="<%= I18n.translate(locale, "revoke") %>">
|
||||||
</form>
|
</form>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
@ -35,11 +35,11 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
-->
|
-->
|
||||||
<style>
|
<style>
|
||||||
#descexpansionbutton ~ label > a::after {
|
#descexpansionbutton ~ label > a::after {
|
||||||
content: "<%= translate(locale, "Show more") %>"
|
content: "<%= I18n.translate(locale, "Show more") %>"
|
||||||
}
|
}
|
||||||
|
|
||||||
#descexpansionbutton:checked ~ label > a::after {
|
#descexpansionbutton:checked ~ label > a::after {
|
||||||
content: "<%= translate(locale, "Show less") %>"
|
content: "<%= I18n.translate(locale, "Show less") %>"
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -53,12 +53,12 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
"length_seconds" => video.length_seconds.to_f,
|
"length_seconds" => video.length_seconds.to_f,
|
||||||
"play_next" => !video.related_videos.empty? && !plid && params.continue,
|
"play_next" => !video.related_videos.empty? && !plid && params.continue,
|
||||||
"next_video" => video.related_videos.select { |rv| rv["id"]? }[0]?.try &.["id"],
|
"next_video" => video.related_videos.select { |rv| rv["id"]? }[0]?.try &.["id"],
|
||||||
"youtube_comments_text" => HTML.escape(translate(locale, "View YouTube comments")),
|
"youtube_comments_text" => HTML.escape(I18n.translate(locale, "View YouTube comments")),
|
||||||
"reddit_comments_text" => HTML.escape(translate(locale, "View Reddit comments")),
|
"reddit_comments_text" => HTML.escape(I18n.translate(locale, "View Reddit comments")),
|
||||||
"reddit_permalink_text" => HTML.escape(translate(locale, "View more comments on Reddit")),
|
"reddit_permalink_text" => HTML.escape(I18n.translate(locale, "View more comments on Reddit")),
|
||||||
"comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")),
|
"comments_text" => HTML.escape(I18n.translate(locale, "View `x` comments", "{commentCount}")),
|
||||||
"hide_replies_text" => HTML.escape(translate(locale, "Hide replies")),
|
"hide_replies_text" => HTML.escape(I18n.translate(locale, "Hide replies")),
|
||||||
"show_replies_text" => HTML.escape(translate(locale, "Show replies")),
|
"show_replies_text" => HTML.escape(I18n.translate(locale, "Show replies")),
|
||||||
"params" => params,
|
"params" => params,
|
||||||
"preferences" => preferences,
|
"preferences" => preferences,
|
||||||
"premiere_timestamp" => video.premiere_timestamp.try &.to_unix,
|
"premiere_timestamp" => video.premiere_timestamp.try &.to_unix,
|
||||||
@ -78,11 +78,11 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<h1>
|
<h1>
|
||||||
<%= title %>
|
<%= title %>
|
||||||
<% if params.listen %>
|
<% if params.listen %>
|
||||||
<a title="<%=translate(locale, "Video mode")%>" href="/watch?<%= env.params.query %>&listen=0">
|
<a title="<%=I18n.translate(locale, "Video mode")%>" href="/watch?<%= env.params.query %>&listen=0">
|
||||||
<i class="icon ion-ios-videocam"></i>
|
<i class="icon ion-ios-videocam"></i>
|
||||||
</a>
|
</a>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a title="<%=translate(locale, "Audio mode")%>" href="/watch?<%= env.params.query %>&listen=1">
|
<a title="<%=I18n.translate(locale, "Audio mode")%>" href="/watch?<%= env.params.query %>&listen=1">
|
||||||
<i class="icon ion-md-headset"></i>
|
<i class="icon ion-md-headset"></i>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -90,7 +90,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
|
|
||||||
<% if !video.is_listed %>
|
<% if !video.is_listed %>
|
||||||
<h3>
|
<h3>
|
||||||
<i class="icon ion-ios-unlock"></i> <%= translate(locale, "Unlisted") %>
|
<i class="icon ion-ios-unlock"></i> <%= I18n.translate(locale, "Unlisted") %>
|
||||||
</h3>
|
</h3>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
@ -100,11 +100,11 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
</h3>
|
</h3>
|
||||||
<% elsif video.premiere_timestamp.try &.> Time.utc %>
|
<% elsif video.premiere_timestamp.try &.> Time.utc %>
|
||||||
<h3>
|
<h3>
|
||||||
<%= video.premiere_timestamp.try { |t| translate(locale, "Premieres in `x`", recode_date((t - Time.utc).ago, locale)) } %>
|
<%= video.premiere_timestamp.try { |t| I18n.translate(locale, "Premieres in `x`", recode_date((t - Time.utc).ago, locale)) } %>
|
||||||
</h3>
|
</h3>
|
||||||
<% elsif video.live_now %>
|
<% elsif video.live_now %>
|
||||||
<h3>
|
<h3>
|
||||||
<%= video.premiere_timestamp.try { |t| translate(locale, "videoinfo_started_streaming_x_ago", recode_date((Time.utc - t).ago, locale)) } %>
|
<%= video.premiere_timestamp.try { |t| I18n.translate(locale, "videoinfo_started_streaming_x_ago", recode_date((Time.utc - t).ago, locale)) } %>
|
||||||
</h3>
|
</h3>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@ -123,13 +123,13 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
link_yt_embed = IV::HttpServer::Utils.add_params_to_url(link_yt_embed, link_yt_param)
|
link_yt_embed = IV::HttpServer::Utils.add_params_to_url(link_yt_embed, link_yt_param)
|
||||||
end
|
end
|
||||||
-%>
|
-%>
|
||||||
<a id="link-yt-watch" rel="noreferrer noopener" data-base-url="<%= link_yt_watch %>" href="<%= link_yt_watch %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
|
<a id="link-yt-watch" rel="noreferrer noopener" data-base-url="<%= link_yt_watch %>" href="<%= link_yt_watch %>"><%= I18n.translate(locale, "videoinfo_watch_on_youTube") %></a>
|
||||||
(<a id="link-yt-embed" rel="noreferrer noopener" data-base-url="<%= link_yt_embed %>" href="<%= link_yt_embed %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
|
(<a id="link-yt-embed" rel="noreferrer noopener" data-base-url="<%= link_yt_embed %>" href="<%= link_yt_embed %>"><%= I18n.translate(locale, "videoinfo_youTube_embed_link") %></a>)
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<p id="watch-on-another-invidious-instance">
|
<p id="watch-on-another-invidious-instance">
|
||||||
<%- link_iv_other = IV::Frontend::Misc.redirect_url(env) -%>
|
<%- link_iv_other = IV::Frontend::Misc.redirect_url(env) -%>
|
||||||
<a id="link-iv-other" data-base-url="<%= link_iv_other %>" href="<%= link_iv_other %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
<a id="link-iv-other" data-base-url="<%= link_iv_other %>" href="<%= link_iv_other %>"><%= I18n.translate(locale, "Switch Invidious Instance") %></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p id="embed-link">
|
<p id="embed-link">
|
||||||
@ -140,17 +140,17 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
link_iv_embed = URI.new(path: "/embed/#{id}")
|
link_iv_embed = URI.new(path: "/embed/#{id}")
|
||||||
link_iv_embed = IV::HttpServer::Utils.add_params_to_url(link_iv_embed, params_iv_embed)
|
link_iv_embed = IV::HttpServer::Utils.add_params_to_url(link_iv_embed, params_iv_embed)
|
||||||
-%>
|
-%>
|
||||||
<a id="link-iv-embed" data-base-url="<%= link_iv_embed %>" href="<%= link_iv_embed %>"><%= translate(locale, "videoinfo_invidious_embed_link") %></a>
|
<a id="link-iv-embed" data-base-url="<%= link_iv_embed %>" href="<%= link_iv_embed %>"><%= I18n.translate(locale, "videoinfo_invidious_embed_link") %></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p id="annotations">
|
<p id="annotations">
|
||||||
<% if params.annotations %>
|
<% if params.annotations %>
|
||||||
<a href="/watch?<%= env.params.query %>&iv_load_policy=3">
|
<a href="/watch?<%= env.params.query %>&iv_load_policy=3">
|
||||||
<%= translate(locale, "Hide annotations") %>
|
<%= I18n.translate(locale, "Hide annotations") %>
|
||||||
</a>
|
</a>
|
||||||
<% else %>
|
<% else %>
|
||||||
<a href="/watch?<%= env.params.query %>&iv_load_policy=1">
|
<a href="/watch?<%= env.params.query %>&iv_load_policy=1">
|
||||||
<%=translate(locale, "Show annotations")%>
|
<%=I18n.translate(locale, "Show annotations")%>
|
||||||
</a>
|
</a>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
@ -160,7 +160,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<% if !playlists.empty? %>
|
<% if !playlists.empty? %>
|
||||||
<form data-onsubmit="return_false" class="pure-form pure-form-stacked" action="/playlist_ajax?action=add_video" method="post" target="_blank">
|
<form data-onsubmit="return_false" class="pure-form pure-form-stacked" action="/playlist_ajax?action=add_video" method="post" target="_blank">
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="playlist_id"><%= translate(locale, "Add to playlist: ") %></label>
|
<label for="playlist_id"><%= I18n.translate(locale, "Add to playlist: ") %></label>
|
||||||
<select style="width:100%" name="playlist_id" id="playlist_id">
|
<select style="width:100%" name="playlist_id" id="playlist_id">
|
||||||
<% playlists.each do |plid, playlist_title| %>
|
<% playlists.each do |plid, playlist_title| %>
|
||||||
<option data-plid="<%= plid %>" value="<%= plid %>"><%= HTML.escape(playlist_title) %></option>
|
<option data-plid="<%= plid %>" value="<%= plid %>"><%= HTML.escape(playlist_title) %></option>
|
||||||
@ -171,7 +171,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<input type="hidden" name="csrf_token" value="<%= URI.encode_www_form(env.get?("csrf_token").try &.as(String) || "") %>">
|
<input type="hidden" name="csrf_token" value="<%= URI.encode_www_form(env.get?("csrf_token").try &.as(String) || "") %>">
|
||||||
<input type="hidden" name="video_id" value="<%= video.id %>">
|
<input type="hidden" name="video_id" value="<%= video.id %>">
|
||||||
<button data-onclick="add_playlist_video" data-id="<%= video.id %>" type="submit" class="pure-button pure-button-primary">
|
<button data-onclick="add_playlist_video" data-id="<%= video.id %>" type="submit" class="pure-button pure-button-primary">
|
||||||
<b><%= translate(locale, "Add to playlist") %></b>
|
<b><%= I18n.translate(locale, "Add to playlist") %></b>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<script id="playlist_data" type="application/json">
|
<script id="playlist_data" type="application/json">
|
||||||
@ -190,7 +190,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<p id="views"><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
|
<p id="views"><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
|
||||||
<p id="likes"><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
|
<p id="likes"><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
|
||||||
<p id="dislikes" style="display: none; visibility: hidden;"></p>
|
<p id="dislikes" style="display: none; visibility: hidden;"></p>
|
||||||
<p id="genre"><%= translate(locale, "Genre: ") %>
|
<p id="genre"><%= I18n.translate(locale, "Genre: ") %>
|
||||||
<% if !video.genre_url %>
|
<% if !video.genre_url %>
|
||||||
<%= video.genre %>
|
<%= video.genre %>
|
||||||
<% else %>
|
<% else %>
|
||||||
@ -199,21 +199,21 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
</p>
|
</p>
|
||||||
<% if video.license %>
|
<% if video.license %>
|
||||||
<% if video.license.empty? %>
|
<% if video.license.empty? %>
|
||||||
<p id="license"><%= translate(locale, "License: ") %><%= translate(locale, "Standard YouTube license") %></p>
|
<p id="license"><%= I18n.translate(locale, "License: ") %><%= I18n.translate(locale, "Standard YouTube license") %></p>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p id="license"><%= translate(locale, "License: ") %><%= video.license %></p>
|
<p id="license"><%= I18n.translate(locale, "License: ") %><%= video.license %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<p id="family_friendly"><%= translate(locale, "Family friendly? ") %><%= translate_bool(locale, video.is_family_friendly) %></p>
|
<p id="family_friendly"><%= I18n.translate(locale, "Family friendly? ") %><%= I18n.translate_bool(locale, video.is_family_friendly) %></p>
|
||||||
<p id="wilson" style="display: none; visibility: hidden;"></p>
|
<p id="wilson" style="display: none; visibility: hidden;"></p>
|
||||||
<p id="rating" style="display: none; visibility: hidden;"></p>
|
<p id="rating" style="display: none; visibility: hidden;"></p>
|
||||||
<p id="engagement" style="display: none; visibility: hidden;"></p>
|
<p id="engagement" style="display: none; visibility: hidden;"></p>
|
||||||
<% if video.allowed_regions.size != REGIONS.size %>
|
<% if video.allowed_regions.size != REGIONS.size %>
|
||||||
<p id="allowed_regions">
|
<p id="allowed_regions">
|
||||||
<% if video.allowed_regions.size < REGIONS.size // 2 %>
|
<% if video.allowed_regions.size < REGIONS.size // 2 %>
|
||||||
<%= translate(locale, "Whitelisted regions: ") %><%= video.allowed_regions.join(", ") %>
|
<%= I18n.translate(locale, "Whitelisted regions: ") %><%= video.allowed_regions.join(", ") %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= translate(locale, "Blacklisted regions: ") %><%= (REGIONS.to_a - video.allowed_regions).join(", ") %>
|
<%= I18n.translate(locale, "Blacklisted regions: ") %><%= (REGIONS.to_a - video.allowed_regions).join(", ") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -245,9 +245,9 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<div class="h-box">
|
<div class="h-box">
|
||||||
<p id="published-date">
|
<p id="published-date">
|
||||||
<% if video.premiere_timestamp.try &.> Time.utc %>
|
<% if video.premiere_timestamp.try &.> Time.utc %>
|
||||||
<b><%= video.premiere_timestamp.try { |t| translate(locale, "Premieres `x`", t.to_s("%B %-d, %R UTC")) } %></b>
|
<b><%= video.premiere_timestamp.try { |t| I18n.translate(locale, "Premieres `x`", t.to_s("%B %-d, %R UTC")) } %></b>
|
||||||
<% else %>
|
<% else %>
|
||||||
<b><%= translate(locale, "Shared `x`", video.published.to_s("%B %-d, %Y")) %></b>
|
<b><%= I18n.translate(locale, "Shared `x`", video.published.to_s("%B %-d, %Y")) %></b>
|
||||||
<% end %>
|
<% end %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -269,7 +269,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<input id="music-desc-expansion" type="checkbox"/>
|
<input id="music-desc-expansion" type="checkbox"/>
|
||||||
<label for="music-desc-expansion">
|
<label for="music-desc-expansion">
|
||||||
<h3 id="music-description-title">
|
<h3 id="music-description-title">
|
||||||
<%= translate(locale, "Music in this video") %>
|
<%= I18n.translate(locale, "Music in this video") %>
|
||||||
<span class="icon ion-ios-arrow-up"></span>
|
<span class="icon ion-ios-arrow-up"></span>
|
||||||
<span class="icon ion-ios-arrow-down"></span>
|
<span class="icon ion-ios-arrow-down"></span>
|
||||||
</h3>
|
</h3>
|
||||||
@ -278,9 +278,9 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<div id="music-description-box">
|
<div id="music-description-box">
|
||||||
<% video.music.each do |music| %>
|
<% video.music.each do |music| %>
|
||||||
<div class="music-item">
|
<div class="music-item">
|
||||||
<p class="music-song"><%= translate(locale, "Song: ") %><%= music.song %></p>
|
<p class="music-song"><%= I18n.translate(locale, "Song: ") %><%= music.song %></p>
|
||||||
<p class="music-artist"><%= translate(locale, "Artist: ") %><%= music.artist %></p>
|
<p class="music-artist"><%= I18n.translate(locale, "Artist: ") %><%= music.artist %></p>
|
||||||
<p class="music-album"><%= translate(locale, "Album: ") %><%= music.album %></p>
|
<p class="music-album"><%= I18n.translate(locale, "Album: ") %><%= music.album %></p>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
@ -293,7 +293,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<% else %>
|
<% else %>
|
||||||
<noscript>
|
<noscript>
|
||||||
<a href="/watch?<%= env.params.query %>&nojs=1">
|
<a href="/watch?<%= env.params.query %>&nojs=1">
|
||||||
<%= translate(locale, "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.") %>
|
<%= I18n.translate(locale, "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.") %>
|
||||||
</a>
|
</a>
|
||||||
</noscript>
|
</noscript>
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -312,7 +312,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<% if !video.related_videos.empty? %>
|
<% if !video.related_videos.empty? %>
|
||||||
<div <% if plid %>style="display:none"<% end %>>
|
<div <% if plid %>style="display:none"<% end %>>
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="continue"><%= translate(locale, "preferences_continue_label") %></label>
|
<label for="continue"><%= I18n.translate(locale, "preferences_continue_label") %></label>
|
||||||
<input name="continue" id="continue" type="checkbox" <% if params.continue %>checked<% end %>>
|
<input name="continue" id="continue" type="checkbox" <% if params.continue %>checked<% end %>>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
@ -356,7 +356,7 @@ we're going to need to do it here in order to allow for translations.
|
|||||||
<b class="width:100%"><%=
|
<b class="width:100%"><%=
|
||||||
views = rv["view_count"]?.try &.to_i?
|
views = rv["view_count"]?.try &.to_i?
|
||||||
views ||= rv["view_count_short"]?.try { |x| short_text_to_number(x) }
|
views ||= rv["view_count_short"]?.try { |x| short_text_to_number(x) }
|
||||||
translate_count(locale, "generic_views_count", views || 0, NumberFormatting::Short)
|
I18n.translate_count(locale, "generic_views_count", views || 0, I18n::NumberFormatting::Short)
|
||||||
%></b>
|
%></b>
|
||||||
</div>
|
</div>
|
||||||
</h5>
|
</h5>
|
||||||
|
@ -144,7 +144,7 @@ def get_ytimg_pool(subdomain)
|
|||||||
if pool = YTIMG_POOLS[subdomain]?
|
if pool = YTIMG_POOLS[subdomain]?
|
||||||
return pool
|
return pool
|
||||||
else
|
else
|
||||||
LOGGER.info("ytimg_pool: Creating a new HTTP pool for \"https://#{subdomain}.ytimg.com\"")
|
Log.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)
|
pool = YoutubeConnectionPool.new(URI.parse("https://#{subdomain}.ytimg.com"), capacity: CONFIG.pool_size)
|
||||||
YTIMG_POOLS[subdomain] = pool
|
YTIMG_POOLS[subdomain] = pool
|
||||||
|
|
||||||
|
@ -40,8 +40,8 @@ private module Parsers
|
|||||||
begin
|
begin
|
||||||
return parse_internal(*args)
|
return parse_internal(*args)
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug("#{{{@type.name}}}: Failed to render item.")
|
Log.debug { "#{{{@type.name}}}: Failed to render item." }
|
||||||
LOGGER.debug("#{{{@type.name}}}: Got exception: #{ex.message}")
|
Log.debug { "#{{{@type.name}}}: Got exception: #{ex.message}" }
|
||||||
ProblematicTimelineItem.new(
|
ProblematicTimelineItem.new(
|
||||||
parse_exception: ex
|
parse_exception: ex
|
||||||
)
|
)
|
||||||
@ -452,7 +452,7 @@ private module Parsers
|
|||||||
end
|
end
|
||||||
|
|
||||||
content_container["items"]?.try &.as_a.each do |item|
|
content_container["items"]?.try &.as_a.each do |item|
|
||||||
result = parse_item(item, author_fallback.name, author_fallback.id)
|
result = YoutubeJSONParser.parse_item(item, author_fallback.name, author_fallback.id)
|
||||||
contents << result if result.is_a?(SearchItem)
|
contents << result if result.is_a?(SearchItem)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1017,6 +1017,10 @@ module HelperExtractors
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module YoutubeJSONParser
|
||||||
|
extend self
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
# Parses an item from Youtube's JSON response into a more usable structure.
|
# Parses an item from Youtube's JSON response into a more usable structure.
|
||||||
# The end result can either be a SearchVideo, SearchPlaylist or SearchChannel.
|
# The end result can either be a SearchVideo, SearchPlaylist or SearchChannel.
|
||||||
def parse_item(item : JSON::Any, author_fallback : String? = "", author_id_fallback : String? = "")
|
def parse_item(item : JSON::Any, author_fallback : String? = "", author_id_fallback : String? = "")
|
||||||
@ -1029,13 +1033,13 @@ def parse_item(item : JSON::Any, author_fallback : String? = "", author_id_fallb
|
|||||||
# Each parser automatically validates the data given to see if the data is
|
# Each parser automatically validates the data given to see if the data is
|
||||||
# applicable to itself. If not nil is returned and the next parser is attempted.
|
# applicable to itself. If not nil is returned and the next parser is attempted.
|
||||||
ITEM_PARSERS.each do |parser|
|
ITEM_PARSERS.each do |parser|
|
||||||
LOGGER.trace("parse_item: Attempting to parse item using \"#{parser.parser_name}\" (cycling...)")
|
Log.trace { "Attempting to parse item using \"#{parser.parser_name}\" (cycling...)" }
|
||||||
|
|
||||||
if result = parser.process(item, author_fallback)
|
if result = parser.process(item, author_fallback)
|
||||||
LOGGER.debug("parse_item: Successfully parsed via #{parser.parser_name}")
|
Log.debug { "Successfully parsed via #{parser.parser_name}" }
|
||||||
return result
|
return result
|
||||||
else
|
else
|
||||||
LOGGER.trace("parse_item: Parser \"#{parser.parser_name}\" does not apply. Cycling to the next one...")
|
Log.trace { "Parser \"#{parser.parser_name}\" does not apply. Cycling to the next one..." }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1056,14 +1060,14 @@ def extract_items(initial_data : InitialData, &)
|
|||||||
|
|
||||||
# This is identical to the parser cycling of parse_item().
|
# This is identical to the parser cycling of parse_item().
|
||||||
ITEM_CONTAINER_EXTRACTOR.each do |extractor|
|
ITEM_CONTAINER_EXTRACTOR.each do |extractor|
|
||||||
LOGGER.trace("extract_items: Attempting to extract item container using \"#{extractor.extractor_name}\" (cycling...)")
|
Log.trace { "Attempting to extract item container using \"#{extractor.extractor_name}\" (cycling...)" }
|
||||||
|
|
||||||
if container = extractor.process(unpackaged_data)
|
if container = extractor.process(unpackaged_data)
|
||||||
LOGGER.debug("extract_items: Successfully unpacked container with \"#{extractor.extractor_name}\"")
|
Log.debug { "Successfully unpacked container with \"#{extractor.extractor_name}\"" }
|
||||||
# Extract items in container
|
# Extract items in container
|
||||||
container.each { |item| yield item }
|
container.each { |item| yield item }
|
||||||
else
|
else
|
||||||
LOGGER.trace("extract_items: Extractor \"#{extractor.extractor_name}\" does not apply. Cycling to the next one...")
|
Log.trace { "Extractor \"#{extractor.extractor_name}\" does not apply. Cycling to the next one..." }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1088,3 +1092,4 @@ def extract_items(
|
|||||||
|
|
||||||
return items, continuation
|
return items, continuation
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
@ -62,8 +62,8 @@ def has_verified_badge?(badges : JSON::Any?)
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug("Unable to parse owner badges. Got exception: #{ex.message}")
|
Log.debug { "Unable to parse owner badges. Got exception: #{ex.message}" }
|
||||||
LOGGER.trace("Owner badges data: #{badges.to_json}")
|
Log.trace { "Owner badges data: #{badges.to_json}" }
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
module YoutubeAPI
|
module YoutubeAPI
|
||||||
extend self
|
extend self
|
||||||
|
Log = ::Log.for(self)
|
||||||
|
|
||||||
# For Android versions, see https://en.wikipedia.org/wiki/Android_version_history
|
# For Android versions, see https://en.wikipedia.org/wiki/Android_version_history
|
||||||
private ANDROID_APP_VERSION = "19.32.34"
|
private ANDROID_APP_VERSION = "19.32.34"
|
||||||
@ -634,9 +635,9 @@ module YoutubeAPI
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
LOGGER.debug("YoutubeAPI: Using endpoint: \"#{endpoint}\"")
|
Log.debug { "Using endpoint: \"#{endpoint}\"" }
|
||||||
LOGGER.trace("YoutubeAPI: ClientConfig: #{client_config}")
|
Log.trace { "ClientConfig: #{client_config}" }
|
||||||
LOGGER.trace("YoutubeAPI: POST data: #{data}")
|
Log.trace { "POST data: #{data}" }
|
||||||
|
|
||||||
# Send the POST request
|
# Send the POST request
|
||||||
body = YT_POOL.client() do |client|
|
body = YT_POOL.client() do |client|
|
||||||
@ -659,9 +660,9 @@ module YoutubeAPI
|
|||||||
message = initial_data["error"]["message"].to_s.sub(/(\\n)+\^$/, "")
|
message = initial_data["error"]["message"].to_s.sub(/(\\n)+\^$/, "")
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
LOGGER.error("YoutubeAPI: Got error #{code} when requesting #{endpoint}")
|
Log.error { "Got error #{code} when requesting #{endpoint}" }
|
||||||
LOGGER.error("YoutubeAPI: #{message}")
|
Log.error { message }
|
||||||
LOGGER.info("YoutubeAPI: POST data was: #{data}")
|
Log.info { "POST data was: #{data}" }
|
||||||
|
|
||||||
raise InfoException.new("Could not extract JSON. Youtube API returned \
|
raise InfoException.new("Could not extract JSON. Youtube API returned \
|
||||||
error #{code} with message:<br>\"#{message}\"")
|
error #{code} with message:<br>\"#{message}\"")
|
||||||
@ -683,14 +684,16 @@ module YoutubeAPI
|
|||||||
endpoint : String,
|
endpoint : String,
|
||||||
data : Hash,
|
data : Hash,
|
||||||
) : Hash(String, JSON::Any)
|
) : Hash(String, JSON::Any)
|
||||||
|
log_name = "invidious-companion"
|
||||||
|
|
||||||
headers = HTTP::Headers{
|
headers = HTTP::Headers{
|
||||||
"Content-Type" => "application/json; charset=UTF-8",
|
"Content-Type" => "application/json; charset=UTF-8",
|
||||||
"Authorization" => "Bearer #{CONFIG.invidious_companion_key}",
|
"Authorization" => "Bearer #{CONFIG.invidious_companion_key}",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
LOGGER.debug("Invidious companion: Using endpoint: \"#{endpoint}\"")
|
Log.debug { "Using endpoint: \"#{endpoint}\"" }
|
||||||
LOGGER.trace("Invidious companion: POST data: #{data}")
|
Log.trace { "POST data: #{data}" }
|
||||||
|
|
||||||
# Send the POST request
|
# Send the POST request
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user