Merge remote-tracking branch 'upstream/master' into side-menu

This commit is contained in:
Tommy Miland 2019-03-12 16:11:31 +01:00
commit 1b2f5a5f53
21 changed files with 271 additions and 215 deletions

View File

@ -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)

View File

@ -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;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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: ": ""
}

View File

@ -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: ": ""
}

View File

@ -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: "
}

View File

@ -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: ": ""
}

View File

@ -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: ": ""
}

View File

@ -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: ": ""
}

View File

@ -10,8 +10,9 @@
"newest": "nyeste",
"oldest": "eldste",
"popular": "populært",
"Preview page": "Forhåndsvis side",
"last": "",
"Next page": "Neste side",
"Previous page": "Forrige side",
"Clear watch history?": "Tøm visningshistorikk?",
"Yes": "Ja",
"No": "Nei",
@ -28,7 +29,6 @@
"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",
@ -87,7 +87,7 @@
"CAPTCHA enabled? ": "CAPTCHA påskrudd? ",
"Login enabled? ": "Innlogging påskrudd? ",
"Registration enabled? ": "Registrering påskrudd? ",
"Report statistics? ": "",
"Report statistics? ": "Innrapporter statistikk? ",
"Save preferences": "Lagre innstillinger",
"Subscription manager": "Abonnementsbehandler",
"`x` subscriptions": "`x` abonnementer",
@ -284,5 +284,8 @@
"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"
"Video mode": "Video-modus",
"Videos": "",
"Playlists": "",
"Current version: ": ""
}

View File

@ -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: ": ""
}

View File

@ -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: ": ""
}

View File

@ -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": "Альтернативный фронтенд для YouTube",
"JavaScript license information": "Лицензии JavaScript",
"source": "источник",
@ -82,14 +82,14 @@
"Manage subscriptions": "Управление подписками",
"Watch history": "История просмотров",
"Delete account": "Удалить аккаунт",
"Administrator preferences": "",
"Default homepage: ": "",
"Feed menu: ": "",
"Top enabled? ": "",
"CAPTCHA enabled? ": "",
"Login enabled? ": "",
"Registration enabled? ": "",
"Report statistics? ": "",
"Administrator preferences": "Настройки администратора",
"Default homepage: ": "Главная страница по умолчанию: ",
"Feed menu: ": "Меню ленты: ",
"Top enabled? ": "Включить ТОП? ",
"CAPTCHA enabled? ": "Включить капчу? ",
"Login enabled? ": "Включить логин? ",
"Registration enabled? ": "Включить регистрацию? ",
"Report statistics? ": "Отображать статистику? ",
"Save preferences": "Сохранить настройки",
"Subscription manager": "Менеджер подписок",
"`x` subscriptions": "`x` подписок",
@ -101,10 +101,6 @@
"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",
@ -290,5 +286,8 @@
"Youtube permalink of the comment": "Прямая ссылка на YouTube",
"`x` marked it with a ❤": "❤ от автора канала \"`x`\"",
"Audio mode": "Аудио режим",
"Video mode": "Видео режим"
"Video mode": "Видео режим",
"Videos": "",
"Playlists": "",
"Current version: ": ""
}

View File

