mirror of
https://github.com/iv-org/invidious.git
synced 2025-08-01 18:28:29 +00:00
Merge remote-tracking branch 'upstream/master' into side-menu
This commit is contained in:
commit
1b2f5a5f53
@ -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)
|
||||
|
@ -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;
|
||||
|
3
assets/js/dash.mediaplayer.min.js
vendored
3
assets/js/dash.mediaplayer.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: "
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
@ -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: ": ""
|
||||
}
|
||||
|
275
src/invidious.cr
275
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?("+")
|
||||
@ -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
|
||||
|
@ -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}
|
||||
|
||||
|
@ -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 = ""
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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 %>
|
||||
|
@ -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';
|
||||
|
Loading…
Reference in New Issue
Block a user