From 004fb96b2f9b848172aa52a72fac08a616a7913b Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 4 Mar 2019 07:53:31 -0600 Subject: [PATCH 01/81] Add nonce to pubsub token --- src/invidious.cr | 14 ++++++++++---- src/invidious/channels.cr | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 0951fd72c..a19a742b5 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -92,7 +92,7 @@ PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com") TEXTCAPTCHA_URL = URI.parse("http://textcaptcha.com/omarroth@hotmail.com.json") CURRENT_COMMIT = `git rev-list HEAD --max-count=1 --abbrev-commit`.strip CURRENT_VERSION = `git describe --tags $(git rev-list --tags --max-count=1)`.strip -CURRENT_BRANCH = `git status | head -1`.strip +CURRENT_BRANCH = `git status | head -1`.strip LOCALES = { "ar" => load_locale("ar"), @@ -136,7 +136,7 @@ if config.statistics_enabled "software" => { "name" => "invidious", "version" => "#{CURRENT_VERSION}-#{CURRENT_COMMIT}", - "branch" => "#{CURRENT_BRANCH}", + "branch" => "#{CURRENT_BRANCH}", }, "openRegistrations" => config.registration_enabled, "usage" => { @@ -2329,13 +2329,19 @@ get "/feed/webhook/:token" do |env| challenge = env.params.query["hub.challenge"] lease_seconds = env.params.query["hub.lease_seconds"] - time, signature = verify_token.split(":") + if verify_token.starts_with? "v1" + _, time, nonce, signature = verify_token.split(":") + data = "#{time}:#{nonce}" + else + time, signature = verify_token.split(":") + data = "#{time}" + end if Time.now.to_unix - time.to_i > 600 halt env, status_code: 400 end - if OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, time) != signature + if OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, data) != signature halt env, status_code: 400 end diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index bb5480453..b38c5e1a6 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -194,11 +194,13 @@ end def subscribe_pubsub(ucid, key, config) client = make_client(PUBSUB_URL) time = Time.now.to_unix.to_s + nonce = Random::Secure.hex(4) + signature = "#{time}:#{nonce}" host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) body = { - "hub.callback" => "#{host_url}/feed/webhook/#{time}:#{OpenSSL::HMAC.hexdigest(:sha1, key, time)}", + "hub.callback" => "#{host_url}/feed/webhook/v1:#{time}:#{nonce}:#{OpenSSL::HMAC.hexdigest(:sha1, key, signature)}", "hub.topic" => "https://www.youtube.com/feeds/videos.xml?channel_id=#{ucid}", "hub.verify" => "async", "hub.mode" => "subscribe", From aa63c3f70ed98a12a2915de94fd9afbf476b7ccb Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 4 Mar 2019 10:46:58 -0600 Subject: [PATCH 02/81] Update formatting and default feed menu --- src/invidious.cr | 6 +++--- src/invidious/helpers/helpers.cr | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index a19a742b5..4154f3eec 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2319,7 +2319,7 @@ get "/feed/playlist/:plid" do |env| document end -# Add support for subscribing to channels via PubSubHubbub +# Support push notifications via PubSubHubbub get "/feed/webhook/:token" do |env| verify_token = env.params.url["token"] @@ -2363,11 +2363,11 @@ post "/feed/webhook/:token" do |env| rss.xpath_nodes("//feed/entry").each do |entry| id = entry.xpath_node("videoid").not_nil!.content - video = get_video(id, PG_DB, proxies) + video = get_video(id, PG_DB, proxies, region: nil) video = ChannelVideo.new(id, video.title, video.published, Time.now, video.ucid, video.author, video.length_seconds) PG_DB.exec("UPDATE users SET notifications = notifications || $1 \ - WHERE updated < $2 AND $3 = ANY(subscriptions) AND $1 <> ALL(notifications)", video.id, video.published, video.ucid) + WHERE updated < $2 AND $3 = ANY(subscriptions) AND $1 <> ALL(notifications)", video.id, video.published, video.ucid) video_array = video.to_a args = arg_array(video_array) diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 3574e5cc0..3cc4d9cfa 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -17,7 +17,7 @@ user: String, domain: String?, # Domain to be used for links to resources on the site where an absolute URL is required use_pubsub_feeds: {type: Bool, default: false}, # Subscribe to channels using PubSubHubbub (requires domain, hmac_key) default_home: {type: String, default: "Top"}, - feed_menu: {type: Array(String), default: ["Popular", "Top", "Trending"]}, + feed_menu: {type: Array(String), default: ["Popular", "Top", "Trending", "Subscriptions"]}, top_enabled: {type: Bool, default: true}, captcha_enabled: {type: Bool, default: true}, login_enabled: {type: Bool, default: true}, From 6375a624658bfeccf3fb403d0a7e0ae5194d5d95 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 4 Mar 2019 11:07:27 -0600 Subject: [PATCH 03/81] Clean up handling for callback endpoint --- src/invidious.cr | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 4154f3eec..e0967753d 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2352,29 +2352,35 @@ get "/feed/webhook/:token" do |env| end post "/feed/webhook/:token" do |env| + token = env.params.url["token"] body = env.request.body.not_nil!.gets_to_end signature = env.request.headers["X-Hub-Signature"].lchop("sha1=") if signature != OpenSSL::HMAC.hexdigest(:sha1, HMAC_KEY, body) + logger.write("#{token} : Invalid signature") halt env, status_code: 200 end - rss = XML.parse_html(body) - rss.xpath_nodes("//feed/entry").each do |entry| - id = entry.xpath_node("videoid").not_nil!.content + spawn do + rss = XML.parse_html(body) + rss.xpath_nodes("//feed/entry").each do |entry| + id = entry.xpath_node("videoid").not_nil!.content + published = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z", Time::Location.local) + updated = Time.parse(entry.xpath_node("updated").not_nil!.content, "%FT%X%z", Time::Location.local) - video = get_video(id, PG_DB, proxies, region: nil) - video = ChannelVideo.new(id, video.title, video.published, Time.now, video.ucid, video.author, video.length_seconds) + video = get_video(id, PG_DB, proxies, region: nil) + video = ChannelVideo.new(id, video.title, published, updated, video.ucid, video.author, video.length_seconds) - PG_DB.exec("UPDATE users SET notifications = notifications || $1 \ - WHERE updated < $2 AND $3 = ANY(subscriptions) AND $1 <> ALL(notifications)", video.id, video.published, video.ucid) + PG_DB.exec("UPDATE users SET notifications = notifications || $1 \ + WHERE updated < $2 AND $3 = ANY(subscriptions) AND $1 <> ALL(notifications)", video.id, video.published, video.ucid) - video_array = video.to_a - args = arg_array(video_array) + video_array = video.to_a + args = arg_array(video_array) - PG_DB.exec("INSERT INTO channel_videos VALUES (#{args}) \ - ON CONFLICT (id) DO UPDATE SET title = $2, published = $3, \ - updated = $4, ucid = $5, author = $6, length_seconds = $7", video_array) + PG_DB.exec("INSERT INTO channel_videos VALUES (#{args}) \ + ON CONFLICT (id) DO UPDATE SET title = $2, published = $3, \ + updated = $4, ucid = $5, author = $6, length_seconds = $7", video_array) + end end halt env, status_code: 200 From f16273772e8e76bedd14872405300c17b9878677 Mon Sep 17 00:00:00 2001 From: dimqua Date: Mon, 4 Mar 2019 23:14:24 +0300 Subject: [PATCH 04/81] (preferences) fix word wrap --- assets/css/default.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/css/default.css b/assets/css/default.css index 85a8911ff..b9e9d0b8d 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -339,3 +339,7 @@ img.thumbnail { margin-top: 0.5em; margin-bottom: 0.5em; } + +.pure-control-group label { + word-wrap: normal; +} From 32b9c0c8402076522601a3ed4e66235e05d24274 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 4 Mar 2019 14:43:17 -0600 Subject: [PATCH 05/81] Fix tagging for current branch --- src/invidious.cr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index e0967753d..9c75de1cf 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -90,9 +90,9 @@ REDDIT_URL = URI.parse("https://www.reddit.com") LOGIN_URL = URI.parse("https://accounts.google.com") PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com") TEXTCAPTCHA_URL = URI.parse("http://textcaptcha.com/omarroth@hotmail.com.json") +CURRENT_BRANCH = `git branch | sed -n '/\* /s///p'`.strip CURRENT_COMMIT = `git rev-list HEAD --max-count=1 --abbrev-commit`.strip CURRENT_VERSION = `git describe --tags $(git rev-list --tags --max-count=1)`.strip -CURRENT_BRANCH = `git status | head -1`.strip LOCALES = { "ar" => load_locale("ar"), @@ -2536,15 +2536,15 @@ end get "/api/v1/stats" do |env| env.response.content_type = "application/json" - if statistics["error"]? - halt env, status_code: 500, response: statistics.to_json - end - if !config.statistics_enabled error_message = {"error" => "Statistics are not enabled."}.to_json halt env, status_code: 400, response: error_message end + if statistics["error"]? + halt env, status_code: 500, response: statistics.to_json + end + if env.params.query["pretty"]? && env.params.query["pretty"] == "1" statistics.to_pretty_json else From 2840d98fd425b78dbe336b4c806f95c286b9f6e3 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 4 Mar 2019 15:17:09 -0600 Subject: [PATCH 06/81] Fix tagging for current version --- src/invidious.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 9c75de1cf..0bf07cf03 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -92,7 +92,7 @@ PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com") TEXTCAPTCHA_URL = URI.parse("http://textcaptcha.com/omarroth@hotmail.com.json") CURRENT_BRANCH = `git branch | sed -n '/\* /s///p'`.strip CURRENT_COMMIT = `git rev-list HEAD --max-count=1 --abbrev-commit`.strip -CURRENT_VERSION = `git describe --tags $(git rev-list --tags --max-count=1)`.strip +CURRENT_VERSION = `git describe --tags --abbrev=0`.strip LOCALES = { "ar" => load_locale("ar"), From 2a1befb41a7fa2bc8f15bff704761a79847b51ba Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 07:17:29 -0600 Subject: [PATCH 07/81] Fix sorting for latest_only --- src/invidious.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 0bf07cf03..9c3248abc 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1964,7 +1964,7 @@ get "/feed/subscriptions" do |env| # Show latest video from each channel videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM #{view_name} \ - ORDER BY ucid, published", as: ChannelVideo) + ORDER BY ucid, published DESC", as: ChannelVideo) end videos.sort_by! { |video| video.published }.reverse! @@ -2210,7 +2210,7 @@ get "/feed/private" do |env| if latest_only videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM #{view_name} \ - ORDER BY ucid, published", as: ChannelVideo) + ORDER BY ucid, published DESC", as: ChannelVideo) videos.sort_by! { |video| video.published }.reverse! else From 1435516a9cead37ecdbab56cc4e8396ad0aaae95 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 12:56:59 -0600 Subject: [PATCH 08/81] Add port number to host URL --- src/invidious.cr | 29 ++++++++++++++++------------- src/invidious/channels.cr | 2 +- src/invidious/helpers/utils.cr | 17 +++++++++++++---- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 9c3248abc..3556ef148 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -417,7 +417,7 @@ get "/watch" do |env| video.description = replace_links(video.description) description = video.short_description - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) host_params = env.request.query_params host_params.delete_all("v") @@ -517,7 +517,7 @@ get "/embed/:id" do |env| video.description = replace_links(video.description) description = video.short_description - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) host_params = env.request.query_params host_params.delete_all("v") @@ -603,7 +603,7 @@ get "/opensearch.xml" do |env| locale = LOCALES[env.get("locale").as(String)]? env.response.content_type = "application/opensearchdescription+xml" - host = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host = make_host_url(config, Kemal.config) XML.build(indent: " ", encoding: "UTF-8") do |xml| xml.element("OpenSearchDescription", xmlns: "http://a9.com/-/spec/opensearch/1.1/") do @@ -1500,7 +1500,7 @@ get "/subscription_manager" do |env| subscriptions.sort_by! { |channel| channel.author.downcase } if action_takeout - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) if format == "json" env.response.content_type = "application/json" @@ -2057,7 +2057,8 @@ end get "/feed/channel/:ucid" do |env| locale = LOCALES[env.get("locale").as(String)]? - env.response.content_type = "text/xml" + env.response.content_type = "application/atom+xml" + ucid = env.params.url["ucid"] begin @@ -2101,7 +2102,7 @@ get "/feed/channel/:ucid" do |env| ) end - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) path = env.request.path feed = XML.build(indent: " ", encoding: "UTF-8") do |xml| @@ -2168,6 +2169,8 @@ end get "/feed/private" do |env| locale = LOCALES[env.get("locale").as(String)]? + env.response.content_type = "application/atom+xml" + token = env.params.query["token"]? if !token @@ -2235,7 +2238,7 @@ get "/feed/private" do |env| videos = videos[0..max_results] end - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) path = env.request.path query = env.request.query.not_nil! @@ -2281,16 +2284,17 @@ get "/feed/private" do |env| end end - env.response.content_type = "application/atom+xml" feed end get "/feed/playlist/:plid" do |env| locale = LOCALES[env.get("locale").as(String)]? + env.response.content_type = "application/atom+xml" + plid = env.params.url["plid"] - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) path = env.request.path client = make_client(YT_URL) @@ -2315,7 +2319,6 @@ get "/feed/playlist/:plid" do |env| document = document.gsub(match[0], "#{content}") end - env.response.content_type = "text/xml" document end @@ -2897,7 +2900,7 @@ get "/api/v1/videos/:id" do |env| end if video.player_response["streamingData"]?.try &.["hlsManifestUrl"]? - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) host_params = env.request.query_params host_params.delete_all("v") @@ -4091,7 +4094,7 @@ get "/api/manifest/hls_variant/*" do |env| env.response.content_type = "application/x-mpegURL" env.response.headers.add("Access-Control-Allow-Origin", "*") - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) manifest = manifest.body manifest.gsub("https://www.youtube.com", host_url) @@ -4105,7 +4108,7 @@ get "/api/manifest/hls_playlist/*" do |env| halt env, status_code: manifest.status_code end - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) manifest = manifest.body.gsub("https://www.youtube.com", host_url) manifest = manifest.gsub(/https:\/\/r\d---.{11}\.c\.youtube\.com/, host_url) diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index b38c5e1a6..5ce9775f5 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -197,7 +197,7 @@ def subscribe_pubsub(ucid, key, config) nonce = Random::Secure.hex(4) signature = "#{time}:#{nonce}" - host_url = make_host_url(Kemal.config.ssl || config.https_only, config.domain) + host_url = make_host_url(config, Kemal.config) body = { "hub.callback" => "#{host_url}/feed/webhook/v1:#{time}:#{nonce}:#{OpenSSL::HMAC.hexdigest(:sha1, key, signature)}", diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 5ccc10095..c200801f4 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -193,19 +193,28 @@ def arg_array(array, start = 1) return args end -def make_host_url(ssl, host) +def make_host_url(config, kemal_config) + ssl = config.https_only || kemal_config.ssl + if ssl scheme = "https://" else scheme = "http://" end - if host - host = host.lchop(".") - return "#{scheme}#{host}" + if kemal_config.port != 80 && kemal_config.port != 443 + port = ":#{kemal_config.port}" else + port = "" + end + + if !config.domain return "" end + + host = config.domain.not_nil!.lchop(".") + + return "#{scheme}#{host}#{port}" end def get_referer(env, fallback = "/") From e4dc430c740870399504a9641a0fb10ffced79f5 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 13:46:08 -0600 Subject: [PATCH 09/81] Update hub topic URL --- src/invidious/channels.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index 5ce9775f5..484aac2fa 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -201,7 +201,7 @@ def subscribe_pubsub(ucid, key, config) body = { "hub.callback" => "#{host_url}/feed/webhook/v1:#{time}:#{nonce}:#{OpenSSL::HMAC.hexdigest(:sha1, key, signature)}", - "hub.topic" => "https://www.youtube.com/feeds/videos.xml?channel_id=#{ucid}", + "hub.topic" => "https://www.youtube.com/xml/feeds/videos.xml?channel_id=#{ucid}", "hub.verify" => "async", "hub.mode" => "subscribe", "hub.lease_seconds" => "432000", From 99d9c3a9003d98a34575b49c56cb26ac913f428a Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 14:41:38 -0600 Subject: [PATCH 10/81] Fix rows for subscribe job --- src/invidious/jobs.cr | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/invidious/jobs.cr b/src/invidious/jobs.cr index 49745abad..503746016 100644 --- a/src/invidious/jobs.cr +++ b/src/invidious/jobs.cr @@ -158,11 +158,13 @@ def subscribe_to_feeds(db, logger, key, config) spawn do loop do db.query_all("SELECT id FROM channels WHERE CURRENT_TIMESTAMP - subscribed > '4 days'") do |rs| - ucid = rs.read(String) - response = subscribe_pubsub(ucid, key, config) + rs.each do + ucid = rs.read(String) + response = subscribe_pubsub(ucid, key, config) - if response.status_code >= 400 - logger.write("#{ucid} : #{response.body}\n") + if response.status_code >= 400 + logger.write("#{ucid} : #{response.body}\n") + end end end From b2f4a0276a0c9974e3c2e09eb2ea1e4b8e9bbabb Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 14:43:09 -0600 Subject: [PATCH 11/81] Remove "lease_seconds" from pubsub response --- src/invidious.cr | 1 - 1 file changed, 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 3556ef148..3f2dba661 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2330,7 +2330,6 @@ get "/feed/webhook/:token" do |env| mode = env.params.query["hub.mode"] topic = env.params.query["hub.topic"] challenge = env.params.query["hub.challenge"] - lease_seconds = env.params.query["hub.lease_seconds"] if verify_token.starts_with? "v1" _, time, nonce, signature = verify_token.split(":") From a65998274fffd1334cf4d3d0f79a58e78b1b450e Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 15:22:04 -0600 Subject: [PATCH 12/81] Defer loading videojs-share until last --- src/invidious/views/components/player.ecr | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 99f99c581..e2cfa3a1b 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -137,8 +137,6 @@ player.on('error', function(event) { } }); -player.share(shareOptions); - <% if params[:video_start] > 0 || params[:video_end] > 0 %> player.markers({ onMarkerReached: function(marker) { @@ -188,4 +186,7 @@ if (bpb) { }); } <% end %> + +// Since videojs-share can sometimes be blocked, we try to load it last +player.share(shareOptions); From 7448159d6bb7295bdbf2a54ffed3d926c9b6beb5 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 5 Mar 2019 23:55:06 -0600 Subject: [PATCH 13/81] Update CHANGELOG and bump version --- CHANGELOG.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ shard.yml | 2 +- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46de1263a..6030f01f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,65 @@ +# 0.15.0 (2019-03-06) + +## Version 0.15.0: Preferences and Channel Playlists + +The project has seen quite a bit of activity this past month. Large focus has been on fixing bugs, but there's still quite a few new features I'm happy to announce. There have been [133 commits](https://github.com/omarroth/invidious/compare/0.14.0...0.15.0) from 15 contributors this past month. + +As a couple miscellaneous changes, a couple [nice screenshots](https://github.com/omarroth/invidious#screenshots) have been added to the README, so folks can see more of what the site has to offer without creating an account. + +The footer has also been cleaned up quite a bit, and now displays the current version, so it's easier to know what features are available from the current instance. + +## For Administrators + +This past month there has been a minor release - `0.14.1` - which fixes a breaking change made by YouTube for their polymer redesign. + +There have been several new features that unfortunately require a database migration. There are migration scripts provided in `config/migrate-scripts`, and the [wiki](https://github.com/omarroth/invidious/wiki/Updating) has instructions for automatically applying them. I'll do my best to keep those changes to a minimum, and expect to see a corresponding script to automatically apply any new changes. + +Administrator preferences have been added with [#312](https://github.com/omarroth/invidious/issues/312), which allows administrators to customize their instance. Administrators can change the order of feed menus, change the default homepage, disable open registration, and several other options. There's a short 'how-to' [here](https://github.com/omarroth/invidious/issues/312#issuecomment-468831842), and the new options are documented [here](https://github.com/omarroth/invidious/wiki/Configuration). + +An `/api/v1/stats` endpoint has been added with [#356](https://github.com/omarroth/invidious/issues/356), which reports the instance version and number of active users. Statistics are disabled by default, and can be enabled in administator preferences. Statistics for the official instance are available [here](https://invidio.us/api/v1/stats?pretty=1). + +## For Developers + +`/api/v1/channels/:ucid` now provides an `autoGenerated` tag, which returns true for [topic channels](https://www.youtube.com/channel/UCE80FOXpJydkkMo-BYoJdEg), and larger [genre channels](https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ) generated by YouTube. These channels don't have any videos of their own, so `latestVideos` will be empty. It is recommended instead to display a list of playlists generated by YouTube. + +You can now pull a list of playlists from a channel with `/api/v1/channels/playlists/:ucid`. Supported options are documented in the [wiki](https://github.com/omarroth/invidious/wiki/API#get-apiv1channelsplaylistsucid-apiv1channelsucidplaylists). Pagination is handled with a `continuation` token, which is generated on each call. Of note is that auto-generated channels currently have one page of results, and subsequent calls will be empty. + +For quickly pulling the latest 30 videos from a channel, there is now `/api/v1/channels/latest/:ucid`. It is much faster than a call to `/api/v1/channels/:ucid`. It will not convert an author name to a valid ucid automatically, and will not return any extra data about a channel. + +## Preferences + +In addition to administrator preferences mentioned above, you can now change your preferences without an account (see [#42](https://github.com/omarroth/invidious/pull/42)). I think this is quite an improvement to the usability of the site, and is much friendlier to privacy-conscious folks that don't want to make an account. Preferences will be automatically imported to a newly created account. + +Several issues with sorting subscriptions have been fixed, and `/manage_subscriptions` has been sped up significantly. The subscription feed has also seen a bump in performance. Delayed notifications have unfortunately started becoming a problem now that there are more users on the site. Some new changes are currently being tested which should mostly resolve the issue, so expect to see more in the next release. + +## Channel Playlists + +You can now view available playlists from a channel, and [auto-generated channels](https://invidio.us/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ) are no longer empty. You can sort as you would on YouTube, and all the same functionality should be available. I'm quite pleased to finally have it implemented, since it's currently the only data available from the above mentioned auto-generated channels, and makes it much easier to consume music on the site. + +There's also more discussion on improving Invidious for streaming music in [#304](https://github.com/omarroth/invidious/issues/304), and adding support for music.youtube.com. I would appreciate any thoughts on how to improve that experience, since it's a very large and useful part of YouTube. + +## Finances + +### Donations + +[Patreon](https://www.patreon.com/omarroth) : \$42.42 +[Liberapay](https://liberapay.com/omarroth) : \$30.97 +Crypto : ~\$0.00 (converted from BCH, BTC) +Total : \$73.39 + +### Expenses + +invidious-load1 (nyc1) : \$10.00 (load balancer) +invidious-update1 (s-1vcpu-1gb) : \$5.00 (updates feeds) +invidious-node1 (s-1vcpu-1gb) : \$5.00 (web server) +invidious-node2 (s-1vcpu-1gb) : \$5.00 (web server) +invidious-node3 (s-1vcpu-1gb) : \$5.00 (web server) +invidious-node4 (s-1vcpu-1gb) : \$5.00 (web server) +invidious-db1 (s-4vcpu-8gb) : \$40.00 (database) +Total : \$75.00 + +It's been very humbling to see how fast the project has grown, and I look forward to making the site even better. Thank you everyone. + # 0.14.0 (2019-02-06) ## Version 0.14.0: Community diff --git a/shard.yml b/shard.yml index 7edab3545..f576c9ee5 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: invidious -version: 0.14.1 +version: 0.15.0 authors: - Omar Roth From bc9d70109c9f6e966b5d27a0ab617e578cdfc604 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Wed, 6 Mar 2019 08:45:04 -0600 Subject: [PATCH 14/81] Fix typo in index --- src/invidious.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 3f2dba661..fa4f4a17a 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -264,7 +264,7 @@ get "/" do |env| if user user = user.as(User) if user.preferences.redirect_feed - env.redirect "/feed/subscriptions" + next env.redirect "/feed/subscriptions" end end From 7557ffcda1c06d5a87fddfff8b3935c8e98ccb23 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Wed, 6 Mar 2019 09:54:56 -0600 Subject: [PATCH 15/81] Mark deleted channels in /subscription_manager --- assets/css/darktheme.css | 4 ++++ assets/css/default.css | 4 ++++ src/invidious.cr | 9 +-------- src/invidious/views/subscription_manager.ecr | 10 ++++++---- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/assets/css/darktheme.css b/assets/css/darktheme.css index dce2cb91a..1b70956bb 100644 --- a/assets/css/darktheme.css +++ b/assets/css/darktheme.css @@ -28,6 +28,10 @@ body { color: rgba(35, 35, 35, 1); } +.pure-form input[type="file"] { + color: #f0f0f0; +} + .navbar > .searchbar input { background-color: inherit; color: inherit; diff --git a/assets/css/default.css b/assets/css/default.css index 85a8911ff..cc29e1efc 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -1,3 +1,7 @@ +.deleted { + background-color: rgb(255, 0, 0, 0.5); +} + .channel-owner { background-color: #008bec; color: #fff; diff --git a/src/invidious.cr b/src/invidious.cr index fa4f4a17a..bbf9b4c94 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1583,14 +1583,7 @@ post "/data_control" do |env| user.subscriptions += body["subscriptions"].as_a.map { |a| a.as_s } user.subscriptions.uniq! - user.subscriptions.select! do |ucid| - begin - get_channel(ucid, PG_DB, false, false) - true - rescue ex - false - end - end + user.subscriptions = get_batch_channels(user.subscriptions, PG_DB, false, false) PG_DB.exec("UPDATE users SET subscriptions = $1 WHERE email = $2", user.subscriptions, user.email) end diff --git a/src/invidious/views/subscription_manager.ecr b/src/invidious/views/subscription_manager.ecr index 3c0836eaa..0f9762f99 100644 --- a/src/invidious/views/subscription_manager.ecr +++ b/src/invidious/views/subscription_manager.ecr @@ -4,7 +4,9 @@
-

<%= translate(locale, "`x` subscriptions", %(#{subscriptions.size})) %>

+

+ <%= translate(locale, "`x` subscriptions", %(#{subscriptions.size})) %> +

@@ -20,15 +22,15 @@ <% subscriptions.each do |channel| %>
-
+
-

+

Date: Thu, 7 Mar 2019 12:26:30 +0500 Subject: [PATCH 16/81] Add alternate link with rss feed to playlist page --- src/invidious/views/playlist.ecr | 1 + 1 file changed, 1 insertion(+) diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index e6775e154..cd7671402 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -1,5 +1,6 @@ <% content_for "header" do %> <%= playlist.title %> - Invidious + <% end %> From ae10052aaf816fe6e309dc9f92a1e6f98464a3df Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Thu, 7 Mar 2019 21:13:54 -0600 Subject: [PATCH 18/81] Fix date parsing for RSS feeds --- src/invidious.cr | 8 ++++---- src/invidious/channels.cr | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index f4f84e86a..26f98b2c1 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2072,8 +2072,8 @@ get "/feed/channel/:ucid" do |env| video_id = entry.xpath_node("videoid").not_nil!.content title = entry.xpath_node("title").not_nil!.content - published = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z", Time::Location.local) - updated = Time.parse(entry.xpath_node("updated").not_nil!.content, "%FT%X%z", Time::Location.local) + published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content) + updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) author = entry.xpath_node("author/name").not_nil!.content ucid = entry.xpath_node("channelid").not_nil!.content @@ -2361,8 +2361,8 @@ post "/feed/webhook/:token" do |env| rss = XML.parse_html(body) rss.xpath_nodes("//feed/entry").each do |entry| id = entry.xpath_node("videoid").not_nil!.content - published = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z", Time::Location.local) - updated = Time.parse(entry.xpath_node("updated").not_nil!.content, "%FT%X%z", Time::Location.local) + published = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) + updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) video = get_video(id, PG_DB, proxies, region: nil) video = ChannelVideo.new(id, video.title, published, updated, video.ucid, video.author, video.length_seconds) diff --git a/src/invidious/channels.cr b/src/invidious/channels.cr index 484aac2fa..3ecfe529e 100644 --- a/src/invidious/channels.cr +++ b/src/invidious/channels.cr @@ -112,8 +112,8 @@ def fetch_channel(ucid, db, pull_all_videos = true, locale = nil) rss.xpath_nodes("//feed/entry").each do |entry| video_id = entry.xpath_node("videoid").not_nil!.content title = entry.xpath_node("title").not_nil!.content - published = Time.parse(entry.xpath_node("published").not_nil!.content, "%FT%X%z", Time::Location.local) - updated = Time.parse(entry.xpath_node("updated").not_nil!.content, "%FT%X%z", Time::Location.local) + published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content) + updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) author = entry.xpath_node("author/name").not_nil!.content ucid = entry.xpath_node("channelid").not_nil!.content From b9c750101258fce1cf1bc0819bd857a8d61faa84 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Thu, 7 Mar 2019 21:49:52 -0600 Subject: [PATCH 19/81] Fix typo in pubsub update --- src/invidious.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 26f98b2c1..e0e124b8a 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2361,7 +2361,7 @@ post "/feed/webhook/:token" do |env| rss = XML.parse_html(body) rss.xpath_nodes("//feed/entry").each do |entry| id = entry.xpath_node("videoid").not_nil!.content - published = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) + published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content) updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content) video = get_video(id, PG_DB, proxies, region: nil) From ce528c978313b26a7060fdac5b2aa26153c01194 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 10:34:52 -0600 Subject: [PATCH 20/81] Update sorting for subscriptions --- src/invidious.cr | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index e0e124b8a..f5c97c136 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1909,12 +1909,6 @@ get "/feed/subscriptions" do |env| offset = (page - 1) * max_results end - if preferences.sort == "published - reverse" - sort = "" - else - sort = "DESC" - end - notifications = PG_DB.query_one("SELECT notifications FROM users WHERE email = $1", user.email, as: Array(String)) view_name = "subscriptions_#{sha256(user.email)[0..7]}" @@ -1925,7 +1919,7 @@ get "/feed/subscriptions" do |env| args = arg_array(notifications) notifications = PG_DB.query_all("SELECT * FROM channel_videos WHERE id IN (#{args}) - ORDER BY published #{sort}", notifications, as: ChannelVideo) + ORDER BY published DESC", notifications, as: ChannelVideo) videos = [] of ChannelVideo notifications.sort_by! { |video| video.published }.reverse! @@ -1953,7 +1947,7 @@ get "/feed/subscriptions" do |env| end videos = PG_DB.query_all("SELECT DISTINCT ON (ucid) * FROM #{view_name} WHERE \ NOT id = ANY (#{values}) \ - ORDER BY ucid, published #{sort}", as: ChannelVideo) + ORDER BY ucid, published DESC", as: ChannelVideo) else # Show latest video from each channel @@ -1973,12 +1967,12 @@ get "/feed/subscriptions" do |env| end videos = PG_DB.query_all("SELECT * FROM #{view_name} WHERE \ NOT id = ANY (#{values}) \ - ORDER BY published #{sort} LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) + ORDER BY published DESC LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) else # Sort subscriptions as normal videos = PG_DB.query_all("SELECT * FROM #{view_name} \ - ORDER BY published #{sort} LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) + ORDER BY published DESC LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) end end @@ -2197,12 +2191,6 @@ get "/feed/private" do |env| sort = env.params.query["sort"]? sort ||= "published" - if sort == "published - reverse" - desc = "" - else - desc = "DESC" - end - view_name = "subscriptions_#{sha256(user.email)[0..7]}" if latest_only @@ -2212,7 +2200,7 @@ get "/feed/private" do |env| videos.sort_by! { |video| video.published }.reverse! else videos = PG_DB.query_all("SELECT * FROM #{view_name} \ - ORDER BY published #{desc} LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) + ORDER BY published DESC LIMIT $1 OFFSET $2", limit, offset, as: ChannelVideo) end case sort From eaf55bf12c6de23d88b71ee17cc5bddf370350ff Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 10:35:18 -0600 Subject: [PATCH 21/81] Fix styling for watch indicator --- assets/css/lighttheme.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/css/lighttheme.css b/assets/css/lighttheme.css index 7f2a5226e..b28ef0eec 100644 --- a/assets/css/lighttheme.css +++ b/assets/css/lighttheme.css @@ -9,7 +9,7 @@ a { } /* All links that do not fit with the default color goes here */ -a > .icon, +a:not([data-id]) > .icon, .pure-u-md-1-5 > .h-box > a[href^="/watch?"], .playlist-restricted > ol > li > a { color: #303030; From 79c10407968f71737c9cc508e8bb5a3e0fa009c8 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 10:36:01 -0600 Subject: [PATCH 22/81] Remove sourceMap link for JS source --- assets/js/dash.mediaplayer.min.js | 3 +-- assets/js/silvermine-videojs-quality-selector.min.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/js/dash.mediaplayer.min.js b/assets/js/dash.mediaplayer.min.js index bb2144059..22207e418 100644 --- a/assets/js/dash.mediaplayer.min.js +++ b/assets/js/dash.mediaplayer.min.js @@ -25,5 +25,4 @@ function V(){sa.info("Native video element event: pause"),qa.trigger(o.default.P ;var f=a(98),g=d(f),h=a(163),i=d(h),j=a(183),k=a(46),l=d(k),m=a(50),n=d(m),o=a(47),p=d(o),q=a(45),r=d(q),s=a(158),t=d(s),u=1,v=5e3;e.__dashjs_factory_name="TimeSyncController";var w=p.default.getSingletonFactory(e);w.TIME_SYNC_FAILED_ERROR_CODE=u,w.HTTP_TIMEOUT_MS=v,p.default.updateSingletonFactory(e.__dashjs_factory_name,w),c.default=w,b.exports=c.default},{158:158,163:163,183:183,45:45,46:46,47:47,50:50,98:98}],112:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){A.on(k.default.XLINK_ELEMENT_LOADED,j,C),H=(0,g.default)(z).create({errHandler:a.errHandler,metricsModel:a.metricsModel,mediaPlayerModel:a.mediaPlayerModel,requestModifier:a.requestModifier})}function c(a){a&&(D=a)}function d(a){a&&(E=a)}function e(a){var b=void 0;G=new o.default({escapeMode:!1,attributePrefix:"",arrayAccessForm:"property",emptyNodeForm:"object",stripWhitespaces:!1,enableToStringFunc:!1,ignoreRoot:!0,matchers:D}),F=a,b=m(F.Period_asArray,F,t,r),h(b,t,r)}function f(){A.off(k.default.XLINK_ELEMENT_LOADED,j,C),H&&(H.reset(),H=null)}function h(a,b,c){var d={},e=void 0,f=void 0;d.elements=a,d.type=b,d.resolveType=c,0===d.elements.length&&l(d);for(var g=0;g")+2),d=b.resolvedContent.substr(0,e)+""+b.resolvedContent.substr(e)+"",b.resolvedContent=G.xml_str2json(d)}x(c)&&l(c)}function l(a){var b=[],c=void 0,d=void 0;if(n(a),a.resolveType===s&&A.trigger(k.default.XLINK_READY,{manifest:F}),a.resolveType===r)switch(a.type){case t:for(c=0;c=0;g--)f=a[g],f.hasOwnProperty("xlink:href")&&f["xlink:href"]===w&&a.splice(g,1);for(g=0;g=0;f--){if(c=a.elements[f],d=c.type+"_asArray",!c.resolvedContent||y())delete c.originalContent["xlink:actuate"],delete c.originalContent["xlink:href"],b.push(c.originalContent);else if(c.resolvedContent)for(g=0;g0&&E.run(F)}function p(a,b,c,d,e,f){return{url:a,parentElement:b,type:c,index:d,resolveType:e,originalContent:f,resolvedContent:null,resolved:!1}}function x(a){var b=void 0,c=void 0;for(b=0;b=a-c})}function l(a){N=N.filter(function(b){return isNaN(b.startTime)||void 0!==a&&b.startTime+b.duration=b-d||isNaN(c.duration)||c.startTime+c.duration<=a+d}))}function r(a,b){if(!a||0===a.length)return void k();for(var c=0,d=0,e=a.length;d0&&n(c,b)}function t(){J.abort(),O=[]}function u(a){switch(a.action){case m.default.ACTION_COMPLETE:N.push(a),z(a,q),L.debug("executeRequest trigger STREAM_COMPLETED"),H.trigger(i.default.STREAM_COMPLETED,{request:a,fragmentModel:this});break;case m.default.ACTION_DOWNLOAD:z(a,p),O.push(a),v(a);break;default:L.warn("Unknown request action.")}}function v(a){H.trigger(i.default.FRAGMENT_LOADING_STARTED,{sender:K,request:a}),J.load(a)}function w(a,b,c){for(var d=a.length-1,e=d;e>=0;e--){var f=a[e],g=f.startTime,h=g+f.duration;if(c=isNaN(c)?j(f):c,!isNaN(g)&&!isNaN(h)&&b+c>=g&&b-cR&&d[b].shift()}function s(a,b,c,d,e,f){var g=new m.default;return g.tcpid=b,g.dest=c,g.topen=d,g.tclose=e,g.tconnect=f,B(a,i.default.TCP_CONNECTION,g),g}function u(a,b,c,d){var e=new n.HTTPRequestTrace;return e.s=b,e.d=c,e.b=d,a.trace.push(e),a.interval||(a.interval=0),a.interval+=c,e}function w(a,b,c,d,e,f,g,h,j,k,l,m,o,p){var q=new n.HTTPRequest;return e&&e!==d&&(w(a,null,c,d,null,null,g,h,null,null,null,m,null,null),q.actualurl=e),q.tcpid=b,q.type=c,q.url=d,q.range=g,q.trequest=h,q.tresponse=j,q.responsecode=l,q._tfinish=k,q._stream=a,q._mediaduration=m,q._responseHeaders=o,q._serviceLocation=f,p?p.forEach(function(a){u(q,a.s,a.d,a.b)}):(delete q.interval,delete q.trace),B(a,i.default.HTTP_REQUEST,q),q}function z(a,b,c,d,e){var f=new p.default;return f.t=b,f.mt=c,f.to=d,e?f.lto=e:delete f.lto,B(a,i.default.TRACK_SWITCH,f),f}function B(a,b,c){q(a,b,c),f(a,b,c)}function D(a,b,c){var d=new r.default;return d.t=b,d.level=c,B(a,i.default.BUFFER_LEVEL,d),d}function F(a,b,c){var d=new t.default;return d.target=c,d.state=b,B(a,i.default.BUFFER_STATE,d),d}function H(a,b,c,d){var e=new v.default;return e.time=b,e.range=d,e.manifestInfo=c,B(a,i.default.DVR_INFO,e),e}function I(a,b){var c=new x.default,d=o(a).DroppedFrames;return c.time=b.creationTime,c.droppedFrames=b.droppedVideoFrames,d.length>0&&d[d.length-1]==c?d[d.length-1]:(B(a,i.default.DROPPED_FRAMES,c),c)}function J(a,b,c,d,e,f,g,h,j){var k=new A.default;return k.mediaType=a,k.t=b,k.type=c,k.startTime=d,k.availabilityStartTime=e,k.duration=f,k.quality=g,k.range=h,k.state=j,B(a,i.default.SCHEDULING_INFO,k),k}function K(a,b,c){var d=new E.default;d.loadingRequests=b,d.executedRequests=c,o(a).RequestsQueue=d,f(a,i.default.REQUESTS_QUEUE,d)}function L(a,b,c,d,e,h,j,k,l,m){var n=new y.ManifestUpdate;return n.mediaType=a,n.type=b,n.requestTime=c,n.fetchTime=d,n.availabilityStartTime=e,n.presentationStartTime=h,n.clientTimeOffset=j,n.currentTime=k,n.buffered=l,n.latency=m,q(g.default.STREAM,i.default.MANIFEST_UPDATE,n),f(a,i.default.MANIFEST_UPDATE,n),n}function M(a,b){if(a){for(var c in b)a[c]=b[c];e(a.mediaType,i.default.MANIFEST_UPDATE,a)}}function N(a,b,c,d,f){if(a){var g=new y.ManifestUpdateStreamInfo;return g.id=b,g.index=c,g.start=d,g.duration=f,a.streamInfo.push(g),e(a.mediaType,i.default.MANIFEST_UPDATE_STREAM_INFO,a),g}return null}function O(a,b,c,d,f,g,h,j){if(a){var k=new y.ManifestUpdateRepresentationInfo;return k.id=b,k.index=c,k.streamIndex=d,k.mediaType=f,k.startNumber=h,k.fragmentInfoType=j,k.presentationTimeOffset=g,a.representationInfo.push(k),e(a.mediaType,i.default.MANIFEST_UPDATE_TRACK_INFO,a),k}return null}function P(a){var b=g.default.STREAM;return a.trace&&Array.isArray(a.trace)?a.trace.forEach(function(a){a.hasOwnProperty("subreplevel")&&!a.subreplevel&&delete a.subreplevel}):delete a.trace,B(b,i.default.PLAY_LIST,a),a}function Q(a){return B(g.default.STREAM,i.default.DVB_ERRORS,a),a}var R=1e3,S=this.context,T=(0,C.default)(S).getInstance(),U=void 0,V=void 0,W=void 0;return U={clearCurrentMetricsForType:h,clearAllCurrentMetrics:j,getReadOnlyMetricsFor:l,getMetricsFor:o,addTcpConnection:s,addHttpRequest:w,addRepresentationSwitch:z,addBufferLevel:D,addBufferState:F,addDVRInfo:H,addDroppedFrames:I,addSchedulingInfo:J,addRequestsQueue:K,addManifestUpdate:L,updateManifestUpdateInfo:M,addManifestUpdateStreamInfo:N,addManifestUpdateRepresentationInfo:O,addPlayList:P,addDVBErrors:Q,setConfig:b},a(),U}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(99),i=d(h),j=a(171),k=d(j),l=a(189),m=d(l),n=a(183),o=a(186),p=d(o),q=a(179),r=d(q),s=a(180),t=d(s),u=a(181),v=d(u),w=a(182),x=d(w),y=a(184),z=a(188),A=d(z),B=a(46),C=d(B),D=a(187),E=d(D),F=a(50),G=d(F),H=a(47),I=d(H);e.__dashjs_factory_name="MetricsModel",c.default=I.default.getSingletonFactory(e),b.exports=c.default},{171:171,179:179,180:180,181:181,182:182,183:183,184:184,186:186,187:187,188:188,189:189,46:46,47:47,50:50,98:98,99:99}],118:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){if(c=new g.default,!a)return null;var b=a.indexOf("#");if(-1!==b)for(var d=a.substr(b+1).split("&"),e=0,f=d.length;e0?Y.addEventListener("canplay",d):Y.playbackRate=a)}function f(a,b){if(Y){if(Y.currentTime==a)return;try{a=b?g(a):a,Y.currentTime=a}catch(c){0===Y.readyState&&c.code===c.INVALID_STATE_ERR&&setTimeout(function(){Y.currentTime=a},400)}}}function g(a){var b=K(),c=a,d=9999999999;if(b)for(var e=0;e=f&&a<=g)return a;h0}function u(a){var b=void 0;null===a||Y.seeking||-1!==da.indexOf(a)||(da.push(a),Y&&1===da.length&&(b=document.createEvent("Event"),b.initEvent("waiting",!0,!1),_=Y.playbackRate,e(0),Y.dispatchEvent(b)))}function v(a){var b=da.indexOf(a),c=void 0;null!==a&&(-1!==b&&da.splice(b,1),Y&&!1===t()&&0===Y.playbackRate&&(e(_||1),Y.paused||(c=document.createEvent("Event"),c.initEvent("playing",!0,!1),Y.dispatchEvent(c))))}function w(a,b){b?u(a):v(a)}function x(){if(Y&&t()&&0===Y.playbackRate){var a=document.createEvent("Event");a.initEvent("waiting",!0,!1),Y.dispatchEvent(a)}}function y(){if(!Y)return null;var a="webkitDroppedFrameCount"in Y&&"webkitDecodedFrameCount"in Y,b="getVideoPlaybackQuality"in Y,c=null;return b?c=Y.getVideoPlaybackQuality():a&&(c={droppedVideoFrames:Y.webkitDroppedFrameCount,totalVideoFrames:Y.webkitDroppedFrameCount+Y.webkitDecodedFrameCount,creationTime:new Date}),c}function z(){if(Y){Y.autoplay=!0;var a=Y.play();a&&"undefined"!=typeof Promise&&a instanceof Promise&&a.catch(function(a){"NotAllowedError"===a.name&&ca.trigger(k.default.PLAYBACK_NOT_ALLOWED),X.warn("Caught pending play exception - continuing ("+a+")")})}}function A(){return Y?Y.paused:null}function B(){Y&&(Y.pause(),Y.autoplay=!1)}function C(){return Y?Y.seeking:null}function D(){return Y?Y.currentTime:null}function E(){return Y?Y.playbackRate:null}function F(){return Y?Y.played:null}function G(){return Y?Y.ended:null}function H(a,b){Y&&Y.addEventListener(a,b)}function I(a,b){Y&&Y.removeEventListener(a,b)}function J(){return Y?Y.readyState:NaN}function K(){return Y?Y.buffered:null}function L(){return Y?Y.clientWidth:NaN}function M(){return Y?Y.clientHeight:NaN}function N(){return Y?Y.videoWidth:NaN}function O(){return Y?Y.videoHeight:NaN}function P(){return Y&&Y.parentNode?Y.getBoundingClientRect().top-Y.parentNode.getBoundingClientRect().top:NaN}function Q(){return Y&&Y.parentNode?Y.getBoundingClientRect().left-Y.parentNode.getBoundingClientRect().left:NaN}function R(){return Y?Y.textTracks:[]}function S(a,b,c,d,e){if(Y)for(var f=0;f0){q=d(q,g),o+=g.length,s.push({ts:Date.now(),bytes:g.length});var h=(0,i.default)().getInstance().findLastTopIsoBoxCompleted(["moov","mdat"],q,r);if(h.found){var j=h.lastCompletedOffset+h.size,k=void 0;j===q.length?(k=q,q=new Uint8Array):(k=new Uint8Array(q.subarray(0,j)),q=q.subarray(j)),a.progress({data:k.buffer,lengthComputable:!1,noTrace:!0}),r=0}else r=h.lastCompletedOffset,p||(a.progress({lengthComputable:!1,noTrace:!0}),p=!0)}c(a,b)})}).catch(function(b){a.onerror&&a.onerror(b)})}function c(a,b){a.reader.read().then(b).catch(function(b){a.onerror&&200===a.response.status&&a.onerror(b)})}function d(a,b){if(0===a.length)return b;var c=new Uint8Array(a.length+b.length);return c.set(a),c.set(b,a.length),c}function e(a){if(a.abortController)a.abortController.abort();else if(a.reader)try{a.reader.cancel()}catch(b){}}function f(a,b){if(a=a.filter(function(c){return c.bytes>b/4/a.length}),a.length>1){var c=function(){var b=0,c=(a[a.length-1].ts-a[0].ts)/a.length;return a.forEach(function(d,e){var f=a[e+1];if(f){var g=f.ts-d.ts;b+=g0?(b--,t.push(setTimeout(function(){c(a,b)},m.getRetryIntervalForType(d.type)))):(i.downloadError(u[d.type],d.url,d),a.error&&a.error(d,"error",x.response.statusText),a.complete&&a.complete(d,x.response.statusText))))},A=function(b){var c=new Date;f&&(f=!1,(!b.lengthComputable||b.lengthComputable&&b.total!==b.loaded)&&(d.firstByteDate=c)),b.lengthComputable&&(d.bytesLoaded=b.loaded,d.bytesTotal=b.total),b.noTrace||(e.push({s:v,d:b.time?b.time:c.getTime()-v.getTime(),b:[b.loaded?b.loaded-w:0]}),v=c,w=b.loaded),a.progress&&b&&a.progress(b)},B=function(){x.response.status>=200&&x.response.status<=299&&(y(!0), a.success&&a.success(x.response.response,x.response.statusText,x.response.responseURL),a.complete&&a.complete(d,x.response.statusText))},C=function(){a.abort&&a.abort(d)},D=void 0;D=p&&window.fetch&&"arraybuffer"===d.responseType?(0,j.default)(g).create({requestModifier:n}):(0,h.default)(g).create({requestModifier:n});var E=n.modifyRequestURL(d.url),F=d.checkExistenceOnly?k.HTTPRequest.HEAD:k.HTTPRequest.GET,G=m.getXHRWithCredentialsForType(d.type);x={url:E,method:F,withCredentials:G,request:d,onload:B,onend:z,onerror:z,progress:A,onabort:C,loader:D};var H=(new Date).getTime();isNaN(d.delayLoadingTime)||H>=d.delayLoadingTime?(r.push(x),D.load(x)):function(){var a={httpRequest:x};s.push(a),a.delayTimeout=setTimeout(function(){if(-1!==s.indexOf(a)){s.splice(s.indexOf(a),1);try{q=new Date,v=q,r.push(a.httpRequest),D.load(a.httpRequest)}catch(b){a.httpRequest.onerror()}}},d.delayLoadingTime-H)}()}function d(a){a.request&&c(a,m.getRetryAttemptsForType(a.request.type))}function f(){t.forEach(function(a){return clearTimeout(a)}),t=[],s.forEach(function(a){return clearTimeout(a.delayTimeout)}),s=[],r.forEach(function(a){a.onloadend=a.onerror=a.onprogress=void 0,a.loader.abort(a),a.onabort()}),r=[]}a=a||{};var g=this.context,i=a.errHandler,l=a.metricsModel,m=a.mediaPlayerModel,n=a.requestModifier,p=a.useFetch||!1,q=void 0,r=void 0,s=void 0,t=void 0,u=void 0;return q={load:d,abort:f},b(),q}Object.defineProperty(c,"__esModule",{value:!0});var g=a(122),h=d(g),i=a(120),j=d(i),k=a(183),l=a(47),m=d(l),n=a(151),o=d(n);f.__dashjs_factory_name="HTTPLoader";var p=m.default.getClassFactory(f);c.default=p,b.exports=c.default},{120:120,122:122,151:151,183:183,47:47}],122:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(a){var b=new Date,c=a.request,e=new XMLHttpRequest;e.open(a.method,a.url,!0),c.responseType&&(e.responseType=c.responseType),c.range&&e.setRequestHeader("Range","bytes="+c.range),c.requestStartDate||(c.requestStartDate=b),d&&(e=d.modifyRequestHeader(e)),e.withCredentials=a.withCredentials,e.onload=a.onload,e.onloadend=a.onend,e.onerror=a.onerror,e.onprogress=a.progress,e.onabort=a.onabort,e.send(),a.response=e}function c(a){var b=a.response;b.onloadend=b.onerror=b.onprogress=void 0,b.abort()}a=a||{};var d=a.requestModifier;return{load:b,abort:c}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="XHRLoader";var h=g.default.getClassFactory(e);c.default=h,b.exports=c.default},{47:47}],123:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){var c=b&&b.droppedVideoFrames?b.droppedVideoFrames:0,g=b&&b.totalVideoFrames?b.totalVideoFrames:0,h=c-e;e=c;var i=g-f;f=g,isNaN(a)||(d[a]?(d[a].droppedVideoFrames+=h,d[a].totalVideoFrames+=i):d[a]={droppedVideoFrames:h,totalVideoFrames:i})}function b(){return d}function c(a){d=[],e=a.droppedVideoFrames,f=a.totalVideoFrames}var d=[],e=0,f=0;return{push:a,getFrameHistory:b,reset:c}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="DroppedFramesHistory";var h=g.default.getClassFactory(e);c.default=h,b.exports=c.default},{47:47}],124:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){return n.mediaInfo.type}function c(){return n.mediaInfo.streamInfo}function d(){return n.mediaInfo}function e(){return n}function f(){return m}function g(){return l}function h(){return o}function i(){return p}function j(){return q}function k(){return r}a=a||{};var l=a.abrController,m=a.streamProcessor,n=a.streamProcessor.getCurrentRepresentationInfo(),o=a.switchHistory,p=a.droppedFramesHistory,q=a.currentRequest,r=a.useBufferOccupancyABR;return{getMediaType:b,getMediaInfo:d,getDroppedFramesHistory:i,getCurrentRequest:j,getSwitchHistory:h,getStreamInfo:c,getStreamProcessor:f,getAbrController:g,getRepresentationInfo:e,useBufferOccupancyABR:k}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="RulesContext",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],125:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a,b,c){function d(a){var b=i.DEFAULT;return a!==i.DEFAULT&&a!==i.STRONG&&a!==i.WEAK||(b=a),b}var e=void 0,f=void 0,g=void 0;return e=void 0===a?h:a,f=d(c),g=void 0===b?null:b,{quality:e,reason:g,priority:f}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=-1,i={DEFAULT:.5,STRONG:1,WEAK:0};e.__dashjs_factory_name="SwitchRequest";var j=g.default.getClassFactory(e);j.NO_CHANGE=h,j.PRIORITY=i,g.default.updateClassFactory(e.__dashjs_factory_name,j),c.default=j,b.exports=c.default},{47:47}],126:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){a.newValue===i.default.NO_CHANGE&&(a.newValue=a.oldValue),d[a.oldValue]||(d[a.oldValue]={noDrops:0,drops:0,dropSize:0});var b=a.newValue-a.oldValue,c=b<0?1:0,f=c?-b:0,g=c?0:1;if(d[a.oldValue].drops+=c,d[a.oldValue].dropSize+=f,d[a.oldValue].noDrops+=g,e.push({idx:a.oldValue,noDrop:g,drop:c,dropSize:f}),e.length>j){var h=e.shift();d[h.idx].drops-=h.drop,d[h.idx].dropSize-=h.dropSize,d[h.idx].noDrops-=h.noDrop}}function b(){return d}function c(){d=[],e=[]}var d=[],e=[];return{push:a,getSwitchRequests:b,reset:c}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(125),i=d(h),j=8;e.__dashjs_factory_name="SwitchRequestHistory";var k=g.default.getClassFactory(e);c.default=k,b.exports=c.default},{125:125,47:47}],127:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){F={throughputHalfLife:{fast:x,slow:w},latencyHalfLife:{fast:z,slow:y}},p()}function c(a,b,c){return a===g.default.VIDEO?c0&&!B[a].hasCachedEntries)return;B[a].hasCachedEntries=!0}else B[a]&&B[a].hasCachedEntries&&o(a);B[a].push(k),B[a].length>q&&B[a].shift(),C[a].push(f),C[a].length>q&&C[a].shift(),e(D[a],k,.001*g,F.throughputHalfLife),e(E[a],f,1,F.latencyHalfLife)}}function e(a,b,c,d){var e=Math.pow(.5,c/d.fast);a.fastEstimate=(1-e)*b+e*a.fastEstimate;var f=Math.pow(.5,c/d.slow);a.slowEstimate=(1-f)*b+f*a.slowEstimate,a.totalWeight+=c}function f(a,b,c){var d=void 0,e=void 0;if(a?(d=B[b],e=c?r:s):(d=C[b],e=t),d){if(e>=d.length)e=d.length;else if(a)for(var f=1;f=v||g<=1/u)&&(e+=1)===d.length)break}}else e=0;return e}function h(a,b,c){return A.getMovingAverageMethod()!==g.default.MOVING_AVERAGE_SLIDING_WINDOW?j(a,b):i(a,b,c)}function i(a,b,c){var d=f(a,b,c),e=a?B:C,g=e[b];return 0!==d&&g&&0!==g.length?(g=g.slice(-d),g.reduce(function(a,b){return a+b})/g.length):NaN}function j(a,b){var c=a?F.throughputHalfLife:F.latencyHalfLife,d=a?D[b]:E[b];if(!d||d.totalWeight<=0)return NaN;var e=d.fastEstimate/(1-Math.pow(.5,d.totalWeight/c.fast)),f=d.slowEstimate/(1-Math.pow(.5,d.totalWeight/c.slow));return a?Math.min(e,f):Math.max(e,f)}function k(a,b){return h(!0,a,b)}function l(a,b){var c=k(a,b);return isNaN(c)||(c*=A.getBandwidthSafetyFactor()),c}function m(a){return h(!1,a)}function n(a){B[a]=B[a]||[],C[a]=C[a]||[],D[a]=D[a]||{fastEstimate:0,slowEstimate:0,totalWeight:0},E[a]=E[a]||{fastEstimate:0,slowEstimate:0,totalWeight:0}}function o(a){delete B[a],delete C[a],delete D[a],delete E[a],n(a)}function p(){B={},C={},D={},E={}}a=a||{};var q=20,r=3,s=4,t=4,u=1.3,v=1.3,w=8,x=3,y=2,z=1,A=a.mediaPlayerModel,B=void 0,C=void 0,D=void 0,E=void 0,F=void 0,G={push:d,getAverageThroughput:k,getSafeAverageThroughput:l,getAverageLatency:m,reset:p};return b(),G}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="ThroughputHistory",c.default=i.default.getClassFactory(e),b.exports=c.default},{47:47,98:98}],128:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){r=[],s=[],l.getUseDefaultABRRules()&&(r.push((0,q.default)(j).create({metricsModel:n,dashMetrics:p,mediaPlayerModel:l})),r.push((0,g.default)(j).create({metricsModel:n,dashMetrics:p})),r.push((0,i.default)(j).create({metricsModel:n,dashMetrics:p})),r.push((0,o.default)(j).create()),r.push((0,m.default)(j).create()),s.push((0,k.default)(j).create({metricsModel:n,dashMetrics:p,mediaPlayerModel:l}))),l.getABRCustomRules().forEach(function(a){a.type===v&&r.push(a.rule(j).create()),a.type===w&&s.push(a.rule(j).create())})}function c(a){return a.filter(function(a){return a.quality>u.default.NO_CHANGE})}function d(a){var b={},c=void 0,d=void 0,e=void 0,f=void 0,g=void 0;if(0!==a.length){for(b[u.default.PRIORITY.STRONG]=u.default.NO_CHANGE,b[u.default.PRIORITY.WEAK]=u.default.NO_CHANGE,b[u.default.PRIORITY.DEFAULT]=u.default.NO_CHANGE,c=0,d=a.length;cu.default.NO_CHANGE?Math.min(b[e.priority],e.quality):e.quality);return b[u.default.PRIORITY.WEAK]!==u.default.NO_CHANGE&&(f=b[u.default.PRIORITY.WEAK]),b[u.default.PRIORITY.DEFAULT]!==u.default.NO_CHANGE&&(f=b[u.default.PRIORITY.DEFAULT]),b[u.default.PRIORITY.STRONG]!==u.default.NO_CHANGE&&(f=b[u.default.PRIORITY.STRONG]),f!==u.default.NO_CHANGE&&(g=f),(0,u.default)(j).create(g)}}function e(a){return d(c(r.map(function(b){return b.getMaxIndex(a)})))||(0,u.default)(j).create()}function f(a){return d(c(s.map(function(b){return b.shouldAbandon(a)})))||(0,u.default)(j).create()}function h(){[r,s].forEach(function(a){a&&a.length&&a.forEach(function(a){return a.reset&&a.reset()})}),r=[],s=[]}a=a||{};var j=this.context,l=a.mediaPlayerModel,n=a.metricsModel,p=a.dashMetrics,r=void 0,s=void 0;return{initialize:b,reset:h,getMaxQuality:e,shouldAbandonFragment:f}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(134),g=d(f),h=a(132),i=d(h),j=a(129),k=d(j),l=a(131),m=d(l),n=a(133),o=d(n),p=a(130),q=d(p),r=a(47),s=d(r),t=a(125),u=d(t),v="qualitySwitchRules",w="abandonFragmentRules";e.__dashjs_factory_name="ABRRulesCollection";var x=s.default.getClassFactory(e);x.QUALITY_SWITCH_RULES=v,x.ABANDON_FRAGMENT_RULES=w,s.default.updateSingletonFactory(e.__dashjs_factory_name,x),c.default=x,b.exports=c.default},{125:125,129:129,130:130,131:131,132:132,133:133,134:134,47:47}],129:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){r=(0,k.default)(m).getInstance().getLogger(q),h()}function c(a,b){s[a]=s[a]||{},s[a][b]=s[a][b]||{}}function d(a,b){u[a]=u[a]||[],u[a].push(b)}function f(a){var b=(0,g.default)(m).create(g.default.NO_CHANGE,{name:e.__dashjs_factory_name});if(!(a&&a.hasOwnProperty("getMediaInfo")&&a.hasOwnProperty("getMediaType")&&a.hasOwnProperty("getCurrentRequest")&&a.hasOwnProperty("getRepresentationInfo")&&a.hasOwnProperty("getAbrController")))return b;var f=a.getMediaInfo(),h=a.getMediaType(),k=a.getCurrentRequest();if(!isNaN(k.index)){c(h,k.index);var q=n.getStableBufferTime();if(p.getCurrentBufferLevel(o.getReadOnlyMetricsFor(h))>q)return b;var v=s[h][k.index];if(null===v||null===k.firstByteDate||t.hasOwnProperty(v.id))return b;if(void 0===v.firstByteTime&&(u[h]=[],v.firstByteTime=k.firstByteDate.getTime(),v.segmentDuration=k.duration,v.bytesTotal=k.bytesTotal,v.id=k.index),v.bytesLoaded=k.bytesLoaded,v.elapsedTime=(new Date).getTime()-v.firstByteTime,v.bytesLoaded>0&&v.elapsedTime>0&&d(h,Math.round(8*v.bytesLoaded/v.elapsedTime)),u[h].length>=l&&v.elapsedTime>j&&v.bytesLoadedv.bytesTotal*z[A].bitrate/z[x.getQualityFor(h,f.streamInfo)].bitrate&&(b.quality=A,b.reason.throughput=v.measuredBandwidthInKbps,b.reason.fragmentID=v.id,t[v.id]=v,r.debug("( ",h,"frag id",v.id,") is asking to abandon and switch to quality to ",A," measured bandwidth was",v.measuredBandwidthInKbps),delete s[h][v.id])}}else v.bytesLoaded===v.bytesTotal&&delete s[h][v.id]}return b}function h(){s={},t={},u=[]}a=a||{};var i=1.8,j=500,l=5,m=this.context,n=a.mediaPlayerModel,o=a.metricsModel,p=a.dashMetrics,q=void 0,r=void 0,s=void 0,t=void 0,u=void 0;return q={shouldAbandon:f,reset:h},b(),q}Object.defineProperty(c,"__esModule",{value:!0});var f=a(125),g=d(f),h=a(47),i=d(h),j=a(45),k=d(j);e.__dashjs_factory_name="AbandonRequestsRule",c.default=i.default.getClassFactory(e),b.exports=c.default},{125:125,45:45,47:47}],130:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){P=(0,r.default)(J).getInstance().getLogger(O),H(),N.on(p.default.BUFFER_EMPTY,y,O),N.on(p.default.PLAYBACK_SEEKING,z,O),N.on(p.default.PERIOD_SWITCH_STARTED,A,O),N.on(p.default.MEDIA_FRAGMENT_LOADED,B,O),N.on(p.default.METRIC_ADDED,C,O),N.on(p.default.QUALITY_CHANGE_REQUESTED,E,O),N.on(p.default.FRAGMENT_LOADING_ABANDONED,F,O)}function c(a){return a.map(function(a){return Math.log(a)})}function d(a,b,c){var d=c.reduce(function(a,b,d){return b>c[a]?d:a},0);if(0===d)return null;var e=Math.max(a,v+w*b.length),f=(c[d]-1)/(e/v-1);return{gp:f,Vp:v/f}}function e(a){var b={},e=a.getMediaInfo(),g=e.bitrateList.map(function(a){return a.bandwidth}),h=c(g);h=h.map(function(a){return a-h[0]+1});var i=M.getStableBufferTime(),j=d(i,g,h);return j?(b.state=t,b.bitrates=g,b.utilities=h,b.stableBufferTime=i,b.Vp=j.Vp,b.gp=j.gp,b.lastQuality=0,f(b)):b.state=s,b}function f(a){a.placeholderBuffer=0,a.mostAdvancedSegmentStart=NaN,a.lastSegmentWasReplacement=!1,a.lastSegmentStart=NaN,a.lastSegmentDurationS=NaN,a.lastSegmentRequestTimeMs=NaN,a.lastSegmentFinishTimeMs=NaN}function h(a,b){var c=M.getStableBufferTime();if(a.stableBufferTime!==c){var e=d(c,a.bitrates,a.utilities);if(e.Vp!==a.Vp||e.gp!==a.gp){var f=K.getCurrentBufferLevel(L.getReadOnlyMetricsFor(b)),g=f+a.placeholderBuffer;g-=v,g*=e.Vp/a.Vp,g+=v,a.stableBufferTime=c,a.Vp=e.Vp,a.gp=e.gp,a.placeholderBuffer=Math.max(0,g-f)}}}function j(a){var b=a.getMediaType(),c=Q[b];return c?c.state!==s&&h(c,b):(c=e(a),Q[b]=c),c}function k(a,b){for(var c=a.bitrates.length,d=NaN,e=NaN,f=0;f=e)&&(e=g,d=f)}return d}function m(a,b){return a.Vp*(a.utilities[b]+a.gp)}function o(a,b){for(var c=a.bitrates[b],d=a.utilities[b],e=0,f=b-1;f>=0;--f)if(a.utilities[f]b.mostAdvancedSegmentStart?(b.mostAdvancedSegmentStart=c,b.lastSegmentWasReplacement=!1):b.lastSegmentWasReplacement=!0,b.lastSegmentStart=c,b.lastSegmentDurationS=a.chunk.duration,b.lastQuality=a.chunk.quality,D(b,a.chunk.mediaInfo.type)}}}function C(a){if(a&&a.metric===g.default.HTTP_REQUEST&&a.value&&a.value.type===l.HTTPRequest.MEDIA_SEGMENT_TYPE&&a.value.trace&&a.value.trace.length){var b=Q[a.mediaType];b&&b.state!==s&&(b.lastSegmentRequestTimeMs=a.value.trequest.getTime(),b.lastSegmentFinishTimeMs=a.value._tfinish.getTime(),D(b,a.mediaType))}}function D(a,b){if(!isNaN(a.lastSegmentStart)&&!isNaN(a.lastSegmentRequestTimeMs)&&!isNaN(a.placeholderBuffer)){if(a.placeholderBuffer*=x,!isNaN(a.lastSegmentFinishTimeMs)){var c=K.getCurrentBufferLevel(L.getReadOnlyMetricsFor(b)),d=c+.001*(a.lastSegmentFinishTimeMs-a.lastSegmentRequestTimeMs),e=m(a,a.lastQuality),f=Math.max(0,e-d);a.placeholderBuffer=Math.min(f,a.placeholderBuffer)}a.lastSegmentWasReplacement&&!isNaN(a.lastSegmentDurationS)&&(a.placeholderBuffer+=a.lastSegmentDurationS),a.lastSegmentStart=NaN,a.lastSegmentRequestTimeMs=NaN}}function E(a){if(a){var b=Q[a.mediaType];b&&b.state!==s&&(b.abrQuality=a.newQuality)}}function F(a){if(a){var b=Q[a.mediaType];if(b&&b.state!==s){var c=K.getCurrentBufferLevel(L.getReadOnlyMetricsFor(a.mediaType)),d=void 0;d=b.abrQuality>0?o(b,b.abrQuality):v;var e=Math.max(0,d-c);b.placeholderBuffer=Math.min(b.placeholderBuffer,e)}}}function G(a){var b=a.getMediaInfo(),c=a.getMediaType(),d=L.getReadOnlyMetricsFor(c),e=a.getStreamProcessor(),g=a.getStreamInfo(),h=a.getAbrController(),l=h.getThroughputHistory(),n=g?g.id:null,p=g&&g.manifestInfo&&g.manifestInfo.isDynamic,r=a.useBufferOccupancyABR(),v=(0,i.default)(J).create();if(v.reason=v.reason||{},!r)return v;e.getScheduleController().setTimeToLoadDelay(0);var w=j(a);if(w.state===s)return v;var x=K.getCurrentBufferLevel(d),y=l.getAverageThroughput(c,p),z=l.getSafeAverageThroughput(c,p),A=l.getAverageLatency(c),B=void 0;if(v.reason.state=w.state,v.reason.throughput=y,v.reason.latency=A,isNaN(y))return v;switch(w.state){case t:B=h.getQualityForBitrate(b,z,A),v.quality=B,v.reason.throughput=z,w.placeholderBuffer=Math.max(0,o(w,B)-x),w.lastQuality=B,!isNaN(w.lastSegmentDurationS)&&x>=w.lastSegmentDurationS&&(w.state=u);break;case u:q(w,c),B=k(w,x+w.placeholderBuffer);var C=h.getQualityForBitrate(b,z,A);B>w.lastQuality&&B>C&&(B=Math.max(C,w.lastQuality));var D=Math.max(0,x+w.placeholderBuffer-m(w,B));D<=w.placeholderBuffer?(w.placeholderBuffer-=D,D=0):(D-=w.placeholderBuffer,w.placeholderBuffer=0,Bg&&h/j>f)){k=l-1,e.debug("index: "+k+" Dropped Frames: "+h+" Total Frames: "+j);break}return(0,i.default)(c).create(k,{droppedFrames:h})}return(0,i.default)(c).create()}var c=this.context,d=void 0,e=void 0,f=.15,g=375;return d={getMaxIndex:b},a(),d}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(125),i=d(h),j=a(45),k=d(j);e.__dashjs_factory_name="DroppedFramesRule",c.default=g.default.getClassFactory(e),b.exports=c.default},{125:125,45:45,47:47}],132:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){t=(0,o.default)(m).getInstance().getLogger(s),f(),n.on(k.default.PLAYBACK_SEEKING,h,s)}function c(){if(!(p&&p.hasOwnProperty("getReadOnlyMetricsFor")&&r&&r.hasOwnProperty("getCurrentBufferLevel")))throw new Error("Missing config parameter(s)")}function d(a){var b=(0,q.default)(m).create();if(!a||!a.hasOwnProperty("getMediaType"))return b;c();var d=a.getMediaType(),f=p.getReadOnlyMetricsFor(d),h=f.BufferState.length>0?f.BufferState[f.BufferState.length-1]:null,i=a.getRepresentationInfo(),j=i.fragmentDuration;if(!h||!e(d,h)||!j)return b;if(h.state===g.default.BUFFER_EMPTY)t.info("Switch to index 0; buffer is empty."),b.quality=0,b.reason="InsufficientBufferRule: Buffer is empty";else{var k=a.getMediaInfo(),n=a.getAbrController(),o=n.getThroughputHistory(),s=r.getCurrentBufferLevel(f),u=o.getAverageThroughput(d),v=o.getAverageLatency(d),w=u*(s/j)*l;b.quality=n.getQualityForBitrate(k,w,v),b.reason="InsufficientBufferRule: being conservative to avoid immediate rebuffering"}return b}function e(a,b){u[a]=u[a]||{};var c=!1;return u[a].firstBufferLoadedEvent?c=!0:b&&b.state===g.default.BUFFER_LOADED&&(u[a].firstBufferLoadedEvent=!0,c=!0),c}function f(){u={}}function h(){f()}function j(){f(),n.off(k.default.PLAYBACK_SEEKING,h,s)}a=a||{};var l=.5,m=this.context,n=(0,i.default)(m).getInstance(),p=a.metricsModel,r=a.dashMetrics,s=void 0,t=void 0,u=void 0;return s={getMaxIndex:d,reset:j},b(),s}Object.defineProperty(c,"__esModule",{value:!0});var f=a(103),g=d(f),h=a(46),i=d(h),j=a(50),k=d(j),l=a(47),m=d(l),n=a(45),o=d(n),p=a(125),q=d(p);e.__dashjs_factory_name="InsufficientBufferRule",c.default=m.default.getClassFactory(e),b.exports=c.default},{103:103,125:125,45:45,46:46,47:47,50:50}],133:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){e=(0,i.default)(c).getInstance().getLogger(d)}function b(a){for(var b=a?a.getSwitchHistory():null,d=b?b.getSwitchRequests():[],h=0,i=0,j=0,l=(0,k.default)(c).create(),m=0;m=g&&h/i>f)){l.quality=m>0&&d[m].drops>0?m-1:m,l.reason={index:l.quality,drops:h,noDrops:i,dropSize:j},e.info("Switch history rule index: "+l.quality+" samples: "+(h+i)+" drops: "+h);break}return l}var c=this.context,d=void 0,e=void 0,f=.075,g=6;return d={getMaxIndex:b},a(),d}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(45),i=d(h),j=a(125),k=d(j);e.__dashjs_factory_name="SwitchHistoryRule",c.default=g.default.getClassFactory(e),b.exports=c.default},{125:125,45:45,47:47}],134:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){k=(0,m.default)(f).getInstance().getLogger(j)}function c(){if(!h||!h.hasOwnProperty("getReadOnlyMetricsFor"))throw new Error("Missing config parameter(s)")}function d(a){var b=(0,o.default)(f).create();if(!(a&&a.hasOwnProperty("getMediaInfo")&&a.hasOwnProperty("getMediaType")&&a.hasOwnProperty("useBufferOccupancyABR")&&a.hasOwnProperty("getAbrController")&&a.hasOwnProperty("getStreamProcessor")))return b;c();var d=a.getMediaInfo(),e=a.getMediaType(),j=h.getReadOnlyMetricsFor(e),l=a.getStreamProcessor(),m=a.getAbrController(),n=a.getStreamInfo(),p=n&&n.manifestInfo?n.manifestInfo.isDynamic:null,q=m.getThroughputHistory(),r=q.getSafeAverageThroughput(e,p),s=q.getAverageLatency(e),t=j.BufferState.length>0?j.BufferState[j.BufferState.length-1]:null,u=a.useBufferOccupancyABR();return!j||isNaN(r)||!t||u?b:(m.getAbandonmentStateFor(e)!==i.default.ABANDON_LOAD&&(t.state===g.default.BUFFER_LOADED||p)&&(b.quality=m.getQualityForBitrate(d,r,s),l.getScheduleController().setTimeToLoadDelay(0),k.info("requesting switch to index: ",b.quality,"type: ",e,"Average throughput",Math.round(r),"kbps"),b.reason={throughput:r,latency:s}),b)}function e(){}a=a||{};var f=this.context,h=a.metricsModel,j=void 0,k=void 0;return j={getMaxIndex:d,reset:e},b(),j}Object.defineProperty(c,"__esModule",{value:!0});var f=a(103),g=d(f),h=a(100),i=d(h),j=a(47),k=d(j),l=a(45),m=d(l),n=a(125),o=d(n);e.__dashjs_factory_name="ThroughputRule",c.default=k.default.getClassFactory(e),b.exports=c.default},{100:100,103:103,125:125,45:45,47:47}],135:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){}function c(a,b){return e.getCurrentBufferLevel(f.getReadOnlyMetricsFor(a.getType()))=h.getLongFormContentDurationThreshold();c=n?h.getBufferTimeAtTopQualityLongForm():h.getBufferTimeAtTopQuality()}else c=h.getStableBufferTime()}return c}a=a||{};var e=a.dashMetrics,f=a.metricsModel,h=a.mediaPlayerModel,i=a.textController,j=a.abrController,k={execute:c,getBufferTarget:d};return b(),k}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="BufferLevelRule",c.default=i.default.getClassFactory(e),b.exports=c.default},{47:47,98:98}],136:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){j=(0,i.default)(d).getInstance().getLogger(h)}function c(a,b){if(!a)return null;var c=a.getCurrentRepresentationInfo(),d=c.mediaInfo,h=d.type,i=a.getScheduleController(),k=i.getSeekTarget(),l=!isNaN(k),n=a.getBufferController(),o=a.getPlaybackController().getTime(),p=l?k:e.getIndexHandlerTime(a),q=!1,r=void 0;if(l&&i.setSeekTarget(NaN),isNaN(p)||h===g.default.FRAGMENTED_TEXT&&!f.isTextEnabled())return null;if(n){var s=n.getRangeAt(p),t=n.getRangeAt(o),u=n.getBuffer().getAllBufferRanges(),v=u?u.length:0;null===s&&null===t||l||((!s||t&&t.start!=s.start&&t.end!=s.end)&&(v>1&&(a.getFragmentModel().removeExecutedRequestsAfterTime(t.end),q=!0),s=t),j.debug("Prior to making a request for time, NextFragmentRequestRule is aligning index handler's currentTime with bufferedRange.end for",h,".",p,"was changed to",s.end),p=s.end)}if(b)p=b.startTime+b.duration/2,r=e.getFragmentRequestForTime(a,c,p,{timeThreshold:0,ignoreIsFinished:!0});else{for(r=e.getFragmentRequestForTime(a,c,p,{keepIdx:!l&&!q});r&&r.action!==m.default.ACTION_COMPLETE&&a.getFragmentModel().isFragmentLoaded(r);)r=e.getNextFragmentRequest(a,c);r&&(isNaN(r.startTime+r.duration)||e.setIndexHandlerTime(a,r.startTime+r.duration),r.delayLoadingTime=(new Date).getTime()+i.getTimeToLoadDelay(),i.setTimeToLoadDelay(0))}return r}a=a||{};var d=this.context,e=a.adapter,f=a.textController,h=void 0,j=void 0;return h={execute:c},b(),h}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(45),i=d(h),j=a(47),k=d(j),l=a(165),m=d(l);e.__dashjs_factory_name="NextFragmentRequestRule",c.default=k.default.getClassFactory(e),b.exports=c.default},{165:165,45:45,47:47,98:98}],137:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){for(var b="",c=0;c0&&(o.spans.push({name:p,line:w,row:t}),w="");var C="style_cea608_"+B.foreground+"_"+B.background;B.underline&&(C+="_underline"),B.italics&&(C+="_italics"),r.hasOwnProperty(C)||(r[C]=JSON.parse(JSON.stringify(B))),x=B,p=C}w+=A.uchar}w.trim().length>0&&o.spans.push({name:p,line:w,row:t}),m=!0,n=y}else m=!1,n=-1,k&&(k.p.push(o),o={start:g,end:i,spans:[]},k.y2=t,k.name="region_"+k.x+"_"+k.y1+"_"+k.y2,!1===q.hasOwnProperty(k.name)?(s.push(k),q[k.name]=k):(l=q[k.name],l.p.contat(k.p)),k=null)}k&&(k.p.push(o),k.y2=t+1,k.name="region_"+k.x+"_"+k.y1+"_"+k.y2,!1===q.hasOwnProperty(k.name)?(s.push(k),q[k.name]=k):(l=q[k.name],l.p.contat(k.p)),k=null);var D=[];for(t=0;t0){if(0!==u&&M!=N.row){var O=document.createElement("br");O.className="lineBreak",J.appendChild(O)}var P=!1;M===N.row&&(P=!0),M=N.row;var Q=r[N.name],R=document.createElement("span");R.className="spanPadding "+N.name+" customSpanColor",R.style.cssText=d(c,Q),0!==u&&P?u===L.spans.length-1?R.textContent=f(N.line):R.textContent=N.line:L.spans.length>1&&u0?d[b.roles[0]]:d.caption;return a=a===d.caption||a===d.subtitle?a:d.caption},f=function(){var a=!1;return b.codec&&b.codec.search(g.default.STPP)>=0&&(a=!0),b.mimeType&&b.mimeType.search(g.default.TTML)>=0&&(a=!0),a};c.captionData=a,c.lang=b.lang,c.label=b.id?b.id:b.index,c.index=b.index,c.isTTML=f(),c.defaultTrack=C(b),c.isFragmented=!O.getIsTextTrack(b.mimeType),c.isEmbedded=!!b.isEmbedded,c.kind=e(),c.roles=b.roles,c.accessibility=b.accessibility;var h=(V?V.length:0)+da.length;W.addTextTrack(c,h)}function y(a,b){var c=void 0,d=void 0,e=void 0,f=void 0,i=void 0,j=void 0,k=void 0,l=b.mediaInfo,m=l.type,n=l.mimeType,o=l.codec||n;if(!o)return void L.error("No text type defined");if(m===g.default.FRAGMENTED_TEXT)if(Y)if(j=U.getSamplesInfo(a),d=j.sampleList,!ba&&d.length>0&&(ba=d[0].cts-b.start*Z),o.search(g.default.STPP)>=0)for(R=null!==R?R:E(o),e=0;e0&&W.addCaptions(ca,0,D)}else Y=!0,Z=U.getMediaTimescaleFromMoov(a);else if(m===g.default.TEXT){var s=new DataView(a,0,a.byteLength);k=z.default.Utils.dataViewToString(s,g.default.UTF8);try{c=E(o).parse(k,0),W.addCaptions(W.getCurrentTrackIdx(),0,c)}catch(ka){N.timedTextError(ka,"parse",k)}}else if(m===g.default.VIDEO)if(b.segmentType===h.HTTPRequest.INIT_SEGMENT_TYPE){if(0===fa)for(fa=U.getMediaTimescaleFromMoov(a),e=0;e0&&(f.cts!==h?i=0:i+=1,c.fields[l].push([f.cts+ka*fa,k[l],i]),h=f.cts);return c.fields.forEach(function(a){a.sort(function(a,b){return a[0]===b[0]?a[2]-b[2]:a[0]-b[0]})}),c}function C(a){var b=!1;return da.length>1&&a.isEmbedded?b=a.id&&a.id===g.default.CC1:1===da.length?a.id&&"CC"===a.id.substring(0,2)&&(b=!0):0===da.length&&(b=a.index===V[0].index),b}function E(a){var b=void 0;return a.search(g.default.VTT)>=0?b=S:(a.search(g.default.TTML)>=0||a.search(g.default.STPP)>=0)&&(b=T),b}function G(a,b){void 0===a&&a===b&&(a=this.buffered.start(0),b=this.buffered.end(this.buffered.length-1)),this.buffered.remove(a,b)}var H=this.context,I=(0,D.default)(H).getInstance(),J=!1,K=void 0,L=void 0,M=void 0,N=void 0,O=void 0,P=void 0,Q=void 0,R=void 0,S=void 0,T=void 0,U=void 0,V=void 0,W=void 0,X=void 0,Y=void 0,Z=void 0,$=void 0,_=void 0,aa=void 0,ba=void 0,ca=void 0,da=void 0,ea=void 0,fa=void 0,ga=void 0,ha=void 0,ia=void 0,ja=void 0,ka=void 0;return K={initialize:d,append:y,abort:f,addEmbeddedTrack:q,resetEmbedded:o,setConfig:r,getConfig:s,setCurrentFragmentedTrackIdx:u,remove:G,reset:i},a(),K}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(183),i=a(175),j=d(i),k=a(71),l=d(k),m=a(146),n=d(m),o=a(148),p=d(o),q=a(47),r=d(q),s=a(45),t=d(s),u=a(142),v=d(u),w=a(137),x=d(w),y=a(5),z=d(y),A=a(2),B=d(A),C=a(46),D=d(C),E=a(50),F=d(E);e.__dashjs_factory_name="TextSourceBuffer",c.default=r.default.getSingletonFactory(e),b.exports=c.default},{137:137,142:142,146:146,148:148,175:175,183:183,2:2,45:45,46:46,47:47,5:5,50:50,71:71,98:98}],142:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){H=(0,o.default)(E).getInstance().getLogger(G)}function b(){"undefined"!=typeof window&&"undefined"!=typeof navigator&&(I=window.VTTCue||window.TextTrackCue,K=[],L=[],M=-1,N=0,O=0,P=0,Q=0,R=null,S=null,U=!1,W=2147483647,V=null,void 0!==document.fullscreenElement?T="fullscreenElement":void 0!==document.webkitIsFullScreen?T="webkitIsFullScreen":document.msFullscreenElement?T="msFullscreenElement":document.mozFullScreen&&(T="mozFullScreen"))}function c(a){var b=K[a].kind,c=void 0!==K[a].label?K[a].label:K[a].lang,d=K[a].lang,e=K[a].isTTML,f=K[a].isEmbedded,g=J.addTextTrack(b,c,d);return g.isEmbedded=f,g.isTTML=e,g}function d(a){U=a,R&&!document[T]&&(R.style.zIndex=a?W:null)}function e(a,b){if(K.length===b)return void H.error("Trying to add too many tracks.");if(K.push(a),K.length===b){K.sort(function(a,b){return a.index-b.index}),R=J.getTTMLRenderingDiv();for(var d=-1,e=0;e=0)for(var i=0;ih?(j=b,i=j/d*c):(i=a,j=i/c*d);var k=0,l=0,m=0,n=0;return i/j>e?(n=j,m=j*e):(m=i,n=i/e),k=(a-m)/2,l=(b-n)/2,f?{x:k+.1*m,y:l+.1*n,w:.8*m,h:.8*n}:{x:k,y:l,w:m,h:n}}function h(a,b){var c=J.getClientWidth(),d=J.getClientHeight(),e=J.getVideoWidth(),g=J.getVideoHeight(),h=J.getVideoRelativeOffsetTop(),i=J.getVideoRelativeOffsetLeft(),j=e/g,l=!1;a.isFromCEA608&&(j=3.5/3,l=!0);var m=f.call(this,c,d,e,g,j,l),n=m.w,o=m.h,p=m.x,q=m.y;if(n!=P||o!=Q||p!=N||q!=O||b){if(N=p+i,O=q+h,P=n,Q=o,R){var r=R.style;r.left=N+"px",r.top=O+"px",r.width=P+"px",r.height=Q+"px",r.zIndex=T&&document[T]||U?W:null,F.trigger(k.default.CAPTION_CONTAINER_RESIZE,{})}var s=a.activeCues;if(s)for(var t=s.length,u=0;u=0&&K[a]?J.getTextTrack(K[a].kind,K[a].label,K[a].lang,K[a].isTTML,K[a].isEmbedded):null}function q(){return M}function r(a){for(var b=-1,c=0;c=0;d--)a.removeCue(b[d])}function v(a){var b=n(a);b&&u(b)}function w(){for(var a=L?L.length:0,b=0;b0&&e.forEach(function(a){a.segmentInfoType===i.default.SEGMENT_TEMPLATE&&a.segmentDuration>0&&a.media&&d(a)}),x.length>0&&(x.sort(function(a,b){return a.bitrate-b.bitrate}),y=x.length-1)}}}}}function d(a){var b=new m.default;b.id=a.id,b.bitrate=a.bandwidth,b.width=a.width,b.height=a.height,b.tilesHor=1,b.tilesVert=1,b.startNumber=a.startNumber,b.segmentDuration=a.segmentDuration,b.timescale=a.timescale,b.templateUrl=e(a),a.essentialProperties&&a.essentialProperties.forEach(function(a){if(a.schemeIdUri===q&&a.value){var c=a.value.split("x");2!==c.length||isNaN(c[0])||isNaN(c[1])||(b.tilesHor=parseInt(c[0],10),b.tilesVert=parseInt(c[1],10))}}),b.tilesHor>0&&b.tilesVert>0&&(b.widthPerTile=b.width/b.tilesHor,b.heightPerTile=b.height/b.tilesVert,x.push(b))}function e(a){var b=v.isRelative(a.media)?v.resolve(a.media,t.resolve(a.path).url):a.media;return b?(0,p.replaceIDForTemplate)(b,a.id):""}function f(){return x}function h(){return y}function j(){return y<0?null:x[y]}function k(a){x&&0!==x.length&&(a>=x.length&&(a=x.length-1),y=a)}function l(){x=[],y=-1}var n=this.context,r=a.dashManifestModel,s=a.adapter,t=a.baseURLController,u=a.stream,v=(0,o.default)(n).getInstance(),w=void 0,x=void 0,y=void 0;return w={initialize:b,getTracks:f,reset:l,setTrackByIndex:k,getCurrentTrack:j,getCurrentTrackIndex:h},b(),w}Object.defineProperty(c,"__esModule",{value:!0});var f=a(98),g=d(f),h=a(57),i=d(h),j=a(47),k=d(j),l=a(177),m=d(l),n=a(158),o=d(n),p=a(75),q="http://dashif.org/thumbnail_tile";e.__dashjs_factory_name="ThumbnailTracks",c.default=k.default.getClassFactory(e),b.exports=c.default},{158:158,177:177,47:47,57:57,75:75,98:98}],145:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){p=(0,k.default)(h).create({updateEventName:i.default.SERVICE_LOCATION_BLACKLIST_CHANGED,addBlacklistEventName:i.default.SERVICE_LOCATION_BLACKLIST_ADD}),q=(0,o.default)(h).create({blacklistController:p}),t=(0,m.default)(h).create({blacklistController:p}),u=q}function b(a){a.selector&&(u=a.selector),a.dashManifestModel&&(l=a.dashManifestModel)}function c(){if(!l||!l.hasOwnProperty("getIsDVB"))throw new Error("Missing config parameter(s)")}function d(a){c(),u=l.getIsDVB(a)?t:q}function e(a){var b=a.baseUrls,c=a.selectedIdx;if(!isNaN(c))return b[c];var d=u.select(b);return d?(a.selectedIdx=b.indexOf(d),d):(j.trigger(i.default.URL_RESOLUTION_FAILED,{error:new Error(r,s)}),void(u===q&&f()))}function f(){p.reset()}var h=this.context,j=(0,g.default)(h).getInstance(),l=void 0,n=void 0,p=void 0,q=void 0,t=void 0,u=void 0;return n={chooseSelectorFromManifest:d,select:e,reset:f,setConfig:b},a(),n}Object.defineProperty(c,"__esModule",{value:!0});var f=a(46),g=d(f),h=a(50),i=d(h),j=a(102),k=d(j),l=a(161),m=d(l),n=a(160),o=d(n),p=a(47),q=d(p),r=1,s="Failed to resolve a valid URL";e.__dashjs_factory_name="BaseURLSelector";var t=q.default.getClassFactory(e);t.URL_RESOLUTION_FAILED_GENERIC_ERROR_CODE=r,t.URL_RESOLUTION_FAILED_GENERIC_ERROR_MESSAGE=s,q.default.updateClassFactory(e.__dashjs_factory_name,t),c.default=t,b.exports=c.default},{102:102,160:160,161:161,46:46,47:47,50:50}],146:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){if(!a)return null;void 0===a.fileStart&&(a.fileStart=0);var b=k.default.parseBuffer(a),c=(0,g.default)(e).create();return c.setData(b),c}function b(a,b,e){if(void 0===e&&(e=0),!b||e+8>=b.byteLength)return new m.default(0,!1);for(var f=b instanceof ArrayBuffer?new Uint8Array(b):b,g=void 0,h=0;e=0?g=new m.default(e,!0,i):h=e+i),e+=i}return g||new m.default(h,!1)}function c(a,b){return a[b+3]>>>0|a[b+2]<<8>>>0|a[b+1]<<16>>>0|a[b]<<24>>>0}function d(a,b){return String.fromCharCode(a[b++])+String.fromCharCode(a[b++])+String.fromCharCode(a[b++])+String.fromCharCode(a[b])}var e=this.context;return{parse:a,findLastTopIsoBoxCompleted:b}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(153),g=d(f),h=a(47),i=d(h),j=a(5),k=d(j),l=a(168),m=d(l);e.__dashjs_factory_name="BoxParser",c.default=i.default.getSingletonFactory(e),b.exports=c.default},{153:153,168:168,47:47,5:5}],147:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){g=!1}function b(){var a="WebKitMediaSource"in window,b="MediaSource"in window;return a||b}function c(){return g}function d(a){g=a}function e(a){return!!("MediaSource"in window&&MediaSource.isTypeSupported(a))||!!("WebKitMediaSource"in window&&WebKitMediaSource.isTypeSupported(a))}var f=void 0,g=void 0;return f={supportsMediaSource:b,supportsEncryptedMedia:c,supportsCodec:e,setEncryptedMediaSupported:d},a(),f}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="Capabilities",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47}],148:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){var c=0;for(c=0;cthis.customTimeRangeArray[c].start;c++);for(this.customTimeRangeArray.splice(c,0,{start:a,end:b}),c=0;c=this.customTimeRangeArray[c].end)this.customTimeRangeArray.splice(c,1),c--;else{if(a>this.customTimeRangeArray[c].start&&bthis.customTimeRangeArray[c].start&&athis.customTimeRangeArray[c].start&&b=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].start}function g(a){return e(a),a>=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].end}return{customTimeRangeArray:[],length:0,add:a,clear:b,remove:c,mergeRanges:d,start:f,end:g}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="CustomTimeRanges",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],149:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){w=(0,i.default)(t).getInstance().getLogger(v),d()}function c(a){if(void 0!==x)return x;x=!1;var b="1",c="1",d=void 0;try{"undefined"!=typeof window&&(d=window[a])}catch(e){return w.warn("DOMStorage access denied: "+e.message),x}if(!d||a!==m&&a!==n)return x;try{d.setItem(b,c),d.removeItem(b),x=!0}catch(e){w.warn("DOMStorage is supported, but cannot be used: "+e.message)}return x}function d(){c(m)&&j.forEach(function(a){var b=localStorage.getItem(a.oldKey);if(b){localStorage.removeItem(a.oldKey);try{localStorage.setItem(a.newKey,b)}catch(c){w.error(c.message)}}})}function e(){var a=6e5;return Math.round((new Date).getTime()/a)*a}function f(a,b){return c(a)&&u["get"+b+"CachingInfo"]().enabled}function g(){if(!u||!u.hasOwnProperty("getLastMediaSettingsCachingInfo"))throw new Error("Missing config parameter(s)")}function h(a){if(g(),!f(m,p))return null;var b=null,c=l.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1;b=d.settings,e&&(localStorage.removeItem(c),b=null)}catch(h){return null}return b}function q(a){var b=NaN;if(g(),f(m,o)){var c=k.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1,h=parseFloat(d.bitrate);isNaN(h)||e?e&&localStorage.removeItem(c):(b=h,w.debug("Last saved bitrate for "+a+" was "+h))}catch(i){return null}}return b}function r(a,b){if(f(m,p)){var c=l.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({settings:b,timestamp:e()}))}catch(d){w.error(d.message)}}}function s(a,b){if(f(m,o)&&b){var c=k.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({bitrate:b.toFixed(3),timestamp:e()}))}catch(d){w.error(d.message)}}}a=a||{};var t=this.context,u=a.mediaPlayerModel,v=void 0,w=void 0,x=void 0;return v={getSavedBitrateSettings:q,setSavedBitrateSettings:s,getSavedMediaSettings:h,setSavedMediaSettings:r},b(),v}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(45),i=d(h),j=[{oldKey:"dashjs_vbitrate",newKey:"dashjs_video_bitrate"},{oldKey:"dashjs_abitrate",newKey:"dashjs_audio_bitrate"},{oldKey:"dashjs_vsettings",newKey:"dashjs_video_settings"},{oldKey:"dashjs_asettings",newKey:"dashjs_audio_settings"}],k="dashjs_?_bitrate",l="dashjs_?_settings",m="localStorage",n="sessionStorage",o="LastBitrate",p="LastMediaSettings";e.__dashjs_factory_name="DOMStorage";var q=g.default.getSingletonFactory(e);c.default=q,b.exports=c.default},{45:45,47:47}],150:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){return n}function c(a){n=a}function d(a,b){var c=!0,d=0,e=void 0,f=void 0;if(void 0===b&&(b=!1),a.tag>16777215?(m.getUint32(n)!==a.tag&&(c=!1),d=4):a.tag>65535?(e=m.getUint16(n),f=m.getUint8(n+2),256*e+f!==a.tag&&(c=!1),d=3):a.tag>255?(m.getUint16(n)!==a.tag&&(c=!1),d=2):(m.getUint8(n)!==a.tag&&(c=!1),d=1),!c&&a.required&&!b)throw new Error("required tag not found");return c&&(n+=d),c}function e(a,b){var c=d(a,b);return c&&h(),c}function f(a){var b=void 0;return d(a),b=h(),l[a.parse](b)}function g(a,b){var c=d(a,b),e=void 0;return c&&(e=h(),n+=e),c}function h(a){var b=1,c=128,d=8,e=-1,f=0,g=m.getUint8(n),h=0;for(h=0;h>=1}for(h=0;h0?c[c.length-1]:null}function e(a){if(!a)return null;var b=new g.default(a);return a.hasOwnProperty("_incomplete")&&(b.isComplete=!a._incomplete),b}var f=void 0;return{getBox:a,getBoxes:b,setData:c,getLastBox:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(167),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="IsoFile",c.default=i.default.getClassFactory(e),b.exports=c.default},{167:167,47:47}],154:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){if(!(e&&e.hasOwnProperty("getExpectedLiveEdge")&&f&&f.hasOwnProperty("getCurrentRepresentationInfo")))throw new Error("Missing config parameter(s)")}function c(){b();var a=f.getCurrentRepresentationInfo(),c=a.DVRWindow.end;return a.useCalculatedLiveEdgeTime&&(c=e.getExpectedLiveEdge(),e.setClientTimeOffset(c-a.DVRWindow.end)),c}function d(){e=null,f=null}a=a||{};var e=a.timelineConverter,f=a.streamProcessor;return{getLiveEdge:c,reset:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="LiveEdgeFinder",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],155:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){return(0,i.default)(a,b)}return{areEqual:a}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(6),i=d(h);e.__dashjs_factory_name="ObjectUtils",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47,6:6}],156:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){return a}function b(a){return a}return{modifyRequestURL:a,modifyRequestHeader:b}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="RequestModifier",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47}],157:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){g=(0,i.default)(d).getInstance().getLogger(f)}function b(){var a="cue_TTML_"+h;return h++,a}function c(a,c,d,f,h){var i=void 0,j="",k=[],l=void 0,o=void 0,p={},q={},r="",s="",t={onOpenTag:function(a,b,c){if("image"===b&&"http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt"===a){if(!c[" imagetype"]||"PNG"!==c[" imagetype"].value)return void g.warn("smpte-tt imagetype != PNG. Discarded");r=c["http://www.w3.org/XML/1998/namespace id"].value}},onCloseTag:function(){r&&(q[r]=s.trim()),s="",r=""},onText:function(a){r&&(s+=a)}};if(!a)throw j="no ttml data to parse",new Error(j);p.data=a,e.trigger(m.default.TTML_TO_PARSE,p);var u=(0,n.fromXML)(p.data,function(a){j=a},t);e.trigger(m.default.TTML_PARSED,{ttmlString:p.data,ttmlDoc:u});var v=u.getMediaTimeEvents();for(i=0;if?f:v[i+1]+c,l/,n=/(^[\s]+|[\s]+$)/g,o=/\s\b/g}function b(a){var b=[],e=void 0,g=void 0;if(!a)return b;a=a.split(l),e=a.length,g=-1;for(var h=0;h0&&i!==j&&i.match(m)){var o=d(i),p=o.cuePoints,q=o.styles,r=f(a,h+1),s=c(p[0].replace(n,"")),t=c(p[1].replace(n,""));!isNaN(s)&&!isNaN(t)&&s>=g&&t>s?""!==r?(g=s,b.push({start:s,end:t,data:r,styles:q})):k.error("Skipping cue due to empty/malformed cue text"):k.error("Skipping cue due to incorrect cue timing")}}return b}function c(a){var b=a.split(":"),c=b.length-1;return a=60*parseInt(b[c-1],10)+parseFloat(b[c]),2===c&&(a+=3600*parseInt(b[0],10)),a}function d(a){var b=a.split(m),c=b[1].split(o);return c.shift(),b[1]=c[0],c.shift(),{cuePoints:b,styles:e(c)}}function e(a){var b={};return a.forEach(function(a){if(a.split(/:/).length>1){var c=a.split(/:/)[1];c&&-1!=c.search(/%/)&&(c=parseInt(c.replace(/%/,""),10)),(a.match(/align/)||a.match(/A/))&&(b.align=c),(a.match(/line/)||a.match(/L/))&&(b.line=c),(a.match(/position/)||a.match(/P/))&&(b.position=c),(a.match(/size/)||a.match(/S/))&&(b.size=c)}}),b}function f(a,b){for(var c=b,d="",e="",f=void 0;""!==a[c]&&c1)for(var g=0;g1&&(h.forEach(function(a){d+=a.dvb_weight,e.push(d)}),g=Math.floor(Math.random()*(d-1)),e.every(function(a,b){return f=b,!(g=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].start}function g(a){return e(a),a>=this.customTimeRangeArray.length||a<0?NaN:this.customTimeRangeArray[a].end}return{customTimeRangeArray:[],length:0,add:a,clear:b,remove:c,mergeRanges:d,start:f,end:g}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="CustomTimeRanges",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],149:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){w=(0,i.default)(t).getInstance().getLogger(v),d()}function c(a){if(void 0!==x)return x;x=!1;var b="1",c="1",d=void 0;try{"undefined"!=typeof window&&(d=window[a])}catch(e){return w.warn("DOMStorage access denied: "+e.message),x}if(!d||a!==m&&a!==n)return x;try{d.setItem(b,c),d.removeItem(b),x=!0}catch(e){w.warn("DOMStorage is supported, but cannot be used: "+e.message)}return x}function d(){c(m)&&j.forEach(function(a){var b=localStorage.getItem(a.oldKey);if(b){localStorage.removeItem(a.oldKey);try{localStorage.setItem(a.newKey,b)}catch(c){w.error(c.message)}}})}function e(){var a=6e5;return Math.round((new Date).getTime()/a)*a}function f(a,b){return c(a)&&u["get"+b+"CachingInfo"]().enabled}function g(){if(!u||!u.hasOwnProperty("getLastMediaSettingsCachingInfo"))throw new Error("Missing config parameter(s)")}function h(a){if(g(),!f(m,p))return null;var b=null,c=l.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1;b=d.settings,e&&(localStorage.removeItem(c),b=null)}catch(h){return null}return b}function q(a){var b=NaN;if(g(),f(m,o)){var c=k.replace(/\?/,a);try{var d=JSON.parse(localStorage.getItem(c))||{},e=(new Date).getTime()-parseInt(d.timestamp,10)>=u.getLastMediaSettingsCachingInfo().ttl||!1,h=parseFloat(d.bitrate);isNaN(h)||e?e&&localStorage.removeItem(c):(b=h,w.debug("Last saved bitrate for "+a+" was "+h))}catch(i){return null}}return b}function r(a,b){if(f(m,p)){var c=l.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({settings:b,timestamp:e()}))}catch(d){w.error(d.message)}}}function s(a,b){if(f(m,o)&&b){var c=k.replace(/\?/,a);try{localStorage.setItem(c,JSON.stringify({bitrate:b.toFixed(3),timestamp:e()}))}catch(d){w.error(d.message)}}}a=a||{};var t=this.context,u=a.mediaPlayerModel,v=void 0,w=void 0,x=void 0;return v={getSavedBitrateSettings:q,setSavedBitrateSettings:s,getSavedMediaSettings:h,setSavedMediaSettings:r},b(),v}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(45),i=d(h),j=[{oldKey:"dashjs_vbitrate",newKey:"dashjs_video_bitrate"},{oldKey:"dashjs_abitrate",newKey:"dashjs_audio_bitrate"},{oldKey:"dashjs_vsettings",newKey:"dashjs_video_settings"},{oldKey:"dashjs_asettings",newKey:"dashjs_audio_settings"}],k="dashjs_?_bitrate",l="dashjs_?_settings",m="localStorage",n="sessionStorage",o="LastBitrate",p="LastMediaSettings";e.__dashjs_factory_name="DOMStorage";var q=g.default.getSingletonFactory(e);c.default=q,b.exports=c.default},{45:45,47:47}],150:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){return n}function c(a){n=a}function d(a,b){var c=!0,d=0,e=void 0,f=void 0;if(void 0===b&&(b=!1),a.tag>16777215?(m.getUint32(n)!==a.tag&&(c=!1),d=4):a.tag>65535?(e=m.getUint16(n),f=m.getUint8(n+2),256*e+f!==a.tag&&(c=!1),d=3):a.tag>255?(m.getUint16(n)!==a.tag&&(c=!1),d=2):(m.getUint8(n)!==a.tag&&(c=!1),d=1),!c&&a.required&&!b)throw new Error("required tag not found");return c&&(n+=d),c}function e(a,b){var c=d(a,b);return c&&h(),c}function f(a){var b=void 0;return d(a),b=h(),l[a.parse](b)}function g(a,b){var c=d(a,b),e=void 0;return c&&(e=h(),n+=e),c}function h(a){var b=1,c=128,d=8,e=-1,f=0,g=m.getUint8(n),h=0;for(h=0;h>=1}for(h=0;h0?c[c.length-1]:null}function e(a){if(!a)return null;var b=new g.default(a);return a.hasOwnProperty("_incomplete")&&(b.isComplete=!a._incomplete),b}var f=void 0;return{getBox:a,getBoxes:b,setData:c,getLastBox:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(167),g=d(f),h=a(47),i=d(h);e.__dashjs_factory_name="IsoFile",c.default=i.default.getClassFactory(e),b.exports=c.default},{167:167,47:47}],154:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(a){function b(){if(!(e&&e.hasOwnProperty("getExpectedLiveEdge")&&f&&f.hasOwnProperty("getCurrentRepresentationInfo")))throw new Error("Missing config parameter(s)")}function c(){b();var a=f.getCurrentRepresentationInfo(),c=a.DVRWindow.end;return a.useCalculatedLiveEdgeTime&&(c=e.getExpectedLiveEdge(),e.setClientTimeOffset(c-a.DVRWindow.end)),c}function d(){e=null,f=null}a=a||{};var e=a.timelineConverter,f=a.streamProcessor;return{getLiveEdge:c,reset:d}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="LiveEdgeFinder",c.default=g.default.getClassFactory(e),b.exports=c.default},{47:47}],155:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a,b){return(0,i.default)(a,b)}return{areEqual:a}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f),h=a(6),i=d(h);e.__dashjs_factory_name="ObjectUtils",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47,6:6}],156:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(a){return a}function b(a){return a}return{modifyRequestURL:a,modifyRequestHeader:b}}Object.defineProperty(c,"__esModule",{value:!0});var f=a(47),g=d(f);e.__dashjs_factory_name="RequestModifier",c.default=g.default.getSingletonFactory(e),b.exports=c.default},{47:47}],157:[function(a,b,c){"use strict";function d(a){return a&&a.__esModule?a:{default:a}}function e(){function a(){g=(0,i.default)(d).getInstance().getLogger(f)}function b(){var a="cue_TTML_"+h;return h++,a}function c(a,c,d,f,h){var i=void 0,j="",k=[],l=void 0,o=void 0,p={},q={},r="",s="",t={onOpenTag:function(a,b,c){if("image"===b&&"http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt"===a){if(!c[" imagetype"]||"PNG"!==c[" imagetype"].value)return void g.warn("smpte-tt imagetype != PNG. Discarded");r=c["http://www.w3.org/XML/1998/namespace id"].value}},onCloseTag:function(){r&&(q[r]=s.trim()),s="",r=""},onText:function(a){r&&(s+=a)}};if(!a)throw j="no ttml data to parse",new Error(j);p.data=a,e.trigger(m.default.TTML_TO_PARSE,p);var u=(0,n.fromXML)(p.data,function(a){j=a},t);e.trigger(m.default.TTML_PARSED,{ttmlString:p.data,ttmlDoc:u});var v=u.getMediaTimeEvents();for(i=0;if?f:v[i+1]+c,l/,n=/(^[\s]+|[\s]+$)/g,o=/\s\b/g}function b(a){var b=[],e=void 0,g=void 0;if(!a)return b;a=a.split(l),e=a.length,g=-1;for(var h=0;h0&&i!==j&&i.match(m)){var o=d(i),p=o.cuePoints,q=o.styles,r=f(a,h+1),s=c(p[0].replace(n,"")),t=c(p[1].replace(n,""));!isNaN(s)&&!isNaN(t)&&s>=g&&t>s?""!==r?(g=s,b.push({start:s,end:t,data:r,styles:q})):k.error("Skipping cue due to empty/malformed cue text"):k.error("Skipping cue due to incorrect cue timing")}}return b}function c(a){var b=a.split(":"),c=b.length-1;return a=60*parseInt(b[c-1],10)+parseFloat(b[c]),2===c&&(a+=3600*parseInt(b[0],10)),a}function d(a){var b=a.split(m),c=b[1].split(o);return c.shift(),b[1]=c[0],c.shift(),{cuePoints:b,styles:e(c)}}function e(a){var b={};return a.forEach(function(a){if(a.split(/:/).length>1){var c=a.split(/:/)[1];c&&-1!=c.search(/%/)&&(c=parseInt(c.replace(/%/,""),10)),(a.match(/align/)||a.match(/A/))&&(b.align=c),(a.match(/line/)||a.match(/L/))&&(b.line=c),(a.match(/position/)||a.match(/P/))&&(b.position=c),(a.match(/size/)||a.match(/S/))&&(b.size=c)}}),b}function f(a,b){for(var c=b,d="",e="",f=void 0;""!==a[c]&&c1)for(var g=0;g1&&(h.forEach(function(a){d+=a.dvb_weight,e.push(d)}),g=Math.floor(Math.random()*(d-1)),e.every(function(a,b){return f=b,!(g=0&&u0?0:a-1;return arguments.length<3&&(i=e[o?o[c]:c],c+=n),t(e,r,i,o,c,a)}}function r(n){return function(t,e,r){e=x(e,r);for(var i=T(t),u=n>0?0:i-1;u>=0&&u0?o=u>=0?u:Math.max(u+a,o):a=u>=0?Math.min(u+1,a):u+a+1;else if(e&&u&&a)return u=e(r,i),r[u]===i?u:-1;if(i!==i)return(u=t(p.call(r,o,a),m.isNaN))>=0?u+o:-1;for(u=n>0?o:a-1;u>=0&&u=0&&t<=E};m.each=m.forEach=function(n,t,e){t=b(t,e);var r,i;if(A(n))for(r=0,i=n.length;r=0},m.invoke=function(n,t){var e=p.call(arguments,2),r=m.isFunction(t);return m.map(n,function(n){var i=r?t:n[t];return null==i?i:i.apply(n,e)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,e){var r,i,u=-1/0,o=-1/0;if(null==t&&null!=n)for(var a=0,c=(n=A(n)?n:m.values(n)).length;au&&(u=r);else t=x(t,e),m.each(n,function(n,e,r){((i=t(n,e,r))>o||i===-1/0&&u===-1/0)&&(u=n,o=i)});return u},m.min=function(n,t,e){var r,i,u=1/0,o=1/0;if(null==t&&null!=n)for(var a=0,c=(n=A(n)?n:m.values(n)).length;ar||void 0===e)return 1;if(et?(o&&(clearTimeout(o),o=null),a=s,u=n.apply(r,i),o||(r=i=null)):o||!1===e.trailing||(o=setTimeout(c,l)),u}},m.debounce=function(n,t,e){var r,i,u,o,a,c=function(){var s=m.now()-o;s=0?r=setTimeout(c,t-s):(r=null,e||(a=n.apply(u,i),r||(u=i=null)))};return function(){u=this,i=arguments,o=m.now();var s=e&&!r;return r||(r=setTimeout(c,t)),s&&(a=n.apply(u,i),u=i=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var e=t,r=n[t].apply(this,arguments);e--;)r=n[e].call(this,r);return r}},m.after=function(n,t){return function(){if(--n<1)return t.apply(this,arguments)}},m.before=function(n,t){var e;return function(){return--n>0&&(e=t.apply(this,arguments)),n<=1&&(t=null),e}},m.once=m.partial(m.before,2);var F=!{toString:null}.propertyIsEnumerable("toString"),q=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(d)return d(n);var t=[];for(var e in n)m.has(n,e)&&t.push(e);return F&&u(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var e in n)t.push(e);return F&&u(n,t),t},m.values=function(n){for(var t=m.keys(n),e=t.length,r=Array(e),i=0;i":">",'"':""","'":"'","`":"`"},N=m.invert(M),R=function(n){var t=function(t){return n[t]},e="(?:"+m.keys(n).join("|")+")",r=RegExp(e),i=RegExp(e,"g");return function(n){return n=null==n?"":""+n,r.test(n)?n.replace(i,t):n}};m.escape=R(M),m.unescape=R(N),m.result=function(n,t,e){var r=null==n?void 0:n[t];return void 0===r&&(r=e),m.isFunction(r)?r.call(n):r};var L=0;m.uniqueId=function(n){var t=++L+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var U=/(.)^/,D={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},W=/\\|'|\r|\n|\u2028|\u2029/g,P=function(n){return"\\"+D[n]};m.template=function(n,t,e){!t&&e&&(t=e),t=m.defaults({},t,m.templateSettings);var r=RegExp([(t.escape||U).source,(t.interpolate||U).source,(t.evaluate||U).source].join("|")+"|$","g"),i=0,u="__p+='";n.replace(r,function(t,e,r,o,a){return u+=n.slice(i,a).replace(W,P),i=a+t.length,e?u+="'+\n((__t=("+e+"))==null?'':_.escape(__t))+\n'":r?u+="'+\n((__t=("+r+"))==null?'':__t)+\n'":o&&(u+="';\n"+o+"\n__p+='"),t}),u+="';\n",t.variable||(u="with(obj||{}){\n"+u+"}\n"),u="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+u+"return __p;\n";try{var o=new Function(t.variable||"obj","_",u)}catch(n){throw n.source=u,n}var a=function(n){return o.call(this,n,m)},c=t.variable||"obj";return a.source="function("+c+"){\n"+u+"}",a},m.chain=function(n){var t=m(n);return t._chain=!0,t};var B=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var e=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return f.apply(n,arguments),B(this,e.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=c[n];m.prototype[n]=function(){var e=this._wrapped;return t.apply(e,arguments),"shift"!==n&&"splice"!==n||0!==e.length||delete e[0],B(this,e)}}),m.each(["concat","join","slice"],function(n){var t=c[n];m.prototype[n]=function(){return B(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this)},{}],3:[function(n,t,e){"use strict";var r=n("underscore"),i=n("../events");t.exports=function(n){var t=n.getComponent("MenuItem");return n.extend(t,{constructor:function(n,e){var i=e.source;if(!r.isObject(i))throw new Error('was not provided a "source" object, but rather: '+typeof i);e=r.extend({selectable:!0,label:i.label},e),t.call(this,n,e),this.source=i},handleClick:function(n){t.prototype.handleClick.call(this,n),this.player().trigger(i.QUALITY_REQUESTED,this.source)}})}},{"../events":5,underscore:2}],4:[function(n,t,e){"use strict";var r=n("underscore"),i=n("../events"),u=n("./QualityOption");t.exports=function(n){var t,e=n.getComponent("MenuButton"),o=u(n);return t=n.extend(e,{constructor:function(n,t){e.call(this,n,t),n.on(i.QUALITY_REQUESTED,function(t,e){this.setSelectedSource(e),n.addClass("vjs-quality-changing"),n.one("loadeddata",function(){n.removeClass("vjs-quality-changing")})}.bind(this)),n.on(i.QUALITY_SELECTED,function(n,t){this.setSelectedSource(t)}.bind(this)),n.one("ready",function(){this.selectedSrc=n.src(),this.update()}.bind(this)),this.controlText("Open quality selector menu")},setSelectedSource:function(n){var t=n?n.src:void 0;this.selectedSrc!==t&&(this.selectedSrc=t,this.update())},createItems:function(){var n=this.player(),t=n.currentSources();return r.map(t,function(t){return new o(n,{source:t,selected:t.src===this.selectedSrc})}.bind(this))},buildWrapperCSSClass:function(){return"vjs-quality-selector "+e.prototype.buildWrapperCSSClass.call(this)}}),n.registerComponent("QualitySelector",t),t}},{"../events":5,"./QualityOption":3,underscore:2}],5:[function(n,t,e){"use strict";t.exports={QUALITY_REQUESTED:"qualityRequested",QUALITY_SELECTED:"qualitySelected"}},{}],6:[function(n,t,e){"use strict";var r=n("underscore"),i=n("./events"),u=n("./components/QualitySelector"),o=n("./middleware/SourceInterceptor"),a=n("./util/SafeSeek");t.exports=function(n){n=n||window.videojs,u(n),o(n),n.hook("setup",function(n){n.on(i.QUALITY_REQUESTED,function(t,e){var i=n.currentSources(),u=n.currentTime(),o=(n.playbackRate(),n.paused());r.each(i,function(n){n.selected=!1}),r.findWhere(i,{src:e.src}).selected=!0,n._qualitySelectorSafeSeek&&n._qualitySelectorSafeSeek.onQualitySelectionChange(),n.src(i),n.ready(function(){n._qualitySelectorSafeSeek&&!n._qualitySelectorSafeSeek.hasFinished()||(n._qualitySelectorSafeSeek=new a(n,u),n.playbackRate=playbackRate),o||n.play()})})})},t.exports.EVENTS=i},{"./components/QualitySelector":4,"./events":5,"./middleware/SourceInterceptor":7,"./util/SafeSeek":9,underscore:2}],7:[function(n,t,e){"use strict";var r=n("underscore"),i=n("../events");t.exports=function(n){n.use("*",function(n){return{setSource:function(t,e){var u,o=n.currentSources();n._qualitySelectorSafeSeek&&n._qualitySelectorSafeSeek.onPlayerSourcesChange(),u=r.find(o,function(n){return!0===n.selected||"true"===n.selected})||t,n.trigger(i.QUALITY_SELECTED,u),e(null,u)}}})}},{"../events":5,underscore:2}],8:[function(n,t,e){"use strict";n("./index")()},{"./index":6}],9:[function(n,t,e){"use strict";var r=n("class.extend");t.exports=r.extend({init:function(n,t){this._player=n,this._seekToTime=t,this._hasFinished=!1,this._keepThisInstanceWhenPlayerSourcesChange=!1,this._seekWhenSafe()},_seekWhenSafe:function(){this._player.readyState()<3?(this._seekFn=this._seek.bind(this),this._player.one("canplay",this._seekFn)):this._seek()},onPlayerSourcesChange:function(){this._keepThisInstanceWhenPlayerSourcesChange?this._keepThisInstanceWhenPlayerSourcesChange=!1:this.cancel()},onQualitySelectionChange:function(){this.hasFinished()||(this._keepThisInstanceWhenPlayerSourcesChange=!0)},_seek:function(){this._player.currentTime(this._seekToTime),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0},hasFinished:function(){return this._hasFinished},cancel:function(){this._player.off("canplay",this._seekFn),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0}})},{"class.extend":1}]},{},[8]); \ No newline at end of file From bc1e62ce5162aa3a7c7b13c7381b4f73f50ce642 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 11:37:52 -0600 Subject: [PATCH 23/81] Add 'external_port' --- src/invidious/helpers/helpers.cr | 1 + src/invidious/helpers/utils.cr | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 3cc4d9cfa..075bf84eb 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -24,6 +24,7 @@ user: String, registration_enabled: {type: Bool, default: true}, statistics_enabled: {type: Bool, default: false}, admins: {type: Array(String), default: [] of String}, + external_port: {type: Int32 | Nil, default: nil}, }) end diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index c200801f4..3123b1d2b 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -195,6 +195,7 @@ end def make_host_url(config, kemal_config) ssl = config.https_only || kemal_config.ssl + port = config.external_port || kemal_config.port if ssl scheme = "https://" @@ -202,7 +203,8 @@ def make_host_url(config, kemal_config) scheme = "http://" end - if kemal_config.port != 80 && kemal_config.port != 443 + # Add if non-standard port + if port != 80 && port != 443 port = ":#{kemal_config.port}" else port = "" From 64e4791dca6186ca15e98afc85076a4fe5e6f377 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 12:01:31 -0600 Subject: [PATCH 24/81] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20f509aae..fd7d452ec 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Invidious is an alternative front-end to YouTube - Audio-only mode (and no need to keep window open on mobile) -- [Open-source](https://github.com/omarroth/invidious) (AGPLv3 licensed) +- [Free software(https://github.com/omarroth/invidious) (AGPLv3 licensed) - No ads - No need to create a Google account to save subscriptions - Lightweight (homepage is ~4 KB compressed) From c69fbb72d3bb7d18bf6f8acb48b26b54b8598710 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 12:01:43 -0600 Subject: [PATCH 25/81] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd7d452ec..d8aaef47c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Invidious is an alternative front-end to YouTube - Audio-only mode (and no need to keep window open on mobile) -- [Free software(https://github.com/omarroth/invidious) (AGPLv3 licensed) +- [Free software](https://github.com/omarroth/invidious) (AGPLv3 licensed) - No ads - No need to create a Google account to save subscriptions - Lightweight (homepage is ~4 KB compressed) From c4d77bc18a726941823dfcc6a0b8e2159d73517e Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 14:42:37 -0600 Subject: [PATCH 26/81] Use host_url for generating thumbnails --- src/invidious.cr | 32 ++++++++++++++++---------------- src/invidious/videos.cr | 32 +++++++++++++++++--------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index f5c97c136..2d1d88ce9 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2826,7 +2826,7 @@ get "/api/v1/videos/:id" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end video.description, description = html_to_content(video.description) @@ -2989,7 +2989,7 @@ get "/api/v1/videos/:id" do |env| json.field "videoId", rv["id"] json.field "title", rv["title"] json.field "videoThumbnails" do - generate_thumbnails(json, rv["id"]) + generate_thumbnails(json, rv["id"], config, Kemal.config) end json.field "author", rv["author"] json.field "lengthSeconds", rv["length_seconds"].to_i @@ -3031,7 +3031,7 @@ get "/api/v1/trending" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "lengthSeconds", video.length_seconds @@ -3072,7 +3072,7 @@ get "/api/v1/popular" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "lengthSeconds", video.length_seconds @@ -3111,7 +3111,7 @@ get "/api/v1/top" do |env| json.field "title", video.title json.field "videoId", video.id json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "lengthSeconds", video.info["length_seconds"].to_i @@ -3294,7 +3294,7 @@ get "/api/v1/channels/:ucid" do |env| end json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "description", video.description @@ -3392,7 +3392,7 @@ end end json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "description", video.description @@ -3444,7 +3444,7 @@ end json.field "authorUrl", "/channel/#{ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "description", video.description @@ -3515,7 +3515,7 @@ end json.field "lengthSeconds", video.length_seconds json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end end @@ -3568,7 +3568,7 @@ get "/api/v1/channels/search/:ucid" do |env| json.field "authorUrl", "/channel/#{item.ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, item.id) + generate_thumbnails(json, item.id, config, Kemal.config) end json.field "description", item.description @@ -3600,7 +3600,7 @@ get "/api/v1/channels/search/:ucid" do |env| json.field "lengthSeconds", video.length_seconds json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end end @@ -3697,7 +3697,7 @@ get "/api/v1/search" do |env| json.field "authorUrl", "/channel/#{item.ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, item.id) + generate_thumbnails(json, item.id, config, Kemal.config) end json.field "description", item.description @@ -3729,7 +3729,7 @@ get "/api/v1/search" do |env| json.field "lengthSeconds", video.length_seconds json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end end @@ -3845,7 +3845,7 @@ get "/api/v1/playlists/:plid" do |env| json.field "authorUrl", "/channel/#{video.ucid}" json.field "videoThumbnails" do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end json.field "index", video.index @@ -3921,7 +3921,7 @@ get "/api/v1/mixes/:rdid" do |env| json.field "videoThumbnails" do json.array do - generate_thumbnails(json, video.id) + generate_thumbnails(json, video.id, config, Kemal.config) end end @@ -4332,7 +4332,7 @@ get "/vi/:id/:name" do |env| client = make_client(URI.parse(host)) if name == "maxres.jpg" - VIDEO_THUMBNAILS.each do |thumb| + build_thumbnails(id, config, Kemal.config).each do |thumb| if client.head("/vi/#{id}/#{thumb[:url]}.jpg").status_code == 200 name = thumb[:url] + ".jpg" break diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 1de591b32..cd24afdc5 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -136,18 +136,6 @@ BYPASS_REGIONS = { "TR", } -VIDEO_THUMBNAILS = { - {name: "maxres", host: "#{CONFIG.domain}", url: "maxres", height: 720, width: 1280}, - {name: "maxresdefault", host: "i.ytimg.com", url: "maxresdefault", height: 720, width: 1280}, - {name: "sddefault", host: "i.ytimg.com", url: "sddefault", height: 480, width: 640}, - {name: "high", host: "i.ytimg.com", url: "hqdefault", height: 360, width: 480}, - {name: "medium", host: "i.ytimg.com", url: "mqdefault", height: 180, width: 320}, - {name: "default", host: "i.ytimg.com", url: "default", height: 90, width: 120}, - {name: "start", host: "i.ytimg.com", url: "1", height: 90, width: 120}, - {name: "middle", host: "i.ytimg.com", url: "2", height: 90, width: 120}, - {name: "end", host: "i.ytimg.com", url: "3", height: 90, width: 120}, -} - # See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L380-#L476 VIDEO_FORMATS = { "5" => {"ext" => "flv", "width" => 400, "height" => 240, "acodec" => "mp3", "abr" => 64, "vcodec" => "h263"}, @@ -826,12 +814,26 @@ def process_video_params(query, preferences) return params end -def generate_thumbnails(json, id) +def build_thumbnails(id, config, kemal_config) + return { + {name: "maxres", host: "#{make_host_url(config, kemal_config)}", url: "maxres", height: 720, width: 1280}, + {name: "maxresdefault", host: "https://i.ytimg.com", url: "maxresdefault", height: 720, width: 1280}, + {name: "sddefault", host: "https://i.ytimg.com", url: "sddefault", height: 480, width: 640}, + {name: "high", host: "https://i.ytimg.com", url: "hqdefault", height: 360, width: 480}, + {name: "medium", host: "https://i.ytimg.com", url: "mqdefault", height: 180, width: 320}, + {name: "default", host: "https://i.ytimg.com", url: "default", height: 90, width: 120}, + {name: "start", host: "https://i.ytimg.com", url: "1", height: 90, width: 120}, + {name: "middle", host: "https://i.ytimg.com", url: "2", height: 90, width: 120}, + {name: "end", host: "https://i.ytimg.com", url: "3", height: 90, width: 120}, + } +end + +def generate_thumbnails(json, id, config, kemal_config) json.array do - VIDEO_THUMBNAILS.each do |thumbnail| + build_thumbnails(id, config, kemal_config).each do |thumbnail| json.object do json.field "quality", thumbnail[:name] - json.field "url", "https://#{thumbnail[:host]}/vi/#{id}/#{thumbnail["url"]}.jpg" + json.field "url", "#{thumbnail[:host]}/vi/#{id}/#{thumbnail["url"]}.jpg" json.field "width", thumbnail[:width] json.field "height", thumbnail[:height] end From 9b8703cf490e85f4f55c1579ff597dd1585e0223 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 22:01:59 -0600 Subject: [PATCH 27/81] Fix tab name for auto-generated channels --- src/invidious/views/channel.ecr | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr index 5d635b4e4..d40d8d9ff 100644 --- a/src/invidious/views/channel.ecr +++ b/src/invidious/views/channel.ecr @@ -22,13 +22,17 @@
<%= translate(locale, "View channel on YouTube") %> + <% if !auto_generated %>
<%= translate(locale, "Videos") %>
+ <% end %>
- <% if !auto_generated %> - <%= translate(locale, "Playlists") %> - <% end %> + <% if auto_generated %> + <%= translate(locale, "Playlists") %> + <% else %> + <%= translate(locale, "Playlists") %> + <% end %>
From a1d38a694048a5b39bfda3c3e72038e919d1116f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Sat, 2 Mar 2019 06:56:49 +0000 Subject: [PATCH 28/81] =?UTF-8?q?Update=20Norwegian=20Bokm=C3=A5l=20transl?= =?UTF-8?q?ation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/nb_NO.json | 572 ++++++++++++++++++++++----------------------- 1 file changed, 286 insertions(+), 286 deletions(-) diff --git a/locales/nb_NO.json b/locales/nb_NO.json index d299da2f3..64b2337c1 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -1,288 +1,288 @@ { - "`x` subscribers": "`x` abonnenter", - "`x` videos": "`x` videoer", - "LIVE": "SANNTIDSVISNING", - "Shared `x` ago": "Delt for `x` siden", - "Unsubscribe": "Opphev abonnement", - "Subscribe": "Abonner", - "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", - "View channel on YouTube": "Vis kanal på YouTube", - "newest": "nyeste", - "oldest": "eldste", - "popular": "populært", - "Preview page": "Forhåndsvis side", - "Next page": "Neste side", - "Clear watch history?": "Tøm visningshistorikk?", - "Yes": "Ja", - "No": "Nei", - "Import and Export Data": "Importer- og eksporter data", - "Import": "Importer", - "Import Invidious data": "Importer Invidious-data", - "Import YouTube subscriptions": "Importer YouTube-abonnenter", - "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", - "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", - "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", - "Export": "Eksporter", - "Export subscriptions as OPML": "Eksporter abonnenter som OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", - "Export data as JSON": "Eksporter data som JSON", - "Delete account?": "Slett konto?", - "History": "Historikk", - "Previous page": "Forrige side", - "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", - "JavaScript license information": "JavaScript-lisensinformasjon", - "source": "kilde", - "Login": "Logg inn", - "Login/Register": "Logg inn/registrer", - "Login to Google": "Logg inn med Google", - "User ID:": "Bruker-ID:", - "Password:": "Passord:", - "Time (h:mm:ss):": "Tid (h:mm:ss):", - "Text CAPTCHA": "Tekst-CAPTCHA", - "Image CAPTCHA": "Bilde-CAPTCHA", - "Sign In": "Innlogging", - "Register": "Registrer", - "Email:": "E-post:", - "Google verification code:": "Google-bekreftelseskode:", - "Preferences": "Innstillinger", - "Player preferences": "Avspillerinnstillinger", - "Always loop: ": "Alltid gjenta: ", - "Autoplay: ": "Autoavspilling: ", - "Autoplay next video: ": "Autospill neste video: ", - "Listen by default: ": "Lytt som forvalg: ", - "Default speed: ": "Forvalgt hastighet: ", - "Preferred video quality: ": "Foretrukket videokvalitet: ", - "Player volume: ": "Avspillerlydstyrke: ", - "Default comments: ": "Forvalgte kommentarer: ", - "Default captions: ": "Forvalgte undertitler: ", - "Fallback captions: ": "Tilbakefallsundertitler: ", - "Show related videos? ": "Vis relaterte videoer? ", - "Visual preferences": "Visuelle innstillinger", - "Dark mode: ": "Mørk drakt: ", - "Thin mode: ": "Tynt modus: ", - "Subscription preferences": "Abonnementsinnstillinger", - "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", - "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", - "Sort videos by: ": "Sorter videoer etter: ", - "published": "publisert", - "published - reverse": "publisert - motsatt", - "alphabetically": "alfabetisk", - "alphabetically - reverse": "alfabetisk - motsatt", - "channel name": "kanalnavn", - "channel name - reverse": "kanalnavn - motsatt", - "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", - "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", - "Only show unwatched: ": "Kun vis usette: ", - "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", - "Data preferences": "Datainnstillinger", - "Clear watch history": "Tøm visningshistorikk", - "Import/Export data": "Importer/eksporter data", - "Manage subscriptions": "Behandle abonnementer", - "Watch history": "Visningshistorikk", - "Delete account": "Slett konto", - "Administrator preferences": "Administratorinnstillinger", - "Default homepage: ": "Forvalgt hjemmeside: ", - "Feed menu: ": "Flyt-meny: ", - "Top enabled? ": "", - "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", - "Login enabled? ": "Innlogging påskrudd? ", - "Registration enabled? ": "Registrering påskrudd? ", - "Report statistics? ": "", - "Save preferences": "Lagre innstillinger", - "Subscription manager": "Abonnementsbehandler", - "`x` subscriptions": "`x` abonnementer", - "Import/Export": "Importer/eksporter", - "unsubscribe": "opphev abonnement", - "Subscriptions": "Abonnement", - "`x` unseen notifications": "`x` usette merknader", - "search": "søk", - "Sign out": "Logg ut", - "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", - "Source available here.": "Kildekode tilgjengelig her.", - "View JavaScript license information.": "Vis JavaScript-lisensinfo.", - "Trending": "Trendsettende", - "Watch video on Youtube": "Vis video på YouTube", - "Genre: ": "Sjanger: ", - "License: ": "Lisens: ", - "Family friendly? ": "Familievennlig? ", - "Wilson score: ": "Wilson-poengsum: ", - "Engagement: ": "Engasjement: ", - "Whitelisted regions: ": "Hvitlistede regioner: ", - "Blacklisted regions: ": "Svartelistede regioner: ", - "Shared `x`": "Delt `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", - "View YouTube comments": "Vis YouTube-kommentarer", - "View more comments on Reddit": "Vis flere kommenterer på Reddit", - "View `x` comments": "Vis `x` kommentarer", - "View Reddit comments": "Vis Reddit-kommentarer", - "Hide replies": "Skjul svar", - "Show replies": "Vis svar", - "Incorrect password": "Feil passord", - "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", - "Invalid TFA code": "Ugyldig tofaktorkode", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", - "Invalid answer": "Ugyldig svar", - "Invalid CAPTCHA": "Ugyldig CAPTCHA", - "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", - "User ID is a required field": "Bruker-ID er et påkrevd felt", - "Password is a required field": "Passord er et påkrevd felt", - "Invalid username or password": "Ugyldig brukernavn eller passord", - "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", - "Password cannot be empty": "Passordet kan ikke være tomt", - "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", - "Please sign in": "Logg inn", - "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", - "channel:`x`": "kanal `x`", - "Deleted or invalid channel": "Slettet eller ugyldig kanal", - "This channel does not exist.": "Denne kanalen finnes ikke.", - "Could not get channel info.": "Kunne ikke innhente kanalinfo.", - "Could not fetch comments": "Kunne ikke hente kommentarer", - "View `x` replies": "Vis `x` svar", - "`x` ago": "`x` siden", - "Load more": "Last inn flere", - "`x` points": "`x` poeng", - "Could not create mix.": "Kunne ikke opprette miks.", - "Playlist is empty": "Spillelisten er tom", - "Invalid playlist.": "Ugyldig spilleliste.", - "Playlist does not exist.": "Spillelisten finnes ikke.", - "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", - "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", - "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", - "Invalid challenge": "Ugyldig utfordring", - "Invalid token": "Ugyldig symbol", - "Invalid user": "Ugyldig bruker", - "Token is expired, please try again": "Symbol utløpt, prøv igjen", - "English": "Engelsk", - "English (auto-generated)": "Engelsk (auto-generert)", - "Afrikaans": "", - "Albanian": "Albansk", - "Amharic": "", - "Arabic": "Arabisk", - "Armenian": "Armensk", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "Hviterussisk", - "Bosnian": "Bosnisk", - "Bulgarian": "Bulgarsk", - "Burmese": "Burmesisk", - "Catalan": "Katalansk", - "Cebuano": "", - "Chinese (Simplified)": "", - "Chinese (Traditional)": "", - "Corsican": "", - "Croatian": "", - "Czech": "Tsjekkisk", - "Danish": "Dansk", - "Dutch": "", - "Esperanto": "Esperanto", - "Estonian": "", - "Filipino": "", - "Finnish": "Finsk", - "French": "Fransk", - "Galician": "", - "Georgian": "", - "German": "", - "Greek": "", - "Gujarati": "", - "Haitian Creole": "", - "Hausa": "", - "Hawaiian": "", - "Hebrew": "", - "Hindi": "", - "Hmong": "", - "Hungarian": "Ungarsk", - "Icelandic": "Islandsk", - "Igbo": "", - "Indonesian": "Indonesisk", - "Irish": "Irsk", - "Italian": "Italiensk", - "Japanese": "Japansk", - "Javanese": "", - "Kannada": "", - "Kazakh": "", - "Khmer": "", - "Korean": "", - "Kurdish": "", - "Kyrgyz": "", - "Lao": "", - "Latin": "", - "Latvian": "", - "Lithuanian": "", - "Luxembourgish": "", - "Macedonian": "", - "Malagasy": "", - "Malay": "", - "Malayalam": "", - "Maltese": "", - "Maori": "", - "Marathi": "", - "Mongolian": "", - "Nepali": "", - "Norwegian": "Norsk bokmål", - "Nyanja": "", - "Pashto": "", - "Persian": "", - "Polish": "", - "Portuguese": "", - "Punjabi": "", - "Romanian": "", - "Russian": "Russisk", - "Samoan": "", - "Scottish Gaelic": "", - "Serbian": "Serbisk", - "Shona": "", - "Sindhi": "", - "Sinhala": "", - "Slovak": "Slovakisk", - "Slovenian": "Slovensk", - "Somali": "Somali", - "Southern Sotho": "", - "Spanish": "Spansk", - "Spanish (Latin America)": "", - "Sundanese": "", - "Swahili": "", - "Swedish": "Svensk", - "Tajik": "", - "Tamil": "", - "Telugu": "", - "Thai": "", - "Turkish": "Tyrkisk", - "Ukrainian": "Ukrainsk", - "Urdu": "", - "Uzbek": "", - "Vietnamese": "Vietnamesisk", - "Welsh": "", - "Western Frisian": "", - "Xhosa": "", - "Yiddish": "", - "Yoruba": "", - "Zulu": "", - "`x` years": "`x` år", - "`x` months": "`x` måneder", - "`x` weeks": "`x` uker", - "`x` days": "`x` dager", - "`x` hours": "`x` timer", - "`x` minutes": "`x` minutter", - "`x` seconds": "`x` sekunder", - "Fallback comments: ": "Tilbakefallskommentarer: ", - "Popular": "Pupulært", - "Top": "Topp", - "About": "Om", - "Rating: ": "Vurdering: ", - "Language: ": "Språk: ", - "Default": "Forvalg", - "Music": "Musikk", - "Gaming": "Spill", - "News": "Nyheter", - "Movies": "Filmer", - "Download": "Last ned", - "Download as: ": "Last ned som: ", - "%A %B %-d, %Y": "", - "(edited)": "(redigert)", - "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", - "`x` marked it with a ❤": "`x` levnet et ❤", - "Audio mode": "Lydmodus", - "Video mode": "Video-modus" + "`x` subscribers": "`x` abonnenter", + "`x` videos": "`x` videoer", + "LIVE": "SANNTIDSVISNING", + "Shared `x` ago": "Delt for `x` siden", + "Unsubscribe": "Opphev abonnement", + "Subscribe": "Abonner", + "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", + "View channel on YouTube": "Vis kanal på YouTube", + "newest": "nyeste", + "oldest": "eldste", + "popular": "populært", + "Preview page": "Forhåndsvis side", + "Next page": "Neste side", + "Clear watch history?": "Tøm visningshistorikk?", + "Yes": "Ja", + "No": "Nei", + "Import and Export Data": "Importer- og eksporter data", + "Import": "Importer", + "Import Invidious data": "Importer Invidious-data", + "Import YouTube subscriptions": "Importer YouTube-abonnenter", + "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", + "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", + "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", + "Export": "Eksporter", + "Export subscriptions as OPML": "Eksporter abonnenter som OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", + "Export data as JSON": "Eksporter data som JSON", + "Delete account?": "Slett konto?", + "History": "Historikk", + "Previous page": "Forrige side", + "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", + "JavaScript license information": "JavaScript-lisensinformasjon", + "source": "kilde", + "Login": "Logg inn", + "Login/Register": "Logg inn/registrer", + "Login to Google": "Logg inn med Google", + "User ID:": "Bruker-ID:", + "Password:": "Passord:", + "Time (h:mm:ss):": "Tid (h:mm:ss):", + "Text CAPTCHA": "Tekst-CAPTCHA", + "Image CAPTCHA": "Bilde-CAPTCHA", + "Sign In": "Innlogging", + "Register": "Registrer", + "Email:": "E-post:", + "Google verification code:": "Google-bekreftelseskode:", + "Preferences": "Innstillinger", + "Player preferences": "Avspillerinnstillinger", + "Always loop: ": "Alltid gjenta: ", + "Autoplay: ": "Autoavspilling: ", + "Autoplay next video: ": "Autospill neste video: ", + "Listen by default: ": "Lytt som forvalg: ", + "Default speed: ": "Forvalgt hastighet: ", + "Preferred video quality: ": "Foretrukket videokvalitet: ", + "Player volume: ": "Avspillerlydstyrke: ", + "Default comments: ": "Forvalgte kommentarer: ", + "Default captions: ": "Forvalgte undertitler: ", + "Fallback captions: ": "Tilbakefallsundertitler: ", + "Show related videos? ": "Vis relaterte videoer? ", + "Visual preferences": "Visuelle innstillinger", + "Dark mode: ": "Mørk drakt: ", + "Thin mode: ": "Tynt modus: ", + "Subscription preferences": "Abonnementsinnstillinger", + "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", + "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", + "Sort videos by: ": "Sorter videoer etter: ", + "published": "publisert", + "published - reverse": "publisert - motsatt", + "alphabetically": "alfabetisk", + "alphabetically - reverse": "alfabetisk - motsatt", + "channel name": "kanalnavn", + "channel name - reverse": "kanalnavn - motsatt", + "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", + "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", + "Only show unwatched: ": "Kun vis usette: ", + "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", + "Data preferences": "Datainnstillinger", + "Clear watch history": "Tøm visningshistorikk", + "Import/Export data": "Importer/eksporter data", + "Manage subscriptions": "Behandle abonnementer", + "Watch history": "Visningshistorikk", + "Delete account": "Slett konto", + "Administrator preferences": "Administratorinnstillinger", + "Default homepage: ": "Forvalgt hjemmeside: ", + "Feed menu: ": "Flyt-meny: ", + "Top enabled? ": "", + "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", + "Login enabled? ": "Innlogging påskrudd? ", + "Registration enabled? ": "Registrering påskrudd? ", + "Report statistics? ": "Innrapporter statistikk? ", + "Save preferences": "Lagre innstillinger", + "Subscription manager": "Abonnementsbehandler", + "`x` subscriptions": "`x` abonnementer", + "Import/Export": "Importer/eksporter", + "unsubscribe": "opphev abonnement", + "Subscriptions": "Abonnement", + "`x` unseen notifications": "`x` usette merknader", + "search": "søk", + "Sign out": "Logg ut", + "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", + "Source available here.": "Kildekode tilgjengelig her.", + "View JavaScript license information.": "Vis JavaScript-lisensinfo.", + "Trending": "Trendsettende", + "Watch video on Youtube": "Vis video på YouTube", + "Genre: ": "Sjanger: ", + "License: ": "Lisens: ", + "Family friendly? ": "Familievennlig? ", + "Wilson score: ": "Wilson-poengsum: ", + "Engagement: ": "Engasjement: ", + "Whitelisted regions: ": "Hvitlistede regioner: ", + "Blacklisted regions: ": "Svartelistede regioner: ", + "Shared `x`": "Delt `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", + "View YouTube comments": "Vis YouTube-kommentarer", + "View more comments on Reddit": "Vis flere kommenterer på Reddit", + "View `x` comments": "Vis `x` kommentarer", + "View Reddit comments": "Vis Reddit-kommentarer", + "Hide replies": "Skjul svar", + "Show replies": "Vis svar", + "Incorrect password": "Feil passord", + "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", + "Invalid TFA code": "Ugyldig tofaktorkode", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", + "Invalid answer": "Ugyldig svar", + "Invalid CAPTCHA": "Ugyldig CAPTCHA", + "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", + "User ID is a required field": "Bruker-ID er et påkrevd felt", + "Password is a required field": "Passord er et påkrevd felt", + "Invalid username or password": "Ugyldig brukernavn eller passord", + "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", + "Password cannot be empty": "Passordet kan ikke være tomt", + "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", + "Please sign in": "Logg inn", + "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", + "channel:`x`": "kanal `x`", + "Deleted or invalid channel": "Slettet eller ugyldig kanal", + "This channel does not exist.": "Denne kanalen finnes ikke.", + "Could not get channel info.": "Kunne ikke innhente kanalinfo.", + "Could not fetch comments": "Kunne ikke hente kommentarer", + "View `x` replies": "Vis `x` svar", + "`x` ago": "`x` siden", + "Load more": "Last inn flere", + "`x` points": "`x` poeng", + "Could not create mix.": "Kunne ikke opprette miks.", + "Playlist is empty": "Spillelisten er tom", + "Invalid playlist.": "Ugyldig spilleliste.", + "Playlist does not exist.": "Spillelisten finnes ikke.", + "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", + "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", + "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", + "Invalid challenge": "Ugyldig utfordring", + "Invalid token": "Ugyldig symbol", + "Invalid user": "Ugyldig bruker", + "Token is expired, please try again": "Symbol utløpt, prøv igjen", + "English": "Engelsk", + "English (auto-generated)": "Engelsk (auto-generert)", + "Afrikaans": "", + "Albanian": "Albansk", + "Amharic": "", + "Arabic": "Arabisk", + "Armenian": "Armensk", + "Azerbaijani": "", + "Bangla": "", + "Basque": "", + "Belarusian": "Hviterussisk", + "Bosnian": "Bosnisk", + "Bulgarian": "Bulgarsk", + "Burmese": "Burmesisk", + "Catalan": "Katalansk", + "Cebuano": "", + "Chinese (Simplified)": "", + "Chinese (Traditional)": "", + "Corsican": "", + "Croatian": "", + "Czech": "Tsjekkisk", + "Danish": "Dansk", + "Dutch": "", + "Esperanto": "Esperanto", + "Estonian": "", + "Filipino": "", + "Finnish": "Finsk", + "French": "Fransk", + "Galician": "", + "Georgian": "", + "German": "", + "Greek": "", + "Gujarati": "", + "Haitian Creole": "", + "Hausa": "", + "Hawaiian": "", + "Hebrew": "", + "Hindi": "", + "Hmong": "", + "Hungarian": "Ungarsk", + "Icelandic": "Islandsk", + "Igbo": "", + "Indonesian": "Indonesisk", + "Irish": "Irsk", + "Italian": "Italiensk", + "Japanese": "Japansk", + "Javanese": "", + "Kannada": "", + "Kazakh": "", + "Khmer": "", + "Korean": "", + "Kurdish": "", + "Kyrgyz": "", + "Lao": "", + "Latin": "", + "Latvian": "", + "Lithuanian": "", + "Luxembourgish": "", + "Macedonian": "", + "Malagasy": "", + "Malay": "", + "Malayalam": "", + "Maltese": "", + "Maori": "", + "Marathi": "", + "Mongolian": "", + "Nepali": "", + "Norwegian": "Norsk bokmål", + "Nyanja": "", + "Pashto": "", + "Persian": "", + "Polish": "", + "Portuguese": "", + "Punjabi": "", + "Romanian": "", + "Russian": "Russisk", + "Samoan": "", + "Scottish Gaelic": "", + "Serbian": "Serbisk", + "Shona": "", + "Sindhi": "", + "Sinhala": "", + "Slovak": "Slovakisk", + "Slovenian": "Slovensk", + "Somali": "Somali", + "Southern Sotho": "", + "Spanish": "Spansk", + "Spanish (Latin America)": "", + "Sundanese": "", + "Swahili": "", + "Swedish": "Svensk", + "Tajik": "", + "Tamil": "", + "Telugu": "", + "Thai": "", + "Turkish": "Tyrkisk", + "Ukrainian": "Ukrainsk", + "Urdu": "", + "Uzbek": "", + "Vietnamese": "Vietnamesisk", + "Welsh": "", + "Western Frisian": "", + "Xhosa": "", + "Yiddish": "", + "Yoruba": "", + "Zulu": "", + "`x` years": "`x` år", + "`x` months": "`x` måneder", + "`x` weeks": "`x` uker", + "`x` days": "`x` dager", + "`x` hours": "`x` timer", + "`x` minutes": "`x` minutter", + "`x` seconds": "`x` sekunder", + "Fallback comments: ": "Tilbakefallskommentarer: ", + "Popular": "Pupulært", + "Top": "Topp", + "About": "Om", + "Rating: ": "Vurdering: ", + "Language: ": "Språk: ", + "Default": "Forvalg", + "Music": "Musikk", + "Gaming": "Spill", + "News": "Nyheter", + "Movies": "Filmer", + "Download": "Last ned", + "Download as: ": "Last ned som: ", + "%A %B %-d, %Y": "", + "(edited)": "(redigert)", + "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", + "`x` marked it with a ❤": "`x` levnet et ❤", + "Audio mode": "Lydmodus", + "Video mode": "Video-modus" } From e96c4732d64fb85acbc9b884604bbefba3a51137 Mon Sep 17 00:00:00 2001 From: dimqua Date: Mon, 4 Mar 2019 21:24:56 +0000 Subject: [PATCH 29/81] Update Russian translation --- locales/ru.json | 584 ++++++++++++++++++++++++------------------------ 1 file changed, 292 insertions(+), 292 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index a840a869b..0c6665892 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,294 +1,294 @@ { - "`x` subscribers": "`x` подписчиков", - "`x` videos": "`x` видео", - "LIVE": "ПРЯМОЙ ЭФИР", - "Shared `x` ago": "Опубликовано `x` назад", - "Unsubscribe": "Отписаться", - "Subscribe": "Подписаться", - "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", - "View channel on YouTube": "Канал на YouTube", - "newest": "новые", - "oldest": "старые", - "popular": "популярные", - "Preview page": "Предварительный просмотр", - "Next page": "Следующая страница", - "Clear watch history?": "Очистить историю просмотров?", - "Yes": "Да", - "No": "Нет", - "Import and Export Data": "Импорт и экспорт данных", - "Import": "Импорт", - "Import Invidious data": "Импортировать данные Invidious", - "Import YouTube subscriptions": "Импортировать YouTube подписки", - "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", - "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", - "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", - "Export": "Экспорт", - "Export subscriptions as OPML": "Экспортировать подписки в OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", - "Export data as JSON": "Экспортировать данные в JSON", - "Delete account?": "Удалить аккаунт?", - "History": "История", - "Previous page": "Предыдущая страница", - "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", - "JavaScript license information": "Лицензии JavaScript", - "source": "источник", - "Login": "Войти", - "Login/Register": "Войти/Регистрация", - "Login to Google": "Войти через Google", - "User ID:": "ID пользователя:", - "Password:": "Пароль:", - "Time (h:mm:ss):": "Время (ч:мм:сс):", - "Text CAPTCHA": "Текст капчи", - "Image CAPTCHA": "Изображение капчи", - "Sign In": "Войти", - "Register": "Регистрация", - "Email:": "Эл. почта:", - "Google verification code:": "Код подтверждения Google:", - "Preferences": "Настройки", - "Player preferences": "Настройки проигрывателя", - "Always loop: ": "Всегда повторять: ", - "Autoplay: ": "Автовоспроизведение: ", - "Autoplay next video: ": "Автовоспроизведение следующего видео: ", - "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", - "Default speed: ": "Скорость по-умолчанию: ", - "Preferred video quality: ": "Предпочтительное качество видео: ", - "Player volume: ": "Громкость воспроизведения: ", - "Default comments: ": "Источник комментариев: ", - "youtube": "YouTube", - "reddit": "Reddit", - "Default captions: ": "Субтитры по-умолчанию: ", - "Fallback captions: ": "Резервные субтитры: ", - "Show related videos? ": "Показывать похожие видео? ", - "Visual preferences": "Визуальные настройки", - "Dark mode: ": "Темная тема: ", - "Thin mode: ": "Облегченный режим: ", - "Subscription preferences": "Настройки подписок", - "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", - "Number of videos shown in feed: ": "Число видео в ленте: ", - "Sort videos by: ": "Сортировать видео по: ", - "published": "дате публикации", - "published - reverse": "дате - обратный порядок", - "alphabetically": "алфавиту", - "alphabetically - reverse": "алфавиту - обратный порядок", - "channel name": "имени канала", - "channel name - reverse": "имени канала - обратный порядок", - "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", - "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", - "Only show unwatched: ": "Отображать только непросмотренные видео: ", - "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", - "Data preferences": "Настройки данных", - "Clear watch history": "Очистить историю просмотра", - "Import/Export data": "Импорт/Экспорт данных", - "Manage subscriptions": "Управление подписками", - "Watch history": "История просмотров", - "Delete account": "Удалить аккаунт", - "Administrator preferences": "", - "Default homepage: ": "", - "Feed menu: ": "", - "Top enabled? ": "", - "CAPTCHA enabled? ": "", - "Login enabled? ": "", - "Registration enabled? ": "", - "Report statistics? ": "", - "Save preferences": "Сохранить настройки", - "Subscription manager": "Менеджер подписок", - "`x` subscriptions": "`x` подписок", - "Import/Export": "Импорт/Экспорт", - "unsubscribe": "отписаться", - "Subscriptions": "Подписки", - "`x` unseen notifications": "`x` новых оповещений", - "search": "поиск", - "Sign out": "Выйти", - "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", - "Source available here.": "Исходный код доступен здесь.", - "Liberapay: ": "Liberapay: ", - "Patreon: ": "Patreon: ", - "BTC: ": "BTC: ", - "BCH: ": "BCH: ", - "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", - "Trending": "В тренде", - "Watch video on Youtube": "Смотреть на YouTube", - "Genre: ": "Жанр: ", - "License: ": "Лицензия: ", - "Family friendly? ": "Семейный просмотр: ", - "Wilson score: ": "Рейтинг Вильсона: ", - "Engagement: ": "Вовлеченность: ", - "Whitelisted regions: ": "Доступно для: ", - "Blacklisted regions: ": "Недоступно для: ", - "Shared `x`": "Опубликовано `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", - "View YouTube comments": "Смотреть комментарии с YouTube", - "View more comments on Reddit": "Больше комментариев на Reddit", - "View `x` comments": "Показать `x` комментариев", - "View Reddit comments": "Смотреть комментарии с Reddit", - "Hide replies": "Скрыть ответы", - "Show replies": "Показать ответы", - "Incorrect password": "Неправильный пароль", - "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", - "Invalid TFA code": "Неправильный TFA код", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", - "Invalid answer": "Неверный ответ", - "Invalid CAPTCHA": "Неверная капча", - "CAPTCHA is a required field": "Необходимо ввести капчу", - "User ID is a required field": "Необходимо ввести идентификатор пользователя", - "Password is a required field": "Необходимо ввести пароль", - "Invalid username or password": "Недопустимый пароль или имя пользователя", - "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", - "Password cannot be empty": "Пароль не может быть пустым", - "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", - "Please sign in": "Пожалуйста, войдите", - "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", - "channel:`x`": "канал: `x`", - "Deleted or invalid channel": "Канал удален или не найден", - "This channel does not exist.": "Такой канал не существует.", - "Could not get channel info.": "Невозможно получить информацию о канале.", - "Could not fetch comments": "Невозможно получить комментарии", - "View `x` replies": "Показать `x` ответов", - "`x` ago": "`x` назад", - "Load more": "Загрузить больше", - "`x` points": "`x` очков", - "Could not create mix.": "Невозможно создать \"микс\".", - "Playlist is empty": "Плейлист пуст", - "Invalid playlist.": "Некорректный плейлист.", - "Playlist does not exist.": "Плейлист не существует.", - "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", - "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", - "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", - "Invalid challenge": "Неправильный ответ в \"challenge\"", - "Invalid token": "Неправильный токен", - "Invalid user": "Недопустимое имя пользователя", - "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", - "English": "Английский", - "English (auto-generated)": "Английский (созданы автоматически)", - "Afrikaans": "Африкаанс", - "Albanian": "Албанский", - "Amharic": "Амхарский", - "Arabic": "Арабский", - "Armenian": "Армянский", - "Azerbaijani": "Азербайджанский", - "Bangla": "Бенгальский", - "Basque": "Баскский", - "Belarusian": "Белорусский", - "Bosnian": "Боснийский", - "Bulgarian": "Болгарский", - "Burmese": "Бирманский", - "Catalan": "Каталонский", - "Cebuano": "Себуанский", - "Chinese (Simplified)": "Китайский (упрощенный)", - "Chinese (Traditional)": "Китайский (традиционный)", - "Corsican": "Корсиканский", - "Croatian": "Хорватский", - "Czech": "Чешский", - "Danish": "Датский", - "Dutch": "Нидерландский", - "Esperanto": "Эсперанто", - "Estonian": "Эстонский", - "Filipino": "Филиппинский", - "Finnish": "Финский", - "French": "Французский", - "Galician": "Галисийский", - "Georgian": "Грузинский", - "German": "Немецкий", - "Greek": "Греческий", - "Gujarati": "Гуджаратский", - "Haitian Creole": "Гаит. креольский", - "Hausa": "Хауса", - "Hawaiian": "Гавайский", - "Hebrew": "Иврит", - "Hindi": "Хинди", - "Hmong": "Хмонг (мяо)", - "Hungarian": "Венгерский", - "Icelandic": "Исландский", - "Igbo": "Игбо", - "Indonesian": "Индонезийский", - "Irish": "Ирландский", - "Italian": "Итальянский", - "Japanese": "Японский", - "Javanese": "Яванский", - "Kannada": "Каннада", - "Kazakh": "Казахский", - "Khmer": "Кхмерский", - "Korean": "Корейский", - "Kurdish": "Курдский", - "Kyrgyz": "Киргизский", - "Lao": "Лаосский", - "Latin": "Латинский", - "Latvian": "Латышский", - "Lithuanian": "Литовский", - "Luxembourgish": "Люксембургский", - "Macedonian": "Македонский", - "Malagasy": "Малагасийский", - "Malay": "Малайский", - "Malayalam": "Малаялам", - "Maltese": "Мальтийский", - "Maori": "Маори", - "Marathi": "Маратхи", - "Mongolian": "Монгольская", - "Nepali": "Непальский", - "Norwegian": "Норвежский", - "Nyanja": "Ньянджа", - "Pashto": "Пушту", - "Persian": "Персидский", - "Polish": "Польский", - "Portuguese": "Португальский", - "Punjabi": "Панджаби", - "Romanian": "Румынский", - "Russian": "Русский", - "Samoan": "Самоанский", - "Scottish Gaelic": "Шотландский (гэльский)", - "Serbian": "Сербский", - "Shona": "Шона", - "Sindhi": "Синдхи", - "Sinhala": "Сингальский", - "Slovak": "Словацкий", - "Slovenian": "Словенский", - "Somali": "Сомалийский", - "Southern Sotho": "Сесото (южный сото)", - "Spanish": "Испанский", - "Spanish (Latin America)": "Испанский (Латинская Америка)", - "Sundanese": "Сунданский", - "Swahili": "Суахили", - "Swedish": "Шведский", - "Tajik": "Таджикский", - "Tamil": "Тамильский", - "Telugu": "Телугу", - "Thai": "Тайский", - "Turkish": "Турецкий", - "Ukrainian": "Украинский", - "Urdu": "Урду", - "Uzbek": "Узбекский", - "Vietnamese": "Вьетнамский", - "Welsh": "Валлийский", - "Western Frisian": "Западнофризский", - "Xhosa": "Коса", - "Yiddish": "Идиш", - "Yoruba": "Йоруба", - "Zulu": "Зулусский", - "`x` years": "`x` лет", - "`x` months": "`x` месяцев", - "`x` weeks": "`x` недель", - "`x` days": "`x` дней", - "`x` hours": "`x` часов", - "`x` minutes": "`x` минут", - "`x` seconds": "`x` секунд", - "Fallback comments: ": "Резервные комментарии: ", - "Popular": "Популярное", - "Top": "Топ", - "About": "О сайте", - "Rating: ": "Рейтинг: ", - "Language: ": "Язык: ", - "Default": "По-умолчанию", - "Music": "Музыка", - "Gaming": "Игры", - "News": "Новости", - "Movies": "Фильмы", - "Download": "Скачать", - "Download as: ": "Скачать как: ", - "%A %B %-d, %Y": "%-d %B %Y, %A", - "(edited)": "(изменено)", - "Youtube permalink of the comment": "Прямая ссылка на YouTube", - "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", - "Audio mode": "Аудио режим", - "Video mode": "Видео режим" + "`x` subscribers": "`x` подписчиков", + "`x` videos": "`x` видео", + "LIVE": "ПРЯМОЙ ЭФИР", + "Shared `x` ago": "Опубликовано `x` назад", + "Unsubscribe": "Отписаться", + "Subscribe": "Подписаться", + "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", + "View channel on YouTube": "Канал на YouTube", + "newest": "новые", + "oldest": "старые", + "popular": "популярные", + "Preview page": "Предварительный просмотр", + "Next page": "Следующая страница", + "Clear watch history?": "Очистить историю просмотров?", + "Yes": "Да", + "No": "Нет", + "Import and Export Data": "Импорт и экспорт данных", + "Import": "Импорт", + "Import Invidious data": "Импортировать данные Invidious", + "Import YouTube subscriptions": "Импортировать YouTube подписки", + "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", + "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", + "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", + "Export": "Экспорт", + "Export subscriptions as OPML": "Экспортировать подписки в OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", + "Export data as JSON": "Экспортировать данные в JSON", + "Delete account?": "Удалить аккаунт?", + "History": "История", + "Previous page": "Предыдущая страница", + "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", + "JavaScript license information": "Лицензии JavaScript", + "source": "источник", + "Login": "Войти", + "Login/Register": "Войти/Регистрация", + "Login to Google": "Войти через Google", + "User ID:": "ID пользователя:", + "Password:": "Пароль:", + "Time (h:mm:ss):": "Время (ч:мм:сс):", + "Text CAPTCHA": "Текст капчи", + "Image CAPTCHA": "Изображение капчи", + "Sign In": "Войти", + "Register": "Регистрация", + "Email:": "Эл. почта:", + "Google verification code:": "Код подтверждения Google:", + "Preferences": "Настройки", + "Player preferences": "Настройки проигрывателя", + "Always loop: ": "Всегда повторять: ", + "Autoplay: ": "Автовоспроизведение: ", + "Autoplay next video: ": "Автовоспроизведение следующего видео: ", + "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", + "Default speed: ": "Скорость по-умолчанию: ", + "Preferred video quality: ": "Предпочтительное качество видео: ", + "Player volume: ": "Громкость воспроизведения: ", + "Default comments: ": "Источник комментариев: ", + "youtube": "YouTube", + "reddit": "Reddit", + "Default captions: ": "Субтитры по-умолчанию: ", + "Fallback captions: ": "Резервные субтитры: ", + "Show related videos? ": "Показывать похожие видео? ", + "Visual preferences": "Визуальные настройки", + "Dark mode: ": "Темная тема: ", + "Thin mode: ": "Облегченный режим: ", + "Subscription preferences": "Настройки подписок", + "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", + "Number of videos shown in feed: ": "Число видео в ленте: ", + "Sort videos by: ": "Сортировать видео по: ", + "published": "дате публикации", + "published - reverse": "дате - обратный порядок", + "alphabetically": "алфавиту", + "alphabetically - reverse": "алфавиту - обратный порядок", + "channel name": "имени канала", + "channel name - reverse": "имени канала - обратный порядок", + "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", + "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", + "Only show unwatched: ": "Отображать только непросмотренные видео: ", + "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", + "Data preferences": "Настройки данных", + "Clear watch history": "Очистить историю просмотра", + "Import/Export data": "Импорт/Экспорт данных", + "Manage subscriptions": "Управление подписками", + "Watch history": "История просмотров", + "Delete account": "Удалить аккаунт", + "Administrator preferences": "Настройки администратора", + "Default homepage: ": "Главная страница по умолчанию: ", + "Feed menu: ": "Меню ленты: ", + "Top enabled? ": "Включить ТОП? ", + "CAPTCHA enabled? ": "Включить капчу? ", + "Login enabled? ": "Включить логин? ", + "Registration enabled? ": "Включить регистрацию? ", + "Report statistics? ": "Отображать статистику? ", + "Save preferences": "Сохранить настройки", + "Subscription manager": "Менеджер подписок", + "`x` subscriptions": "`x` подписок", + "Import/Export": "Импорт/Экспорт", + "unsubscribe": "отписаться", + "Subscriptions": "Подписки", + "`x` unseen notifications": "`x` новых оповещений", + "search": "поиск", + "Sign out": "Выйти", + "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", + "Source available here.": "Исходный код доступен здесь.", + "Liberapay: ": "Liberapay: ", + "Patreon: ": "Patreon: ", + "BTC: ": "BTC: ", + "BCH: ": "BCH: ", + "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", + "Trending": "В тренде", + "Watch video on Youtube": "Смотреть на YouTube", + "Genre: ": "Жанр: ", + "License: ": "Лицензия: ", + "Family friendly? ": "Семейный просмотр: ", + "Wilson score: ": "Рейтинг Вильсона: ", + "Engagement: ": "Вовлеченность: ", + "Whitelisted regions: ": "Доступно для: ", + "Blacklisted regions: ": "Недоступно для: ", + "Shared `x`": "Опубликовано `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", + "View YouTube comments": "Смотреть комментарии с YouTube", + "View more comments on Reddit": "Больше комментариев на Reddit", + "View `x` comments": "Показать `x` комментариев", + "View Reddit comments": "Смотреть комментарии с Reddit", + "Hide replies": "Скрыть ответы", + "Show replies": "Показать ответы", + "Incorrect password": "Неправильный пароль", + "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", + "Invalid TFA code": "Неправильный TFA код", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", + "Invalid answer": "Неверный ответ", + "Invalid CAPTCHA": "Неверная капча", + "CAPTCHA is a required field": "Необходимо ввести капчу", + "User ID is a required field": "Необходимо ввести идентификатор пользователя", + "Password is a required field": "Необходимо ввести пароль", + "Invalid username or password": "Недопустимый пароль или имя пользователя", + "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", + "Password cannot be empty": "Пароль не может быть пустым", + "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", + "Please sign in": "Пожалуйста, войдите", + "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", + "channel:`x`": "канал: `x`", + "Deleted or invalid channel": "Канал удален или не найден", + "This channel does not exist.": "Такой канал не существует.", + "Could not get channel info.": "Невозможно получить информацию о канале.", + "Could not fetch comments": "Невозможно получить комментарии", + "View `x` replies": "Показать `x` ответов", + "`x` ago": "`x` назад", + "Load more": "Загрузить больше", + "`x` points": "`x` очков", + "Could not create mix.": "Невозможно создать \"микс\".", + "Playlist is empty": "Плейлист пуст", + "Invalid playlist.": "Некорректный плейлист.", + "Playlist does not exist.": "Плейлист не существует.", + "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", + "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", + "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", + "Invalid challenge": "Неправильный ответ в \"challenge\"", + "Invalid token": "Неправильный токен", + "Invalid user": "Недопустимое имя пользователя", + "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", + "English": "Английский", + "English (auto-generated)": "Английский (созданы автоматически)", + "Afrikaans": "Африкаанс", + "Albanian": "Албанский", + "Amharic": "Амхарский", + "Arabic": "Арабский", + "Armenian": "Армянский", + "Azerbaijani": "Азербайджанский", + "Bangla": "Бенгальский", + "Basque": "Баскский", + "Belarusian": "Белорусский", + "Bosnian": "Боснийский", + "Bulgarian": "Болгарский", + "Burmese": "Бирманский", + "Catalan": "Каталонский", + "Cebuano": "Себуанский", + "Chinese (Simplified)": "Китайский (упрощенный)", + "Chinese (Traditional)": "Китайский (традиционный)", + "Corsican": "Корсиканский", + "Croatian": "Хорватский", + "Czech": "Чешский", + "Danish": "Датский", + "Dutch": "Нидерландский", + "Esperanto": "Эсперанто", + "Estonian": "Эстонский", + "Filipino": "Филиппинский", + "Finnish": "Финский", + "French": "Французский", + "Galician": "Галисийский", + "Georgian": "Грузинский", + "German": "Немецкий", + "Greek": "Греческий", + "Gujarati": "Гуджаратский", + "Haitian Creole": "Гаит. креольский", + "Hausa": "Хауса", + "Hawaiian": "Гавайский", + "Hebrew": "Иврит", + "Hindi": "Хинди", + "Hmong": "Хмонг (мяо)", + "Hungarian": "Венгерский", + "Icelandic": "Исландский", + "Igbo": "Игбо", + "Indonesian": "Индонезийский", + "Irish": "Ирландский", + "Italian": "Итальянский", + "Japanese": "Японский", + "Javanese": "Яванский", + "Kannada": "Каннада", + "Kazakh": "Казахский", + "Khmer": "Кхмерский", + "Korean": "Корейский", + "Kurdish": "Курдский", + "Kyrgyz": "Киргизский", + "Lao": "Лаосский", + "Latin": "Латинский", + "Latvian": "Латышский", + "Lithuanian": "Литовский", + "Luxembourgish": "Люксембургский", + "Macedonian": "Македонский", + "Malagasy": "Малагасийский", + "Malay": "Малайский", + "Malayalam": "Малаялам", + "Maltese": "Мальтийский", + "Maori": "Маори", + "Marathi": "Маратхи", + "Mongolian": "Монгольская", + "Nepali": "Непальский", + "Norwegian": "Норвежский", + "Nyanja": "Ньянджа", + "Pashto": "Пушту", + "Persian": "Персидский", + "Polish": "Польский", + "Portuguese": "Португальский", + "Punjabi": "Панджаби", + "Romanian": "Румынский", + "Russian": "Русский", + "Samoan": "Самоанский", + "Scottish Gaelic": "Шотландский (гэльский)", + "Serbian": "Сербский", + "Shona": "Шона", + "Sindhi": "Синдхи", + "Sinhala": "Сингальский", + "Slovak": "Словацкий", + "Slovenian": "Словенский", + "Somali": "Сомалийский", + "Southern Sotho": "Сесото (южный сото)", + "Spanish": "Испанский", + "Spanish (Latin America)": "Испанский (Латинская Америка)", + "Sundanese": "Сунданский", + "Swahili": "Суахили", + "Swedish": "Шведский", + "Tajik": "Таджикский", + "Tamil": "Тамильский", + "Telugu": "Телугу", + "Thai": "Тайский", + "Turkish": "Турецкий", + "Ukrainian": "Украинский", + "Urdu": "Урду", + "Uzbek": "Узбекский", + "Vietnamese": "Вьетнамский", + "Welsh": "Валлийский", + "Western Frisian": "Западнофризский", + "Xhosa": "Коса", + "Yiddish": "Идиш", + "Yoruba": "Йоруба", + "Zulu": "Зулусский", + "`x` years": "`x` лет", + "`x` months": "`x` месяцев", + "`x` weeks": "`x` недель", + "`x` days": "`x` дней", + "`x` hours": "`x` часов", + "`x` minutes": "`x` минут", + "`x` seconds": "`x` секунд", + "Fallback comments: ": "Резервные комментарии: ", + "Popular": "Популярное", + "Top": "Топ", + "About": "О сайте", + "Rating: ": "Рейтинг: ", + "Language: ": "Язык: ", + "Default": "По-умолчанию", + "Music": "Музыка", + "Gaming": "Игры", + "News": "Новости", + "Movies": "Фильмы", + "Download": "Скачать", + "Download as: ": "Скачать как: ", + "%A %B %-d, %Y": "%-d %B %Y, %A", + "(edited)": "(изменено)", + "Youtube permalink of the comment": "Прямая ссылка на YouTube", + "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", + "Audio mode": "Аудио режим", + "Video mode": "Видео режим" } From 8f41130a147d8970df0526d2383b6f2dbda905bb Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 8 Mar 2019 22:23:17 -0600 Subject: [PATCH 30/81] Update and add missing text to locales --- locales/ar.json | 13 +- locales/de.json | 13 +- locales/en-US.json | 9 +- locales/eu.json | 9 +- locales/fr.json | 8 +- locales/it.json | 8 +- locales/nb_NO.json | 575 +++++++++++++++--------------- locales/nl.json | 9 +- locales/pl.json | 9 +- locales/ru.json | 583 +++++++++++++++---------------- src/invidious/views/template.ecr | 8 +- 11 files changed, 632 insertions(+), 612 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index e8b59c46b..4f43675fb 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -10,8 +10,9 @@ "newest": "الأجدد", "oldest": "الأقدم", "popular": "الاكثر شعبية", - "Preview page": "معاينة الصفحة", + "last": "", "Next page": "الصفحة الثانية", + "Previous page": "الصفحة السابقة", "Clear watch history?": "مسح السجل ؟", "Yes": "نعم", "No": "لا", @@ -28,7 +29,6 @@ "Export data as JSON": "استخراج البيانات كـ JSON", "Delete account?": "حذف الحساب ؟", "History": "السجل", - "Previous page": "الصفحة السابقة", "An alternative front-end to YouTube": "البديل الكامل لموقع يوتيوب", "JavaScript license information": "معلومات ترخيص JavaScript", "source": "المصدر", @@ -101,10 +101,6 @@ "Sign out": "تسجيل الخروج", "Released under the AGPLv3 by Omar Roth.": "تم الإنشاء تحت AGPLv3 بواسطة عمر روث.", "Source available here.": "الأكواد متوفرة هنا.", - "Liberapay: ": "ليبرباى: ", - "Patreon: ": "باتريون: ", - "BTC: ": "بيتكوين: ", - "BCH: ": "بيتكوين كاش: ", "View JavaScript license information.": "مشاهدة معلومات حول تراخيص الجافاسكريبت.", "Trending": "الشائع", "Watch video on Youtube": "مشاهدة الفيديو على اليوتيوب", @@ -290,5 +286,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/de.json b/locales/de.json index e40c0b31a..d66ebee33 100644 --- a/locales/de.json +++ b/locales/de.json @@ -10,8 +10,9 @@ "newest": "neueste", "oldest": "älteste", "popular": "beliebt", - "Preview page": "Vorschau Seite", + "last": "", "Next page": "Nächste Seite", + "Previous page": "Vorherige Seite", "Clear watch history?": "Verlauf löschen?", "Yes": "Ja", "No": "Nein", @@ -28,7 +29,6 @@ "Export data as JSON": "Daten als JSON exportieren", "Delete account?": "Account löschen?", "History": "Verlauf", - "Previous page": "Vorherige Seite", "An alternative front-end to YouTube": "Eine alternative Oberfläche für YouTube", "JavaScript license information": "JavaScript Lizenzinformationen", "source": "Quelle", @@ -101,10 +101,6 @@ "Sign out": "Abmelden", "Released under the AGPLv3 by Omar Roth.": "Veröffentlicht unter AGPLv3 von Omar Roth.", "Source available here.": "Quellcode verfügbar hier.", - "Liberapay: ": "Liberapay: ", - "Patreon: ": "Patreon: ", - "BTC: ": "BTC: ", - "BCH: ": "BCH: ", "View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.", "Trending": "Trending", "Watch video on Youtube": "Video auf YouTube ansehen", @@ -290,5 +286,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/en-US.json b/locales/en-US.json index 1848ff208..6a05d1bbf 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -10,8 +10,9 @@ "newest": "newest", "oldest": "oldest", "popular": "popular", - "Preview page": "Preview page", + "last": "last", "Next page": "Next page", + "Previous page": "Previous page", "Clear watch history?": "Clear watch history?", "Yes": "Yes", "No": "No", @@ -28,7 +29,6 @@ "Export data as JSON": "Export data as JSON", "Delete account?": "Delete account?", "History": "History", - "Previous page": "Previous page", "An alternative front-end to YouTube": "An alternative front-end to YouTube", "JavaScript license information": "JavaScript license information", "source": "source", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "Youtube permalink of the comment", "`x` marked it with a ❤": "`x` marked it with a ❤", "Audio mode": "Audio mode", - "Video mode": "Video mode" + "Video mode": "Video mode", + "Videos": "Videos", + "Playlists": "Playlists", + "Current version: ": "Current version: " } diff --git a/locales/eu.json b/locales/eu.json index 8f887f715..1e4ad1d4c 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -10,8 +10,9 @@ "newest": "berrienak", "oldest": "zaharrenak", "popular": "ospetsuenak", - "Preview page": "Aurrebista orria", + "last": "", "Next page": "Hurrengo orria", + "Previous page": "Aurreko orria", "Clear watch history?": "Garbitu ikusitakoen historia?", "Yes": "Bai", "No": "Ez", @@ -28,7 +29,6 @@ "Export data as JSON": "Datuak JSON bezala esportatu", "Delete account?": "Kontua ezabatu?", "History": "Historia", - "Previous page": "Aurreko orria", "An alternative front-end to YouTube": "YouTuberako interfaze alternatibo bat", "JavaScript license information": "JavaScript lizentzia informazioa", "source": "iturburua", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/fr.json b/locales/fr.json index f5c34a6e0..28f0b579f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -10,7 +10,9 @@ "newest": "Date d'ajout (la plus récente)", "oldest": "Date d'ajout (la plus ancienne)", "popular": "Les plus populaires", + "last": "", "Next page": "Page suivante", + "Previous page": "Page précédente", "Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?", "Yes": "Oui", "No": "Non", @@ -27,7 +29,6 @@ "Export data as JSON": "Exporter les données au format JSON", "Delete account?": "Supprimer votre compte ?", "History": "Historique", - "Previous page": "Page précédente", "An alternative front-end to YouTube": "Un front-end alternatif à YouTube", "JavaScript license information": "Informations sur les licences JavaScript", "source": "source", @@ -283,5 +284,8 @@ "Youtube permalink of the comment": "Lien YouTube permanent vers le commentaire", "`x` marked it with a ❤": "`x` l'a marqué d'un ❤", "Audio mode": "Mode Audio", - "Video mode": "Mode Vidéo" + "Video mode": "Mode Vidéo", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/it.json b/locales/it.json index eeae6ed31..42dabf6db 100644 --- a/locales/it.json +++ b/locales/it.json @@ -10,7 +10,9 @@ "newest": "Data di aggiunta (più recente)", "oldest": "Data di aggiunta (più vecchia)", "popular": "Tendenze", + "last": "", "Next page": "Pagina successiva", + "Previous page": "Pagina precedente", "Clear watch history?": "Sei sicuro di voler cancellare la cronologia dei video guardati?", "Yes": "Si", "No": "No", @@ -27,7 +29,6 @@ "Export data as JSON": "Esporta i dati in formato JSON", "Delete account?": "Sei sicuro di voler cancellare l'account?", "History": "Cronologia", - "Previous page": "Pagina precedente", "An alternative front-end to YouTube": "Un'interfaccia alternativa per YouTube", "JavaScript license information": "Info licenze JavaScript", "source": "sorgente", @@ -283,5 +284,8 @@ "Youtube permalink of the comment": "Link permanente al commento di YouTube", "`x` marked it with a ❤": "`x` l'ha contrassegnato con un ❤", "Audio mode": "Modalità audio", - "Video mode": "Modalità video" + "Video mode": "Modalità video", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/nb_NO.json b/locales/nb_NO.json index 64b2337c1..f0f544b51 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -1,288 +1,291 @@ { - "`x` subscribers": "`x` abonnenter", - "`x` videos": "`x` videoer", - "LIVE": "SANNTIDSVISNING", - "Shared `x` ago": "Delt for `x` siden", - "Unsubscribe": "Opphev abonnement", - "Subscribe": "Abonner", - "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", - "View channel on YouTube": "Vis kanal på YouTube", - "newest": "nyeste", - "oldest": "eldste", - "popular": "populært", - "Preview page": "Forhåndsvis side", - "Next page": "Neste side", - "Clear watch history?": "Tøm visningshistorikk?", - "Yes": "Ja", - "No": "Nei", - "Import and Export Data": "Importer- og eksporter data", - "Import": "Importer", - "Import Invidious data": "Importer Invidious-data", - "Import YouTube subscriptions": "Importer YouTube-abonnenter", - "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", - "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", - "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", - "Export": "Eksporter", - "Export subscriptions as OPML": "Eksporter abonnenter som OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", - "Export data as JSON": "Eksporter data som JSON", - "Delete account?": "Slett konto?", - "History": "Historikk", - "Previous page": "Forrige side", - "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", - "JavaScript license information": "JavaScript-lisensinformasjon", - "source": "kilde", - "Login": "Logg inn", - "Login/Register": "Logg inn/registrer", - "Login to Google": "Logg inn med Google", - "User ID:": "Bruker-ID:", - "Password:": "Passord:", - "Time (h:mm:ss):": "Tid (h:mm:ss):", - "Text CAPTCHA": "Tekst-CAPTCHA", - "Image CAPTCHA": "Bilde-CAPTCHA", - "Sign In": "Innlogging", - "Register": "Registrer", - "Email:": "E-post:", - "Google verification code:": "Google-bekreftelseskode:", - "Preferences": "Innstillinger", - "Player preferences": "Avspillerinnstillinger", - "Always loop: ": "Alltid gjenta: ", - "Autoplay: ": "Autoavspilling: ", - "Autoplay next video: ": "Autospill neste video: ", - "Listen by default: ": "Lytt som forvalg: ", - "Default speed: ": "Forvalgt hastighet: ", - "Preferred video quality: ": "Foretrukket videokvalitet: ", - "Player volume: ": "Avspillerlydstyrke: ", - "Default comments: ": "Forvalgte kommentarer: ", - "Default captions: ": "Forvalgte undertitler: ", - "Fallback captions: ": "Tilbakefallsundertitler: ", - "Show related videos? ": "Vis relaterte videoer? ", - "Visual preferences": "Visuelle innstillinger", - "Dark mode: ": "Mørk drakt: ", - "Thin mode: ": "Tynt modus: ", - "Subscription preferences": "Abonnementsinnstillinger", - "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", - "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", - "Sort videos by: ": "Sorter videoer etter: ", - "published": "publisert", - "published - reverse": "publisert - motsatt", - "alphabetically": "alfabetisk", - "alphabetically - reverse": "alfabetisk - motsatt", - "channel name": "kanalnavn", - "channel name - reverse": "kanalnavn - motsatt", - "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", - "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", - "Only show unwatched: ": "Kun vis usette: ", - "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", - "Data preferences": "Datainnstillinger", - "Clear watch history": "Tøm visningshistorikk", - "Import/Export data": "Importer/eksporter data", - "Manage subscriptions": "Behandle abonnementer", - "Watch history": "Visningshistorikk", - "Delete account": "Slett konto", - "Administrator preferences": "Administratorinnstillinger", - "Default homepage: ": "Forvalgt hjemmeside: ", - "Feed menu: ": "Flyt-meny: ", - "Top enabled? ": "", - "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", - "Login enabled? ": "Innlogging påskrudd? ", - "Registration enabled? ": "Registrering påskrudd? ", - "Report statistics? ": "Innrapporter statistikk? ", - "Save preferences": "Lagre innstillinger", - "Subscription manager": "Abonnementsbehandler", - "`x` subscriptions": "`x` abonnementer", - "Import/Export": "Importer/eksporter", - "unsubscribe": "opphev abonnement", - "Subscriptions": "Abonnement", - "`x` unseen notifications": "`x` usette merknader", - "search": "søk", - "Sign out": "Logg ut", - "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", - "Source available here.": "Kildekode tilgjengelig her.", - "View JavaScript license information.": "Vis JavaScript-lisensinfo.", - "Trending": "Trendsettende", - "Watch video on Youtube": "Vis video på YouTube", - "Genre: ": "Sjanger: ", - "License: ": "Lisens: ", - "Family friendly? ": "Familievennlig? ", - "Wilson score: ": "Wilson-poengsum: ", - "Engagement: ": "Engasjement: ", - "Whitelisted regions: ": "Hvitlistede regioner: ", - "Blacklisted regions: ": "Svartelistede regioner: ", - "Shared `x`": "Delt `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", - "View YouTube comments": "Vis YouTube-kommentarer", - "View more comments on Reddit": "Vis flere kommenterer på Reddit", - "View `x` comments": "Vis `x` kommentarer", - "View Reddit comments": "Vis Reddit-kommentarer", - "Hide replies": "Skjul svar", - "Show replies": "Vis svar", - "Incorrect password": "Feil passord", - "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", - "Invalid TFA code": "Ugyldig tofaktorkode", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", - "Invalid answer": "Ugyldig svar", - "Invalid CAPTCHA": "Ugyldig CAPTCHA", - "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", - "User ID is a required field": "Bruker-ID er et påkrevd felt", - "Password is a required field": "Passord er et påkrevd felt", - "Invalid username or password": "Ugyldig brukernavn eller passord", - "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", - "Password cannot be empty": "Passordet kan ikke være tomt", - "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", - "Please sign in": "Logg inn", - "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", - "channel:`x`": "kanal `x`", - "Deleted or invalid channel": "Slettet eller ugyldig kanal", - "This channel does not exist.": "Denne kanalen finnes ikke.", - "Could not get channel info.": "Kunne ikke innhente kanalinfo.", - "Could not fetch comments": "Kunne ikke hente kommentarer", - "View `x` replies": "Vis `x` svar", - "`x` ago": "`x` siden", - "Load more": "Last inn flere", - "`x` points": "`x` poeng", - "Could not create mix.": "Kunne ikke opprette miks.", - "Playlist is empty": "Spillelisten er tom", - "Invalid playlist.": "Ugyldig spilleliste.", - "Playlist does not exist.": "Spillelisten finnes ikke.", - "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", - "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", - "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", - "Invalid challenge": "Ugyldig utfordring", - "Invalid token": "Ugyldig symbol", - "Invalid user": "Ugyldig bruker", - "Token is expired, please try again": "Symbol utløpt, prøv igjen", - "English": "Engelsk", - "English (auto-generated)": "Engelsk (auto-generert)", - "Afrikaans": "", - "Albanian": "Albansk", - "Amharic": "", - "Arabic": "Arabisk", - "Armenian": "Armensk", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "Hviterussisk", - "Bosnian": "Bosnisk", - "Bulgarian": "Bulgarsk", - "Burmese": "Burmesisk", - "Catalan": "Katalansk", - "Cebuano": "", - "Chinese (Simplified)": "", - "Chinese (Traditional)": "", - "Corsican": "", - "Croatian": "", - "Czech": "Tsjekkisk", - "Danish": "Dansk", - "Dutch": "", - "Esperanto": "Esperanto", - "Estonian": "", - "Filipino": "", - "Finnish": "Finsk", - "French": "Fransk", - "Galician": "", - "Georgian": "", - "German": "", - "Greek": "", - "Gujarati": "", - "Haitian Creole": "", - "Hausa": "", - "Hawaiian": "", - "Hebrew": "", - "Hindi": "", - "Hmong": "", - "Hungarian": "Ungarsk", - "Icelandic": "Islandsk", - "Igbo": "", - "Indonesian": "Indonesisk", - "Irish": "Irsk", - "Italian": "Italiensk", - "Japanese": "Japansk", - "Javanese": "", - "Kannada": "", - "Kazakh": "", - "Khmer": "", - "Korean": "", - "Kurdish": "", - "Kyrgyz": "", - "Lao": "", - "Latin": "", - "Latvian": "", - "Lithuanian": "", - "Luxembourgish": "", - "Macedonian": "", - "Malagasy": "", - "Malay": "", - "Malayalam": "", - "Maltese": "", - "Maori": "", - "Marathi": "", - "Mongolian": "", - "Nepali": "", - "Norwegian": "Norsk bokmål", - "Nyanja": "", - "Pashto": "", - "Persian": "", - "Polish": "", - "Portuguese": "", - "Punjabi": "", - "Romanian": "", - "Russian": "Russisk", - "Samoan": "", - "Scottish Gaelic": "", - "Serbian": "Serbisk", - "Shona": "", - "Sindhi": "", - "Sinhala": "", - "Slovak": "Slovakisk", - "Slovenian": "Slovensk", - "Somali": "Somali", - "Southern Sotho": "", - "Spanish": "Spansk", - "Spanish (Latin America)": "", - "Sundanese": "", - "Swahili": "", - "Swedish": "Svensk", - "Tajik": "", - "Tamil": "", - "Telugu": "", - "Thai": "", - "Turkish": "Tyrkisk", - "Ukrainian": "Ukrainsk", - "Urdu": "", - "Uzbek": "", - "Vietnamese": "Vietnamesisk", - "Welsh": "", - "Western Frisian": "", - "Xhosa": "", - "Yiddish": "", - "Yoruba": "", - "Zulu": "", - "`x` years": "`x` år", - "`x` months": "`x` måneder", - "`x` weeks": "`x` uker", - "`x` days": "`x` dager", - "`x` hours": "`x` timer", - "`x` minutes": "`x` minutter", - "`x` seconds": "`x` sekunder", - "Fallback comments: ": "Tilbakefallskommentarer: ", - "Popular": "Pupulært", - "Top": "Topp", - "About": "Om", - "Rating: ": "Vurdering: ", - "Language: ": "Språk: ", - "Default": "Forvalg", - "Music": "Musikk", - "Gaming": "Spill", - "News": "Nyheter", - "Movies": "Filmer", - "Download": "Last ned", - "Download as: ": "Last ned som: ", - "%A %B %-d, %Y": "", - "(edited)": "(redigert)", - "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", - "`x` marked it with a ❤": "`x` levnet et ❤", - "Audio mode": "Lydmodus", - "Video mode": "Video-modus" + "`x` subscribers": "`x` abonnenter", + "`x` videos": "`x` videoer", + "LIVE": "SANNTIDSVISNING", + "Shared `x` ago": "Delt for `x` siden", + "Unsubscribe": "Opphev abonnement", + "Subscribe": "Abonner", + "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", + "View channel on YouTube": "Vis kanal på YouTube", + "newest": "nyeste", + "oldest": "eldste", + "popular": "populært", + "last": "", + "Next page": "Neste side", + "Previous page": "Forrige side", + "Clear watch history?": "Tøm visningshistorikk?", + "Yes": "Ja", + "No": "Nei", + "Import and Export Data": "Importer- og eksporter data", + "Import": "Importer", + "Import Invidious data": "Importer Invidious-data", + "Import YouTube subscriptions": "Importer YouTube-abonnenter", + "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", + "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", + "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", + "Export": "Eksporter", + "Export subscriptions as OPML": "Eksporter abonnenter som OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", + "Export data as JSON": "Eksporter data som JSON", + "Delete account?": "Slett konto?", + "History": "Historikk", + "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", + "JavaScript license information": "JavaScript-lisensinformasjon", + "source": "kilde", + "Login": "Logg inn", + "Login/Register": "Logg inn/registrer", + "Login to Google": "Logg inn med Google", + "User ID:": "Bruker-ID:", + "Password:": "Passord:", + "Time (h:mm:ss):": "Tid (h:mm:ss):", + "Text CAPTCHA": "Tekst-CAPTCHA", + "Image CAPTCHA": "Bilde-CAPTCHA", + "Sign In": "Innlogging", + "Register": "Registrer", + "Email:": "E-post:", + "Google verification code:": "Google-bekreftelseskode:", + "Preferences": "Innstillinger", + "Player preferences": "Avspillerinnstillinger", + "Always loop: ": "Alltid gjenta: ", + "Autoplay: ": "Autoavspilling: ", + "Autoplay next video: ": "Autospill neste video: ", + "Listen by default: ": "Lytt som forvalg: ", + "Default speed: ": "Forvalgt hastighet: ", + "Preferred video quality: ": "Foretrukket videokvalitet: ", + "Player volume: ": "Avspillerlydstyrke: ", + "Default comments: ": "Forvalgte kommentarer: ", + "Default captions: ": "Forvalgte undertitler: ", + "Fallback captions: ": "Tilbakefallsundertitler: ", + "Show related videos? ": "Vis relaterte videoer? ", + "Visual preferences": "Visuelle innstillinger", + "Dark mode: ": "Mørk drakt: ", + "Thin mode: ": "Tynt modus: ", + "Subscription preferences": "Abonnementsinnstillinger", + "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", + "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", + "Sort videos by: ": "Sorter videoer etter: ", + "published": "publisert", + "published - reverse": "publisert - motsatt", + "alphabetically": "alfabetisk", + "alphabetically - reverse": "alfabetisk - motsatt", + "channel name": "kanalnavn", + "channel name - reverse": "kanalnavn - motsatt", + "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", + "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", + "Only show unwatched: ": "Kun vis usette: ", + "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", + "Data preferences": "Datainnstillinger", + "Clear watch history": "Tøm visningshistorikk", + "Import/Export data": "Importer/eksporter data", + "Manage subscriptions": "Behandle abonnementer", + "Watch history": "Visningshistorikk", + "Delete account": "Slett konto", + "Administrator preferences": "Administratorinnstillinger", + "Default homepage: ": "Forvalgt hjemmeside: ", + "Feed menu: ": "Flyt-meny: ", + "Top enabled? ": "", + "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", + "Login enabled? ": "Innlogging påskrudd? ", + "Registration enabled? ": "Registrering påskrudd? ", + "Report statistics? ": "Innrapporter statistikk? ", + "Save preferences": "Lagre innstillinger", + "Subscription manager": "Abonnementsbehandler", + "`x` subscriptions": "`x` abonnementer", + "Import/Export": "Importer/eksporter", + "unsubscribe": "opphev abonnement", + "Subscriptions": "Abonnement", + "`x` unseen notifications": "`x` usette merknader", + "search": "søk", + "Sign out": "Logg ut", + "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", + "Source available here.": "Kildekode tilgjengelig her.", + "View JavaScript license information.": "Vis JavaScript-lisensinfo.", + "Trending": "Trendsettende", + "Watch video on Youtube": "Vis video på YouTube", + "Genre: ": "Sjanger: ", + "License: ": "Lisens: ", + "Family friendly? ": "Familievennlig? ", + "Wilson score: ": "Wilson-poengsum: ", + "Engagement: ": "Engasjement: ", + "Whitelisted regions: ": "Hvitlistede regioner: ", + "Blacklisted regions: ": "Svartelistede regioner: ", + "Shared `x`": "Delt `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", + "View YouTube comments": "Vis YouTube-kommentarer", + "View more comments on Reddit": "Vis flere kommenterer på Reddit", + "View `x` comments": "Vis `x` kommentarer", + "View Reddit comments": "Vis Reddit-kommentarer", + "Hide replies": "Skjul svar", + "Show replies": "Vis svar", + "Incorrect password": "Feil passord", + "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", + "Invalid TFA code": "Ugyldig tofaktorkode", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", + "Invalid answer": "Ugyldig svar", + "Invalid CAPTCHA": "Ugyldig CAPTCHA", + "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", + "User ID is a required field": "Bruker-ID er et påkrevd felt", + "Password is a required field": "Passord er et påkrevd felt", + "Invalid username or password": "Ugyldig brukernavn eller passord", + "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", + "Password cannot be empty": "Passordet kan ikke være tomt", + "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", + "Please sign in": "Logg inn", + "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", + "channel:`x`": "kanal `x`", + "Deleted or invalid channel": "Slettet eller ugyldig kanal", + "This channel does not exist.": "Denne kanalen finnes ikke.", + "Could not get channel info.": "Kunne ikke innhente kanalinfo.", + "Could not fetch comments": "Kunne ikke hente kommentarer", + "View `x` replies": "Vis `x` svar", + "`x` ago": "`x` siden", + "Load more": "Last inn flere", + "`x` points": "`x` poeng", + "Could not create mix.": "Kunne ikke opprette miks.", + "Playlist is empty": "Spillelisten er tom", + "Invalid playlist.": "Ugyldig spilleliste.", + "Playlist does not exist.": "Spillelisten finnes ikke.", + "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", + "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", + "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", + "Invalid challenge": "Ugyldig utfordring", + "Invalid token": "Ugyldig symbol", + "Invalid user": "Ugyldig bruker", + "Token is expired, please try again": "Symbol utløpt, prøv igjen", + "English": "Engelsk", + "English (auto-generated)": "Engelsk (auto-generert)", + "Afrikaans": "", + "Albanian": "Albansk", + "Amharic": "", + "Arabic": "Arabisk", + "Armenian": "Armensk", + "Azerbaijani": "", + "Bangla": "", + "Basque": "", + "Belarusian": "Hviterussisk", + "Bosnian": "Bosnisk", + "Bulgarian": "Bulgarsk", + "Burmese": "Burmesisk", + "Catalan": "Katalansk", + "Cebuano": "", + "Chinese (Simplified)": "", + "Chinese (Traditional)": "", + "Corsican": "", + "Croatian": "", + "Czech": "Tsjekkisk", + "Danish": "Dansk", + "Dutch": "", + "Esperanto": "Esperanto", + "Estonian": "", + "Filipino": "", + "Finnish": "Finsk", + "French": "Fransk", + "Galician": "", + "Georgian": "", + "German": "", + "Greek": "", + "Gujarati": "", + "Haitian Creole": "", + "Hausa": "", + "Hawaiian": "", + "Hebrew": "", + "Hindi": "", + "Hmong": "", + "Hungarian": "Ungarsk", + "Icelandic": "Islandsk", + "Igbo": "", + "Indonesian": "Indonesisk", + "Irish": "Irsk", + "Italian": "Italiensk", + "Japanese": "Japansk", + "Javanese": "", + "Kannada": "", + "Kazakh": "", + "Khmer": "", + "Korean": "", + "Kurdish": "", + "Kyrgyz": "", + "Lao": "", + "Latin": "", + "Latvian": "", + "Lithuanian": "", + "Luxembourgish": "", + "Macedonian": "", + "Malagasy": "", + "Malay": "", + "Malayalam": "", + "Maltese": "", + "Maori": "", + "Marathi": "", + "Mongolian": "", + "Nepali": "", + "Norwegian": "Norsk bokmål", + "Nyanja": "", + "Pashto": "", + "Persian": "", + "Polish": "", + "Portuguese": "", + "Punjabi": "", + "Romanian": "", + "Russian": "Russisk", + "Samoan": "", + "Scottish Gaelic": "", + "Serbian": "Serbisk", + "Shona": "", + "Sindhi": "", + "Sinhala": "", + "Slovak": "Slovakisk", + "Slovenian": "Slovensk", + "Somali": "Somali", + "Southern Sotho": "", + "Spanish": "Spansk", + "Spanish (Latin America)": "", + "Sundanese": "", + "Swahili": "", + "Swedish": "Svensk", + "Tajik": "", + "Tamil": "", + "Telugu": "", + "Thai": "", + "Turkish": "Tyrkisk", + "Ukrainian": "Ukrainsk", + "Urdu": "", + "Uzbek": "", + "Vietnamese": "Vietnamesisk", + "Welsh": "", + "Western Frisian": "", + "Xhosa": "", + "Yiddish": "", + "Yoruba": "", + "Zulu": "", + "`x` years": "`x` år", + "`x` months": "`x` måneder", + "`x` weeks": "`x` uker", + "`x` days": "`x` dager", + "`x` hours": "`x` timer", + "`x` minutes": "`x` minutter", + "`x` seconds": "`x` sekunder", + "Fallback comments: ": "Tilbakefallskommentarer: ", + "Popular": "Pupulært", + "Top": "Topp", + "About": "Om", + "Rating: ": "Vurdering: ", + "Language: ": "Språk: ", + "Default": "Forvalg", + "Music": "Musikk", + "Gaming": "Spill", + "News": "Nyheter", + "Movies": "Filmer", + "Download": "Last ned", + "Download as: ": "Last ned som: ", + "%A %B %-d, %Y": "", + "(edited)": "(redigert)", + "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", + "`x` marked it with a ❤": "`x` levnet et ❤", + "Audio mode": "Lydmodus", + "Video mode": "Video-modus", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/nl.json b/locales/nl.json index 2224d326b..b4ea38629 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -10,8 +10,9 @@ "newest": "nieuwste", "oldest": "oudste", "popular": "populair", - "Preview page": "Pagina voorvertonen", + "last": "", "Next page": "Volgende pagina", + "Previous page": "Vorige pagina", "Clear watch history?": "Kijk geschiedenis wissen?", "Yes": "Ja", "No": "Nee", @@ -28,7 +29,6 @@ "Export data as JSON": "Exporteer gegevens als JSON", "Delete account?": "Verwijder account?", "History": "Geschiedenis", - "Previous page": "Vorige pagina", "An alternative front-end to YouTube": "Een alternatieve front-end voor YouTube", "JavaScript license information": "JavaScript licentie informatie", "source": "bron", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "", "`x` marked it with a ❤": "", "Audio mode": "", - "Video mode": "" + "Video mode": "", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/pl.json b/locales/pl.json index d9a21cf1e..0db43c51b 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -10,8 +10,9 @@ "newest": "najnowsze", "oldest": "najstarsze", "popular": "popularne", - "Preview page": "Podgląd strony", + "last": "", "Next page": "Następna strona", + "Previous page": "Poprzednia strona", "Clear watch history?": "Wyczyścić historię?", "Yes": "Tak", "No": "Nie", @@ -28,7 +29,6 @@ "Export data as JSON": "Eksportuj dane jako JSON", "Delete account?": "Usunąć konto?", "History": "Historia", - "Previous page": "Poprzednia strona", "An alternative front-end to YouTube": "Alternatywny front-end dla YouTube", "JavaScript license information": "Informacja o licencji JavaScript", "source": "źródło", @@ -284,5 +284,8 @@ "Youtube permalink of the comment": "Odnośnik bezpośredni do komentarza na YouTube", "`x` marked it with a ❤": "", "Audio mode": "Tryb audio", - "Video mode": "Tryb wideo" + "Video mode": "Tryb wideo", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/locales/ru.json b/locales/ru.json index 0c6665892..222b693fc 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,294 +1,293 @@ { - "`x` subscribers": "`x` подписчиков", - "`x` videos": "`x` видео", - "LIVE": "ПРЯМОЙ ЭФИР", - "Shared `x` ago": "Опубликовано `x` назад", - "Unsubscribe": "Отписаться", - "Subscribe": "Подписаться", - "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", - "View channel on YouTube": "Канал на YouTube", - "newest": "новые", - "oldest": "старые", - "popular": "популярные", - "Preview page": "Предварительный просмотр", - "Next page": "Следующая страница", - "Clear watch history?": "Очистить историю просмотров?", - "Yes": "Да", - "No": "Нет", - "Import and Export Data": "Импорт и экспорт данных", - "Import": "Импорт", - "Import Invidious data": "Импортировать данные Invidious", - "Import YouTube subscriptions": "Импортировать YouTube подписки", - "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", - "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", - "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", - "Export": "Экспорт", - "Export subscriptions as OPML": "Экспортировать подписки в OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", - "Export data as JSON": "Экспортировать данные в JSON", - "Delete account?": "Удалить аккаунт?", - "History": "История", - "Previous page": "Предыдущая страница", - "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", - "JavaScript license information": "Лицензии JavaScript", - "source": "источник", - "Login": "Войти", - "Login/Register": "Войти/Регистрация", - "Login to Google": "Войти через Google", - "User ID:": "ID пользователя:", - "Password:": "Пароль:", - "Time (h:mm:ss):": "Время (ч:мм:сс):", - "Text CAPTCHA": "Текст капчи", - "Image CAPTCHA": "Изображение капчи", - "Sign In": "Войти", - "Register": "Регистрация", - "Email:": "Эл. почта:", - "Google verification code:": "Код подтверждения Google:", - "Preferences": "Настройки", - "Player preferences": "Настройки проигрывателя", - "Always loop: ": "Всегда повторять: ", - "Autoplay: ": "Автовоспроизведение: ", - "Autoplay next video: ": "Автовоспроизведение следующего видео: ", - "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", - "Default speed: ": "Скорость по-умолчанию: ", - "Preferred video quality: ": "Предпочтительное качество видео: ", - "Player volume: ": "Громкость воспроизведения: ", - "Default comments: ": "Источник комментариев: ", - "youtube": "YouTube", - "reddit": "Reddit", - "Default captions: ": "Субтитры по-умолчанию: ", - "Fallback captions: ": "Резервные субтитры: ", - "Show related videos? ": "Показывать похожие видео? ", - "Visual preferences": "Визуальные настройки", - "Dark mode: ": "Темная тема: ", - "Thin mode: ": "Облегченный режим: ", - "Subscription preferences": "Настройки подписок", - "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", - "Number of videos shown in feed: ": "Число видео в ленте: ", - "Sort videos by: ": "Сортировать видео по: ", - "published": "дате публикации", - "published - reverse": "дате - обратный порядок", - "alphabetically": "алфавиту", - "alphabetically - reverse": "алфавиту - обратный порядок", - "channel name": "имени канала", - "channel name - reverse": "имени канала - обратный порядок", - "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", - "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", - "Only show unwatched: ": "Отображать только непросмотренные видео: ", - "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", - "Data preferences": "Настройки данных", - "Clear watch history": "Очистить историю просмотра", - "Import/Export data": "Импорт/Экспорт данных", - "Manage subscriptions": "Управление подписками", - "Watch history": "История просмотров", - "Delete account": "Удалить аккаунт", - "Administrator preferences": "Настройки администратора", - "Default homepage: ": "Главная страница по умолчанию: ", - "Feed menu: ": "Меню ленты: ", - "Top enabled? ": "Включить ТОП? ", - "CAPTCHA enabled? ": "Включить капчу? ", - "Login enabled? ": "Включить логин? ", - "Registration enabled? ": "Включить регистрацию? ", - "Report statistics? ": "Отображать статистику? ", - "Save preferences": "Сохранить настройки", - "Subscription manager": "Менеджер подписок", - "`x` subscriptions": "`x` подписок", - "Import/Export": "Импорт/Экспорт", - "unsubscribe": "отписаться", - "Subscriptions": "Подписки", - "`x` unseen notifications": "`x` новых оповещений", - "search": "поиск", - "Sign out": "Выйти", - "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", - "Source available here.": "Исходный код доступен здесь.", - "Liberapay: ": "Liberapay: ", - "Patreon: ": "Patreon: ", - "BTC: ": "BTC: ", - "BCH: ": "BCH: ", - "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", - "Trending": "В тренде", - "Watch video on Youtube": "Смотреть на YouTube", - "Genre: ": "Жанр: ", - "License: ": "Лицензия: ", - "Family friendly? ": "Семейный просмотр: ", - "Wilson score: ": "Рейтинг Вильсона: ", - "Engagement: ": "Вовлеченность: ", - "Whitelisted regions: ": "Доступно для: ", - "Blacklisted regions: ": "Недоступно для: ", - "Shared `x`": "Опубликовано `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", - "View YouTube comments": "Смотреть комментарии с YouTube", - "View more comments on Reddit": "Больше комментариев на Reddit", - "View `x` comments": "Показать `x` комментариев", - "View Reddit comments": "Смотреть комментарии с Reddit", - "Hide replies": "Скрыть ответы", - "Show replies": "Показать ответы", - "Incorrect password": "Неправильный пароль", - "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", - "Invalid TFA code": "Неправильный TFA код", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", - "Invalid answer": "Неверный ответ", - "Invalid CAPTCHA": "Неверная капча", - "CAPTCHA is a required field": "Необходимо ввести капчу", - "User ID is a required field": "Необходимо ввести идентификатор пользователя", - "Password is a required field": "Необходимо ввести пароль", - "Invalid username or password": "Недопустимый пароль или имя пользователя", - "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", - "Password cannot be empty": "Пароль не может быть пустым", - "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", - "Please sign in": "Пожалуйста, войдите", - "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", - "channel:`x`": "канал: `x`", - "Deleted or invalid channel": "Канал удален или не найден", - "This channel does not exist.": "Такой канал не существует.", - "Could not get channel info.": "Невозможно получить информацию о канале.", - "Could not fetch comments": "Невозможно получить комментарии", - "View `x` replies": "Показать `x` ответов", - "`x` ago": "`x` назад", - "Load more": "Загрузить больше", - "`x` points": "`x` очков", - "Could not create mix.": "Невозможно создать \"микс\".", - "Playlist is empty": "Плейлист пуст", - "Invalid playlist.": "Некорректный плейлист.", - "Playlist does not exist.": "Плейлист не существует.", - "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", - "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", - "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", - "Invalid challenge": "Неправильный ответ в \"challenge\"", - "Invalid token": "Неправильный токен", - "Invalid user": "Недопустимое имя пользователя", - "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", - "English": "Английский", - "English (auto-generated)": "Английский (созданы автоматически)", - "Afrikaans": "Африкаанс", - "Albanian": "Албанский", - "Amharic": "Амхарский", - "Arabic": "Арабский", - "Armenian": "Армянский", - "Azerbaijani": "Азербайджанский", - "Bangla": "Бенгальский", - "Basque": "Баскский", - "Belarusian": "Белорусский", - "Bosnian": "Боснийский", - "Bulgarian": "Болгарский", - "Burmese": "Бирманский", - "Catalan": "Каталонский", - "Cebuano": "Себуанский", - "Chinese (Simplified)": "Китайский (упрощенный)", - "Chinese (Traditional)": "Китайский (традиционный)", - "Corsican": "Корсиканский", - "Croatian": "Хорватский", - "Czech": "Чешский", - "Danish": "Датский", - "Dutch": "Нидерландский", - "Esperanto": "Эсперанто", - "Estonian": "Эстонский", - "Filipino": "Филиппинский", - "Finnish": "Финский", - "French": "Французский", - "Galician": "Галисийский", - "Georgian": "Грузинский", - "German": "Немецкий", - "Greek": "Греческий", - "Gujarati": "Гуджаратский", - "Haitian Creole": "Гаит. креольский", - "Hausa": "Хауса", - "Hawaiian": "Гавайский", - "Hebrew": "Иврит", - "Hindi": "Хинди", - "Hmong": "Хмонг (мяо)", - "Hungarian": "Венгерский", - "Icelandic": "Исландский", - "Igbo": "Игбо", - "Indonesian": "Индонезийский", - "Irish": "Ирландский", - "Italian": "Итальянский", - "Japanese": "Японский", - "Javanese": "Яванский", - "Kannada": "Каннада", - "Kazakh": "Казахский", - "Khmer": "Кхмерский", - "Korean": "Корейский", - "Kurdish": "Курдский", - "Kyrgyz": "Киргизский", - "Lao": "Лаосский", - "Latin": "Латинский", - "Latvian": "Латышский", - "Lithuanian": "Литовский", - "Luxembourgish": "Люксембургский", - "Macedonian": "Македонский", - "Malagasy": "Малагасийский", - "Malay": "Малайский", - "Malayalam": "Малаялам", - "Maltese": "Мальтийский", - "Maori": "Маори", - "Marathi": "Маратхи", - "Mongolian": "Монгольская", - "Nepali": "Непальский", - "Norwegian": "Норвежский", - "Nyanja": "Ньянджа", - "Pashto": "Пушту", - "Persian": "Персидский", - "Polish": "Польский", - "Portuguese": "Португальский", - "Punjabi": "Панджаби", - "Romanian": "Румынский", - "Russian": "Русский", - "Samoan": "Самоанский", - "Scottish Gaelic": "Шотландский (гэльский)", - "Serbian": "Сербский", - "Shona": "Шона", - "Sindhi": "Синдхи", - "Sinhala": "Сингальский", - "Slovak": "Словацкий", - "Slovenian": "Словенский", - "Somali": "Сомалийский", - "Southern Sotho": "Сесото (южный сото)", - "Spanish": "Испанский", - "Spanish (Latin America)": "Испанский (Латинская Америка)", - "Sundanese": "Сунданский", - "Swahili": "Суахили", - "Swedish": "Шведский", - "Tajik": "Таджикский", - "Tamil": "Тамильский", - "Telugu": "Телугу", - "Thai": "Тайский", - "Turkish": "Турецкий", - "Ukrainian": "Украинский", - "Urdu": "Урду", - "Uzbek": "Узбекский", - "Vietnamese": "Вьетнамский", - "Welsh": "Валлийский", - "Western Frisian": "Западнофризский", - "Xhosa": "Коса", - "Yiddish": "Идиш", - "Yoruba": "Йоруба", - "Zulu": "Зулусский", - "`x` years": "`x` лет", - "`x` months": "`x` месяцев", - "`x` weeks": "`x` недель", - "`x` days": "`x` дней", - "`x` hours": "`x` часов", - "`x` minutes": "`x` минут", - "`x` seconds": "`x` секунд", - "Fallback comments: ": "Резервные комментарии: ", - "Popular": "Популярное", - "Top": "Топ", - "About": "О сайте", - "Rating: ": "Рейтинг: ", - "Language: ": "Язык: ", - "Default": "По-умолчанию", - "Music": "Музыка", - "Gaming": "Игры", - "News": "Новости", - "Movies": "Фильмы", - "Download": "Скачать", - "Download as: ": "Скачать как: ", - "%A %B %-d, %Y": "%-d %B %Y, %A", - "(edited)": "(изменено)", - "Youtube permalink of the comment": "Прямая ссылка на YouTube", - "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", - "Audio mode": "Аудио режим", - "Video mode": "Видео режим" + "`x` subscribers": "`x` подписчиков", + "`x` videos": "`x` видео", + "LIVE": "ПРЯМОЙ ЭФИР", + "Shared `x` ago": "Опубликовано `x` назад", + "Unsubscribe": "Отписаться", + "Subscribe": "Подписаться", + "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", + "View channel on YouTube": "Канал на YouTube", + "newest": "новые", + "oldest": "старые", + "popular": "популярные", + "last": "", + "Next page": "Следующая страница", + "Previous page": "Предыдущая страница", + "Clear watch history?": "Очистить историю просмотров?", + "Yes": "Да", + "No": "Нет", + "Import and Export Data": "Импорт и экспорт данных", + "Import": "Импорт", + "Import Invidious data": "Импортировать данные Invidious", + "Import YouTube subscriptions": "Импортировать YouTube подписки", + "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", + "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", + "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", + "Export": "Экспорт", + "Export subscriptions as OPML": "Экспортировать подписки в OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", + "Export data as JSON": "Экспортировать данные в JSON", + "Delete account?": "Удалить аккаунт?", + "History": "История", + "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", + "JavaScript license information": "Лицензии JavaScript", + "source": "источник", + "Login": "Войти", + "Login/Register": "Войти/Регистрация", + "Login to Google": "Войти через Google", + "User ID:": "ID пользователя:", + "Password:": "Пароль:", + "Time (h:mm:ss):": "Время (ч:мм:сс):", + "Text CAPTCHA": "Текст капчи", + "Image CAPTCHA": "Изображение капчи", + "Sign In": "Войти", + "Register": "Регистрация", + "Email:": "Эл. почта:", + "Google verification code:": "Код подтверждения Google:", + "Preferences": "Настройки", + "Player preferences": "Настройки проигрывателя", + "Always loop: ": "Всегда повторять: ", + "Autoplay: ": "Автовоспроизведение: ", + "Autoplay next video: ": "Автовоспроизведение следующего видео: ", + "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", + "Default speed: ": "Скорость по-умолчанию: ", + "Preferred video quality: ": "Предпочтительное качество видео: ", + "Player volume: ": "Громкость воспроизведения: ", + "Default comments: ": "Источник комментариев: ", + "youtube": "YouTube", + "reddit": "Reddit", + "Default captions: ": "Субтитры по-умолчанию: ", + "Fallback captions: ": "Резервные субтитры: ", + "Show related videos? ": "Показывать похожие видео? ", + "Visual preferences": "Визуальные настройки", + "Dark mode: ": "Темная тема: ", + "Thin mode: ": "Облегченный режим: ", + "Subscription preferences": "Настройки подписок", + "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", + "Number of videos shown in feed: ": "Число видео в ленте: ", + "Sort videos by: ": "Сортировать видео по: ", + "published": "дате публикации", + "published - reverse": "дате - обратный порядок", + "alphabetically": "алфавиту", + "alphabetically - reverse": "алфавиту - обратный порядок", + "channel name": "имени канала", + "channel name - reverse": "имени канала - обратный порядок", + "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", + "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", + "Only show unwatched: ": "Отображать только непросмотренные видео: ", + "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", + "Data preferences": "Настройки данных", + "Clear watch history": "Очистить историю просмотра", + "Import/Export data": "Импорт/Экспорт данных", + "Manage subscriptions": "Управление подписками", + "Watch history": "История просмотров", + "Delete account": "Удалить аккаунт", + "Administrator preferences": "Настройки администратора", + "Default homepage: ": "Главная страница по умолчанию: ", + "Feed menu: ": "Меню ленты: ", + "Top enabled? ": "Включить ТОП? ", + "CAPTCHA enabled? ": "Включить капчу? ", + "Login enabled? ": "Включить логин? ", + "Registration enabled? ": "Включить регистрацию? ", + "Report statistics? ": "Отображать статистику? ", + "Save preferences": "Сохранить настройки", + "Subscription manager": "Менеджер подписок", + "`x` subscriptions": "`x` подписок", + "Import/Export": "Импорт/Экспорт", + "unsubscribe": "отписаться", + "Subscriptions": "Подписки", + "`x` unseen notifications": "`x` новых оповещений", + "search": "поиск", + "Sign out": "Выйти", + "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", + "Source available here.": "Исходный код доступен здесь.", + "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", + "Trending": "В тренде", + "Watch video on Youtube": "Смотреть на YouTube", + "Genre: ": "Жанр: ", + "License: ": "Лицензия: ", + "Family friendly? ": "Семейный просмотр: ", + "Wilson score: ": "Рейтинг Вильсона: ", + "Engagement: ": "Вовлеченность: ", + "Whitelisted regions: ": "Доступно для: ", + "Blacklisted regions: ": "Недоступно для: ", + "Shared `x`": "Опубликовано `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", + "View YouTube comments": "Смотреть комментарии с YouTube", + "View more comments on Reddit": "Больше комментариев на Reddit", + "View `x` comments": "Показать `x` комментариев", + "View Reddit comments": "Смотреть комментарии с Reddit", + "Hide replies": "Скрыть ответы", + "Show replies": "Показать ответы", + "Incorrect password": "Неправильный пароль", + "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", + "Invalid TFA code": "Неправильный TFA код", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", + "Invalid answer": "Неверный ответ", + "Invalid CAPTCHA": "Неверная капча", + "CAPTCHA is a required field": "Необходимо ввести капчу", + "User ID is a required field": "Необходимо ввести идентификатор пользователя", + "Password is a required field": "Необходимо ввести пароль", + "Invalid username or password": "Недопустимый пароль или имя пользователя", + "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", + "Password cannot be empty": "Пароль не может быть пустым", + "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", + "Please sign in": "Пожалуйста, войдите", + "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", + "channel:`x`": "канал: `x`", + "Deleted or invalid channel": "Канал удален или не найден", + "This channel does not exist.": "Такой канал не существует.", + "Could not get channel info.": "Невозможно получить информацию о канале.", + "Could not fetch comments": "Невозможно получить комментарии", + "View `x` replies": "Показать `x` ответов", + "`x` ago": "`x` назад", + "Load more": "Загрузить больше", + "`x` points": "`x` очков", + "Could not create mix.": "Невозможно создать \"микс\".", + "Playlist is empty": "Плейлист пуст", + "Invalid playlist.": "Некорректный плейлист.", + "Playlist does not exist.": "Плейлист не существует.", + "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", + "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", + "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", + "Invalid challenge": "Неправильный ответ в \"challenge\"", + "Invalid token": "Неправильный токен", + "Invalid user": "Недопустимое имя пользователя", + "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", + "English": "Английский", + "English (auto-generated)": "Английский (созданы автоматически)", + "Afrikaans": "Африкаанс", + "Albanian": "Албанский", + "Amharic": "Амхарский", + "Arabic": "Арабский", + "Armenian": "Армянский", + "Azerbaijani": "Азербайджанский", + "Bangla": "Бенгальский", + "Basque": "Баскский", + "Belarusian": "Белорусский", + "Bosnian": "Боснийский", + "Bulgarian": "Болгарский", + "Burmese": "Бирманский", + "Catalan": "Каталонский", + "Cebuano": "Себуанский", + "Chinese (Simplified)": "Китайский (упрощенный)", + "Chinese (Traditional)": "Китайский (традиционный)", + "Corsican": "Корсиканский", + "Croatian": "Хорватский", + "Czech": "Чешский", + "Danish": "Датский", + "Dutch": "Нидерландский", + "Esperanto": "Эсперанто", + "Estonian": "Эстонский", + "Filipino": "Филиппинский", + "Finnish": "Финский", + "French": "Французский", + "Galician": "Галисийский", + "Georgian": "Грузинский", + "German": "Немецкий", + "Greek": "Греческий", + "Gujarati": "Гуджаратский", + "Haitian Creole": "Гаит. креольский", + "Hausa": "Хауса", + "Hawaiian": "Гавайский", + "Hebrew": "Иврит", + "Hindi": "Хинди", + "Hmong": "Хмонг (мяо)", + "Hungarian": "Венгерский", + "Icelandic": "Исландский", + "Igbo": "Игбо", + "Indonesian": "Индонезийский", + "Irish": "Ирландский", + "Italian": "Итальянский", + "Japanese": "Японский", + "Javanese": "Яванский", + "Kannada": "Каннада", + "Kazakh": "Казахский", + "Khmer": "Кхмерский", + "Korean": "Корейский", + "Kurdish": "Курдский", + "Kyrgyz": "Киргизский", + "Lao": "Лаосский", + "Latin": "Латинский", + "Latvian": "Латышский", + "Lithuanian": "Литовский", + "Luxembourgish": "Люксембургский", + "Macedonian": "Македонский", + "Malagasy": "Малагасийский", + "Malay": "Малайский", + "Malayalam": "Малаялам", + "Maltese": "Мальтийский", + "Maori": "Маори", + "Marathi": "Маратхи", + "Mongolian": "Монгольская", + "Nepali": "Непальский", + "Norwegian": "Норвежский", + "Nyanja": "Ньянджа", + "Pashto": "Пушту", + "Persian": "Персидский", + "Polish": "Польский", + "Portuguese": "Португальский", + "Punjabi": "Панджаби", + "Romanian": "Румынский", + "Russian": "Русский", + "Samoan": "Самоанский", + "Scottish Gaelic": "Шотландский (гэльский)", + "Serbian": "Сербский", + "Shona": "Шона", + "Sindhi": "Синдхи", + "Sinhala": "Сингальский", + "Slovak": "Словацкий", + "Slovenian": "Словенский", + "Somali": "Сомалийский", + "Southern Sotho": "Сесото (южный сото)", + "Spanish": "Испанский", + "Spanish (Latin America)": "Испанский (Латинская Америка)", + "Sundanese": "Сунданский", + "Swahili": "Суахили", + "Swedish": "Шведский", + "Tajik": "Таджикский", + "Tamil": "Тамильский", + "Telugu": "Телугу", + "Thai": "Тайский", + "Turkish": "Турецкий", + "Ukrainian": "Украинский", + "Urdu": "Урду", + "Uzbek": "Узбекский", + "Vietnamese": "Вьетнамский", + "Welsh": "Валлийский", + "Western Frisian": "Западнофризский", + "Xhosa": "Коса", + "Yiddish": "Идиш", + "Yoruba": "Йоруба", + "Zulu": "Зулусский", + "`x` years": "`x` лет", + "`x` months": "`x` месяцев", + "`x` weeks": "`x` недель", + "`x` days": "`x` дней", + "`x` hours": "`x` часов", + "`x` minutes": "`x` минут", + "`x` seconds": "`x` секунд", + "Fallback comments: ": "Резервные комментарии: ", + "Popular": "Популярное", + "Top": "Топ", + "About": "О сайте", + "Rating: ": "Рейтинг: ", + "Language: ": "Язык: ", + "Default": "По-умолчанию", + "Music": "Музыка", + "Gaming": "Игры", + "News": "Новости", + "Movies": "Фильмы", + "Download": "Скачать", + "Download as: ": "Скачать как: ", + "%A %B %-d, %Y": "%-d %B %Y, %A", + "(edited)": "(изменено)", + "Youtube permalink of the comment": "Прямая ссылка на YouTube", + "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", + "Audio mode": "Аудио режим", + "Video mode": "Видео режим", + "Videos": "", + "Playlists": "", + "Current version: ": "" } diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index 0f7058363..de44dd3ee 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -109,15 +109,15 @@
- <%= translate(locale, "BTC: ") %>356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
+ BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
- <%= translate(locale, "BCH: ") %>qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk
+ BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk

From bf11a46abe07ba832f7df45c3ef33a9f3a2ad3b2 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 10:48:38 -0500 Subject: [PATCH 31/81] Bump expire time for pubsub --- src/invidious.cr | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 2d1d88ce9..a5fc3658f 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -2321,7 +2321,8 @@ get "/feed/webhook/:token" do |env| data = "#{time}" end - if Time.now.to_unix - time.to_i > 600 + # The hub will sometimes check if we're still subscribed after delivery errors + if Time.now.to_unix - time.to_i > 432000 halt env, status_code: 400 end From fdc014af6781a306efe91ecd317e13bffb0a282a Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 11:43:48 -0500 Subject: [PATCH 32/81] Add '&local=true' to watch and embed pages --- src/invidious.cr | 56 ++++++++++++++++------- src/invidious/videos.cr | 7 ++- src/invidious/views/components/player.ecr | 6 +-- 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index a5fc3658f..6ea4b54bd 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -396,6 +396,12 @@ get "/watch" do |env| fmt_stream = video.fmt_stream(decrypt_function) adaptive_fmts = video.adaptive_fmts(decrypt_function) + + if params[:local] + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + end + video_streams = video.video_streams(adaptive_fmts) audio_streams = video.audio_streams(adaptive_fmts) @@ -496,6 +502,12 @@ get "/embed/:id" do |env| fmt_stream = video.fmt_stream(decrypt_function) adaptive_fmts = video.adaptive_fmts(decrypt_function) + + if params[:local] + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + end + video_streams = video.video_streams(adaptive_fmts) audio_streams = video.audio_streams(adaptive_fmts) @@ -4209,24 +4221,32 @@ get "/videoplayback" do |env| query_params = env.params.query fvip = query_params["fvip"]? || "3" - mn = query_params["mn"].split(",")[-1] + mn = query_params["mn"].split(",").pop host = "https://r#{fvip}---#{mn}.googlevideo.com" url = "/videoplayback?#{query_params.to_s}" - headers = env.request.headers - headers.delete("Host") - headers.delete("Cookie") - headers.delete("User-Agent") - headers.delete("Referer") + headers = HTTP::Headers.new + {"Range", "Accept", "Accept-Encoding"}.each do |header| + if env.request.headers[header]? + headers[header] = env.request.headers[header] + end + end region = query_params["region"]? response = HTTP::Client::Response.new(403) - loop do + 5.times do begin client = make_client(URI.parse(host), proxies, region) response = client.head(url, headers) break + rescue Socket::Addrinfo::Error + if fvip == "3" + break + end + + fvip = "3" + host = "https://r#{fvip}---#{mn}.googlevideo.com" rescue ex end end @@ -4284,11 +4304,12 @@ get "/ggpht/*" do |env| client = make_client(URI.parse(host)) url = env.request.path.lchop("/ggpht") - headers = env.request.headers - headers.delete("Host") - headers.delete("Cookie") - headers.delete("User-Agent") - headers.delete("Referer") + headers = HTTP::Headers.new + {"Range", "Accept", "Accept-Encoding"}.each do |header| + if env.request.headers[header]? + headers[header] = env.request.headers[header] + end + end client.get(url, headers) do |response| env.response.status_code = response.status_code @@ -4342,11 +4363,12 @@ get "/vi/:id/:name" do |env| end url = "/vi/#{id}/#{name}" - headers = env.request.headers - headers.delete("Host") - headers.delete("Cookie") - headers.delete("User-Agent") - headers.delete("Referer") + headers = HTTP::Headers.new + {"Range", "Accept", "Accept-Encoding"}.each do |header| + if env.request.headers[header]? + headers[header] = env.request.headers[header] + end + end client.get(url, headers) do |response| env.response.status_code = response.status_code diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index cd24afdc5..b5bd20834 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -731,11 +731,12 @@ end def process_video_params(query, preferences) autoplay = query["autoplay"]?.try &.to_i? continue = query["continue"]?.try &.to_i? - related_videos = query["related_videos"]? listen = query["listen"]? && (query["listen"] == "true" || query["listen"] == "1").to_unsafe + local = query["local"]? && (query["local"] == "true").to_unsafe preferred_captions = query["subtitles"]?.try &.split(",").map { |a| a.downcase } quality = query["quality"]? region = query["region"]? + related_videos = query["related_videos"]? speed = query["speed"]?.try &.to_f? video_loop = query["loop"]?.try &.to_i? volume = query["volume"]?.try &.to_i? @@ -765,8 +766,9 @@ def process_video_params(query, preferences) autoplay = autoplay == 1 continue = continue == 1 - related_videos = related_videos == 1 listen = listen == 1 + local = local == 1 + related_videos = related_videos == 1 video_loop = video_loop == 1 if query["t"]? @@ -799,6 +801,7 @@ def process_video_params(query, preferences) continue: continue, controls: controls, listen: listen, + local: local, preferred_captions: preferred_captions, quality: quality, raw: raw, diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index e2cfa3a1b..cecb9b2f8 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -11,7 +11,7 @@ <% else %> <% if params[:listen] %> <% audio_streams.each_with_index do |fmt, i| %> - " type='<%= fmt["type"] %>' label="<%= fmt["bitrate"] %>k" selected="<%= i == 0 ? true : false %>"> + <% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["bitrate"] %>k" selected="<%= i == 0 ? true : false %>"> <% end %> <% else %> <% if params[:quality] == "dash" %> @@ -19,9 +19,9 @@ <% end %> <% fmt_stream.each_with_index do |fmt, i| %> <% if params[:quality] %> - " type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= params[:quality] == fmt["label"].split(" - ")[0] %>"> + <% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= params[:quality] == fmt["label"].split(" - ")[0] %>"> <% else %> - " type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= i == 0 ? true : false %>"> + <% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= i == 0 ? true : false %>"> <% end %> <% end %> <% end %> From 46e985b306b06fe37cd066cc1ecd69b504316fb0 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 12:44:25 -0500 Subject: [PATCH 33/81] Add 'dark_mode', 'thin_mode' as query parameters --- src/invidious.cr | 153 +++++++++++----------- src/invidious/views/components/player.ecr | 4 +- src/invidious/views/template.ecr | 4 +- src/invidious/views/watch.ecr | 10 +- 4 files changed, 84 insertions(+), 87 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 6ea4b54bd..998b92919 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -192,6 +192,7 @@ proxies = PROXY_LIST before_all do |env| env.response.headers["X-XSS-Protection"] = "1; mode=block;" env.response.headers["X-Content-Type-Options"] = "nosniff" + preferences = DEFAULT_USER_PREFERENCES.dup if env.request.cookies.has_key? "SID" headers = HTTP::Headers.new @@ -210,9 +211,9 @@ before_all do |env| env.set "challenge", challenge env.set "token", token - locale = user.preferences.locale + preferences = user.preferences + env.set "user", user - env.set "preferences", user.preferences env.set "sid", sid end else @@ -223,9 +224,9 @@ before_all do |env| env.set "challenge", challenge env.set "token", token - locale = user.preferences.locale + preferences = user.preferences + env.set "user", user - env.set "preferences", user.preferences env.set "sid", sid rescue ex end @@ -234,14 +235,20 @@ before_all do |env| if env.request.cookies.has_key? "PREFS" preferences = Preferences.from_json(env.request.cookies["PREFS"].value) - - locale = preferences.locale - env.set "preferences", preferences end - locale = env.params.query["hl"]? || locale - locale ||= "en-US" - env.set "locale", locale + dark_mode = env.params.query["dark_mode"]? || preferences.dark_mode.to_s + dark_mode = dark_mode == "true" + + thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s + thin_mode = thin_mode == "true" + + locale = env.params.query["hl"]? || preferences.locale + + preferences.dark_mode = dark_mode + preferences.thin_mode = thin_mode + preferences.locale = locale + env.set "preferences", preferences current_page = env.request.path if env.request.query @@ -258,7 +265,7 @@ before_all do |env| end get "/" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -285,14 +292,14 @@ get "/" do |env| end get "/licenses" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? rendered "licenses" end # Videos get "/watch" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? if env.params.query.to_s.includes?("%20") || env.params.query.to_s.includes?("+") @@ -464,7 +471,7 @@ get "/watch" do |env| end get "/embed/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? id = env.params.url["id"] if id.includes?("%20") || id.includes?("+") || env.params.query.to_s.includes?("%20") || env.params.query.to_s.includes?("+") @@ -558,7 +565,7 @@ end # Playlists get "/playlist" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? plid = env.params.query["list"]? if !plid @@ -589,7 +596,7 @@ get "/playlist" do |env| end get "/mix" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? rdid = env.params.query["list"]? if !rdid @@ -612,7 +619,7 @@ end # Search get "/opensearch.xml" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/opensearchdescription+xml" host = make_host_url(config, Kemal.config) @@ -630,7 +637,7 @@ get "/opensearch.xml" do |env| end get "/results" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? query = env.params.query["search_query"]? query ||= env.params.query["q"]? @@ -647,7 +654,7 @@ get "/results" do |env| end get "/search" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? query = env.params.query["search_query"]? @@ -733,7 +740,7 @@ end # Users get "/login" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -779,7 +786,7 @@ end # See https://github.com/rg3/youtube-dl/blob/master/youtube_dl/extractor/youtube.py#L79 post "/login" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? referer = get_referer(env, "/feed/subscriptions") @@ -1156,7 +1163,7 @@ post "/login" do |env| end get "/signout" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1189,22 +1196,17 @@ get "/signout" do |env| end get "/preferences" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + referer = get_referer(env) - if preferences = env.get? "preferences" - preferences = preferences.as(Preferences) + preferences = env.get("preferences").as(Preferences) - templated "preferences" - else - preferences = DEFAULT_USER_PREFERENCES - - templated "preferences" - end + templated "preferences" end post "/preferences" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? referer = get_referer(env) video_loop = env.params.body["video_loop"]?.try &.as(String) @@ -1347,7 +1349,7 @@ post "/preferences" do |env| end get "/toggle_theme" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? referer = get_referer(env) if user = env.get? "user" @@ -1356,14 +1358,9 @@ get "/toggle_theme" do |env| preferences.dark_mode = !preferences.dark_mode PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences.to_json, user.email) - elsif preferences = env.get? "preferences" - preferences = preferences.as(Preferences) - preferences.dark_mode = !preferences.dark_mode - - env.response.cookies["PREFS"] = preferences.to_json else - preferences = DEFAULT_USER_PREFERENCES - preferences.dark_mode = true + preferences = env.get("preferences").as(Preferences) + preferences.dark_mode = !preferences.dark_mode env.response.cookies["PREFS"] = preferences.to_json end @@ -1372,7 +1369,7 @@ get "/toggle_theme" do |env| end get "/mark_watched" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env, "/feed/subscriptions") @@ -1402,7 +1399,7 @@ get "/mark_watched" do |env| end get "/mark_unwatched" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env, "/feed/history") @@ -1434,7 +1431,7 @@ end # /modify_notifications?receive_all_updates=false&receive_no_updates=false # will "unding" all subscriptions. get "/modify_notifications" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1481,7 +1478,7 @@ get "/modify_notifications" do |env| end get "/subscription_manager" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" sid = env.get? "sid" @@ -1558,7 +1555,7 @@ get "/subscription_manager" do |env| end get "/data_control" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1573,7 +1570,7 @@ get "/data_control" do |env| end post "/data_control" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1672,7 +1669,7 @@ post "/data_control" do |env| end get "/subscription_ajax" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1753,7 +1750,7 @@ get "/subscription_ajax" do |env| end get "/delete_account" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1770,7 +1767,7 @@ get "/delete_account" do |env| end post "/delete_account" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1803,7 +1800,7 @@ post "/delete_account" do |env| end get "/clear_watch_history" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1820,7 +1817,7 @@ get "/clear_watch_history" do |env| end post "/clear_watch_history" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -1847,7 +1844,7 @@ end # Feeds get "/feed/top" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? if config.top_enabled templated "top" @@ -1857,13 +1854,13 @@ get "/feed/top" do |env| end get "/feed/popular" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? templated "popular" end get "/feed/trending" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? trending_type = env.params.query["type"]? trending_type ||= "Default" @@ -1882,7 +1879,7 @@ get "/feed/trending" do |env| end get "/feed/subscriptions" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" sid = env.get? "sid" @@ -2030,7 +2027,7 @@ get "/feed/subscriptions" do |env| end get "/feed/history" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" referer = get_referer(env) @@ -2055,7 +2052,7 @@ get "/feed/history" do |env| end get "/feed/channel/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/atom+xml" @@ -2167,7 +2164,7 @@ get "/feed/channel/:ucid" do |env| end get "/feed/private" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/atom+xml" @@ -2282,7 +2279,7 @@ get "/feed/private" do |env| end get "/feed/playlist/:plid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/atom+xml" @@ -2388,7 +2385,7 @@ end # YouTube appears to let users set a "brand" URL that # is different from their username, so we convert that here get "/c/:user" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? client = make_client(YT_URL) user = env.params.url["user"] @@ -2425,7 +2422,7 @@ get "/user/:user/videos" do |env| end get "/channel/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -2478,7 +2475,7 @@ get "/channel/:ucid" do |env| end get "/channel/:ucid/videos" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? ucid = env.params.url["ucid"] params = env.request.query @@ -2493,7 +2490,7 @@ get "/channel/:ucid/videos" do |env| end get "/channel/:ucid/playlists" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? user = env.get? "user" if user @@ -2550,7 +2547,7 @@ get "/api/v1/stats" do |env| end get "/api/v1/captions/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -2655,7 +2652,7 @@ get "/api/v1/captions/:id" do |env| end get "/api/v1/comments/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? env.response.content_type = "application/json" @@ -2722,7 +2719,7 @@ get "/api/v1/comments/:id" do |env| end get "/api/v1/insights/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? id = env.params.url["id"] env.response.content_type = "application/json" @@ -2813,7 +2810,7 @@ get "/api/v1/insights/:id" do |env| end get "/api/v1/videos/:id" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3023,7 +3020,7 @@ get "/api/v1/videos/:id" do |env| end get "/api/v1/trending" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3074,7 +3071,7 @@ get "/api/v1/trending" do |env| end get "/api/v1/popular" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3108,7 +3105,7 @@ get "/api/v1/popular" do |env| end get "/api/v1/top" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3154,7 +3151,7 @@ get "/api/v1/top" do |env| end get "/api/v1/channels/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3362,7 +3359,7 @@ end ["/api/v1/channels/:ucid/videos", "/api/v1/channels/videos/:ucid"].each do |route| get route do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3433,7 +3430,7 @@ end ["/api/v1/channels/:ucid/latest", "/api/v1/channels/latest/:ucid"].each do |route| get route do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3485,7 +3482,7 @@ end ["/api/v1/channels/:ucid/playlists", "/api/v1/channels/playlists/:ucid"].each do |route| get route do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3553,7 +3550,7 @@ end end get "/api/v1/channels/search/:ucid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" @@ -3657,7 +3654,7 @@ get "/api/v1/channels/search/:ucid" do |env| end get "/api/v1/search" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? region = env.params.query["region"]? env.response.content_type = "application/json" @@ -3786,7 +3783,7 @@ get "/api/v1/search" do |env| end get "/api/v1/playlists/:plid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" plid = env.params.url["plid"] @@ -3889,7 +3886,7 @@ get "/api/v1/playlists/:plid" do |env| end get "/api/v1/mixes/:rdid" do |env| - locale = LOCALES[env.get("locale").as(String)]? + locale = LOCALES[env.get("preferences").as(Preferences).locale]? env.response.content_type = "application/json" diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index cecb9b2f8..160afb2d1 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -27,12 +27,12 @@ <% end %> <% preferred_captions.each_with_index do |caption, i| %> - " + " label="<%= caption.name.simpleText %>" <% if i == 0 %>default<% end %>> <% end %> <% captions.each do |caption| %> - " + " label="<%= caption.name.simpleText %>"> <% end %> <% end %> diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index de44dd3ee..c3abc7e94 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -18,14 +18,14 @@ - <% if env.get?("preferences").try &.as(Preferences).dark_mode %> + <% if env.get("preferences").as(Preferences).dark_mode %> <% else %> <% end %> -<% locale = LOCALES[env.get("locale").as(String)]? %> +<% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %>
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 39c46d2e0..5ad52a7f8 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -255,9 +255,9 @@ function get_playlist(timeouts = 0) { var plid = "<%= plid %>" if (plid.startsWith("RD")) { - var plid_url = "/api/v1/mixes/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("locale").as(String) %>"; + var plid_url = "/api/v1/mixes/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; } else { - var plid_url = "/api/v1/playlists/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("locale").as(String) %>"; + var plid_url = "/api/v1/playlists/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; } var xhr = new XMLHttpRequest(); @@ -321,7 +321,7 @@ function get_reddit_comments(timeouts = 0) { comments.innerHTML = '

'; - var url = "/api/v1/comments/<%= video.id %>?source=reddit&format=html&hl=<%= env.get("locale").as(String) %>"; + var url = "/api/v1/comments/<%= video.id %>?source=reddit&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var xhr = new XMLHttpRequest(); xhr.responseType = "json"; xhr.timeout = 20000; @@ -384,7 +384,7 @@ function get_youtube_comments(timeouts = 0) { comments.innerHTML = '

'; - var url = "/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("locale").as(String) %>"; + var url = "/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var xhr = new XMLHttpRequest(); xhr.responseType = "json"; xhr.timeout = 20000; @@ -442,7 +442,7 @@ function get_youtube_replies(target, load_more) { body.innerHTML = '

'; - var url = '/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("locale").as(String) %>&continuation=' + + var url = '/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("preferences").as(Preferences).locale %>&continuation=' + continuation; var xhr = new XMLHttpRequest(); xhr.responseType = 'json'; From 11ff40bcd68f4941fa40017f0bb6f5a8826effa1 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 12:55:05 -0500 Subject: [PATCH 34/81] Fix paths for 'local=true&raw=1' --- src/invidious.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index 998b92919..c89c67713 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -405,8 +405,8 @@ get "/watch" do |env| adaptive_fmts = video.adaptive_fmts(decrypt_function) if params[:local] - fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } - adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } end video_streams = video.video_streams(adaptive_fmts) @@ -511,8 +511,8 @@ get "/embed/:id" do |env| adaptive_fmts = video.adaptive_fmts(decrypt_function) if params[:local] - fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } - adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).query.not_nil! } + fmt_stream.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } + adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path } end video_streams = video.video_streams(adaptive_fmts) From f01152eda1986c901886f81f98cfb5e9cb7bc48b Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 13:14:30 -0500 Subject: [PATCH 35/81] Add 'host' to '/videoplayback' --- src/invidious.cr | 12 +++++++++--- src/invidious/videos.cr | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index c89c67713..ff17ab9bb 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4217,9 +4217,15 @@ end get "/videoplayback" do |env| query_params = env.params.query - fvip = query_params["fvip"]? || "3" - mn = query_params["mn"].split(",").pop - host = "https://r#{fvip}---#{mn}.googlevideo.com" + if query_params["host"]? && !query_params["host"].empty? + pp query_params["host"] + host = "https://#{query_params["host"]}" + else + fvip = query_params["fvip"]? || "3" + mn = query_params["mn"].split(",").pop + host = "https://r#{fvip}---#{mn}.googlevideo.com" + end + url = "/videoplayback?#{query_params.to_s}" headers = HTTP::Headers.new diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index b5bd20834..17d17d93c 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -317,6 +317,7 @@ class Video end streams.each do |fmt| + fmt["url"] += "&host=" + (URI.parse(fmt["url"]).host || "") fmt["url"] += decrypt_signature(fmt, decrypt_function) end @@ -384,6 +385,7 @@ class Video end adaptive_fmts.each do |fmt| + fmt["url"] += "&host=" + (URI.parse(fmt["url"]).host || "") fmt["url"] += decrypt_signature(fmt, decrypt_function) end From 58f4212aa853306ade1b34197f12f532135a48d4 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 13:32:46 -0500 Subject: [PATCH 36/81] Remove 'host' from query params --- src/invidious.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index ff17ab9bb..fa6a40905 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4218,8 +4218,8 @@ get "/videoplayback" do |env| query_params = env.params.query if query_params["host"]? && !query_params["host"].empty? - pp query_params["host"] host = "https://#{query_params["host"]}" + query_params.delete("host") else fvip = query_params["fvip"]? || "3" mn = query_params["mn"].split(",").pop From 1fcd1ff3e8ee1a300431bb24392733b632d41cce Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 14:07:55 -0500 Subject: [PATCH 37/81] Add better fallback for '/videoplayback' --- src/invidious.cr | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/invidious.cr b/src/invidious.cr index fa6a40905..3e5c4d5e8 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4217,13 +4217,14 @@ end get "/videoplayback" do |env| query_params = env.params.query + fvip = query_params["fvip"]? || "3" + mns = query_params["mn"].split(",") + if query_params["host"]? && !query_params["host"].empty? host = "https://#{query_params["host"]}" query_params.delete("host") else - fvip = query_params["fvip"]? || "3" - mn = query_params["mn"].split(",").pop - host = "https://r#{fvip}---#{mn}.googlevideo.com" + host = "https://r#{fvip}---#{mns.pop}.googlevideo.com" end url = "/videoplayback?#{query_params.to_s}" @@ -4244,11 +4245,11 @@ get "/videoplayback" do |env| response = client.head(url, headers) break rescue Socket::Addrinfo::Error - if fvip == "3" - break + if !mns.empty? + mn = mns.pop end - fvip = "3" + host = "https://r#{fvip}---#{mn}.googlevideo.com" rescue ex end From e86eb16d915dfaa9685ecad5c5570e468b818556 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Mon, 11 Mar 2019 16:17:40 -0500 Subject: [PATCH 38/81] Add temporary fix for crystal-lang/crystal#7383 --- src/invidious.cr | 2 +- src/invidious/helpers/helpers.cr | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/invidious.cr b/src/invidious.cr index 3e5c4d5e8..4fe018f95 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -4230,7 +4230,7 @@ get "/videoplayback" do |env| url = "/videoplayback?#{query_params.to_s}" headers = HTTP::Headers.new - {"Range", "Accept", "Accept-Encoding"}.each do |header| + {"Accept", "Accept-Encoding", "Connection", "Range"}.each do |header| if env.request.headers[header]? headers[header] = env.request.headers[header] end diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr index 075bf84eb..1cd40d41e 100644 --- a/src/invidious/helpers/helpers.cr +++ b/src/invidious/helpers/helpers.cr @@ -75,6 +75,14 @@ class DenyFrame < Kemal::Handler end end +# Temp fix for https://github.com/crystal-lang/crystal/issues/7383 +class HTTP::Client + private def handle_response(response) + # close unless response.keep_alive? + response + end +end + def rank_videos(db, n) top = [] of {Float64, String} From 19c32bf993d7909c91c2b511c4e32610148b1109 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 12 Mar 2019 10:01:36 -0500 Subject: [PATCH 39/81] Calculate player height based on viewport --- assets/css/default.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/css/default.css b/assets/css/default.css index cec255dda..33751d410 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -316,12 +316,12 @@ img.thumbnail { } .player-dimensions.vjs-fluid { - padding-top: 46.86%; + padding-top: 82vh; } #player-container { position: relative; - padding-bottom: 46.86%; + padding-bottom: 82vh; margin-left: 1em; margin-right: 1em; height: 0; From cf3f0fcc3986198ea89d4ef4ca11f51009bcb22e Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 12 Mar 2019 10:12:47 -0500 Subject: [PATCH 40/81] Add max-aspect-ratio to player --- assets/css/default.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/assets/css/default.css b/assets/css/default.css index 33751d410..464efd4b2 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -181,6 +181,16 @@ img.thumbnail { margin-right: 1em; } +@media only screen and (max-aspect-ratio: 16/9) { + .player-dimensions.vjs-fluid { + padding-top: 46.86% !important; + } + + #player-container { + padding-bottom: 46.86% !important; + } +} + @media screen and (max-width: 767px) { .navbar { flex-direction: column; From 97e604772551f77b3b73eb1389a679afff4fba94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Sat, 9 Mar 2019 05:04:31 +0000 Subject: [PATCH 41/81] =?UTF-8?q?Update=20Norwegian=20Bokm=C3=A5l=20transl?= =?UTF-8?q?ation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/nb_NO.json | 578 ++++++++++++++++++++++----------------------- 1 file changed, 289 insertions(+), 289 deletions(-) diff --git a/locales/nb_NO.json b/locales/nb_NO.json index f0f544b51..591b70b66 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -1,291 +1,291 @@ { - "`x` subscribers": "`x` abonnenter", - "`x` videos": "`x` videoer", - "LIVE": "SANNTIDSVISNING", - "Shared `x` ago": "Delt for `x` siden", - "Unsubscribe": "Opphev abonnement", - "Subscribe": "Abonner", - "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", - "View channel on YouTube": "Vis kanal på YouTube", - "newest": "nyeste", - "oldest": "eldste", - "popular": "populært", - "last": "", - "Next page": "Neste side", - "Previous page": "Forrige side", - "Clear watch history?": "Tøm visningshistorikk?", - "Yes": "Ja", - "No": "Nei", - "Import and Export Data": "Importer- og eksporter data", - "Import": "Importer", - "Import Invidious data": "Importer Invidious-data", - "Import YouTube subscriptions": "Importer YouTube-abonnenter", - "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", - "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", - "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", - "Export": "Eksporter", - "Export subscriptions as OPML": "Eksporter abonnenter som OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", - "Export data as JSON": "Eksporter data som JSON", - "Delete account?": "Slett konto?", - "History": "Historikk", - "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", - "JavaScript license information": "JavaScript-lisensinformasjon", - "source": "kilde", - "Login": "Logg inn", - "Login/Register": "Logg inn/registrer", - "Login to Google": "Logg inn med Google", - "User ID:": "Bruker-ID:", - "Password:": "Passord:", - "Time (h:mm:ss):": "Tid (h:mm:ss):", - "Text CAPTCHA": "Tekst-CAPTCHA", - "Image CAPTCHA": "Bilde-CAPTCHA", - "Sign In": "Innlogging", - "Register": "Registrer", - "Email:": "E-post:", - "Google verification code:": "Google-bekreftelseskode:", - "Preferences": "Innstillinger", - "Player preferences": "Avspillerinnstillinger", - "Always loop: ": "Alltid gjenta: ", - "Autoplay: ": "Autoavspilling: ", - "Autoplay next video: ": "Autospill neste video: ", - "Listen by default: ": "Lytt som forvalg: ", - "Default speed: ": "Forvalgt hastighet: ", - "Preferred video quality: ": "Foretrukket videokvalitet: ", - "Player volume: ": "Avspillerlydstyrke: ", - "Default comments: ": "Forvalgte kommentarer: ", - "Default captions: ": "Forvalgte undertitler: ", - "Fallback captions: ": "Tilbakefallsundertitler: ", - "Show related videos? ": "Vis relaterte videoer? ", - "Visual preferences": "Visuelle innstillinger", - "Dark mode: ": "Mørk drakt: ", - "Thin mode: ": "Tynt modus: ", - "Subscription preferences": "Abonnementsinnstillinger", - "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", - "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", - "Sort videos by: ": "Sorter videoer etter: ", - "published": "publisert", - "published - reverse": "publisert - motsatt", - "alphabetically": "alfabetisk", - "alphabetically - reverse": "alfabetisk - motsatt", - "channel name": "kanalnavn", - "channel name - reverse": "kanalnavn - motsatt", - "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", - "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", - "Only show unwatched: ": "Kun vis usette: ", - "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", - "Data preferences": "Datainnstillinger", - "Clear watch history": "Tøm visningshistorikk", - "Import/Export data": "Importer/eksporter data", - "Manage subscriptions": "Behandle abonnementer", - "Watch history": "Visningshistorikk", - "Delete account": "Slett konto", - "Administrator preferences": "Administratorinnstillinger", - "Default homepage: ": "Forvalgt hjemmeside: ", - "Feed menu: ": "Flyt-meny: ", - "Top enabled? ": "", - "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", - "Login enabled? ": "Innlogging påskrudd? ", - "Registration enabled? ": "Registrering påskrudd? ", - "Report statistics? ": "Innrapporter statistikk? ", - "Save preferences": "Lagre innstillinger", - "Subscription manager": "Abonnementsbehandler", - "`x` subscriptions": "`x` abonnementer", - "Import/Export": "Importer/eksporter", - "unsubscribe": "opphev abonnement", - "Subscriptions": "Abonnement", - "`x` unseen notifications": "`x` usette merknader", - "search": "søk", - "Sign out": "Logg ut", - "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", - "Source available here.": "Kildekode tilgjengelig her.", - "View JavaScript license information.": "Vis JavaScript-lisensinfo.", - "Trending": "Trendsettende", - "Watch video on Youtube": "Vis video på YouTube", - "Genre: ": "Sjanger: ", - "License: ": "Lisens: ", - "Family friendly? ": "Familievennlig? ", - "Wilson score: ": "Wilson-poengsum: ", - "Engagement: ": "Engasjement: ", - "Whitelisted regions: ": "Hvitlistede regioner: ", - "Blacklisted regions: ": "Svartelistede regioner: ", - "Shared `x`": "Delt `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", - "View YouTube comments": "Vis YouTube-kommentarer", - "View more comments on Reddit": "Vis flere kommenterer på Reddit", - "View `x` comments": "Vis `x` kommentarer", - "View Reddit comments": "Vis Reddit-kommentarer", - "Hide replies": "Skjul svar", - "Show replies": "Vis svar", - "Incorrect password": "Feil passord", - "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", - "Invalid TFA code": "Ugyldig tofaktorkode", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", - "Invalid answer": "Ugyldig svar", - "Invalid CAPTCHA": "Ugyldig CAPTCHA", - "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", - "User ID is a required field": "Bruker-ID er et påkrevd felt", - "Password is a required field": "Passord er et påkrevd felt", - "Invalid username or password": "Ugyldig brukernavn eller passord", - "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", - "Password cannot be empty": "Passordet kan ikke være tomt", - "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", - "Please sign in": "Logg inn", - "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", - "channel:`x`": "kanal `x`", - "Deleted or invalid channel": "Slettet eller ugyldig kanal", - "This channel does not exist.": "Denne kanalen finnes ikke.", - "Could not get channel info.": "Kunne ikke innhente kanalinfo.", - "Could not fetch comments": "Kunne ikke hente kommentarer", - "View `x` replies": "Vis `x` svar", - "`x` ago": "`x` siden", - "Load more": "Last inn flere", - "`x` points": "`x` poeng", - "Could not create mix.": "Kunne ikke opprette miks.", - "Playlist is empty": "Spillelisten er tom", - "Invalid playlist.": "Ugyldig spilleliste.", - "Playlist does not exist.": "Spillelisten finnes ikke.", - "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", - "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", - "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", - "Invalid challenge": "Ugyldig utfordring", - "Invalid token": "Ugyldig symbol", - "Invalid user": "Ugyldig bruker", - "Token is expired, please try again": "Symbol utløpt, prøv igjen", - "English": "Engelsk", - "English (auto-generated)": "Engelsk (auto-generert)", - "Afrikaans": "", - "Albanian": "Albansk", - "Amharic": "", - "Arabic": "Arabisk", - "Armenian": "Armensk", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "Hviterussisk", - "Bosnian": "Bosnisk", - "Bulgarian": "Bulgarsk", - "Burmese": "Burmesisk", - "Catalan": "Katalansk", - "Cebuano": "", - "Chinese (Simplified)": "", - "Chinese (Traditional)": "", - "Corsican": "", - "Croatian": "", - "Czech": "Tsjekkisk", - "Danish": "Dansk", - "Dutch": "", - "Esperanto": "Esperanto", - "Estonian": "", - "Filipino": "", - "Finnish": "Finsk", - "French": "Fransk", - "Galician": "", - "Georgian": "", - "German": "", - "Greek": "", - "Gujarati": "", - "Haitian Creole": "", - "Hausa": "", - "Hawaiian": "", - "Hebrew": "", - "Hindi": "", - "Hmong": "", - "Hungarian": "Ungarsk", - "Icelandic": "Islandsk", - "Igbo": "", - "Indonesian": "Indonesisk", - "Irish": "Irsk", - "Italian": "Italiensk", - "Japanese": "Japansk", - "Javanese": "", - "Kannada": "", - "Kazakh": "", - "Khmer": "", - "Korean": "", - "Kurdish": "", - "Kyrgyz": "", - "Lao": "", - "Latin": "", - "Latvian": "", - "Lithuanian": "", - "Luxembourgish": "", - "Macedonian": "", - "Malagasy": "", - "Malay": "", - "Malayalam": "", - "Maltese": "", - "Maori": "", - "Marathi": "", - "Mongolian": "", - "Nepali": "", - "Norwegian": "Norsk bokmål", - "Nyanja": "", - "Pashto": "", - "Persian": "", - "Polish": "", - "Portuguese": "", - "Punjabi": "", - "Romanian": "", - "Russian": "Russisk", - "Samoan": "", - "Scottish Gaelic": "", - "Serbian": "Serbisk", - "Shona": "", - "Sindhi": "", - "Sinhala": "", - "Slovak": "Slovakisk", - "Slovenian": "Slovensk", - "Somali": "Somali", - "Southern Sotho": "", - "Spanish": "Spansk", - "Spanish (Latin America)": "", - "Sundanese": "", - "Swahili": "", - "Swedish": "Svensk", - "Tajik": "", - "Tamil": "", - "Telugu": "", - "Thai": "", - "Turkish": "Tyrkisk", - "Ukrainian": "Ukrainsk", - "Urdu": "", - "Uzbek": "", - "Vietnamese": "Vietnamesisk", - "Welsh": "", - "Western Frisian": "", - "Xhosa": "", - "Yiddish": "", - "Yoruba": "", - "Zulu": "", - "`x` years": "`x` år", - "`x` months": "`x` måneder", - "`x` weeks": "`x` uker", - "`x` days": "`x` dager", - "`x` hours": "`x` timer", - "`x` minutes": "`x` minutter", - "`x` seconds": "`x` sekunder", - "Fallback comments: ": "Tilbakefallskommentarer: ", - "Popular": "Pupulært", - "Top": "Topp", - "About": "Om", - "Rating: ": "Vurdering: ", - "Language: ": "Språk: ", - "Default": "Forvalg", - "Music": "Musikk", - "Gaming": "Spill", - "News": "Nyheter", - "Movies": "Filmer", - "Download": "Last ned", - "Download as: ": "Last ned som: ", - "%A %B %-d, %Y": "", - "(edited)": "(redigert)", - "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", - "`x` marked it with a ❤": "`x` levnet et ❤", - "Audio mode": "Lydmodus", - "Video mode": "Video-modus", - "Videos": "", - "Playlists": "", - "Current version: ": "" + "`x` subscribers": "`x` abonnenter", + "`x` videos": "`x` videoer", + "LIVE": "SANNTIDSVISNING", + "Shared `x` ago": "Delt for `x` siden", + "Unsubscribe": "Opphev abonnement", + "Subscribe": "Abonner", + "Login to subscribe to `x`": "Logg inn for å abonnere på `x`", + "View channel on YouTube": "Vis kanal på YouTube", + "newest": "nyeste", + "oldest": "eldste", + "popular": "populært", + "last": "siste", + "Next page": "Neste side", + "Previous page": "Forrige side", + "Clear watch history?": "Tøm visningshistorikk?", + "Yes": "Ja", + "No": "Nei", + "Import and Export Data": "Importer- og eksporter data", + "Import": "Importer", + "Import Invidious data": "Importer Invidious-data", + "Import YouTube subscriptions": "Importer YouTube-abonnenter", + "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)", + "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)", + "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)", + "Export": "Eksporter", + "Export subscriptions as OPML": "Eksporter abonnenter som OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)", + "Export data as JSON": "Eksporter data som JSON", + "Delete account?": "Slett konto?", + "History": "Historikk", + "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube", + "JavaScript license information": "JavaScript-lisensinformasjon", + "source": "kilde", + "Login": "Logg inn", + "Login/Register": "Logg inn/registrer", + "Login to Google": "Logg inn med Google", + "User ID:": "Bruker-ID:", + "Password:": "Passord:", + "Time (h:mm:ss):": "Tid (h:mm:ss):", + "Text CAPTCHA": "Tekst-CAPTCHA", + "Image CAPTCHA": "Bilde-CAPTCHA", + "Sign In": "Innlogging", + "Register": "Registrer", + "Email:": "E-post:", + "Google verification code:": "Google-bekreftelseskode:", + "Preferences": "Innstillinger", + "Player preferences": "Avspillerinnstillinger", + "Always loop: ": "Alltid gjenta: ", + "Autoplay: ": "Autoavspilling: ", + "Autoplay next video: ": "Autospill neste video: ", + "Listen by default: ": "Lytt som forvalg: ", + "Default speed: ": "Forvalgt hastighet: ", + "Preferred video quality: ": "Foretrukket videokvalitet: ", + "Player volume: ": "Avspillerlydstyrke: ", + "Default comments: ": "Forvalgte kommentarer: ", + "Default captions: ": "Forvalgte undertitler: ", + "Fallback captions: ": "Tilbakefallsundertitler: ", + "Show related videos? ": "Vis relaterte videoer? ", + "Visual preferences": "Visuelle innstillinger", + "Dark mode: ": "Mørk drakt: ", + "Thin mode: ": "Tynt modus: ", + "Subscription preferences": "Abonnementsinnstillinger", + "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ", + "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ", + "Sort videos by: ": "Sorter videoer etter: ", + "published": "publisert", + "published - reverse": "publisert - motsatt", + "alphabetically": "alfabetisk", + "alphabetically - reverse": "alfabetisk - motsatt", + "channel name": "kanalnavn", + "channel name - reverse": "kanalnavn - motsatt", + "Only show latest video from channel: ": "Kun vis siste video fra kanal: ", + "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ", + "Only show unwatched: ": "Kun vis usette: ", + "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ", + "Data preferences": "Datainnstillinger", + "Clear watch history": "Tøm visningshistorikk", + "Import/Export data": "Importer/eksporter data", + "Manage subscriptions": "Behandle abonnementer", + "Watch history": "Visningshistorikk", + "Delete account": "Slett konto", + "Administrator preferences": "Administratorinnstillinger", + "Default homepage: ": "Forvalgt hjemmeside: ", + "Feed menu: ": "Flyt-meny: ", + "Top enabled? ": "Topp påskrudd? ", + "CAPTCHA enabled? ": "CAPTCHA påskrudd? ", + "Login enabled? ": "Innlogging påskrudd? ", + "Registration enabled? ": "Registrering påskrudd? ", + "Report statistics? ": "Innrapporter statistikk? ", + "Save preferences": "Lagre innstillinger", + "Subscription manager": "Abonnementsbehandler", + "`x` subscriptions": "`x` abonnementer", + "Import/Export": "Importer/eksporter", + "unsubscribe": "opphev abonnement", + "Subscriptions": "Abonnement", + "`x` unseen notifications": "`x` usette merknader", + "search": "søk", + "Sign out": "Logg ut", + "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", + "Source available here.": "Kildekode tilgjengelig her.", + "View JavaScript license information.": "Vis JavaScript-lisensinfo.", + "Trending": "Trendsettende", + "Watch video on Youtube": "Vis video på YouTube", + "Genre: ": "Sjanger: ", + "License: ": "Lisens: ", + "Family friendly? ": "Familievennlig? ", + "Wilson score: ": "Wilson-poengsum: ", + "Engagement: ": "Engasjement: ", + "Whitelisted regions: ": "Hvitlistede regioner: ", + "Blacklisted regions: ": "Svartelistede regioner: ", + "Shared `x`": "Delt `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.", + "View YouTube comments": "Vis YouTube-kommentarer", + "View more comments on Reddit": "Vis flere kommenterer på Reddit", + "View `x` comments": "Vis `x` kommentarer", + "View Reddit comments": "Vis Reddit-kommentarer", + "Hide replies": "Skjul svar", + "Show replies": "Vis svar", + "Incorrect password": "Feil passord", + "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.", + "Invalid TFA code": "Ugyldig tofaktorkode", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.", + "Invalid answer": "Ugyldig svar", + "Invalid CAPTCHA": "Ugyldig CAPTCHA", + "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt", + "User ID is a required field": "Bruker-ID er et påkrevd felt", + "Password is a required field": "Passord er et påkrevd felt", + "Invalid username or password": "Ugyldig brukernavn eller passord", + "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"", + "Password cannot be empty": "Passordet kan ikke være tomt", + "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn", + "Please sign in": "Logg inn", + "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`", + "channel:`x`": "kanal `x`", + "Deleted or invalid channel": "Slettet eller ugyldig kanal", + "This channel does not exist.": "Denne kanalen finnes ikke.", + "Could not get channel info.": "Kunne ikke innhente kanalinfo.", + "Could not fetch comments": "Kunne ikke hente kommentarer", + "View `x` replies": "Vis `x` svar", + "`x` ago": "`x` siden", + "Load more": "Last inn flere", + "`x` points": "`x` poeng", + "Could not create mix.": "Kunne ikke opprette miks.", + "Playlist is empty": "Spillelisten er tom", + "Invalid playlist.": "Ugyldig spilleliste.", + "Playlist does not exist.": "Spillelisten finnes ikke.", + "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.", + "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt", + "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt", + "Invalid challenge": "Ugyldig utfordring", + "Invalid token": "Ugyldig symbol", + "Invalid user": "Ugyldig bruker", + "Token is expired, please try again": "Symbol utløpt, prøv igjen", + "English": "Engelsk", + "English (auto-generated)": "Engelsk (auto-generert)", + "Afrikaans": "", + "Albanian": "Albansk", + "Amharic": "", + "Arabic": "Arabisk", + "Armenian": "Armensk", + "Azerbaijani": "", + "Bangla": "", + "Basque": "", + "Belarusian": "Hviterussisk", + "Bosnian": "Bosnisk", + "Bulgarian": "Bulgarsk", + "Burmese": "Burmesisk", + "Catalan": "Katalansk", + "Cebuano": "", + "Chinese (Simplified)": "", + "Chinese (Traditional)": "", + "Corsican": "", + "Croatian": "", + "Czech": "Tsjekkisk", + "Danish": "Dansk", + "Dutch": "", + "Esperanto": "Esperanto", + "Estonian": "", + "Filipino": "", + "Finnish": "Finsk", + "French": "Fransk", + "Galician": "", + "Georgian": "", + "German": "", + "Greek": "", + "Gujarati": "", + "Haitian Creole": "", + "Hausa": "", + "Hawaiian": "", + "Hebrew": "", + "Hindi": "", + "Hmong": "", + "Hungarian": "Ungarsk", + "Icelandic": "Islandsk", + "Igbo": "", + "Indonesian": "Indonesisk", + "Irish": "Irsk", + "Italian": "Italiensk", + "Japanese": "Japansk", + "Javanese": "", + "Kannada": "", + "Kazakh": "", + "Khmer": "", + "Korean": "", + "Kurdish": "", + "Kyrgyz": "", + "Lao": "", + "Latin": "", + "Latvian": "", + "Lithuanian": "", + "Luxembourgish": "", + "Macedonian": "", + "Malagasy": "", + "Malay": "", + "Malayalam": "", + "Maltese": "", + "Maori": "", + "Marathi": "", + "Mongolian": "", + "Nepali": "", + "Norwegian": "Norsk bokmål", + "Nyanja": "", + "Pashto": "", + "Persian": "", + "Polish": "", + "Portuguese": "", + "Punjabi": "", + "Romanian": "", + "Russian": "Russisk", + "Samoan": "", + "Scottish Gaelic": "", + "Serbian": "Serbisk", + "Shona": "", + "Sindhi": "", + "Sinhala": "", + "Slovak": "Slovakisk", + "Slovenian": "Slovensk", + "Somali": "Somali", + "Southern Sotho": "", + "Spanish": "Spansk", + "Spanish (Latin America)": "", + "Sundanese": "", + "Swahili": "", + "Swedish": "Svensk", + "Tajik": "", + "Tamil": "", + "Telugu": "", + "Thai": "", + "Turkish": "Tyrkisk", + "Ukrainian": "Ukrainsk", + "Urdu": "", + "Uzbek": "", + "Vietnamese": "Vietnamesisk", + "Welsh": "", + "Western Frisian": "", + "Xhosa": "", + "Yiddish": "", + "Yoruba": "", + "Zulu": "", + "`x` years": "`x` år", + "`x` months": "`x` måneder", + "`x` weeks": "`x` uker", + "`x` days": "`x` dager", + "`x` hours": "`x` timer", + "`x` minutes": "`x` minutter", + "`x` seconds": "`x` sekunder", + "Fallback comments: ": "Tilbakefallskommentarer: ", + "Popular": "Pupulært", + "Top": "Topp", + "About": "Om", + "Rating: ": "Vurdering: ", + "Language: ": "Språk: ", + "Default": "Forvalg", + "Music": "Musikk", + "Gaming": "Spill", + "News": "Nyheter", + "Movies": "Filmer", + "Download": "Last ned", + "Download as: ": "Last ned som: ", + "%A %B %-d, %Y": "", + "(edited)": "(redigert)", + "Youtube permalink of the comment": "Permanent YouTube-lenke til innholdet", + "`x` marked it with a ❤": "`x` levnet et ❤", + "Audio mode": "Lydmodus", + "Video mode": "Video-modus", + "Videos": "Videoer", + "Playlists": "Spillelister", + "Current version: ": "Nåværende versjon: " } From 1ac611239e8c49721085418855749eb012236bfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Zieli=C5=84ski?= Date: Sat, 9 Mar 2019 16:16:23 +0000 Subject: [PATCH 42/81] Update Polish translation --- locales/pl.json | 578 ++++++++++++++++++++++++------------------------ 1 file changed, 289 insertions(+), 289 deletions(-) diff --git a/locales/pl.json b/locales/pl.json index 0db43c51b..4bf21efac 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -1,291 +1,291 @@ { - "`x` subscribers": "`x` subskrybcji", - "`x` videos": "`x` filmów", - "LIVE": "NA ŻYWO", - "Shared `x` ago": "Udostępniono `x` temu", - "Unsubscribe": "Odsubskrybuj", - "Subscribe": "Subskrybuj", - "Login to subscribe to `x`": "Zaloguj się, aby subskrybować `x`", - "View channel on YouTube": "Wyświetl kanał na YouTube", - "newest": "najnowsze", - "oldest": "najstarsze", - "popular": "popularne", - "last": "", - "Next page": "Następna strona", - "Previous page": "Poprzednia strona", - "Clear watch history?": "Wyczyścić historię?", - "Yes": "Tak", - "No": "Nie", - "Import and Export Data": "Import i eksport danych", - "Import": "Import", - "Import Invidious data": "Importuj dane Invidious", - "Import YouTube subscriptions": "Importuj subskrybcje z YouTube", - "Import FreeTube subscriptions (.db)": "Importuj subskrybcje z FreeTube (.db)", - "Import NewPipe subscriptions (.json)": "Importuj subskrybcje z NewPipe (.json)", - "Import NewPipe data (.zip)": "Importuj dane NewPipe (.zip)", - "Export": "Eksport", - "Export subscriptions as OPML": "Eksportuj subskrybcje jako OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksportuj subskrybcje jako OPML (dla NewPipe i FreeTube)", - "Export data as JSON": "Eksportuj dane jako JSON", - "Delete account?": "Usunąć konto?", - "History": "Historia", - "An alternative front-end to YouTube": "Alternatywny front-end dla YouTube", - "JavaScript license information": "Informacja o licencji JavaScript", - "source": "źródło", - "Login": "Zaloguj", - "Login/Register": "Zaloguj/Zarejestruj", - "Login to Google": "Zaloguj do Google", - "User ID:": "ID użytkownika:", - "Password:": "Hasło:", - "Time (h:mm:ss):": "Godzina (h:mm:ss):", - "Text CAPTCHA": "Tekst CAPTCHA", - "Image CAPTCHA": "Obraz CAPTCHA", - "Sign In": "Zaloguj się", - "Register": "Zarejestruj się", - "Email:": "Email:", - "Google verification code:": "Kod weryfikacyjny Google:", - "Preferences": "Preferencje", - "Player preferences": "Ustawienia odtwarzacza", - "Always loop: ": "Zawsze zapętlaj: ", - "Autoplay: ": "Autoodtwarzanie: ", - "Autoplay next video: ": "Odtwórz następny film: ", - "Listen by default: ": "Tryb dźwiękowy: ", - "Default speed: ": "Domyślna prędkość: ", - "Preferred video quality: ": "Preferowana jakość filmów: ", - "Player volume: ": "Głośność odtwarzacza: ", - "Default comments: ": "Domyślne komentarze: ", - "Default captions: ": "Domyślne napisy: ", - "Fallback captions: ": "Rezerwowe napisy: ", - "Show related videos? ": "Pokaż powiązane filmy? ", - "Visual preferences": "Preferencje Wizualne", - "Dark mode: ": "Ciemny motyw: ", - "Thin mode: ": "Tryb minimalny: ", - "Subscription preferences": "Preferencje subskrybcji", - "Redirect homepage to feed: ": "Przekieruj stronę główną do subskrybcji: ", - "Number of videos shown in feed: ": "Liczba filmów widoczna na stronie subskrybcji: ", - "Sort videos by: ": "Sortuj filmy po: ", - "published": "czasie publikacji", - "published - reverse": "czasie publikacji od najstarszych", - "alphabetically": "alfabetycznie", - "alphabetically - reverse": "alfabetycznie od tyłu", - "channel name": "nazwie kanału", - "channel name - reverse": "nazwie kanału od tyłu", - "Only show latest video from channel: ": "Pokazuj tylko najnowszy film z kanału: ", - "Only show latest unwatched video from channel: ": "Pokazuj tylko najnowszy nie obejrzany film z kanału: ", - "Only show unwatched: ": "Pokazuj tylko nie obejrzane: ", - "Only show notifications (if there are any): ": "Pokazuj tylko powiadomienia (jeśli są): ", - "Data preferences": "Preferencje danych", - "Clear watch history": "Wyczyść historię", - "Import/Export data": "Import/Eksport danych", - "Manage subscriptions": "Organizuj subskrybcje", - "Watch history": "Historia", - "Delete account": "Usuń konto", - "Administrator preferences": "", - "Default homepage: ": "", - "Feed menu: ": "", - "Top enabled? ": "", - "CAPTCHA enabled? ": "", - "Login enabled? ": "", - "Registration enabled? ": "", - "Report statistics? ": "", - "Save preferences": "Zapisz preferencje", - "Subscription manager": "Manager subskrybcji", - "`x` subscriptions": "`x` subskrybcji", - "Import/Export": "Import/Eksport", - "unsubscribe": "odsubskrybuj", - "Subscriptions": "Subskrybcje", - "`x` unseen notifications": "`x` niewidzianych powiadomień", - "search": "szukaj", - "Sign out": "Wyloguj", - "Released under the AGPLv3 by Omar Roth.": "Wydano na licencji AGPLv3 przez Omar Roth.", - "Source available here.": "Kod źródłowy dostępny tutaj.", - "View JavaScript license information.": "Wyświetl informację o licencji JavaScript.", - "Trending": "Na czasie", - "Watch video on Youtube": "Zobacz film na YouTube", - "Genre: ": "Gatunek: ", - "License: ": "Licencja: ", - "Family friendly? ": "Przyjazny rodzinie? ", - "Wilson score: ": "Punktacja Wilsona: ", - "Engagement: ": "Zaangażowanie: ", - "Whitelisted regions: ": "Dostępny na obszarach: ", - "Blacklisted regions: ": "Niedostępny na obszarach: ", - "Shared `x`": "Udostępniono `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Cześć! Wygląda na to, że masz wyłączoną obsługę JavaScriptu. Kliknij tutaj, żeby zobaczyć komentarze. Pamiętaj, że wczytywanie może potrwać dłużej.", - "View YouTube comments": "Wyświetl komentarze z YouTube", - "View more comments on Reddit": "Wyświetl więcej komentarzy na Reddicie", - "View `x` comments": "Wyświetl `x` komentarzy", - "View Reddit comments": "Wyświetl komentarze z Redditta", - "Hide replies": "Ukryj odpowiedzi", - "Show replies": "Pokaż odpowiedzi", - "Incorrect password": "Niepoprawne hasło", - "Quota exceeded, try again in a few hours": "Przekroczony limit zapytań, spróbuj ponownie za kilka godzin", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Nie udało się zalogować, upewnij się, że dwuetapowe uwierzytelnianie (Autentykator lub SMS) jest aktywne.", - "Invalid TFA code": "Niepoprawny kod TFA", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Nie udało się zalogować. To może być spowodowane wyłączoną dwustopniową autoryzacją na twoim koncie.", - "Invalid answer": "Niepoprawna odpowiedź", - "Invalid CAPTCHA": "CAPTCHA wykonane błędnie", - "CAPTCHA is a required field": "CAPTCHA jest polem wymaganym", - "User ID is a required field": "ID użytkownika jest polem wymaganym", - "Password is a required field": "Hasło jest polem wymaganym", - "Invalid username or password": "Niepoprawny login lub hasło", - "Please sign in using 'Sign in with Google'": "Zaloguj się używając \"Zaloguj się przez Google\"", - "Password cannot be empty": "Hasło nie może być puste", - "Password cannot be longer than 55 characters": "Hasło nie może być dłuższe niż 55 znaków", - "Please sign in": "Proszę się zalogować", - "Invidious Private Feed for `x`": "", - "channel:`x`": "kanał:`x", - "Deleted or invalid channel": "Usunięty lub niepoprawny kanał", - "This channel does not exist.": "Ten kanał nie istnieje.", - "Could not get channel info.": "Nie udało się uzyskać informacji o kanale.", - "Could not fetch comments": "Nie udało się pobrać komentarzy", - "View `x` replies": "Wyświetl `x` odpowiedzi", - "`x` ago": "`x` temu", - "Load more": "Wczytaj więcej", - "`x` points": "`x` punktów", - "Could not create mix.": "Nie udało się utworzyć miksu.", - "Playlist is empty": "Lista odtwarzania jest pusta", - "Invalid playlist.": "Niepoprawna lista.", - "Playlist does not exist.": "Lista odtwarzania nie istnieje.", - "Could not pull trending pages.": "Nie udało się pobrać strony na czasie.", - "Hidden field \"challenge\" is a required field": "Ukryte pole \"wyzwanie\" jest polem wymaganym", - "Hidden field \"token\" is a required field": "Ukryte pole \"token\" jest polem wymaganym", - "Invalid challenge": "Niepoprawne wyzwanie", - "Invalid token": "Niepoprawny token", - "Invalid user": "Niepoprawny użytkownik", - "Token is expired, please try again": "Token wygasł, spróbuj ponownie", - "English": "angielski", - "English (auto-generated)": "angielski (automatycznie generowane)", - "Afrikaans": "", - "Albanian": "albański", - "Amharic": "", - "Arabic": "arabski", - "Armenian": "", - "Azerbaijani": "", - "Bangla": "", - "Basque": "", - "Belarusian": "białoruski", - "Bosnian": "bośniacki", - "Bulgarian": "bułgarski", - "Burmese": "birmański", - "Catalan": "kataloński", - "Cebuano": "", - "Chinese (Simplified)": "chiński (uproszczony)", - "Chinese (Traditional)": "chiński (tradycyjny)", - "Corsican": "korsykański", - "Croatian": "chorwacki", - "Czech": "czeski", - "Danish": "duński", - "Dutch": "holenderski", - "Esperanto": "esperanto", - "Estonian": "estoński", - "Filipino": "filipiński", - "Finnish": "fiński", - "French": "francuski", - "Galician": "galicyjski", - "Georgian": "gruziński", - "German": "niemiecki", - "Greek": "grecki", - "Gujarati": "", - "Haitian Creole": "", - "Hausa": "", - "Hawaiian": "hawajski", - "Hebrew": "hebrajski", - "Hindi": "hindi", - "Hmong": "", - "Hungarian": "węgierski", - "Icelandic": "islandzki", - "Igbo": "", - "Indonesian": "indonezyjski", - "Irish": "irlandzki", - "Italian": "włoski", - "Japanese": "japoński", - "Javanese": "jawajski", - "Kannada": "", - "Kazakh": "kazachski", - "Khmer": "", - "Korean": "koreański", - "Kurdish": "kurdyjski", - "Kyrgyz": "kirgiski", - "Lao": "", - "Latin": "łaciński", - "Latvian": "łotewski", - "Lithuanian": "litewski", - "Luxembourgish": "luksemburski", - "Macedonian": "macedoński", - "Malagasy": "malgaski", - "Malay": "malajski", - "Malayalam": "", - "Maltese": "maltański", - "Maori": "", - "Marathi": "", - "Mongolian": "mongolski", - "Nepali": "nepalski", - "Norwegian": "norweski", - "Nyanja": "", - "Pashto": "", - "Persian": "perski", - "Polish": "polski", - "Portuguese": "portugalski", - "Punjabi": "", - "Romanian": "rumuński", - "Russian": "rosyjski", - "Samoan": "", - "Scottish Gaelic": "", - "Serbian": "serbski", - "Shona": "", - "Sindhi": "", - "Sinhala": "", - "Slovak": "słowacki", - "Slovenian": "słoweński", - "Somali": "somalijski", - "Southern Sotho": "", - "Spanish": "hiszpański", - "Spanish (Latin America)": "hiszpański (ameryka łacińska)", - "Sundanese": "", - "Swahili": "", - "Swedish": "szwedzki", - "Tajik": "", - "Tamil": "", - "Telugu": "", - "Thai": "tajski", - "Turkish": "turecki", - "Ukrainian": "ukraiński", - "Urdu": "", - "Uzbek": "uzbecki", - "Vietnamese": "wietnamski", - "Welsh": "walijski", - "Western Frisian": "", - "Xhosa": "", - "Yiddish": "", - "Yoruba": "", - "Zulu": "", - "`x` years": "`x` lat", - "`x` months": "`x` miesięcy", - "`x` weeks": "`x` tygodni", - "`x` days": "`x` dni", - "`x` hours": "`x` godzin", - "`x` minutes": "`x` minut", - "`x` seconds": "`x` sekund", - "Fallback comments: ": "Zastępcze komentarze: ", - "Popular": "Popularne", - "Top": "Na czasie", - "About": "Informacje", - "Rating: ": "Ocena: ", - "Language: ": "Język: ", - "Default": "", - "Music": "Muzyka", - "Gaming": "Gry", - "News": "Wiadomości", - "Movies": "Filmy", - "Download": "Pobierz", - "Download as: ": "Pobierz jako: ", - "%A %B %-d, %Y": "", - "(edited)": "(edytowany)", - "Youtube permalink of the comment": "Odnośnik bezpośredni do komentarza na YouTube", - "`x` marked it with a ❤": "", - "Audio mode": "Tryb audio", - "Video mode": "Tryb wideo", - "Videos": "", - "Playlists": "", - "Current version: ": "" + "`x` subscribers": "`x` subskrybcji", + "`x` videos": "`x` filmów", + "LIVE": "NA ŻYWO", + "Shared `x` ago": "Udostępniono `x` temu", + "Unsubscribe": "Odsubskrybuj", + "Subscribe": "Subskrybuj", + "Login to subscribe to `x`": "Zaloguj się, aby subskrybować `x`", + "View channel on YouTube": "Wyświetl kanał na YouTube", + "newest": "najnowsze", + "oldest": "najstarsze", + "popular": "popularne", + "last": "", + "Next page": "Następna strona", + "Previous page": "Poprzednia strona", + "Clear watch history?": "Wyczyścić historię?", + "Yes": "Tak", + "No": "Nie", + "Import and Export Data": "Import i eksport danych", + "Import": "Import", + "Import Invidious data": "Importuj dane Invidious", + "Import YouTube subscriptions": "Importuj subskrybcje z YouTube", + "Import FreeTube subscriptions (.db)": "Importuj subskrybcje z FreeTube (.db)", + "Import NewPipe subscriptions (.json)": "Importuj subskrybcje z NewPipe (.json)", + "Import NewPipe data (.zip)": "Importuj dane NewPipe (.zip)", + "Export": "Eksport", + "Export subscriptions as OPML": "Eksportuj subskrybcje jako OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksportuj subskrybcje jako OPML (dla NewPipe i FreeTube)", + "Export data as JSON": "Eksportuj dane jako JSON", + "Delete account?": "Usunąć konto?", + "History": "Historia", + "An alternative front-end to YouTube": "Alternatywny front-end dla YouTube", + "JavaScript license information": "Informacja o licencji JavaScript", + "source": "źródło", + "Login": "Zaloguj", + "Login/Register": "Zaloguj/Zarejestruj", + "Login to Google": "Zaloguj do Google", + "User ID:": "ID użytkownika:", + "Password:": "Hasło:", + "Time (h:mm:ss):": "Godzina (h:mm:ss):", + "Text CAPTCHA": "Tekst CAPTCHA", + "Image CAPTCHA": "Obraz CAPTCHA", + "Sign In": "Zaloguj się", + "Register": "Zarejestruj się", + "Email:": "Email:", + "Google verification code:": "Kod weryfikacyjny Google:", + "Preferences": "Preferencje", + "Player preferences": "Ustawienia odtwarzacza", + "Always loop: ": "Zawsze zapętlaj: ", + "Autoplay: ": "Autoodtwarzanie: ", + "Autoplay next video: ": "Odtwórz następny film: ", + "Listen by default: ": "Tryb dźwiękowy: ", + "Default speed: ": "Domyślna prędkość: ", + "Preferred video quality: ": "Preferowana jakość filmów: ", + "Player volume: ": "Głośność odtwarzacza: ", + "Default comments: ": "Domyślne komentarze: ", + "Default captions: ": "Domyślne napisy: ", + "Fallback captions: ": "Zastępcze napisy: ", + "Show related videos? ": "Pokaż powiązane filmy? ", + "Visual preferences": "Preferencje Wizualne", + "Dark mode: ": "Ciemny motyw: ", + "Thin mode: ": "Tryb minimalny: ", + "Subscription preferences": "Preferencje subskrybcji", + "Redirect homepage to feed: ": "Przekieruj stronę główną do subskrybcji: ", + "Number of videos shown in feed: ": "Liczba filmów widoczna na stronie subskrybcji: ", + "Sort videos by: ": "Sortuj filmy: ", + "published": "po czasie publikacji", + "published - reverse": "po czasie publikacji od najstarszych", + "alphabetically": "alfabetycznie", + "alphabetically - reverse": "alfabetycznie od tyłu", + "channel name": "po nazwie kanału", + "channel name - reverse": "po nazwie kanału od tyłu", + "Only show latest video from channel: ": "Pokazuj tylko najnowszy film z kanału: ", + "Only show latest unwatched video from channel: ": "Pokazuj tylko najnowszy nie obejrzany film z kanału: ", + "Only show unwatched: ": "Pokazuj tylko nie obejrzane: ", + "Only show notifications (if there are any): ": "Pokazuj tylko powiadomienia (jeśli są): ", + "Data preferences": "Preferencje danych", + "Clear watch history": "Wyczyść historię", + "Import/Export data": "Import/Eksport danych", + "Manage subscriptions": "Organizuj subskrybcje", + "Watch history": "Historia", + "Delete account": "Usuń konto", + "Administrator preferences": "Preferencje administratora", + "Default homepage: ": "Domyślna strona główna: ", + "Feed menu: ": "", + "Top enabled? ": "", + "CAPTCHA enabled? ": "CAPTCHA aktywna? ", + "Login enabled? ": "Logowanie włączone? ", + "Registration enabled? ": "Rejestracja włączona? ", + "Report statistics? ": "Raportować statystyki? ", + "Save preferences": "Zapisz preferencje", + "Subscription manager": "Manager subskrybcji", + "`x` subscriptions": "`x` subskrybcji", + "Import/Export": "Import/Eksport", + "unsubscribe": "odsubskrybuj", + "Subscriptions": "Subskrybcje", + "`x` unseen notifications": "`x` niewidzianych powiadomień", + "search": "szukaj", + "Sign out": "Wyloguj", + "Released under the AGPLv3 by Omar Roth.": "Wydano na licencji AGPLv3 przez Omar Roth.", + "Source available here.": "Kod źródłowy dostępny tutaj.", + "View JavaScript license information.": "Wyświetl informację o licencji JavaScript.", + "Trending": "Na czasie", + "Watch video on Youtube": "Zobacz film na YouTube", + "Genre: ": "Gatunek: ", + "License: ": "Licencja: ", + "Family friendly? ": "Przyjazny rodzinie? ", + "Wilson score: ": "Punktacja Wilsona: ", + "Engagement: ": "Zaangażowanie: ", + "Whitelisted regions: ": "Dostępny na obszarach: ", + "Blacklisted regions: ": "Niedostępny na obszarach: ", + "Shared `x`": "Udostępniono `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Cześć! Wygląda na to, że masz wyłączoną obsługę JavaScriptu. Kliknij tutaj, żeby zobaczyć komentarze. Pamiętaj, że wczytywanie może potrwać dłużej.", + "View YouTube comments": "Wyświetl komentarze z YouTube", + "View more comments on Reddit": "Wyświetl więcej komentarzy na Reddicie", + "View `x` comments": "Wyświetl `x` komentarzy", + "View Reddit comments": "Wyświetl komentarze z Redditta", + "Hide replies": "Ukryj odpowiedzi", + "Show replies": "Pokaż odpowiedzi", + "Incorrect password": "Niepoprawne hasło", + "Quota exceeded, try again in a few hours": "Przekroczony limit zapytań, spróbuj ponownie za kilka godzin", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Nie udało się zalogować, upewnij się, że dwuetapowe uwierzytelnianie (Autentykator lub SMS) jest aktywne.", + "Invalid TFA code": "Niepoprawny kod TFA", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Nie udało się zalogować. To może być spowodowane wyłączoną dwustopniową autoryzacją na twoim koncie.", + "Invalid answer": "Niepoprawna odpowiedź", + "Invalid CAPTCHA": "CAPTCHA wykonane błędnie", + "CAPTCHA is a required field": "CAPTCHA jest polem wymaganym", + "User ID is a required field": "ID użytkownika jest polem wymaganym", + "Password is a required field": "Hasło jest polem wymaganym", + "Invalid username or password": "Niepoprawny login lub hasło", + "Please sign in using 'Sign in with Google'": "Zaloguj się używając \"Zaloguj się przez Google\"", + "Password cannot be empty": "Hasło nie może być puste", + "Password cannot be longer than 55 characters": "Hasło nie może być dłuższe niż 55 znaków", + "Please sign in": "Proszę się zalogować", + "Invidious Private Feed for `x`": "", + "channel:`x`": "kanał:`x", + "Deleted or invalid channel": "Usunięty lub niepoprawny kanał", + "This channel does not exist.": "Ten kanał nie istnieje.", + "Could not get channel info.": "Nie udało się uzyskać informacji o kanale.", + "Could not fetch comments": "Nie udało się pobrać komentarzy", + "View `x` replies": "Wyświetl `x` odpowiedzi", + "`x` ago": "`x` temu", + "Load more": "Wczytaj więcej", + "`x` points": "`x` punktów", + "Could not create mix.": "Nie udało się utworzyć miksu.", + "Playlist is empty": "Lista odtwarzania jest pusta", + "Invalid playlist.": "Niepoprawna lista.", + "Playlist does not exist.": "Lista odtwarzania nie istnieje.", + "Could not pull trending pages.": "Nie udało się pobrać strony na czasie.", + "Hidden field \"challenge\" is a required field": "Ukryte pole \"wyzwanie\" jest polem wymaganym", + "Hidden field \"token\" is a required field": "Ukryte pole \"token\" jest polem wymaganym", + "Invalid challenge": "Niepoprawne wyzwanie", + "Invalid token": "Niepoprawny token", + "Invalid user": "Niepoprawny użytkownik", + "Token is expired, please try again": "Token wygasł, spróbuj ponownie", + "English": "angielski", + "English (auto-generated)": "angielski (automatycznie generowane)", + "Afrikaans": "afrykanerski", + "Albanian": "albański", + "Amharic": "amharski", + "Arabic": "arabski", + "Armenian": "armeński", + "Azerbaijani": "azerski", + "Bangla": "bengalski", + "Basque": "baskijski", + "Belarusian": "białoruski", + "Bosnian": "bośniacki", + "Bulgarian": "bułgarski", + "Burmese": "birmański", + "Catalan": "kataloński", + "Cebuano": "cebuański", + "Chinese (Simplified)": "chiński (uproszczony)", + "Chinese (Traditional)": "chiński (tradycyjny)", + "Corsican": "korsykański", + "Croatian": "chorwacki", + "Czech": "czeski", + "Danish": "duński", + "Dutch": "holenderski", + "Esperanto": "esperanto", + "Estonian": "estoński", + "Filipino": "filipiński", + "Finnish": "fiński", + "French": "francuski", + "Galician": "galicyjski", + "Georgian": "gruziński", + "German": "niemiecki", + "Greek": "grecki", + "Gujarati": "gudźarati", + "Haitian Creole": "kreolski haitański", + "Hausa": "hausa", + "Hawaiian": "hawajski", + "Hebrew": "hebrajski", + "Hindi": "hindi", + "Hmong": "hmong", + "Hungarian": "węgierski", + "Icelandic": "islandzki", + "Igbo": "ibo", + "Indonesian": "indonezyjski", + "Irish": "irlandzki", + "Italian": "włoski", + "Japanese": "japoński", + "Javanese": "jawajski", + "Kannada": "kannada", + "Kazakh": "kazachski", + "Khmer": "khmerski", + "Korean": "koreański", + "Kurdish": "kurdyjski", + "Kyrgyz": "kirgiski", + "Lao": "laotański", + "Latin": "łaciński", + "Latvian": "łotewski", + "Lithuanian": "litewski", + "Luxembourgish": "luksemburski", + "Macedonian": "macedoński", + "Malagasy": "malgaski", + "Malay": "malajski", + "Malayalam": "malajalam", + "Maltese": "maltański", + "Maori": "maoryski", + "Marathi": "marathi", + "Mongolian": "mongolski", + "Nepali": "nepalski", + "Norwegian": "norweski", + "Nyanja": "njandża", + "Pashto": "paszto", + "Persian": "perski", + "Polish": "polski", + "Portuguese": "portugalski", + "Punjabi": "pendżabski", + "Romanian": "rumuński", + "Russian": "rosyjski", + "Samoan": "samoański", + "Scottish Gaelic": "gaelicki szkocki", + "Serbian": "serbski", + "Shona": "shona", + "Sindhi": "sindhi", + "Sinhala": "syngaleski", + "Slovak": "słowacki", + "Slovenian": "słoweński", + "Somali": "somalijski", + "Southern Sotho": "sotho południowy", + "Spanish": "hiszpański", + "Spanish (Latin America)": "hiszpański (ameryka łacińska)", + "Sundanese": "sundajski", + "Swahili": "suahili", + "Swedish": "szwedzki", + "Tajik": "tadżycki", + "Tamil": "tamilski", + "Telugu": "telugu", + "Thai": "tajski", + "Turkish": "turecki", + "Ukrainian": "ukraiński", + "Urdu": "urdu", + "Uzbek": "uzbecki", + "Vietnamese": "wietnamski", + "Welsh": "walijski", + "Western Frisian": "zachodniofryzyjski", + "Xhosa": "xhosa", + "Yiddish": "jidysz", + "Yoruba": "joruba", + "Zulu": "zuluski", + "`x` years": "`x` lat", + "`x` months": "`x` miesięcy", + "`x` weeks": "`x` tygodni", + "`x` days": "`x` dni", + "`x` hours": "`x` godzin", + "`x` minutes": "`x` minut", + "`x` seconds": "`x` sekund", + "Fallback comments: ": "Zastępcze komentarze: ", + "Popular": "Popularne", + "Top": "Na czasie", + "About": "Informacje", + "Rating: ": "Ocena: ", + "Language: ": "Język: ", + "Default": "Domyślnie", + "Music": "Muzyka", + "Gaming": "Gry", + "News": "Wiadomości", + "Movies": "Filmy", + "Download": "Pobierz", + "Download as: ": "Pobierz jako: ", + "%A %B %-d, %Y": "", + "(edited)": "(edytowany)", + "Youtube permalink of the comment": "Odnośnik bezpośredni do komentarza na YouTube", + "`x` marked it with a ❤": "'x' oznaczonych ❤", + "Audio mode": "Tryb audio", + "Video mode": "Tryb wideo", + "Videos": "Filmy", + "Playlists": "Playlisty", + "Current version: ": "Aktualna wersja: " } From 21ebc398fa30d6a3ceb66e47a3d0fa637d9cd685 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 12 Mar 2019 20:51:23 -0500 Subject: [PATCH 43/81] Add privacy policy --- locales/ar.json | 1 + locales/de.json | 1 + locales/en-US.json | 1 + locales/eu.json | 1 + locales/fr.json | 1 + locales/it.json | 1 + locales/nb_NO.json | 1 + locales/nl.json | 1 + locales/pl.json | 1 + locales/ru.json | 1 + src/invidious.cr | 6 +++ src/invidious/views/privacy.ecr | 75 ++++++++++++++++++++++++++++++++ src/invidious/views/template.ecr | 7 ++- 13 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/invidious/views/privacy.ecr diff --git a/locales/ar.json b/locales/ar.json index 4f43675fb..6e0fef9f5 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -102,6 +102,7 @@ "Released under the AGPLv3 by Omar Roth.": "تم الإنشاء تحت AGPLv3 بواسطة عمر روث.", "Source available here.": "الأكواد متوفرة هنا.", "View JavaScript license information.": "مشاهدة معلومات حول تراخيص الجافاسكريبت.", + "View privacy policy.": "", "Trending": "الشائع", "Watch video on Youtube": "مشاهدة الفيديو على اليوتيوب", "Genre: ": "النوع: ", diff --git a/locales/de.json b/locales/de.json index d66ebee33..578a73d33 100644 --- a/locales/de.json +++ b/locales/de.json @@ -102,6 +102,7 @@ "Released under the AGPLv3 by Omar Roth.": "Veröffentlicht unter AGPLv3 von Omar Roth.", "Source available here.": "Quellcode verfügbar hier.", "View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.", + "View privacy policy.": "", "Trending": "Trending", "Watch video on Youtube": "Video auf YouTube ansehen", "Genre: ": "Genre: ", diff --git a/locales/en-US.json b/locales/en-US.json index 6a05d1bbf..4e919ad1b 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "Released under the AGPLv3 by Omar Roth.", "Source available here.": "Source available here.", "View JavaScript license information.": "View JavaScript license information.", + "View privacy policy.": "View privacy policy.", "Trending": "Trending", "Watch video on Youtube": "Watch video on Youtube", "Genre: ": "Genre: ", diff --git a/locales/eu.json b/locales/eu.json index 1e4ad1d4c..6bbd73be0 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "", "Source available here.": "", "View JavaScript license information.": "", + "View privacy policy.": "", "Trending": "", "Watch video on Youtube": "", "Genre: ": "", diff --git a/locales/fr.json b/locales/fr.json index 28f0b579f..a459697d4 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "Publié sous licence AGPLv3 par Omar Roth.", "Source available here.": "Code Source.", "View JavaScript license information.": "Voir les informations des licences JavaScript.", + "View privacy policy.": "", "Trending": "Tendances", "Watch video on Youtube": "Voir la vidéo sur Youtube", "Genre: ": "Genre : ", diff --git a/locales/it.json b/locales/it.json index 42dabf6db..e813c3bde 100644 --- a/locales/it.json +++ b/locales/it.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "Pubblicato con licenza AGPLv3 da Omar Roth.", "Source available here.": "Codice sorgente.", "View JavaScript license information.": "Guarda le informazioni di licenza del codice JavaScript.", + "View privacy policy.": "", "Trending": "Tendenze", "Watch video on Youtube": "Guarda il video su YouTube", "Genre: ": "Genere: ", diff --git a/locales/nb_NO.json b/locales/nb_NO.json index f0f544b51..978128292 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.", "Source available here.": "Kildekode tilgjengelig her.", "View JavaScript license information.": "Vis JavaScript-lisensinfo.", + "View privacy policy.": "", "Trending": "Trendsettende", "Watch video on Youtube": "Vis video på YouTube", "Genre: ": "Sjanger: ", diff --git a/locales/nl.json b/locales/nl.json index b4ea38629..08df38db5 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "Uitgegeven onder AGPLv3 door Omar Roth.", "Source available here.": "Bron beschikbaar hier.", "View JavaScript license information.": "Bekijk JavaScript licentie informatie.", + "View privacy policy.": "", "Trending": "Trending", "Watch video on Youtube": "Bekijk video op Youtube", "Genre: ": "Genre: ", diff --git a/locales/pl.json b/locales/pl.json index 0db43c51b..f2cfaeeeb 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -100,6 +100,7 @@ "Released under the AGPLv3 by Omar Roth.": "Wydano na licencji AGPLv3 przez Omar Roth.", "Source available here.": "Kod źródłowy dostępny tutaj.", "View JavaScript license information.": "Wyświetl informację o licencji JavaScript.", + "View privacy policy.": "", "Trending": "Na czasie", "Watch video on Youtube": "Zobacz film na YouTube", "Genre: ": "Gatunek: ", diff --git a/locales/ru.json b/locales/ru.json index 222b693fc..d6db76d9a 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -102,6 +102,7 @@ "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", "Source available here.": "Исходный код доступен здесь.", "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", + "View privacy policy.": "", "Trending": "В тренде", "Watch video on Youtube": "Смотреть на YouTube", "Genre: ": "Жанр: ", diff --git a/src/invidious.cr b/src/invidious.cr index 4fe018f95..1091b6ffe 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -17,6 +17,7 @@ require "digest/md5" require "file_utils" require "kemal" +require "markdown" require "openssl/hmac" require "option_parser" require "pg" @@ -291,6 +292,11 @@ get "/" do |env| end end +get "/privacy" do |env| + locale = LOCALES[env.get("preferences").as(Preferences).locale]? + templated "privacy" +end + get "/licenses" do |env| locale = LOCALES[env.get("preferences").as(Preferences).locale]? rendered "licenses" diff --git a/src/invidious/views/privacy.ecr b/src/invidious/views/privacy.ecr new file mode 100644 index 000000000..63f99ab67 --- /dev/null +++ b/src/invidious/views/privacy.ecr @@ -0,0 +1,75 @@ +<% content_for "header" do %> +Privacy Policy - Invidious +<% end %> + +
+<%= Markdown.to_html(<<-END_PRIVACY_POLICY + ## Privacy + + This document concerns what data you provide to this website, the purpose of the data, how the data is stored, and how the data can be removed. + + ### Data you directly provide + + Data that you provide to the website for the purpose of the site's operation (for example: an account name, account password, or channel subscription) will be stored in the website's database until the user decides to remove it. This data will not be intentionally shared with anyone or anything. + + Information stored about a registered user is limited to: + + - a list of session tokens for remaining logged in across devices + - the last time an account was updated (to provide accurate notifications) + - a list of video IDs identifying notifications from a user's subscriptions + - a list of channel UCIDs the user is subscribed to + - a user ID (for persistent storage of subscriptions and preferences) + - a json object containing user preferences + - a hashed password if applicable (not present on google accounts) + - a randomly generated token for providing an RSS feed of a user's subscriptions + - a list of video IDs identifying watched videos + + The above list reflects [this code](https://github.com/omarroth/invidious/blob/master/src/invidious/users.cr#L14-L51). + + Users can clear their watch history using the [clear watch history](/clear_watch_history) page. + + If a user is logged in with a Google account, no password will ever be stored. This website uses the session token provided by Google to identify a user, but does not store the information required to make requests on a user's behalf without their knowledge or consent. + + ### Data you passively provide + + When you request any resource from this website (for example: a page, a font, an image, or an API endpoint) information about the request may be logged. + + Information about a request is limited to: + + - the time the request was made + - the status code of the response + - the method of the request + - the requested URL + - how long it took to complete the request. + + No identifying information is logged, such as the visitor's cookie, user-agent, or IP address. Here are a couple lines to serve as an example: + + ``` + 2019-01-19 16:37:47 +00:00 200 GET /api/v1/comments/xrlETJYzH-c?format=html&hl=en-US 1345.88ms + 2019-01-19 16:37:53 +00:00 200 GET /vi/r5P-f5arPXE/maxres.jpg 1085.41ms + 2019-01-19 16:37:54 +00:00 200 GET /watch 7.04ms + ``` + + This website does not store the visitor's user-agent or IP address and does not use fingerprinting, advertisements, or tracking of any form. + + This website provides links to googlevideo.com to provide audio and video playback. googlevideo.com is owned by Google and is subject to their [privacy policy](https://policies.google.com/privacy). + + ### Data stored in your browser + + This website uses browser cookies to authenticate registered users. This data consists of: + + - An account token to keep you logged into the website between visits, which is sent when any page is loaded while you are logged in + + This website also provides an option to store site preferences, such as the theme or locale, without an account. Using this feature will store a cookie in the visitor's browser containing their preferences. This cookie is sent on every request and does not contain any identifying information. + + You can remove this data from your browser by logging out of this website, or by using your browser's cookie-related controls to delete the data. + + ### Removal of data + + To remove data stored in your browser, you can log out of the website, or you can use your browser's cookie-related controls to delete the data. + + To remove data that has been stored in the website's database, you can use the [delete my account](/delete_account) page. + END_PRIVACY_POLICY + ) +%> +
\ No newline at end of file diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index c3abc7e94..496719ce7 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -124,7 +124,12 @@ <%= translate(locale, "View JavaScript license information.") %> -
+ / + + + <%= translate(locale, "View privacy policy.") %> + +
<%= translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> From e738e57e262e3b65930632e49d9988855abe8b36 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Tue, 12 Mar 2019 21:05:49 -0500 Subject: [PATCH 44/81] Add 'local' option to preferences --- locales/ar.json | 1 + locales/de.json | 1 + locales/en-US.json | 1 + locales/eu.json | 1 + locales/fr.json | 1 + locales/it.json | 1 + locales/nb_NO.json | 1 + locales/nl.json | 1 + locales/pl.json | 1 + locales/ru.json | 1 + src/invidious.cr | 5 +++++ src/invidious/users.cr | 5 +++++ src/invidious/videos.cr | 6 ++++-- src/invidious/views/preferences.ecr | 5 +++++ 14 files changed, 29 insertions(+), 2 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index 6e0fef9f5..18078836a 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -50,6 +50,7 @@ "Autoplay: ": "تشغيل تلقائى: ", "Autoplay next video: ": "شغل الفيديو التالى تلقائى: ", "Listen by default: ": "تشغيل النسخة السمعية تلقائى: ", + "Proxy videos? ": "", "Default speed: ": "السرعة الإفتراضية: ", "Preferred video quality: ": "الجودة المفضلة للفيديوهات: ", "Player volume: ": "صوت المشغل: ", diff --git a/locales/de.json b/locales/de.json index 578a73d33..89bc09ea8 100644 --- a/locales/de.json +++ b/locales/de.json @@ -50,6 +50,7 @@ "Autoplay: ": "Automatisch abspielen: ", "Autoplay next video: ": "nächstes Video automatisch abspielen: ", "Listen by default: ": "Nur Ton als Standard: ", + "Proxy videos? ": "", "Default speed: ": "Standardgeschwindigkeit: ", "Preferred video quality: ": "Bevorzugte Videoqualität: ", "Player volume: ": "Playerlautstärke: ", diff --git a/locales/en-US.json b/locales/en-US.json index 4e919ad1b..68204a04c 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -50,6 +50,7 @@ "Autoplay: ": "Autoplay: ", "Autoplay next video: ": "Autoplay next video: ", "Listen by default: ": "Listen by default: ", + "Proxy videos? ": "Proxy videos? ", "Default speed: ": "Default speed: ", "Preferred video quality: ": "Preferred video quality: ", "Player volume: ": "Player volume: ", diff --git a/locales/eu.json b/locales/eu.json index 6bbd73be0..b71a163e0 100644 --- a/locales/eu.json +++ b/locales/eu.json @@ -50,6 +50,7 @@ "Autoplay: ": "", "Autoplay next video: ": "", "Listen by default: ": "", + "Proxy videos? ": "", "Default speed: ": "", "Preferred video quality: ": "", "Player volume: ": "", diff --git a/locales/fr.json b/locales/fr.json index a459697d4..7647be53f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -50,6 +50,7 @@ "Autoplay: ": "Lire Automatiquement : ", "Autoplay next video: ": "Lire automatiquement la vidéo suivante : ", "Listen by default: ": "Audio Uniquement par défaut : ", + "Proxy videos? ": "", "Default speed: ": "Vitesse par défaut : ", "Preferred video quality: ": "Qualité vidéo souhaitée : ", "Player volume: ": "Volume du lecteur : ", diff --git a/locales/it.json b/locales/it.json index e813c3bde..6fae1259c 100644 --- a/locales/it.json +++ b/locales/it.json @@ -50,6 +50,7 @@ "Autoplay: ": "Riproduzione automatica: ", "Autoplay next video: ": "Riproduci automaticamente il prossimo video: ", "Listen by default: ": "Modalità solo audio come predefinita: ", + "Proxy videos? ": "", "Default speed: ": "Velocità di riproduzione predefinita: ", "Preferred video quality: ": "Preferenza sulla qualità video: ", "Player volume: ": "Volume di riproduzione: ", diff --git a/locales/nb_NO.json b/locales/nb_NO.json index 978128292..47670a73e 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -50,6 +50,7 @@ "Autoplay: ": "Autoavspilling: ", "Autoplay next video: ": "Autospill neste video: ", "Listen by default: ": "Lytt som forvalg: ", + "Proxy videos? ": "", "Default speed: ": "Forvalgt hastighet: ", "Preferred video quality: ": "Foretrukket videokvalitet: ", "Player volume: ": "Avspillerlydstyrke: ", diff --git a/locales/nl.json b/locales/nl.json index 08df38db5..3bfe0ac48 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -50,6 +50,7 @@ "Autoplay: ": "Automatisch afspelen: ", "Autoplay next video: ": "Automatisch volgende video afspelen: ", "Listen by default: ": "Standaard luisteren: ", + "Proxy videos? ": "", "Default speed: ": "Standaard snelheid: ", "Preferred video quality: ": "Video kwaliteit voorkeur: ", "Player volume: ": "Afspeler volume: ", diff --git a/locales/pl.json b/locales/pl.json index f2cfaeeeb..05332ca11 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -50,6 +50,7 @@ "Autoplay: ": "Autoodtwarzanie: ", "Autoplay next video: ": "Odtwórz następny film: ", "Listen by default: ": "Tryb dźwiękowy: ", + "Proxy videos? ": "", "Default speed: ": "Domyślna prędkość: ", "Preferred video quality: ": "Preferowana jakość filmów: ", "Player volume: ": "Głośność odtwarzacza: ", diff --git a/locales/ru.json b/locales/ru.json index d6db76d9a..bfcd449e7 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -50,6 +50,7 @@ "Autoplay: ": "Автовоспроизведение: ", "Autoplay next video: ": "Автовоспроизведение следующего видео: ", "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", + "Proxy videos? ": "", "Default speed: ": "Скорость по-умолчанию: ", "Preferred video quality: ": "Предпочтительное качество видео: ", "Player volume: ": "Громкость воспроизведения: ", diff --git a/src/invidious.cr b/src/invidious.cr index 1091b6ffe..0e7a3ac1c 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -1231,6 +1231,10 @@ post "/preferences" do |env| listen ||= "off" listen = listen == "on" + local = env.params.body["local"]?.try &.as(String) + local ||= "off" + local = local == "on" + speed = env.params.body["speed"]?.try &.as(String).to_f? speed ||= DEFAULT_USER_PREFERENCES.speed @@ -1292,6 +1296,7 @@ post "/preferences" do |env| "autoplay" => autoplay, "continue" => continue, "listen" => listen, + "local" => local, "speed" => speed, "quality" => quality, "volume" => volume, diff --git a/src/invidious/users.cr b/src/invidious/users.cr index 42468228c..308786c24 100644 --- a/src/invidious/users.cr +++ b/src/invidious/users.cr @@ -31,6 +31,7 @@ DEFAULT_USER_PREFERENCES = Preferences.from_json({ "video_loop" => false, "autoplay" => false, "continue" => false, + "local" => false, "listen" => false, "speed" => 1.0, "quality" => "hd720", @@ -80,6 +81,10 @@ class Preferences type: Bool, default: DEFAULT_USER_PREFERENCES.continue, }, + local: { + type: Bool, + default: DEFAULT_USER_PREFERENCES.local, + }, listen: { type: Bool, default: DEFAULT_USER_PREFERENCES.listen, diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 17d17d93c..78dd1e5a7 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -747,10 +747,11 @@ def process_video_params(query, preferences) # region ||= preferences.region autoplay ||= preferences.autoplay.to_unsafe continue ||= preferences.continue.to_unsafe - related_videos ||= preferences.related_videos.to_unsafe listen ||= preferences.listen.to_unsafe + local ||= preferences.local.to_unsafe preferred_captions ||= preferences.captions quality ||= preferences.quality + related_videos ||= preferences.related_videos.to_unsafe speed ||= preferences.speed video_loop ||= preferences.video_loop.to_unsafe volume ||= preferences.volume @@ -758,10 +759,11 @@ def process_video_params(query, preferences) autoplay ||= DEFAULT_USER_PREFERENCES.autoplay.to_unsafe continue ||= DEFAULT_USER_PREFERENCES.continue.to_unsafe - related_videos ||= DEFAULT_USER_PREFERENCES.related_videos.to_unsafe listen ||= DEFAULT_USER_PREFERENCES.listen.to_unsafe + local ||= DEFAULT_USER_PREFERENCES.local.to_unsafe preferred_captions ||= DEFAULT_USER_PREFERENCES.captions quality ||= DEFAULT_USER_PREFERENCES.quality + related_videos ||= DEFAULT_USER_PREFERENCES.related_videos.to_unsafe speed ||= DEFAULT_USER_PREFERENCES.speed video_loop ||= DEFAULT_USER_PREFERENCES.video_loop.to_unsafe volume ||= DEFAULT_USER_PREFERENCES.volume diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr index 20762bc57..d26b21793 100644 --- a/src/invidious/views/preferences.ecr +++ b/src/invidious/views/preferences.ecr @@ -28,6 +28,11 @@ function update_value(element) { checked<% end %>>
+
+ + checked<% end %>> +
+
checked<% end %>> From 2b4a6284e41d64b93866820ba836a958bbc10652 Mon Sep 17 00:00:00 2001 From: Esmail EL BoB Date: Wed, 13 Mar 2019 12:26:43 +0000 Subject: [PATCH 45/81] Update ar.json --- locales/ar.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index 18078836a..7331b0220 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -284,12 +284,12 @@ "Download as: ": "تحميل كـ", "Download": "تحميل", "%A %B %-d, %Y": "", - "(edited)": "", - "Youtube permalink of the comment": "", - "`x` marked it with a ❤": "", - "Audio mode": "", - "Video mode": "", - "Videos": "", - "Playlists": "", - "Current version: ": "" + "(edited)": "(تم تعديلة)", + "Youtube permalink of the comment": "رابط التعليق على اليوتيوب", + "`x` marked it with a ❤": "'x' اعجب بهذا", + "Audio mode": "الوضع الصوتى", + "Video mode": "وضع الفيديو", + "Videos": "الفيديوهات", + "Playlists": "قوائم التشغيل", + "Current version: ": "الإصدار الحالى" } From d1803320f1e354bff129e36a64ce2d91b6413a11 Mon Sep 17 00:00:00 2001 From: dimqua Date: Wed, 13 Mar 2019 13:34:55 +0000 Subject: [PATCH 46/81] Update Russian translation --- locales/ru.json | 586 ++++++++++++++++++++++++------------------------ 1 file changed, 293 insertions(+), 293 deletions(-) diff --git a/locales/ru.json b/locales/ru.json index bfcd449e7..c0d313139 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -1,295 +1,295 @@ { - "`x` subscribers": "`x` подписчиков", - "`x` videos": "`x` видео", - "LIVE": "ПРЯМОЙ ЭФИР", - "Shared `x` ago": "Опубликовано `x` назад", - "Unsubscribe": "Отписаться", - "Subscribe": "Подписаться", - "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", - "View channel on YouTube": "Канал на YouTube", - "newest": "новые", - "oldest": "старые", - "popular": "популярные", - "last": "", - "Next page": "Следующая страница", - "Previous page": "Предыдущая страница", - "Clear watch history?": "Очистить историю просмотров?", - "Yes": "Да", - "No": "Нет", - "Import and Export Data": "Импорт и экспорт данных", - "Import": "Импорт", - "Import Invidious data": "Импортировать данные Invidious", - "Import YouTube subscriptions": "Импортировать YouTube подписки", - "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", - "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", - "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", - "Export": "Экспорт", - "Export subscriptions as OPML": "Экспортировать подписки в OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", - "Export data as JSON": "Экспортировать данные в JSON", - "Delete account?": "Удалить аккаунт?", - "History": "История", - "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", - "JavaScript license information": "Лицензии JavaScript", - "source": "источник", - "Login": "Войти", - "Login/Register": "Войти/Регистрация", - "Login to Google": "Войти через Google", - "User ID:": "ID пользователя:", - "Password:": "Пароль:", - "Time (h:mm:ss):": "Время (ч:мм:сс):", - "Text CAPTCHA": "Текст капчи", - "Image CAPTCHA": "Изображение капчи", - "Sign In": "Войти", - "Register": "Регистрация", - "Email:": "Эл. почта:", - "Google verification code:": "Код подтверждения Google:", - "Preferences": "Настройки", - "Player preferences": "Настройки проигрывателя", - "Always loop: ": "Всегда повторять: ", - "Autoplay: ": "Автовоспроизведение: ", - "Autoplay next video: ": "Автовоспроизведение следующего видео: ", - "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", - "Proxy videos? ": "", - "Default speed: ": "Скорость по-умолчанию: ", - "Preferred video quality: ": "Предпочтительное качество видео: ", - "Player volume: ": "Громкость воспроизведения: ", - "Default comments: ": "Источник комментариев: ", - "youtube": "YouTube", - "reddit": "Reddit", - "Default captions: ": "Субтитры по-умолчанию: ", - "Fallback captions: ": "Резервные субтитры: ", - "Show related videos? ": "Показывать похожие видео? ", - "Visual preferences": "Визуальные настройки", - "Dark mode: ": "Темная тема: ", - "Thin mode: ": "Облегченный режим: ", - "Subscription preferences": "Настройки подписок", - "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", - "Number of videos shown in feed: ": "Число видео в ленте: ", - "Sort videos by: ": "Сортировать видео по: ", - "published": "дате публикации", - "published - reverse": "дате - обратный порядок", - "alphabetically": "алфавиту", - "alphabetically - reverse": "алфавиту - обратный порядок", - "channel name": "имени канала", - "channel name - reverse": "имени канала - обратный порядок", - "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", - "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", - "Only show unwatched: ": "Отображать только непросмотренные видео: ", - "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", - "Data preferences": "Настройки данных", - "Clear watch history": "Очистить историю просмотра", - "Import/Export data": "Импорт/Экспорт данных", - "Manage subscriptions": "Управление подписками", - "Watch history": "История просмотров", - "Delete account": "Удалить аккаунт", - "Administrator preferences": "Настройки администратора", - "Default homepage: ": "Главная страница по умолчанию: ", - "Feed menu: ": "Меню ленты: ", - "Top enabled? ": "Включить ТОП? ", - "CAPTCHA enabled? ": "Включить капчу? ", - "Login enabled? ": "Включить логин? ", - "Registration enabled? ": "Включить регистрацию? ", - "Report statistics? ": "Отображать статистику? ", - "Save preferences": "Сохранить настройки", - "Subscription manager": "Менеджер подписок", - "`x` subscriptions": "`x` подписок", - "Import/Export": "Импорт/Экспорт", - "unsubscribe": "отписаться", - "Subscriptions": "Подписки", - "`x` unseen notifications": "`x` новых оповещений", - "search": "поиск", - "Sign out": "Выйти", - "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", - "Source available here.": "Исходный код доступен здесь.", - "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", - "View privacy policy.": "", - "Trending": "В тренде", - "Watch video on Youtube": "Смотреть на YouTube", - "Genre: ": "Жанр: ", - "License: ": "Лицензия: ", - "Family friendly? ": "Семейный просмотр: ", - "Wilson score: ": "Рейтинг Вильсона: ", - "Engagement: ": "Вовлеченность: ", - "Whitelisted regions: ": "Доступно для: ", - "Blacklisted regions: ": "Недоступно для: ", - "Shared `x`": "Опубликовано `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", - "View YouTube comments": "Смотреть комментарии с YouTube", - "View more comments on Reddit": "Больше комментариев на Reddit", - "View `x` comments": "Показать `x` комментариев", - "View Reddit comments": "Смотреть комментарии с Reddit", - "Hide replies": "Скрыть ответы", - "Show replies": "Показать ответы", - "Incorrect password": "Неправильный пароль", - "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", - "Invalid TFA code": "Неправильный TFA код", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", - "Invalid answer": "Неверный ответ", - "Invalid CAPTCHA": "Неверная капча", - "CAPTCHA is a required field": "Необходимо ввести капчу", - "User ID is a required field": "Необходимо ввести идентификатор пользователя", - "Password is a required field": "Необходимо ввести пароль", - "Invalid username or password": "Недопустимый пароль или имя пользователя", - "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", - "Password cannot be empty": "Пароль не может быть пустым", - "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", - "Please sign in": "Пожалуйста, войдите", - "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", - "channel:`x`": "канал: `x`", - "Deleted or invalid channel": "Канал удален или не найден", - "This channel does not exist.": "Такой канал не существует.", - "Could not get channel info.": "Невозможно получить информацию о канале.", - "Could not fetch comments": "Невозможно получить комментарии", - "View `x` replies": "Показать `x` ответов", - "`x` ago": "`x` назад", - "Load more": "Загрузить больше", - "`x` points": "`x` очков", - "Could not create mix.": "Невозможно создать \"микс\".", - "Playlist is empty": "Плейлист пуст", - "Invalid playlist.": "Некорректный плейлист.", - "Playlist does not exist.": "Плейлист не существует.", - "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", - "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", - "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", - "Invalid challenge": "Неправильный ответ в \"challenge\"", - "Invalid token": "Неправильный токен", - "Invalid user": "Недопустимое имя пользователя", - "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", - "English": "Английский", - "English (auto-generated)": "Английский (созданы автоматически)", - "Afrikaans": "Африкаанс", - "Albanian": "Албанский", - "Amharic": "Амхарский", - "Arabic": "Арабский", - "Armenian": "Армянский", - "Azerbaijani": "Азербайджанский", - "Bangla": "Бенгальский", - "Basque": "Баскский", - "Belarusian": "Белорусский", - "Bosnian": "Боснийский", - "Bulgarian": "Болгарский", - "Burmese": "Бирманский", - "Catalan": "Каталонский", - "Cebuano": "Себуанский", - "Chinese (Simplified)": "Китайский (упрощенный)", - "Chinese (Traditional)": "Китайский (традиционный)", - "Corsican": "Корсиканский", - "Croatian": "Хорватский", - "Czech": "Чешский", - "Danish": "Датский", - "Dutch": "Нидерландский", - "Esperanto": "Эсперанто", - "Estonian": "Эстонский", - "Filipino": "Филиппинский", - "Finnish": "Финский", - "French": "Французский", - "Galician": "Галисийский", - "Georgian": "Грузинский", - "German": "Немецкий", - "Greek": "Греческий", - "Gujarati": "Гуджаратский", - "Haitian Creole": "Гаит. креольский", - "Hausa": "Хауса", - "Hawaiian": "Гавайский", - "Hebrew": "Иврит", - "Hindi": "Хинди", - "Hmong": "Хмонг (мяо)", - "Hungarian": "Венгерский", - "Icelandic": "Исландский", - "Igbo": "Игбо", - "Indonesian": "Индонезийский", - "Irish": "Ирландский", - "Italian": "Итальянский", - "Japanese": "Японский", - "Javanese": "Яванский", - "Kannada": "Каннада", - "Kazakh": "Казахский", - "Khmer": "Кхмерский", - "Korean": "Корейский", - "Kurdish": "Курдский", - "Kyrgyz": "Киргизский", - "Lao": "Лаосский", - "Latin": "Латинский", - "Latvian": "Латышский", - "Lithuanian": "Литовский", - "Luxembourgish": "Люксембургский", - "Macedonian": "Македонский", - "Malagasy": "Малагасийский", - "Malay": "Малайский", - "Malayalam": "Малаялам", - "Maltese": "Мальтийский", - "Maori": "Маори", - "Marathi": "Маратхи", - "Mongolian": "Монгольская", - "Nepali": "Непальский", - "Norwegian": "Норвежский", - "Nyanja": "Ньянджа", - "Pashto": "Пушту", - "Persian": "Персидский", - "Polish": "Польский", - "Portuguese": "Португальский", - "Punjabi": "Панджаби", - "Romanian": "Румынский", - "Russian": "Русский", - "Samoan": "Самоанский", - "Scottish Gaelic": "Шотландский (гэльский)", - "Serbian": "Сербский", - "Shona": "Шона", - "Sindhi": "Синдхи", - "Sinhala": "Сингальский", - "Slovak": "Словацкий", - "Slovenian": "Словенский", - "Somali": "Сомалийский", - "Southern Sotho": "Сесото (южный сото)", - "Spanish": "Испанский", - "Spanish (Latin America)": "Испанский (Латинская Америка)", - "Sundanese": "Сунданский", - "Swahili": "Суахили", - "Swedish": "Шведский", - "Tajik": "Таджикский", - "Tamil": "Тамильский", - "Telugu": "Телугу", - "Thai": "Тайский", - "Turkish": "Турецкий", - "Ukrainian": "Украинский", - "Urdu": "Урду", - "Uzbek": "Узбекский", - "Vietnamese": "Вьетнамский", - "Welsh": "Валлийский", - "Western Frisian": "Западнофризский", - "Xhosa": "Коса", - "Yiddish": "Идиш", - "Yoruba": "Йоруба", - "Zulu": "Зулусский", - "`x` years": "`x` лет", - "`x` months": "`x` месяцев", - "`x` weeks": "`x` недель", - "`x` days": "`x` дней", - "`x` hours": "`x` часов", - "`x` minutes": "`x` минут", - "`x` seconds": "`x` секунд", - "Fallback comments: ": "Резервные комментарии: ", - "Popular": "Популярное", - "Top": "Топ", - "About": "О сайте", - "Rating: ": "Рейтинг: ", - "Language: ": "Язык: ", - "Default": "По-умолчанию", - "Music": "Музыка", - "Gaming": "Игры", - "News": "Новости", - "Movies": "Фильмы", - "Download": "Скачать", - "Download as: ": "Скачать как: ", - "%A %B %-d, %Y": "%-d %B %Y, %A", - "(edited)": "(изменено)", - "Youtube permalink of the comment": "Прямая ссылка на YouTube", - "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", - "Audio mode": "Аудио режим", - "Video mode": "Видео режим", - "Videos": "", - "Playlists": "", - "Current version: ": "" + "`x` subscribers": "`x` подписчиков", + "`x` videos": "`x` видео", + "LIVE": "ПРЯМОЙ ЭФИР", + "Shared `x` ago": "Опубликовано `x` назад", + "Unsubscribe": "Отписаться", + "Subscribe": "Подписаться", + "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`", + "View channel on YouTube": "Канал на YouTube", + "newest": "новые", + "oldest": "старые", + "popular": "популярные", + "last": "недавно обновленные", + "Next page": "Следующая страница", + "Previous page": "Предыдущая страница", + "Clear watch history?": "Очистить историю просмотров?", + "Yes": "Да", + "No": "Нет", + "Import and Export Data": "Импорт и экспорт данных", + "Import": "Импорт", + "Import Invidious data": "Импортировать данные Invidious", + "Import YouTube subscriptions": "Импортировать YouTube подписки", + "Import FreeTube subscriptions (.db)": "Импортировать FreeTube подписки (.db)", + "Import NewPipe subscriptions (.json)": "Импортировать NewPipe подписки (.json)", + "Import NewPipe data (.zip)": "Импортировать данные NewPipe (.zip)", + "Export": "Экспорт", + "Export subscriptions as OPML": "Экспортировать подписки в OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)", + "Export data as JSON": "Экспортировать данные в JSON", + "Delete account?": "Удалить аккаунт?", + "History": "История", + "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube", + "JavaScript license information": "Лицензии JavaScript", + "source": "источник", + "Login": "Войти", + "Login/Register": "Войти/Регистрация", + "Login to Google": "Войти через Google", + "User ID:": "ID пользователя:", + "Password:": "Пароль:", + "Time (h:mm:ss):": "Время (ч:мм:сс):", + "Text CAPTCHA": "Текст капчи", + "Image CAPTCHA": "Изображение капчи", + "Sign In": "Войти", + "Register": "Регистрация", + "Email:": "Эл. почта:", + "Google verification code:": "Код подтверждения Google:", + "Preferences": "Настройки", + "Player preferences": "Настройки проигрывателя", + "Always loop: ": "Всегда повторять: ", + "Autoplay: ": "Автовоспроизведение: ", + "Autoplay next video: ": "Автовоспроизведение следующего видео: ", + "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ", + "Proxy videos? ": "Проксировать видео? ", + "Default speed: ": "Скорость по-умолчанию: ", + "Preferred video quality: ": "Предпочтительное качество видео: ", + "Player volume: ": "Громкость воспроизведения: ", + "Default comments: ": "Источник комментариев: ", + "youtube": "YouTube", + "reddit": "Reddit", + "Default captions: ": "Субтитры по-умолчанию: ", + "Fallback captions: ": "Резервные субтитры: ", + "Show related videos? ": "Показывать похожие видео? ", + "Visual preferences": "Визуальные настройки", + "Dark mode: ": "Темная тема: ", + "Thin mode: ": "Облегченный режим: ", + "Subscription preferences": "Настройки подписок", + "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ", + "Number of videos shown in feed: ": "Число видео в ленте: ", + "Sort videos by: ": "Сортировать видео по: ", + "published": "дате публикации", + "published - reverse": "дате - обратный порядок", + "alphabetically": "алфавиту", + "alphabetically - reverse": "алфавиту - обратный порядок", + "channel name": "имени канала", + "channel name - reverse": "имени канала - обратный порядок", + "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ", + "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ", + "Only show unwatched: ": "Отображать только непросмотренные видео: ", + "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ", + "Data preferences": "Настройки данных", + "Clear watch history": "Очистить историю просмотра", + "Import/Export data": "Импорт/Экспорт данных", + "Manage subscriptions": "Управление подписками", + "Watch history": "История просмотров", + "Delete account": "Удалить аккаунт", + "Administrator preferences": "Настройки администратора", + "Default homepage: ": "Главная страница по умолчанию: ", + "Feed menu: ": "Меню ленты: ", + "Top enabled? ": "Включить ТОП? ", + "CAPTCHA enabled? ": "Включить капчу? ", + "Login enabled? ": "Включить логин? ", + "Registration enabled? ": "Включить регистрацию? ", + "Report statistics? ": "Отображать статистику? ", + "Save preferences": "Сохранить настройки", + "Subscription manager": "Менеджер подписок", + "`x` subscriptions": "`x` подписок", + "Import/Export": "Импорт/Экспорт", + "unsubscribe": "отписаться", + "Subscriptions": "Подписки", + "`x` unseen notifications": "`x` новых оповещений", + "search": "поиск", + "Sign out": "Выйти", + "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.", + "Source available here.": "Исходный код доступен здесь.", + "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.", + "View privacy policy.": "См. политику конфиденциальности.", + "Trending": "В тренде", + "Watch video on Youtube": "Смотреть на YouTube", + "Genre: ": "Жанр: ", + "License: ": "Лицензия: ", + "Family friendly? ": "Семейный просмотр: ", + "Wilson score: ": "Рейтинг Вильсона: ", + "Engagement: ": "Вовлеченность: ", + "Whitelisted regions: ": "Доступно для: ", + "Blacklisted regions: ": "Недоступно для: ", + "Shared `x`": "Опубликовано `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).", + "View YouTube comments": "Смотреть комментарии с YouTube", + "View more comments on Reddit": "Больше комментариев на Reddit", + "View `x` comments": "Показать `x` комментариев", + "View Reddit comments": "Смотреть комментарии с Reddit", + "Hide replies": "Скрыть ответы", + "Show replies": "Показать ответы", + "Incorrect password": "Неправильный пароль", + "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.", + "Invalid TFA code": "Неправильный TFA код", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.", + "Invalid answer": "Неверный ответ", + "Invalid CAPTCHA": "Неверная капча", + "CAPTCHA is a required field": "Необходимо ввести капчу", + "User ID is a required field": "Необходимо ввести идентификатор пользователя", + "Password is a required field": "Необходимо ввести пароль", + "Invalid username or password": "Недопустимый пароль или имя пользователя", + "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google", + "Password cannot be empty": "Пароль не может быть пустым", + "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов", + "Please sign in": "Пожалуйста, войдите", + "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`", + "channel:`x`": "канал: `x`", + "Deleted or invalid channel": "Канал удален или не найден", + "This channel does not exist.": "Такой канал не существует.", + "Could not get channel info.": "Невозможно получить информацию о канале.", + "Could not fetch comments": "Невозможно получить комментарии", + "View `x` replies": "Показать `x` ответов", + "`x` ago": "`x` назад", + "Load more": "Загрузить больше", + "`x` points": "`x` очков", + "Could not create mix.": "Невозможно создать \"микс\".", + "Playlist is empty": "Плейлист пуст", + "Invalid playlist.": "Некорректный плейлист.", + "Playlist does not exist.": "Плейлист не существует.", + "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".", + "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"", + "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"", + "Invalid challenge": "Неправильный ответ в \"challenge\"", + "Invalid token": "Неправильный токен", + "Invalid user": "Недопустимое имя пользователя", + "Token is expired, please try again": "Срок действия токена истек, попробуйте позже", + "English": "Английский", + "English (auto-generated)": "Английский (созданы автоматически)", + "Afrikaans": "Африкаанс", + "Albanian": "Албанский", + "Amharic": "Амхарский", + "Arabic": "Арабский", + "Armenian": "Армянский", + "Azerbaijani": "Азербайджанский", + "Bangla": "Бенгальский", + "Basque": "Баскский", + "Belarusian": "Белорусский", + "Bosnian": "Боснийский", + "Bulgarian": "Болгарский", + "Burmese": "Бирманский", + "Catalan": "Каталонский", + "Cebuano": "Себуанский", + "Chinese (Simplified)": "Китайский (упрощенный)", + "Chinese (Traditional)": "Китайский (традиционный)", + "Corsican": "Корсиканский", + "Croatian": "Хорватский", + "Czech": "Чешский", + "Danish": "Датский", + "Dutch": "Нидерландский", + "Esperanto": "Эсперанто", + "Estonian": "Эстонский", + "Filipino": "Филиппинский", + "Finnish": "Финский", + "French": "Французский", + "Galician": "Галисийский", + "Georgian": "Грузинский", + "German": "Немецкий", + "Greek": "Греческий", + "Gujarati": "Гуджаратский", + "Haitian Creole": "Гаит. креольский", + "Hausa": "Хауса", + "Hawaiian": "Гавайский", + "Hebrew": "Иврит", + "Hindi": "Хинди", + "Hmong": "Хмонг (мяо)", + "Hungarian": "Венгерский", + "Icelandic": "Исландский", + "Igbo": "Игбо", + "Indonesian": "Индонезийский", + "Irish": "Ирландский", + "Italian": "Итальянский", + "Japanese": "Японский", + "Javanese": "Яванский", + "Kannada": "Каннада", + "Kazakh": "Казахский", + "Khmer": "Кхмерский", + "Korean": "Корейский", + "Kurdish": "Курдский", + "Kyrgyz": "Киргизский", + "Lao": "Лаосский", + "Latin": "Латинский", + "Latvian": "Латышский", + "Lithuanian": "Литовский", + "Luxembourgish": "Люксембургский", + "Macedonian": "Македонский", + "Malagasy": "Малагасийский", + "Malay": "Малайский", + "Malayalam": "Малаялам", + "Maltese": "Мальтийский", + "Maori": "Маори", + "Marathi": "Маратхи", + "Mongolian": "Монгольская", + "Nepali": "Непальский", + "Norwegian": "Норвежский", + "Nyanja": "Ньянджа", + "Pashto": "Пушту", + "Persian": "Персидский", + "Polish": "Польский", + "Portuguese": "Португальский", + "Punjabi": "Панджаби", + "Romanian": "Румынский", + "Russian": "Русский", + "Samoan": "Самоанский", + "Scottish Gaelic": "Шотландский (гэльский)", + "Serbian": "Сербский", + "Shona": "Шона", + "Sindhi": "Синдхи", + "Sinhala": "Сингальский", + "Slovak": "Словацкий", + "Slovenian": "Словенский", + "Somali": "Сомалийский", + "Southern Sotho": "Сесото (южный сото)", + "Spanish": "Испанский", + "Spanish (Latin America)": "Испанский (Латинская Америка)", + "Sundanese": "Сунданский", + "Swahili": "Суахили", + "Swedish": "Шведский", + "Tajik": "Таджикский", + "Tamil": "Тамильский", + "Telugu": "Телугу", + "Thai": "Тайский", + "Turkish": "Турецкий", + "Ukrainian": "Украинский", + "Urdu": "Урду", + "Uzbek": "Узбекский", + "Vietnamese": "Вьетнамский", + "Welsh": "Валлийский", + "Western Frisian": "Западнофризский", + "Xhosa": "Коса", + "Yiddish": "Идиш", + "Yoruba": "Йоруба", + "Zulu": "Зулусский", + "`x` years": "`x` лет", + "`x` months": "`x` месяцев", + "`x` weeks": "`x` недель", + "`x` days": "`x` дней", + "`x` hours": "`x` часов", + "`x` minutes": "`x` минут", + "`x` seconds": "`x` секунд", + "Fallback comments: ": "Резервные комментарии: ", + "Popular": "Популярное", + "Top": "Топ", + "About": "О сайте", + "Rating: ": "Рейтинг: ", + "Language: ": "Язык: ", + "Default": "По-умолчанию", + "Music": "Музыка", + "Gaming": "Игры", + "News": "Новости", + "Movies": "Фильмы", + "Download": "Скачать", + "Download as: ": "Скачать как: ", + "%A %B %-d, %Y": "%-d %B %Y, %A", + "(edited)": "(изменено)", + "Youtube permalink of the comment": "Прямая ссылка на YouTube", + "`x` marked it with a ❤": "❤ от автора канала \"`x`\"", + "Audio mode": "Аудио режим", + "Video mode": "Видео режим", + "Videos": "Видео", + "Playlists": "Плейлисты", + "Current version: ": "Текущая версия: " } From 986699bce55c2bfa746780cd09f56ef1de344701 Mon Sep 17 00:00:00 2001 From: Anne Onyme 017 Date: Wed, 13 Mar 2019 22:02:12 +0000 Subject: [PATCH 47/81] Update French translation --- locales/fr.json | 582 ++++++++++++++++++++++++------------------------ 1 file changed, 291 insertions(+), 291 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 7647be53f..98b855a7f 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,293 +1,293 @@ { - "`x` subscribers": "`x` abonnés", - "`x` videos": "`x` vidéos", - "LIVE": "EN DIRECT", - "Shared `x` ago": "Partagé il y a `x`", - "Unsubscribe": "Se désabonner", - "Subscribe": "S'abonner", - "Login to subscribe to `x`": "Vous devez vous connecter pour vous abonner à `x`", - "View channel on YouTube": "Voir la chaîne sur YouTube", - "newest": "Date d'ajout (la plus récente)", - "oldest": "Date d'ajout (la plus ancienne)", - "popular": "Les plus populaires", - "last": "", - "Next page": "Page suivante", - "Previous page": "Page précédente", - "Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?", - "Yes": "Oui", - "No": "Non", - "Import and Export Data": "Importer et Exporter les Données", - "Import": "Importer", - "Import Invidious data": "Importer des données Invidious", - "Import YouTube subscriptions": "Importer des abonnements YouTube", - "Import FreeTube subscriptions (.db)": "Importer des abonnements FreeTube (.db)", - "Import NewPipe subscriptions (.json)": "Importer des abonnements NewPipe (.json)", - "Import NewPipe data (.zip)": "Importer des données NewPipe (.zip)", - "Export": "Exporter", - "Export subscriptions as OPML": "Exporter les abonnements en OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporter les abonnements en OPML (pour NewPipe & FreeTube)", - "Export data as JSON": "Exporter les données au format JSON", - "Delete account?": "Supprimer votre compte ?", - "History": "Historique", - "An alternative front-end to YouTube": "Un front-end alternatif à YouTube", - "JavaScript license information": "Informations sur les licences JavaScript", - "source": "source", - "Login": "Connexion", - "Login/Register": "Connexion/S'inscrire", - "Login to Google": "Se connecter à Google", - "User ID:": "ID utilisateur :", - "Password:": "Mot de passe :", - "Time (h:mm:ss):": "Heure (h:mm:ss) :", - "Text CAPTCHA": "CAPTCHA Texte", - "Image CAPTCHA": "CAPTCHA Image", - "Sign In": "S'identifier", - "Register": "S'inscrire", - "Email:": "Email :", - "Google verification code:": "Code de vérification Google :", - "Preferences": "Préférences", - "Player preferences": "Préférences du Lecteur", - "Always loop: ": "Lire en boucle : ", - "Autoplay: ": "Lire Automatiquement : ", - "Autoplay next video: ": "Lire automatiquement la vidéo suivante : ", - "Listen by default: ": "Audio Uniquement par défaut : ", - "Proxy videos? ": "", - "Default speed: ": "Vitesse par défaut : ", - "Preferred video quality: ": "Qualité vidéo souhaitée : ", - "Player volume: ": "Volume du lecteur : ", - "Default comments: ": "Source des Commentaires : ", - "Default captions: ": "Sous-titres principal : ", - "Fallback captions: ": "Sous-titres secondaire : ", - "Show related videos? ": "Voir les vidéos liées à ce sujet ? ", - "Visual preferences": "Préférences visuelles", - "Dark mode: ": "Mode Sombre : ", - "Thin mode: ": "Mode Simplifié : ", - "Subscription preferences": "Préférences de la page d'abonnements", - "Redirect homepage to feed: ": "Rediriger la page d'accueil vers la page d'abonnements : ", - "Number of videos shown in feed: ": "Nombre de vidéos montrées dans la page d'abonnements : ", - "Sort videos by: ": "Trier les vidéos par : ", - "published": "publication", - "published - reverse": "publication - inversé", - "alphabetically": "alphabétiquement", - "alphabetically - reverse": "alphabétiquement - inversé", - "channel name": "nom de la chaîne", - "channel name - reverse": "nom de la chaîne - inversé", - "Only show latest video from channel: ": "Afficher uniquement la dernière vidéo de la chaîne : ", - "Only show latest unwatched video from channel: ": "Afficher uniquement la dernière vidéo de la chaîne non regardée : ", - "Only show unwatched: ": "Afficher uniquement les vidéos non regardées : ", - "Only show notifications (if there are any): ": "Afficher uniquement les notifications (s'il y en a) : ", - "Data preferences": "Préférences liées aux données", - "Clear watch history": "Supprimer l'historique des vidéos regardées", - "Import/Export data": "Importer/exporter les données", - "Manage subscriptions": "Gérer les abonnements", - "Watch history": "Historique de visionnage", - "Delete account": "Supprimer votre compte", - "Administrator preferences": "", - "Default homepage: ": "", - "Feed menu: ": "", - "Top enabled? ": "", - "CAPTCHA enabled? ": "", - "Login enabled? ": "", - "Registration enabled? ": "", - "Report statistics? ": "", - "Save preferences": "Enregistrer les préférences", - "Subscription manager": "Gestionnaire d'abonnement", - "`x` subscriptions": "`x` abonnements", - "Import/Export": "Importer/Exporter", - "unsubscribe": "se désabonner", - "Subscriptions": "Abonnements", - "`x` unseen notifications": "`x` notifications non vues", - "search": "Rechercher", - "Sign out": "Déconnexion", - "Released under the AGPLv3 by Omar Roth.": "Publié sous licence AGPLv3 par Omar Roth.", - "Source available here.": "Code Source.", - "View JavaScript license information.": "Voir les informations des licences JavaScript.", - "View privacy policy.": "", - "Trending": "Tendances", - "Watch video on Youtube": "Voir la vidéo sur Youtube", - "Genre: ": "Genre : ", - "License: ": "Licence : ", - "Family friendly? ": "Tout Public ? ", - "Wilson score: ": "Score de Wilson : ", - "Engagement: ": "Poucentage de spectateur aillant aimé Liker ou Disliker la vidéo : ", - "Whitelisted regions: ": "Régions en liste blanche : ", - "Blacklisted regions: ": "Régions sur liste noire : ", - "Shared `x`": "Partagée `x`", - "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Il semblerait que JavaScript sois désactivé. Cliquez ici pour voir les commentaires. Gardez à l'esprit que le chargement peut prendre plus de temps.", - "View YouTube comments": "Voir les commentaires YouTube", - "View more comments on Reddit": "Voir plus de commentaires sur Reddit", - "View `x` comments": "Voir `x` commentaires", - "View Reddit comments": "Voir les commentaires Reddit", - "Hide replies": "Masquer les réponses", - "Show replies": "Afficher les réponses", - "Incorrect password": "Mot de passe incorrect", - "Quota exceeded, try again in a few hours": "Nombre de tentative de connexion dépassé, réessayez dans quelques heures", - "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Si vous ne parvenez pas à vous connecter, assurez-vous que l'authentification à deux facteurs (Authenticator ou SMS) est activée.", - "Invalid TFA code": "Code d'authentification à deux facteurs invalide", - "Login failed. This may be because two-factor authentication is not enabled on your account.": "La connexion a échoué. Cela peut être dû au fait que l'authentification à deux facteurs n'est pas activée sur votre compte.", - "Invalid answer": "Réponse non valide", - "Invalid CAPTCHA": "CAPTCHA invalide", - "CAPTCHA is a required field": "Veuillez rentrez un CAPTCHA", - "User ID is a required field": "Veuillez rentrez un Identifiant Utilisateur", - "Password is a required field": "Veuillez rentrez un Mot de passe", - "Invalid username or password": "Nom d'utilisateur ou mot de passe invalide", - "Please sign in using 'Sign in with Google'": "Veuillez vous connecter en utilisant \"S'identifier avec Google\"", - "Password cannot be empty": "Le mot de passe ne peut pas être vide", - "Password cannot be longer than 55 characters": "Le mot de passe ne doit pas comporter plus de 55 caractères", - "Please sign in": "Veuillez vous connecter", - "Invidious Private Feed for `x`": "Flux RSS privé pour `x`", - "channel:`x`": "chaîne:`x`", - "Deleted or invalid channel": "Chaîne supprimée ou invalide", - "This channel does not exist.": "Cette chaine n'existe pas.", - "Could not get channel info.": "Impossible de charger les informations de cette chaîne.", - "Could not fetch comments": "Impossible de charger les commentaires", - "View `x` replies": "Voir `x` réponses", - "`x` ago": "il y a `x`", - "Load more": "Charger plus", - "`x` points": "`x` points", - "Could not create mix.": "Impossible de charger cette liste de lecture.", - "Playlist is empty": "La liste de lecture est vide", - "Invalid playlist.": "Liste de lecture invalide.", - "Playlist does not exist.": "La liste de lecture n'existe pas.", - "Could not pull trending pages.": "Impossible de charger les pages de tendances.", - "Hidden field \"challenge\" is a required field": "Hidden field \"challenge\" is a required field", - "Hidden field \"token\" is a required field": "Hidden field \"token\" is a required field", - "Invalid challenge": "Invalid challenge", - "Invalid token": "Invalid token", - "Invalid user": "Invalid user", - "Token is expired, please try again": "Token is expired, please try again", - "English": "Anglais", - "English (auto-generated)": "Anglais (générés automatiquement)", - "Afrikaans": "Afrikaans", - "Albanian": "Albanais", - "Amharic": "Amharique", - "Arabic": "Arabe", - "Armenian": "Arménien", - "Azerbaijani": "Azerbaïdjanais", - "Bangla": "Bangla", - "Basque": "Basque", - "Belarusian": "Belarusian", - "Bosnian": "Bosnian", - "Bulgarian": "Bulgarian", - "Burmese": "Birman", - "Catalan": "Catalan", - "Cebuano": "Cebuano", - "Chinese (Simplified)": "Chinois (Simplifié)", - "Chinese (Traditional)": "Chinois (Traditionnel)", - "Corsican": "Corse", - "Croatian": "Croate", - "Czech": "Tchèque", - "Danish": "Danois", - "Dutch": "Hollandais", - "Esperanto": "Espéranto", - "Estonian": "Estonien", - "Filipino": "Philippin", - "Finnish": "Finlandais", - "French": "Français", - "Galician": "Galicien", - "Georgian": "Géorgien", - "German": "Allemand", - "Greek": "Grec", - "Gujarati": "Gujarati", - "Haitian Creole": "Créole Haïtien", - "Hausa": "Haoussa", - "Hawaiian": "Hawaïen", - "Hebrew": "Hébraïque", - "Hindi": "Hindi", - "Hmong": "Hmong", - "Hungarian": "Hongrois", - "Icelandic": "Islandais", - "Igbo": "Igbo", - "Indonesian": "Indonésien", - "Irish": "Irlandais", - "Italian": "Italien", - "Japanese": "Japonais", - "Javanese": "Javanais", - "Kannada": "Kannada", - "Kazakh": "Kazakh", - "Khmer": "Khmer", - "Korean": "Coréen", - "Kurdish": "Kurde", - "Kyrgyz": "Kirghize", - "Lao": "Lao", - "Latin": "Latin", - "Latvian": "Letton", - "Lithuanian": "Lituanien", - "Luxembourgish": "Luxembourgeois", - "Macedonian": "Macédonien", - "Malagasy": "Malgache", - "Malay": "Malais", - "Malayalam": "Malayalam", - "Maltese": "Maltais", - "Maori": "Maori", - "Marathi": "Marathi", - "Mongolian": "Mongol", - "Nepali": "Népalais", - "Norwegian": "Norvégien", - "Nyanja": "Nyanja", - "Pashto": "Pachtou", - "Persian": "Persan", - "Polish": "Polonais", - "Portuguese": "Portugais", - "Punjabi": "Punjabi", - "Romanian": "Roumain", - "Russian": "Russe", - "Samoan": "Samoan", - "Scottish Gaelic": "Eaélique Ècossais", - "Serbian": "Serbe", - "Shona": "Shona", - "Sindhi": "Sindhi", - "Sinhala": "Cinghalais", - "Slovak": "Slovaque", - "Slovenian": "Slovène", - "Somali": "Somalien", - "Southern Sotho": "Sotho du Sud", - "Spanish": "Espagnol", - "Spanish (Latin America)": "Espagnol (Amérique latine)", - "Sundanese": "Sundanais", - "Swahili": "Swahili", - "Swedish": "Suédois", - "Tajik": "Tajik", - "Tamil": "Tamil", - "Telugu": "Telugu", - "Thai": "Thaï", - "Turkish": "Turc", - "Ukrainian": "Ukrainien", - "Urdu": "Ourdou", - "Uzbek": "Ouzbek", - "Vietnamese": "Vietnamien", - "Welsh": "Gallois", - "Western Frisian": "Frison occidental", - "Xhosa": "Xhosa", - "Yiddish": "Yiddish", - "Yoruba": "Yoruba", - "Zulu": "Zoulou", - "`x` years": "`x` ans", - "`x` months": "`x` mois", - "`x` weeks": "`x` semaines", - "`x` days": "`x` jours", - "`x` hours": "`x` heures", - "`x` minutes": "`x` minutes", - "`x` seconds": "`x` secondes", - "Fallback comments: ": "Commentaires secondaires : ", - "Popular": "Populaire", - "Top": "Top", - "About": "A Propos", - "Rating: ": "Évaluation : ", - "Language: ": "Langue : ", - "Default": "Défaut", - "Music": "Musique", - "Gaming": "Jeux Vidéo", - "News": "Actualités", - "Movies": "Films", - "Download": "Télécharger", - "Download as: ": "Télécharger en : ", - "%A %B %-d, %Y": "%A %-d %B %Y", - "(edited)": "(modifié)", - "Youtube permalink of the comment": "Lien YouTube permanent vers le commentaire", - "`x` marked it with a ❤": "`x` l'a marqué d'un ❤", - "Audio mode": "Mode Audio", - "Video mode": "Mode Vidéo", - "Videos": "", - "Playlists": "", - "Current version: ": "" + "`x` subscribers": "`x` abonnés", + "`x` videos": "`x` vidéos", + "LIVE": "EN DIRECT", + "Shared `x` ago": "Partagé il y a `x`", + "Unsubscribe": "Se désabonner", + "Subscribe": "S'abonner", + "Login to subscribe to `x`": "Vous devez vous connecter pour vous abonner à `x`", + "View channel on YouTube": "Voir la chaîne sur YouTube", + "newest": "Date d'ajout (la plus récente)", + "oldest": "Date d'ajout (la plus ancienne)", + "popular": "Les plus populaires", + "last": "", + "Next page": "Page suivante", + "Previous page": "Page précédente", + "Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?", + "Yes": "Oui", + "No": "Non", + "Import and Export Data": "Importer et Exporter les Données", + "Import": "Importer", + "Import Invidious data": "Importer des données Invidious", + "Import YouTube subscriptions": "Importer des abonnements YouTube", + "Import FreeTube subscriptions (.db)": "Importer des abonnements FreeTube (.db)", + "Import NewPipe subscriptions (.json)": "Importer des abonnements NewPipe (.json)", + "Import NewPipe data (.zip)": "Importer des données NewPipe (.zip)", + "Export": "Exporter", + "Export subscriptions as OPML": "Exporter les abonnements en OPML", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporter les abonnements en OPML (pour NewPipe & FreeTube)", + "Export data as JSON": "Exporter les données au format JSON", + "Delete account?": "Supprimer votre compte ?", + "History": "Historique", + "An alternative front-end to YouTube": "Un front-end alternatif à YouTube", + "JavaScript license information": "Informations sur les licences JavaScript", + "source": "source", + "Login": "Connexion", + "Login/Register": "Connexion/S'inscrire", + "Login to Google": "Se connecter à Google", + "User ID:": "ID utilisateur :", + "Password:": "Mot de passe :", + "Time (h:mm:ss):": "Heure (h:mm:ss) :", + "Text CAPTCHA": "CAPTCHA Texte", + "Image CAPTCHA": "CAPTCHA Image", + "Sign In": "S'identifier", + "Register": "S'inscrire", + "Email:": "Email :", + "Google verification code:": "Code de vérification Google :", + "Preferences": "Préférences", + "Player preferences": "Préférences du Lecteur", + "Always loop: ": "Lire en boucle : ", + "Autoplay: ": "Lire Automatiquement : ", + "Autoplay next video: ": "Lire automatiquement la vidéo suivante : ", + "Listen by default: ": "Audio Uniquement par défaut : ", + "Proxy videos? ": "", + "Default speed: ": "Vitesse par défaut : ", + "Preferred video quality: ": "Qualité vidéo souhaitée : ", + "Player volume: ": "Volume du lecteur : ", + "Default comments: ": "Source des Commentaires : ", + "Default captions: ": "Sous-titres principal : ", + "Fallback captions: ": "Sous-titres secondaire : ", + "Show related videos? ": "Voir les vidéos liées à ce sujet ? ", + "Visual preferences": "Préférences visuelles", + "Dark mode: ": "Mode Sombre : ", + "Thin mode: ": "Mode Simplifié : ", + "Subscription preferences": "Préférences de la page d'abonnements", + "Redirect homepage to feed: ": "Rediriger la page d'accueil vers la page d'abonnements : ", + "Number of videos shown in feed: ": "Nombre de vidéos montrées dans la page d'abonnements : ", + "Sort videos by: ": "Trier les vidéos par : ", + "published": "publication", + "published - reverse": "publication - inversé", + "alphabetically": "alphabétiquement", + "alphabetically - reverse": "alphabétiquement - inversé", + "channel name": "nom de la chaîne", + "channel name - reverse": "nom de la chaîne - inversé", + "Only show latest video from channel: ": "Afficher uniquement la dernière vidéo de la chaîne : ", + "Only show latest unwatched video from channel: ": "Afficher uniquement la dernière vidéo de la chaîne non regardée : ", + "Only show unwatched: ": "Afficher uniquement les vidéos non regardées : ", + "Only show notifications (if there are any): ": "Afficher uniquement les notifications (s'il y en a) : ", + "Data preferences": "Préférences liées aux données", + "Clear watch history": "Supprimer l'historique des vidéos regardées", + "Import/Export data": "Importer/exporter les données", + "Manage subscriptions": "Gérer les abonnements", + "Watch history": "Historique de visionnage", + "Delete account": "Supprimer votre compte", + "Administrator preferences": "", + "Default homepage: ": "", + "Feed menu: ": "", + "Top enabled? ": "", + "CAPTCHA enabled? ": "", + "Login enabled? ": "", + "Registration enabled? ": "", + "Report statistics? ": "", + "Save preferences": "Enregistrer les préférences", + "Subscription manager": "Gestionnaire d'abonnement", + "`x` subscriptions": "`x` abonnements", + "Import/Export": "Importer/Exporter", + "unsubscribe": "se désabonner", + "Subscriptions": "Abonnements", + "`x` unseen notifications": "`x` notifications non vues", + "search": "Rechercher", + "Sign out": "Déconnexion", + "Released under the AGPLv3 by Omar Roth.": "Publié sous licence AGPLv3 par Omar Roth.", + "Source available here.": "Code Source.", + "View JavaScript license information.": "Voir les informations des licences JavaScript.", + "View privacy policy.": "", + "Trending": "Tendances", + "Watch video on Youtube": "Voir la vidéo sur Youtube", + "Genre: ": "Genre : ", + "License: ": "Licence : ", + "Family friendly? ": "Tout Public ? ", + "Wilson score: ": "Score de Wilson : ", + "Engagement: ": "Poucentage de spectateur aillant aimé Liker ou Disliker la vidéo : ", + "Whitelisted regions: ": "Régions en liste blanche : ", + "Blacklisted regions: ": "Régions sur liste noire : ", + "Shared `x`": "Partagée `x`", + "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Il semblerait que JavaScript sois désactivé. Cliquez ici pour voir les commentaires. Gardez à l'esprit que le chargement peut prendre plus de temps.", + "View YouTube comments": "Voir les commentaires YouTube", + "View more comments on Reddit": "Voir plus de commentaires sur Reddit", + "View `x` comments": "Voir `x` commentaires", + "View Reddit comments": "Voir les commentaires Reddit", + "Hide replies": "Masquer les réponses", + "Show replies": "Afficher les réponses", + "Incorrect password": "Mot de passe incorrect", + "Quota exceeded, try again in a few hours": "Nombre de tentative de connexion dépassé, réessayez dans quelques heures", + "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Si vous ne parvenez pas à vous connecter, assurez-vous que l'authentification à deux facteurs (Authenticator ou SMS) est activée.", + "Invalid TFA code": "Code d'authentification à deux facteurs invalide", + "Login failed. This may be because two-factor authentication is not enabled on your account.": "La connexion a échoué. Cela peut être dû au fait que l'authentification à deux facteurs n'est pas activée sur votre compte.", + "Invalid answer": "Réponse non valide", + "Invalid CAPTCHA": "CAPTCHA invalide", + "CAPTCHA is a required field": "Veuillez rentrez un CAPTCHA", + "User ID is a required field": "Veuillez rentrez un Identifiant Utilisateur", + "Password is a required field": "Veuillez rentrez un Mot de passe", + "Invalid username or password": "Nom d'utilisateur ou mot de passe invalide", + "Please sign in using 'Sign in with Google'": "Veuillez vous connecter en utilisant \"S'identifier avec Google\"", + "Password cannot be empty": "Le mot de passe ne peut pas être vide", + "Password cannot be longer than 55 characters": "Le mot de passe ne doit pas comporter plus de 55 caractères", + "Please sign in": "Veuillez vous connecter", + "Invidious Private Feed for `x`": "Flux RSS privé pour `x`", + "channel:`x`": "chaîne:`x`", + "Deleted or invalid channel": "Chaîne supprimée ou invalide", + "This channel does not exist.": "Cette chaine n'existe pas.", + "Could not get channel info.": "Impossible de charger les informations de cette chaîne.", + "Could not fetch comments": "Impossible de charger les commentaires", + "View `x` replies": "Voir `x` réponses", + "`x` ago": "il y a `x`", + "Load more": "Charger plus", + "`x` points": "`x` points", + "Could not create mix.": "Impossible de charger cette liste de lecture.", + "Playlist is empty": "La liste de lecture est vide", + "Invalid playlist.": "Liste de lecture invalide.", + "Playlist does not exist.": "La liste de lecture n'existe pas.", + "Could not pull trending pages.": "Impossible de charger les pages de tendances.", + "Hidden field \"challenge\" is a required field": "Hidden field \"challenge\" is a required field", + "Hidden field \"token\" is a required field": "Hidden field \"token\" is a required field", + "Invalid challenge": "Invalid challenge", + "Invalid token": "Invalid token", + "Invalid user": "Invalid user", + "Token is expired, please try again": "Token is expired, please try again", + "English": "Anglais", + "English (auto-generated)": "Anglais (générés automatiquement)", + "Afrikaans": "Afrikaans", + "Albanian": "Albanais", + "Amharic": "Amharique", + "Arabic": "Arabe", + "Armenian": "Arménien", + "Azerbaijani": "Azerbaïdjanais", + "Bangla": "Bangla", + "Basque": "Basque", + "Belarusian": "Belarusian", + "Bosnian": "Bosnian", + "Bulgarian": "Bulgarian", + "Burmese": "Birman", + "Catalan": "Catalan", + "Cebuano": "Cebuano", + "Chinese (Simplified)": "Chinois (Simplifié)", + "Chinese (Traditional)": "Chinois (Traditionnel)", + "Corsican": "Corse", + "Croatian": "Croate", + "Czech": "Tchèque", + "Danish": "Danois", + "Dutch": "Hollandais", + "Esperanto": "Espéranto", + "Estonian": "Estonien", + "Filipino": "Philippin", + "Finnish": "Finlandais", + "French": "Français", + "Galician": "Galicien", + "Georgian": "Géorgien", + "German": "Allemand", + "Greek": "Grec", + "Gujarati": "Gujarati", + "Haitian Creole": "Créole Haïtien", + "Hausa": "Haoussa", + "Hawaiian": "Hawaïen", + "Hebrew": "Hébraïque", + "Hindi": "Hindi", + "Hmong": "Hmong", + "Hungarian": "Hongrois", + "Icelandic": "Islandais", + "Igbo": "Igbo", + "Indonesian": "Indonésien", + "Irish": "Irlandais", + "Italian": "Italien", + "Japanese": "Japonais", + "Javanese": "Javanais", + "Kannada": "Kannada", + "Kazakh": "Kazakh", + "Khmer": "Khmer", + "Korean": "Coréen", + "Kurdish": "Kurde", + "Kyrgyz": "Kirghize", + "Lao": "Lao", + "Latin": "Latin", + "Latvian": "Letton", + "Lithuanian": "Lituanien", + "Luxembourgish": "Luxembourgeois", + "Macedonian": "Macédonien", + "Malagasy": "Malgache", + "Malay": "Malais", + "Malayalam": "Malayalam", + "Maltese": "Maltais", + "Maori": "Maori", + "Marathi": "Marathi", + "Mongolian": "Mongol", + "Nepali": "Népalais", + "Norwegian": "Norvégien", + "Nyanja": "Nyanja", + "Pashto": "Pachtou", + "Persian": "Persan", + "Polish": "Polonais", + "Portuguese": "Portugais", + "Punjabi": "Punjabi", + "Romanian": "Roumain", + "Russian": "Russe", + "Samoan": "Samoan", + "Scottish Gaelic": "Eaélique Ècossais", + "Serbian": "Serbe", + "Shona": "Shona", + "Sindhi": "Sindhi", + "Sinhala": "Cinghalais", + "Slovak": "Slovaque", + "Slovenian": "Slovène", + "Somali": "Somalien", + "Southern Sotho": "Sotho du Sud", + "Spanish": "Espagnol", + "Spanish (Latin America)": "Espagnol (Amérique latine)", + "Sundanese": "Sundanais", + "Swahili": "Swahili", + "Swedish": "Suédois", + "Tajik": "Tajik", + "Tamil": "Tamil", + "Telugu": "Telugu", + "Thai": "Thaï", + "Turkish": "Turc", + "Ukrainian": "Ukrainien", + "Urdu": "Ourdou", + "Uzbek": "Ouzbek", + "Vietnamese": "Vietnamien", + "Welsh": "Gallois", + "Western Frisian": "Frison occidental", + "Xhosa": "Xhosa", + "Yiddish": "Yiddish", + "Yoruba": "Yoruba", + "Zulu": "Zoulou", + "`x` years": "`x` ans", + "`x` months": "`x` mois", + "`x` weeks": "`x` semaines", + "`x` days": "`x` jours", + "`x` hours": "`x` heures", + "`x` minutes": "`x` minutes", + "`x` seconds": "`x` secondes", + "Fallback comments: ": "Commentaires secondaires : ", + "Popular": "Populaire", + "Top": "Haut", + "About": "A Propos", + "Rating: ": "Évaluation : ", + "Language: ": "Langue : ", + "Default": "Défaut", + "Music": "Musique", + "Gaming": "Jeux Vidéo", + "News": "Actualités", + "Movies": "Films", + "Download": "Télécharger", + "Download as: ": "Télécharger en : ", + "%A %B %-d, %Y": "%A %-d %B %Y", + "(edited)": "(modifié)", + "Youtube permalink of the comment": "Lien YouTube permanent vers le commentaire", + "`x` marked it with a ❤": "`x` l'a marqué d'un ❤", + "Audio mode": "Mode Audio", + "Video mode": "Mode Vidéo", + "Videos": "", + "Playlists": "", + "Current version: ": "" } From db245add0f4da953b3f21ab99d2d542869c92d65 Mon Sep 17 00:00:00 2001 From: TheFrenchGhosty <47571719+TheFrenchGhosty@users.noreply.github.com> Date: Fri, 15 Mar 2019 01:28:27 +0000 Subject: [PATCH 48/81] French translation updated, some translation restored (#412) * French translation updated --- locales/fr.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/locales/fr.json b/locales/fr.json index 7647be53f..6ce605754 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -10,7 +10,7 @@ "newest": "Date d'ajout (la plus récente)", "oldest": "Date d'ajout (la plus ancienne)", "popular": "Les plus populaires", - "last": "", + "last": "Dernières", "Next page": "Page suivante", "Previous page": "Page précédente", "Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?", @@ -27,7 +27,7 @@ "Export subscriptions as OPML": "Exporter les abonnements en OPML", "Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporter les abonnements en OPML (pour NewPipe & FreeTube)", "Export data as JSON": "Exporter les données au format JSON", - "Delete account?": "Supprimer votre compte ?", + "Delete account?": "Êtes-vous sûr de vouloir supprimer votre compte ?", "History": "Historique", "An alternative front-end to YouTube": "Un front-end alternatif à YouTube", "JavaScript license information": "Informations sur les licences JavaScript", @@ -35,14 +35,14 @@ "Login": "Connexion", "Login/Register": "Connexion/S'inscrire", "Login to Google": "Se connecter à Google", - "User ID:": "ID utilisateur :", + "User ID:": "Identifiant utilisateur :", "Password:": "Mot de passe :", "Time (h:mm:ss):": "Heure (h:mm:ss) :", "Text CAPTCHA": "CAPTCHA Texte", "Image CAPTCHA": "CAPTCHA Image", "Sign In": "S'identifier", "Register": "S'inscrire", - "Email:": "Email :", + "Email:": "E-mail :", "Google verification code:": "Code de vérification Google :", "Preferences": "Préférences", "Player preferences": "Préférences du Lecteur", @@ -50,7 +50,7 @@ "Autoplay: ": "Lire Automatiquement : ", "Autoplay next video: ": "Lire automatiquement la vidéo suivante : ", "Listen by default: ": "Audio Uniquement par défaut : ", - "Proxy videos? ": "", + "Proxy videos? ": "Souhaitez vous charger les vidéos à travers un proxy ?", "Default speed: ": "Vitesse par défaut : ", "Preferred video quality: ": "Qualité vidéo souhaitée : ", "Player volume: ": "Volume du lecteur : ", @@ -58,7 +58,7 @@ "Default captions: ": "Sous-titres principal : ", "Fallback captions: ": "Sous-titres secondaire : ", "Show related videos? ": "Voir les vidéos liées à ce sujet ? ", - "Visual preferences": "Préférences visuelles", + "Visual preferences": "Préférences du site", "Dark mode: ": "Mode Sombre : ", "Thin mode: ": "Mode Simplifié : ", "Subscription preferences": "Préférences de la page d'abonnements", @@ -81,14 +81,14 @@ "Manage subscriptions": "Gérer les abonnements", "Watch history": "Historique de visionnage", "Delete account": "Supprimer votre compte", - "Administrator preferences": "", - "Default homepage: ": "", - "Feed menu: ": "", - "Top enabled? ": "", - "CAPTCHA enabled? ": "", - "Login enabled? ": "", - "Registration enabled? ": "", - "Report statistics? ": "", + "Administrator preferences": "Préferences d'Administrateur", + "Default homepage: ": "Page d'accueil par defaut :", + "Feed menu: ": "Menu des Flux :", + "Top enabled? ": "Top activé ?", + "CAPTCHA enabled? ": "CAPTCHA activé ?", + "Login enabled? ": "Connexion activé ?", + "Registration enabled? ": "Inscription activé ?", + "Report statistics? ": "Telemetrie activé ?", "Save preferences": "Enregistrer les préférences", "Subscription manager": "Gestionnaire d'abonnement", "`x` subscriptions": "`x` abonnements", @@ -101,7 +101,7 @@ "Released under the AGPLv3 by Omar Roth.": "Publié sous licence AGPLv3 par Omar Roth.", "Source available here.": "Code Source.", "View JavaScript license information.": "Voir les informations des licences JavaScript.", - "View privacy policy.": "", + "View privacy policy.": "Politique de confidentialité", "Trending": "Tendances", "Watch video on Youtube": "Voir la vidéo sur Youtube", "Genre: ": "Genre : ", @@ -287,7 +287,7 @@ "`x` marked it with a ❤": "`x` l'a marqué d'un ❤", "Audio mode": "Mode Audio", "Video mode": "Mode Vidéo", - "Videos": "", - "Playlists": "", - "Current version: ": "" + "Videos": "Vidéos", + "Playlists": "Liste de lecture", + "Current version: ": "Version actuelle :" } From 4a56a2cad6498a83c36a17fd1886b7382da79041 Mon Sep 17 00:00:00 2001 From: Omar Roth Date: Fri, 15 Mar 2019 08:34:37 -0500 Subject: [PATCH 49/81] Remove outline when clicking on player --- src/invidious/views/components/player.ecr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr index 160afb2d1..6012abfb5 100644 --- a/src/invidious/views/components/player.ecr +++ b/src/invidious/views/components/player.ecr @@ -1,4 +1,4 @@ -
diff --git a/src/invidious/views/playlist.ecr b/src/invidious/views/playlist.ecr index cd7671402..98da42dc0 100644 --- a/src/invidious/views/playlist.ecr +++ b/src/invidious/views/playlist.ecr @@ -31,7 +31,7 @@ <%= rendered "components/item" %> <% end %> <% end %> -
+
diff --git a/src/invidious/views/privacy.ecr b/src/invidious/views/privacy.ecr index 63f99ab67..79206c566 100644 --- a/src/invidious/views/privacy.ecr +++ b/src/invidious/views/privacy.ecr @@ -5,15 +5,15 @@
<%= Markdown.to_html(<<-END_PRIVACY_POLICY ## Privacy - + This document concerns what data you provide to this website, the purpose of the data, how the data is stored, and how the data can be removed. - + ### Data you directly provide - + Data that you provide to the website for the purpose of the site's operation (for example: an account name, account password, or channel subscription) will be stored in the website's database until the user decides to remove it. This data will not be intentionally shared with anyone or anything. - + Information stored about a registered user is limited to: - + - a list of session tokens for remaining logged in across devices - the last time an account was updated (to provide accurate notifications) - a list of video IDs identifying notifications from a user's subscriptions @@ -23,51 +23,51 @@ - a hashed password if applicable (not present on google accounts) - a randomly generated token for providing an RSS feed of a user's subscriptions - a list of video IDs identifying watched videos - + The above list reflects [this code](https://github.com/omarroth/invidious/blob/master/src/invidious/users.cr#L14-L51). - + Users can clear their watch history using the [clear watch history](/clear_watch_history) page. - + If a user is logged in with a Google account, no password will ever be stored. This website uses the session token provided by Google to identify a user, but does not store the information required to make requests on a user's behalf without their knowledge or consent. - + ### Data you passively provide - + When you request any resource from this website (for example: a page, a font, an image, or an API endpoint) information about the request may be logged. - + Information about a request is limited to: - + - the time the request was made - the status code of the response - the method of the request - the requested URL - how long it took to complete the request. - + No identifying information is logged, such as the visitor's cookie, user-agent, or IP address. Here are a couple lines to serve as an example: - + ``` 2019-01-19 16:37:47 +00:00 200 GET /api/v1/comments/xrlETJYzH-c?format=html&hl=en-US 1345.88ms 2019-01-19 16:37:53 +00:00 200 GET /vi/r5P-f5arPXE/maxres.jpg 1085.41ms 2019-01-19 16:37:54 +00:00 200 GET /watch 7.04ms ``` - + This website does not store the visitor's user-agent or IP address and does not use fingerprinting, advertisements, or tracking of any form. - + This website provides links to googlevideo.com to provide audio and video playback. googlevideo.com is owned by Google and is subject to their [privacy policy](https://policies.google.com/privacy). - + ### Data stored in your browser - + This website uses browser cookies to authenticate registered users. This data consists of: - + - An account token to keep you logged into the website between visits, which is sent when any page is loaded while you are logged in - + This website also provides an option to store site preferences, such as the theme or locale, without an account. Using this feature will store a cookie in the visitor's browser containing their preferences. This cookie is sent on every request and does not contain any identifying information. You can remove this data from your browser by logging out of this website, or by using your browser's cookie-related controls to delete the data. - + ### Removal of data - + To remove data stored in your browser, you can log out of the website, or you can use your browser's cookie-related controls to delete the data. - + To remove data that has been stored in the website's database, you can use the [delete my account](/delete_account) page. END_PRIVACY_POLICY ) diff --git a/src/invidious/views/search.ecr b/src/invidious/views/search.ecr index 2cfb5b6aa..a0d3bb7b5 100644 --- a/src/invidious/views/search.ecr +++ b/src/invidious/views/search.ecr @@ -8,7 +8,7 @@ <%= rendered "components/item" %> <% end %> <% end %> -
+
diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr index 496719ce7..35e52dcf5 100644 --- a/src/invidious/views/template.ecr +++ b/src/invidious/views/template.ecr @@ -116,7 +116,7 @@
Liberapay - / + / Patreon
+
<%= translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 3595a4f5f..5173c9fb4 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -18,7 +18,7 @@ - + @@ -33,7 +33,7 @@

- <%= HTML.escape(video.title) %> + <%= HTML.escape(video.title) %> <% if params[:listen] %> " href="/watch?<%= env.params.query %>&listen=0"> @@ -53,7 +53,7 @@

<%= translate(locale, "Watch video on Youtube") %>

- +
@@ -150,7 +150,7 @@ <% if params[:related_videos] %>
- + <% if !rvs.empty? %>
style="display:none"<% end %>>
@@ -187,7 +187,7 @@