@ -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?("+")
@ -396,6 +403,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"]).full_path }
adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path }
end
video_streams = video.video_streams(adaptive_fmts)
audio_streams = video.audio_streams(adaptive_fmts)
@ -458,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?("+")
@ -496,6 +509,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"]).full_path }
adaptive_fmts.each { |fmt| fmt["url"] = URI.parse(fmt["url"]).full_path }
end
video_streams = video.video_streams(adaptive_fmts)
audio_streams = video.audio_streams(adaptive_fmts)
@ -546,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
@ -577,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
@ -600,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)
@ -618,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"]?
@ -635,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"]?
@ -721,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
@ -767,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")
@ -1144,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)
@ -1177,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)
@ -1335,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"
@ -1344,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
@ -1360,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")
@ -1390,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")
@ -1422,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)
@ -1469,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"
@ -1546,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)
@ -1561,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)
@ -1660,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)
@ -1741,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)
@ -1758,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)
@ -1791,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)
@ -1808,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)
@ -1835,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"
@ -1845,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"
@ -1870,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"
@ -1909,12 +1918,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 +1928,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 +1956,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 +1976,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
@ -2024,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)
@ -2049,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"
@ -2161,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"
@ -2197,12 +2200,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 +2209,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
@ -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"
@ -2333,7 +2330,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
@ -2387,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"]
@ -2424,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
@ -2477,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
@ -2492,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
@ -2549,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"
@ -2654,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"
@ -2721,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"
@ -2812,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"
@ -2838,7 +2836,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)
@ -3001,7 +2999,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
@ -3022,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"
@ -3043,7 +3041,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
@ -3073,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"
@ -3084,7 +3082,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
@ -3107,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"
@ -3123,7 +3121,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
@ -3153,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"
@ -3306,7 +3304,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
@ -3361,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"
@ -3404,7 +3402,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
@ -3432,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"
@ -3456,7 +3454,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
@ -3484,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"
@ -3527,7 +3525,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
@ -3552,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"
@ -3580,7 +3578,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
@ -3612,7 +3610,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
@ -3656,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"
@ -3709,7 +3707,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
@ -3741,7 +3739,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
@ -3785,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"]
@ -3857,7 +3855,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
@ -3888,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"
@ -3933,7 +3931,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
@ -4220,24 +4218,39 @@ get "/videoplayback" do |env|
query_params = env.params.query
fvip = query_params["fvip"]? || "3"
mn = query_params["mn"].split(",")[-1]
host = "https://r#{fvip}---#{mn}.googlevideo.com"
mns = query_params["mn"].split(",")
if query_params["host"]? && !query_params["host"].empty?
host = "https://#{query_params["host"]}"
query_params.delete("host")
else
host = "https://r#{fvip}---#{mns.pop}.googlevideo.com"
end
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
{"Accept", "Accept-Encoding", "Connection", "Range"}.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 !mns.empty?
mn = mns.pop
end
fvip = "3"
host = "https://r#{fvip}---#{mn}.googlevideo.com"
rescue ex
end
end
@ -4295,11 +4308,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
@ -4344,7 +4358,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
@ -4353,11 +4367,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

View File

@ -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
@ -74,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}

View File

@ -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 = ""

View File

@ -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"},
@ -329,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
@ -396,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
@ -743,11 +733,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?
@ -777,8 +768,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"]?
@ -811,6 +803,7 @@ def process_video_params(query, preferences)
continue: continue,
controls: controls,
listen: listen,
local: local,
preferred_captions: preferred_captions,
quality: quality,
raw: raw,
@ -826,12 +819,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

View File

@ -22,13 +22,17 @@
<div class="pure-g h-box">
<div class="pure-u-1-3">
<a href="https://www.youtube.com/channel/<%= ucid %>"><%= translate(locale, "View channel on YouTube") %></a>
<% if !auto_generated %>
<div class="pure-u-1 pure-md-1-3">
<b><%= translate(locale, "Videos") %></b>
</div>
<% end %>
<div class="pure-u-1 pure-md-1-3">
<% if !auto_generated %>
<a href="/channel/<%= ucid %>/playlists"><%= translate(locale, "Playlists") %></a>
<% end %>
<% if auto_generated %>
<b><%= translate(locale, "Playlists") %></b>
<% else %>
<a href="/channel/<%= ucid %>/playlists"><%= translate(locale, "Playlists") %></a>
<% end %>
</div>
</div>
<div class="pure-u-1-3">

View File

@ -11,7 +11,7 @@
<% else %>
<% if params[:listen] %>
<% audio_streams.each_with_index do |fmt, i| %>
<source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %>" type='<%= fmt["type"] %>' label="<%= fmt["bitrate"] %>k" selected="<%= i == 0 ? true : false %>">
<source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %><% 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,20 +19,20 @@
<% end %>
<% fmt_stream.each_with_index do |fmt, i| %>
<% if params[:quality] %>
<source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= params[:quality] == fmt["label"].split(" - ")[0] %>">
<source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %><% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= params[:quality] == fmt["label"].split(" - ")[0] %>">
<% else %>
<source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= i == 0 ? true : false %>">
<source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %><% if params[:local] %>&local=true<% end %>" type='<%= fmt["type"] %>' label="<%= fmt["label"] %>" selected="<%= i == 0 ? true : false %>">
<% end %>
<% end %>
<% end %>
<% preferred_captions.each_with_index do |caption, i| %>
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name.simpleText %>&hl=<%= env.get("locale").as(String) %>"
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name.simpleText %>&hl=<%= env.get("preferences").as(Preferences).locale %>"
label="<%= caption.name.simpleText %>" <% if i == 0 %>default<% end %>>
<% end %>
<% captions.each do |caption| %>
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name.simpleText %>&hl=<%= env.get("locale").as(String) %>"
<track kind="captions" src="/api/v1/captions/<%= video.id %>?label=<%= caption.name.simpleText %>&hl=<%= env.get("preferences").as(Preferences).locale %>"
label="<%= caption.name.simpleText %>">
<% end %>
<% end %>

View File

@ -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 =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3>';
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 =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3>';
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 =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3>';
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';