diff options
39 files changed, 419 insertions, 124 deletions
@@ -154,6 +154,7 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab, - [Yattee](https://github.com/yattee/yattee): Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV. - [TubiTui](https://codeberg.org/777/TubiTui): A lightweight, libre, TUI-based YouTube client. - [Ytfzf](https://github.com/pystardust/ytfzf): A posix script to find and watch youtube videos from the terminal. (Without API) +- [Playlet](https://github.com/iBicha/playlet): Unofficial Youtube client for Roku TV ## Liability diff --git a/assets/css/default.css b/assets/css/default.css index 80bf6a20..9788e9f7 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -490,8 +490,9 @@ hr { } /* Description Expansion Styling*/ -#descexpansionbutton { - display: none +#descexpansionbutton, +#music-desc-expansion { + display: none; } #descexpansionbutton ~ div { @@ -509,7 +510,8 @@ hr { margin-top: 20px; } -label[for="descexpansionbutton"]:hover { +label[for="descexpansionbutton"]:hover, +label[for="music-desc-expansion"]:hover { cursor: pointer; } @@ -521,14 +523,38 @@ h4, h5, p, #descriptionWrapper, -#description-box { - unicode-bidi: plaintext; - text-align: start; +#description-box, +#music-description-box { + unicode-bidi: plaintext; + text-align: start; } #descriptionWrapper { - max-width: 600px; - white-space: pre-wrap; + max-width: 600px; + white-space: pre-wrap; +} + +#music-description-box { + display: none; +} + +#music-desc-expansion:checked ~ #music-description-box { + display: block; +} + +#music-desc-expansion ~ label > h3 > .ion-ios-arrow-up, +#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-down { + display: none; +} + +#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-up, +#music-desc-expansion ~ label > h3 > .ion-ios-arrow-down { + display: inline; +} + +/* Select all the music items except the first one */ +.music-item + .music-item { + border-top: 1px solid #ffffff; } /* Center the "invidious" logo on the search page */ diff --git a/locales/af.json b/locales/af.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/locales/af.json @@ -0,0 +1 @@ +{} diff --git a/locales/ar.json b/locales/ar.json index 55dea5f3..181ff933 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -540,5 +540,8 @@ "channel_tab_shorts_label": "الفيديوهات القصيرة", "channel_tab_streams_label": "البث المباشر", "channel_tab_playlists_label": "قوائم التشغيل", - "channel_tab_channels_label": "القنوات" + "channel_tab_channels_label": "القنوات", + "Music in this video": "الموسيقى في هذا الفيديو", + "Album: ": "الألبوم: ", + "Artist: ": "الفنان: " } diff --git a/locales/cs.json b/locales/cs.json index 7502de0b..51db1550 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -492,5 +492,8 @@ "channel_tab_shorts_label": "Shorts", "channel_tab_playlists_label": "Playlisty", "channel_tab_channels_label": "Kanály", - "channel_tab_streams_label": "Živé přenosy" + "channel_tab_streams_label": "Živé přenosy", + "Music in this video": "Hudba v tomto videu", + "Artist: ": "Umělec: ", + "Album: ": "Album: " } diff --git a/locales/en-US.json b/locales/en-US.json index 12955665..a5c16fd7 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -188,6 +188,9 @@ "Engagement: ": "Engagement: ", "Whitelisted regions: ": "Whitelisted regions: ", "Blacklisted regions: ": "Blacklisted regions: ", + "Music in this video": "Music in this video", + "Artist: ": "Artist: ", + "Album: ": "Album: ", "Shared `x`": "Shared `x`", "Premieres in `x`": "Premieres in `x`", "Premieres `x`": "Premieres `x`", diff --git a/locales/eo.json b/locales/eo.json index 56e718f2..9f37c7cb 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -476,5 +476,8 @@ "channel_tab_streams_label": "Tujelsendoj", "channel_tab_playlists_label": "Ludlistoj", "channel_tab_channels_label": "Kanaloj", - "channel_tab_shorts_label": "Mallongaj" + "channel_tab_shorts_label": "Mallongaj", + "Music in this video": "Muziko en ĉi tiu video", + "Artist: ": "Artisto: ", + "Album: ": "Albumo: " } diff --git a/locales/es.json b/locales/es.json index 59d6b145..6cf721f3 100644 --- a/locales/es.json +++ b/locales/es.json @@ -476,5 +476,8 @@ "channel_tab_streams_label": "Directos", "channel_tab_channels_label": "Canales", "channel_tab_shorts_label": "Cortos", - "channel_tab_playlists_label": "Listas de reproducción" + "channel_tab_playlists_label": "Listas de reproducción", + "Music in this video": "Música en este vídeo", + "Artist: ": "Artista: ", + "Album: ": "Álbum: " } diff --git a/locales/fa.json b/locales/fa.json index f2ca2745..fe72a1e8 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -408,9 +408,9 @@ "preferences_region_label": "کشور محتوا: ", "footer_documentation": "مستندات", "footer_original_source_code": "کد منبع اصلی", - "search_filters_duration_option_long": "بلند (> 20 دقیقه)", + "search_filters_duration_option_long": "بلند (> ۲۰ دقیقه)", "adminprefs_modified_source_code_url_label": "URL مخزن کد منبع ویریش شده", - "search_filters_duration_option_short": "کوتاه (< 4 دقیقه)", + "search_filters_duration_option_short": "کوتاه (< ۴ دقیقه)", "search_filters_title": "پالایه", "Chinese (Hong Kong)": "چینی (هنگکنگ)", "Dutch (auto-generated)": "هلندی (تولید خودکار)", @@ -424,5 +424,26 @@ "search_message_no_results": "نتیجهای یافت نشد.", "search_message_change_filters_or_query": "سعی کنید جستوجوی خود را وسیعتر کنید و/یا فیلترها را تغییر دهید.", "Chinese (China)": "چینی (چین)", - "German (auto-generated)": "آلمانی (تولید خودکار)" + "German (auto-generated)": "آلمانی (تولید خودکار)", + "Japanese (auto-generated)": "ژاپنی (تولید خودکار)", + "Korean (auto-generated)": "کرهای (تولید خودکار)", + "Portuguese (Brazil)": "پرتغالی (برزیل)", + "search_filters_apply_button": "اعمال فیلترهای انتخاب شده", + "Italian (auto-generated)": "ایتالیایی (تولید خودکار)", + "Vietnamese (auto-generated)": "ویتنامی (تولید خودکار)", + "search_filters_type_option_all": "هر نوعی", + "search_filters_duration_option_none": "هر مدت زمانی", + "search_filters_date_label": "تاریخ بارگذاری", + "search_filters_date_option_none": "هر تاریخی", + "user_created_playlists": "`x` فهرست پخش ایجاد شد", + "Interlingue": "سرخپوستی", + "Russian (auto-generated)": "روسی (تولید خودکار)", + "Spanish (auto-generated)": "اسپانیایی (تولید خودکار)", + "search_filters_duration_option_medium": "متوسط (۴ تا ۲۰ دقیقه)", + "Portuguese (auto-generated)": "پرتغالی (تولید خودکار)", + "Cantonese (Hong Kong)": "کانتونی (هنگ کنگ)", + "Spanish (Spain)": "اسپانیایی (اسپانیا)", + "Turkish (auto-generated)": "ترکی (تولید خودکار)", + "search_filters_features_option_vr180": "VR180", + "Spanish (Mexico)": "اسپانیایی (مکزیک)" } diff --git a/locales/hr.json b/locales/hr.json index 7914ab16..72cd6a8e 100644 --- a/locales/hr.json +++ b/locales/hr.json @@ -492,5 +492,8 @@ "channel_tab_streams_label": "Prijenosi uživo", "channel_tab_playlists_label": "Zbirke", "channel_tab_channels_label": "Kanali", - "channel_tab_shorts_label": "Kratka videa" + "channel_tab_shorts_label": "Kratka videa", + "Music in this video": "Glazba u ovom videu", + "Album: ": "Album: ", + "Artist: ": "Izvođač: " } diff --git a/locales/it.json b/locales/it.json index f47b032e..c60f760b 100644 --- a/locales/it.json +++ b/locales/it.json @@ -476,5 +476,8 @@ "channel_tab_playlists_label": "Playlist", "channel_tab_channels_label": "Canali", "channel_tab_streams_label": "Livestream", - "channel_tab_community_label": "Comunità" + "channel_tab_community_label": "Comunità", + "Music in this video": "Musica in questo video", + "Artist: ": "Artista: ", + "Album: ": "Album: " } diff --git a/locales/ja.json b/locales/ja.json index a392abfe..3ad4b494 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -53,12 +53,12 @@ "E-mail": "メールアドレス", "Google verification code": "Google 認証コード", "Preferences": "設定", - "preferences_category_player": "プレイヤー設定", + "preferences_category_player": "プレイヤーの設定", "preferences_video_loop_label": "常にループ: ", "preferences_autoplay_label": "自動再生: ", "preferences_continue_label": "デフォルトで次を再生: ", "preferences_continue_autoplay_label": "次の動画を自動再生: ", - "preferences_listen_label": "デフォルトでオーディオモードを使用: ", + "preferences_listen_label": "デフォルトで音声モードを使用: ", "preferences_local_label": "動画をプロキシーに通す: ", "preferences_speed_label": "デフォルトの再生速度: ", "preferences_quality_label": "優先する画質: ", @@ -73,14 +73,14 @@ "preferences_extend_desc_label": "動画の説明文を自動的に拡張: ", "preferences_vr_mode_label": "対話的な360°動画 (WebGL が必要): ", "preferences_category_visual": "外観設定", - "preferences_player_style_label": "プレイヤースタイル: ", + "preferences_player_style_label": "プレイヤーのスタイル: ", "Dark mode: ": "ダークモード: ", "preferences_dark_mode_label": "テーマ: ", "dark": "ダーク", "light": "ライト", "preferences_thin_mode_label": "最小モード: ", - "preferences_category_misc": "雑設定", - "preferences_automatic_instance_redirect_label": "自動的なインスタンスの移転(redirect.invidious.ioにフォールバック): ", + "preferences_category_misc": "ほかの設定", + "preferences_automatic_instance_redirect_label": "インスタンスの自動転送 (redirect.invidious.ioにフォールバック): ", "preferences_category_subscription": "登録チャンネル設定", "preferences_annotations_subscribed_label": "デフォルトで登録チャンネルのアノテーションを表示しますか? ", "Redirect homepage to feed: ": "ホームからフィードにリダイレクト: ", @@ -117,8 +117,8 @@ "Registration enabled: ": "登録を有効化: ", "Report statistics: ": "統計を報告: ", "Save preferences": "設定を保存", - "Subscription manager": "登録チャンネルマネージャー", - "Token manager": "トークンマネージャー", + "Subscription manager": "登録チャンネルの管理", + "Token manager": "トークンの管理", "Token": "トークン", "tokens_count_0": "{{count}} 個のトークン", "Import/export": "インポート/エクスポート", @@ -128,7 +128,7 @@ "subscriptions_unseen_notifs_count_0": "{{count}} 個の未読通知", "search": "検索", "Log out": "ログアウト", - "Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開されています。", + "Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開", "Source available here.": "ソースはここで閲覧可能です。", "View JavaScript license information.": "JavaScript ライセンス情報", "View privacy policy.": "プライバシーポリシー", @@ -136,24 +136,24 @@ "Public": "公開", "Unlisted": "限定公開", "Private": "非公開", - "View all playlists": "再生リストをすべて見る", + "View all playlists": "すべての再生リストを表示", "Updated `x` ago": "`x`前に更新", "Delete playlist `x`?": "再生リスト `x` を削除しますか?", "Delete playlist": "再生リストを削除", "Create playlist": "再生リストを作成", "Title": "タイトル", - "Playlist privacy": "再生リストのプライバシー", + "Playlist privacy": "再生リストの公開設定", "Editing playlist `x`": "再生リスト `x` を編集中", - "Show more": "表示を増やす", - "Show less": "表示を減らす", + "Show more": "もっと見る", + "Show less": "表示を少なく", "Watch on YouTube": "YouTube で視聴", - "Switch Invidious Instance": "Invidiousインスタンスの変更", + "Switch Invidious Instance": "Invidious インスタンスの変更", "Hide annotations": "アノテーションを隠す", "Show annotations": "アノテーションを表示", "Genre: ": "ジャンル: ", "License: ": "ライセンス: ", "Family friendly? ": "家族向け: ", - "Wilson score: ": "ウィルソンスコア: ", + "Wilson score: ": "ウィルソン得点区間: ", "Engagement: ": "エンゲージメント: ", "Whitelisted regions: ": "ホワイトリストの地域: ", "Blacklisted regions: ": "ブラックリストの地域: ", @@ -181,11 +181,11 @@ "User ID is a required field": "ユーザー ID は必須項目です", "Password is a required field": "パスワードは必須項目です", "Wrong username or password": "ユーザー名またはパスワードが間違っています", - "Please sign in using 'Log in with Google'": "'Google でログイン' を使用してログインしてください", - "Password cannot be empty": "パスワードを空にすることはできません", + "Please sign in using 'Log in with Google'": "「Google でログイン」を使用してログインしてください", + "Password cannot be empty": "パスワードは空にできません", "Password cannot be longer than 55 characters": "パスワードは55文字より長くできません", - "Please log in": "ログインをしてください", - "Invidious Private Feed for `x`": "`x` の Invidious プライベートフィード", + "Please log in": "ログインしてください", + "Invidious Private Feed for `x`": "`x` 個人の Invidious によるフィード", "channel:`x`": "チャンネル:`x`", "Deleted or invalid channel": "削除済みまたは無効なチャンネルです", "This channel does not exist.": "このチャンネルは存在しません。", @@ -194,18 +194,18 @@ "comments_view_x_replies_0": "{{count}} 件の返信を見る", "`x` ago": "`x`前", "Load more": "もっと読み込む", - "comments_points_count_0": "{{count}} ポイント", + "comments_points_count_0": "{{count}}点", "Could not create mix.": "ミックスを作成できませんでした。", "Empty playlist": "空の再生リスト", "Not a playlist.": "再生リストではありません。", "Playlist does not exist.": "再生リストが存在しません。", "Could not pull trending pages.": "急上昇ページを取得できませんでした。", - "Hidden field \"challenge\" is a required field": "非表示項目 \"challenge\" は必須項目です", - "Hidden field \"token\" is a required field": "非表示項目 \"token\" は必須項目です", + "Hidden field \"challenge\" is a required field": "非表示項目 challenge は必須項目です", + "Hidden field \"token\" is a required field": "非表示項目 token は必須項目です", "Erroneous challenge": "チャレンジが間違っています", "Erroneous token": "トークンが間違っています", "No such user": "ユーザーが存在しません", - "Token is expired, please try again": "トークンが期限切れです。再度試してください", + "Token is expired, please try again": "トークンが期限切れです。再度お試しください", "English": "英語", "English (auto-generated)": "英語 (自動生成)", "Afrikaans": "アフリカーンス語", @@ -313,7 +313,7 @@ "Yoruba": "ヨルバ語", "Zulu": "ズール語", "generic_count_years_0": "{{count}}年", - "generic_count_months_0": "{{count}}ヶ月", + "generic_count_months_0": "{{count}}か月", "generic_count_weeks_0": "{{count}}週", "generic_count_days_0": "{{count}}日", "generic_count_hours_0": "{{count}}時間", @@ -338,21 +338,21 @@ "(edited)": "(編集済み)", "YouTube comment permalink": "YouTube コメントのパーマリンク", "permalink": "パーマリンク", - "`x` marked it with a ❤": "`x` が❤を込めてマークしました", - "Audio mode": "オーディオモード", - "Video mode": "ビデオモード", + "`x` marked it with a ❤": "`x` が❤を送りました", + "Audio mode": "音声モード", + "Video mode": "動画モード", "channel_tab_videos_label": "動画", - "Playlists": "プレイリスト", + "Playlists": "再生リスト", "channel_tab_community_label": "コミュニティ", - "search_filters_sort_option_relevance": "関連", + "search_filters_sort_option_relevance": "関連度", "search_filters_sort_option_rating": "評価", - "search_filters_sort_option_date": "時刻", + "search_filters_sort_option_date": "アップロード日", "search_filters_sort_option_views": "再生回数", - "search_filters_type_label": "コンテンツの種類", + "search_filters_type_label": "種類", "search_filters_duration_label": "再生時間", - "search_filters_features_label": "機能", + "search_filters_features_label": "特徴", "search_filters_sort_label": "順番", - "search_filters_date_option_hour": "1時間前", + "search_filters_date_option_hour": "1時間以内", "search_filters_date_option_today": "今日", "search_filters_date_option_week": "今週", "search_filters_date_option_month": "今月", @@ -377,9 +377,9 @@ "search_filters_duration_option_short": "4 分未満", "footer_documentation": "文書", "footer_source_code": "ソースコード", - "footer_original_source_code": "ソースコード(元)", - "footer_modfied_source_code": "ソースコード(編集)", - "adminprefs_modified_source_code_url_label": "編集したソースコードのレポジトリーURL", + "footer_original_source_code": "ソースコード (元)", + "footer_modfied_source_code": "ソースコード (改変)", + "adminprefs_modified_source_code_url_label": "改変されたソースコードのレポジトリのURL", "search_filters_duration_option_long": "20 分以上", "preferences_region_label": "地域: ", "footer_donate_page": "寄付する", @@ -406,10 +406,10 @@ "preferences_quality_option_dash": "DASH (適応品質)", "preferences_quality_dash_option_worst": "最悪", "preferences_quality_dash_option_best": "最高", - "videoinfo_started_streaming_x_ago": "`x`分前に配信を開始", + "videoinfo_started_streaming_x_ago": "`x`前に配信を開始", "videoinfo_watch_on_youTube": "YouTube上で見る", - "user_created_playlists": "`x`が作成したプレイリスト", - "Video unavailable": "ビデオは利用できません", + "user_created_playlists": "`x`個の作成した再生リスト", + "Video unavailable": "動画は利用できません", "Chinese": "中国語", "Chinese (Taiwan)": "中国語 (台湾)", "Korean (auto-generated)": "韓国語 (自動生成)", @@ -434,24 +434,34 @@ "Vietnamese (auto-generated)": "ベトナム語 (自動生成)", "search_filters_title": "フィルタ", "search_filters_features_option_three_sixty": "360°", - "search_message_change_filters_or_query": "別のキーワードを試してみるか、検索フィルタを削除してください", - "search_message_no_results": "一致する検索結果はありませんでした", + "search_message_change_filters_or_query": "別の検索語句を試したり、検索フィルタを変更してください。", + "search_message_no_results": "一致する検索結果はありません。", "English (United States)": "英語 (アメリカ)", "search_filters_date_label": "アップロード日", "search_filters_features_option_vr180": "VR180", - "crash_page_switch_instance": "<a href=\"`x`\">別のインスタンスを使用</a>しようとしました", + "crash_page_switch_instance": "<a href=\"`x`\">別のインスタンスを使用</a>を試す", "crash_page_read_the_faq": "<a href=\"`x`\">よくある質問 (FAQ)</a> を読む", "Popular enabled: ": "人気動画を有効化 ", - "search_message_use_another_instance": " <a href=\"`x`\">別のインスタンスで検索</a>することもできます。", + "search_message_use_another_instance": " <a href=\"`x`\">別のインスタンス上でも検索</a>できます。", "search_filters_apply_button": "選択したフィルターを適用", - "user_saved_playlists": "`x` 個の保存済みプレイリスト", + "user_saved_playlists": "`x` 個の保存した再生リスト", "crash_page_you_found_a_bug": "Invidious でバグを見つけたようです。", - "crash_page_refresh": "<a href=\"`x`\">ページを更新</a>しようとしました", - "preferences_watch_history_label": "視聴履歴を有効化 ", - "search_filters_date_option_none": "任意の日付", - "search_filters_type_option_all": "いかなるタイプ", - "search_filters_duration_option_none": "任意の期間", - "search_filters_duration_option_medium": "ミディアム (4 ~ 20 分)", + "crash_page_refresh": "<a href=\"`x`\">ページを更新</a>を試す", + "preferences_watch_history_label": "再生履歴を有効化 ", + "search_filters_date_option_none": "すべて", + "search_filters_type_option_all": "すべての種類", + "search_filters_duration_option_none": "すべての長さ", + "search_filters_duration_option_medium": "4 ~ 20 分", "preferences_save_player_pos_label": "再生位置を保存: ", - "crash_page_before_reporting": "バグを報告する前に、次のことを確認してください。" + "crash_page_before_reporting": "バグを報告する前に、次のことを確認してください。", + "crash_page_report_issue": "上記が助けにならないなら、<a href=\"`x`\">GitHub</a> に新しい issue を作成し(英語が好ましい)、メッセージに次のテキストを含めてください(テキストは翻訳しない)。", + "crash_page_search_issue": "<a href=\"`x`\">GitHub の既存の問題 (issue)</a> を検索", + "channel_tab_streams_label": "ライブ", + "channel_tab_playlists_label": "再生リスト", + "error_video_not_in_playlist": "要求された動画はこの再生リスト内に存在しません。<a href=\"`x`\">再生リストのホームへ。</a>", + "channel_tab_shorts_label": "ショート", + "channel_tab_channels_label": "チャンネル", + "Music in this video": "この動画の音楽", + "Artist: ": "アーティスト: ", + "Album: ": "アルバム: " } diff --git a/locales/pl.json b/locales/pl.json index b9c2a638..2dd3ed87 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -67,7 +67,7 @@ "preferences_annotations_label": "Domyślnie pokazuj adnotacje: ", "preferences_extend_desc_label": "Automatycznie rozwijaj opisy filmów: ", "preferences_vr_mode_label": "Interaktywne filmy 360 stopni (wymaga WebGL): ", - "preferences_category_visual": "Preferencje Wizualne", + "preferences_category_visual": "Preferencje wizualne", "preferences_player_style_label": "Styl odtwarzacza: ", "Dark mode: ": "Ciemny motyw: ", "preferences_dark_mode_label": "Motyw: ", @@ -443,7 +443,7 @@ "user_saved_playlists": "`x` zapisanych playlist", "Video unavailable": "Film niedostępny", "preferences_save_player_pos_label": "Zapisz pozycję odtwarzania: ", - "preferences_region_label": "Region zawartości: ", + "preferences_region_label": "Kraj treści: ", "Released under the AGPLv3 on Github.": "Wydany na licencji AGPLv3 na GitHub.", "search_filters_duration_option_short": "Krótka (< 4 minut)", "search_filters_duration_option_long": "Długa (> 20 minut)", @@ -481,7 +481,7 @@ "search_message_no_results": "Nie znaleziono wyników.", "preferences_watch_history_label": "Włącz historię oglądania: ", "search_filters_apply_button": "Zastosuj wybrane filtry", - "search_message_change_filters_or_query": "Spróbuj poszerzyć zapytanie i/lub zmienić filtry.", + "search_message_change_filters_or_query": "Spróbuj poszerzyć zapytanie wyszukiwania i/lub zmienić filtry.", "search_filters_date_label": "Data przesłania", "search_filters_features_option_vr180": "VR180", "search_filters_date_option_none": "Dowolna data", @@ -492,5 +492,8 @@ "channel_tab_streams_label": "Na żywo", "channel_tab_channels_label": "Kanały", "channel_tab_playlists_label": "Playlisty", - "channel_tab_shorts_label": "Shorts" + "channel_tab_shorts_label": "Shorts", + "Music in this video": "Muzyka w tym filmie", + "Artist: ": "Wykonawca: ", + "Album: ": "Album: " } diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 112ed4b7..afd31ede 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -472,5 +472,9 @@ "search_filters_duration_option_medium": "Médio (4 - 20 minutos)", "search_filters_features_option_vr180": "VR180", "Popular enabled: ": "Popular habilitado: ", - "error_video_not_in_playlist": "O vídeo solicitado não existe nesta playlist. <a href=\"`x`\">Clique aqui para acessar a página inicial da playlist.</a>" + "error_video_not_in_playlist": "O vídeo solicitado não existe nesta playlist. <a href=\"`x`\">Clique aqui para acessar a página inicial da playlist.</a>", + "channel_tab_channels_label": "Canais", + "channel_tab_playlists_label": "Listas de reprodução", + "channel_tab_shorts_label": "Curtos", + "channel_tab_streams_label": "Ao Vivo" } diff --git a/locales/pt.json b/locales/pt.json index 2facba94..b6b6c110 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -472,5 +472,12 @@ "search_filters_type_option_all": "Qualquer tipo", "search_filters_duration_option_none": "Qualquer duração", "Popular enabled: ": "Página \"popular\" ativada: ", - "error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>" + "error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>", + "channel_tab_playlists_label": "Listas de reprodução", + "channel_tab_channels_label": "Canais", + "channel_tab_shorts_label": "Curtos", + "channel_tab_streams_label": "Diretos", + "Music in this video": "Música neste vídeo", + "Artist: ": "Artista: ", + "Album: ": "Álbum: " } diff --git a/locales/ru.json b/locales/ru.json index e54937a6..733e0be1 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -379,7 +379,7 @@ "Turkish (auto-generated)": "Турецкий (созданы автоматически)", "Vietnamese (auto-generated)": "Вьетнамский (созданы автоматически)", "footer_documentation": "Документация", - "adminprefs_modified_source_code_url_label": "Ссылка на нашу ветку репозитория", + "adminprefs_modified_source_code_url_label": "URL-адрес репозитория измененного исходного кода", "none": "ничего", "videoinfo_watch_on_youTube": "Смотреть на YouTube", "videoinfo_youTube_embed_link": "Версия для встраивания", @@ -488,5 +488,12 @@ "search_filters_duration_option_medium": "Средние (4 - 20 минут)", "search_filters_apply_button": "Применить фильтры", "Popular enabled: ": "Популярное включено: ", - "error_video_not_in_playlist": "Запрошенного видео нет в этом плейлисте. <a href=\"`x`\">Нажмите тут, чтобы вернуться к странице плейлиста.</a>" + "error_video_not_in_playlist": "Запрошенного видео нет в этом плейлисте. <a href=\"`x`\">Нажмите тут, чтобы вернуться к странице плейлиста.</a>", + "channel_tab_playlists_label": "Плейлисты", + "channel_tab_channels_label": "Каналы", + "channel_tab_streams_label": "Живое вещание", + "channel_tab_shorts_label": "Shorts", + "Music in this video": "Музыка в этом видео", + "Artist: ": "Исполнитель: ", + "Album: ": "Альбом: " } diff --git a/locales/sl.json b/locales/sl.json index f27bb20d..47f295e0 100644 --- a/locales/sl.json +++ b/locales/sl.json @@ -206,7 +206,7 @@ "generic_count_years_2": "{{count}} leti", "generic_count_years_3": "{{count}} leti", "generic_count_days_0": "{{count}} dnevom", - "generic_count_days_1": "{{count}} dnevi", + "generic_count_days_1": "{{count}} dnevoma", "generic_count_days_2": "{{count}} dnevi", "generic_count_days_3": "{{count}} dnevi", "generic_count_hours_0": "{{count}} uro", @@ -246,10 +246,10 @@ "generic_videos_count_1": "{{count}} videa", "generic_videos_count_2": "{{count}} videi", "generic_videos_count_3": "{{count}} videov", - "generic_views_count_0": "{{count}} ogled", - "generic_views_count_1": "{{count}} ogleda", - "generic_views_count_2": "{{count}} ogledi", - "generic_views_count_3": "{{count}} ogledov", + "generic_views_count_0": "Ogledov: {{count}}", + "generic_views_count_1": "Ogledov: {{count}}", + "generic_views_count_2": "Ogledov: {{count}}", + "generic_views_count_3": "Ogledov: {{count}}", "generic_playlists_count_0": "{{count}} seznam predvajanja", "generic_playlists_count_1": "{{count}} seznama predvajanja", "generic_playlists_count_2": "{{count}} seznami predvajanja", @@ -495,7 +495,7 @@ "footer_modfied_source_code": "Spremenjena izvorna koda", "user_created_playlists": "`x` ustvarjenih seznamov predvajanja", "adminprefs_modified_source_code_url_label": "URL do shrambe spremenjene izvorne kode", - "videoinfo_youTube_embed_link": "Vdelati", + "videoinfo_youTube_embed_link": "Vdelaj", "videoinfo_invidious_embed_link": "Povezava za vdelavo", "crash_page_switch_instance": "poskušal/a <a href=\"`x`\">uporabiti drugo instanco</a>", "download_subtitles": "Podnapisi - `x` (.vtt)", @@ -504,5 +504,12 @@ "crash_page_search_issue": "preiskal/a <a href=\"`x`\">obstoječe težave na GitHubu</a>", "crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim <a href=\"`x`\">odpri novo težavo v GitHubu</a> (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):", "Popular enabled: ": "Priljubljeni omogočeni: ", - "error_video_not_in_playlist": "Zahtevani videoposnetek ne obstaja na tem seznamu predvajanja. <a href=\"`x`\">Klikni tukaj za domačo stran seznama predvajanja.</a>" + "error_video_not_in_playlist": "Zahtevani videoposnetek ne obstaja na tem seznamu predvajanja. <a href=\"`x`\">Klikni tukaj za domačo stran seznama predvajanja.</a>", + "channel_tab_playlists_label": "Seznami predvajanja", + "channel_tab_shorts_label": "Kratki videoposnetki", + "channel_tab_channels_label": "Kanali", + "channel_tab_streams_label": "Prenosi v živo", + "Artist: ": "Umetnik/ca: ", + "Music in this video": "Glasba v tem videoposnetku", + "Album: ": "Album: " } diff --git a/locales/sq.json b/locales/sq.json index b8651316..15025750 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -463,5 +463,10 @@ "search_filters_duration_option_none": "Çfarëdo kohëzgjatjeje", "search_filters_duration_option_medium": "Mesatare (4 - 20 minuta)", "search_filters_features_option_vr180": "VR180", - "search_filters_apply_button": "Apliko filtrat e përzgjedhur" + "search_filters_apply_button": "Apliko filtrat e përzgjedhur", + "channel_tab_playlists_label": "Luajlista", + "Artist: ": "Artist: ", + "Album: ": "Album: ", + "channel_tab_channels_label": "Kanale", + "Music in this video": "Muzikë në këtë video" } diff --git a/locales/tr.json b/locales/tr.json index 76cce15a..d98e2038 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -397,8 +397,8 @@ "videoinfo_watch_on_youTube": "YouTube'da İzle", "download_subtitles": "Alt Yazılar - `x` (.vtt)", "preferences_save_player_pos_label": "Oynatma Konumunu Kaydet: ", - "generic_views_count": "{{count}} Görüntüleme", - "generic_views_count_plural": "{{count}} Görüntüleme", + "generic_views_count": "{{count}} Görüntülenme", + "generic_views_count_plural": "{{count}} Görüntülenme", "generic_subscribers_count": "{{count}} Abone", "generic_subscribers_count_plural": "{{count}} Abone", "generic_subscriptions_count": "{{count}} Abonelik", @@ -476,5 +476,8 @@ "channel_tab_channels_label": "Kanallar", "channel_tab_shorts_label": "Kısa Çekimler", "channel_tab_streams_label": "Canlı Yayınlar", - "channel_tab_playlists_label": "Oynatma Listeleri" + "channel_tab_playlists_label": "Oynatma Listeleri", + "Album: ": "Albüm: ", + "Music in this video": "Bu videodaki müzik", + "Artist: ": "Sanatçı: " } diff --git a/locales/uk.json b/locales/uk.json index ae2fb5bd..b44d237f 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -492,5 +492,8 @@ "channel_tab_shorts_label": "Shorts", "channel_tab_streams_label": "Прямі трансляції", "channel_tab_playlists_label": "Добірки", - "channel_tab_channels_label": "Канали" + "channel_tab_channels_label": "Канали", + "Music in this video": "Музика в цьому відео", + "Artist: ": "Виконавець: ", + "Album: ": "Альбом: " } diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 385f16bd..aff6dd3e 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -456,5 +456,12 @@ "search_filters_type_option_all": "任意类型", "search_filters_features_option_vr180": "VR180", "Popular enabled: ": "已启用流行度: ", - "error_video_not_in_playlist": "此播放列表中不存在请求的视频。 <a href=\"`x`\">单击析出查看播放列表主页。</a>" + "error_video_not_in_playlist": "此播放列表中不存在请求的视频。 <a href=\"`x`\">单击析出查看播放列表主页。</a>", + "Music in this video": "此视频中的音乐", + "channel_tab_playlists_label": "播放列表", + "Artist: ": "艺术家: ", + "channel_tab_streams_label": "直播", + "Album: ": "专辑: ", + "channel_tab_shorts_label": "短视频", + "channel_tab_channels_label": "频道" } diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 3b51721d..8aa9869a 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -460,5 +460,8 @@ "channel_tab_shorts_label": "短片", "channel_tab_playlists_label": "播放清單", "channel_tab_channels_label": "頻道", - "channel_tab_streams_label": "直播" + "channel_tab_streams_label": "直播", + "Artist: ": "藝術家: ", + "Album: ": "專輯: ", + "Music in this video": "此影片中的音樂" } diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 76dff555..13af2d8b 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -189,6 +189,32 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) # when .has_key?("pollRenderer") # attachment = attachment["pollRenderer"] # json.field "type", "poll" + when .has_key?("postMultiImageRenderer") + attachment = attachment["postMultiImageRenderer"] + json.field "type", "multiImage" + json.field "images" do + json.array do + attachment["images"].as_a.each do |image| + json.array do + thumbnail = image["backstageImageRenderer"]["image"]["thumbnails"][0].as_h + width = thumbnail["width"].as_i + height = thumbnail["height"].as_i + aspect_ratio = (width.to_f / height.to_f) + url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640") + + qualities = {320, 560, 640, 1280, 2000} + + qualities.each do |quality| + json.object do + json.field "url", url.gsub(/=s\d+/, "=s#{quality}") + json.field "width", quality + json.field "height", (quality / aspect_ratio).ceil.to_i + end + end + end + end + end + end else json.field "type", "unknown" json.field "error", "Unrecognized attachment type." diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr index d691ca36..357a461c 100644 --- a/src/invidious/comments.cr +++ b/src/invidious/comments.cr @@ -181,6 +181,8 @@ def fetch_youtube_comments(id, cursor, format, locale, thin_mode, region, sort_b json.field "content", html_to_content(content_html) json.field "contentHtml", content_html + json.field "isPinned", (node_comment["pinnedCommentBadge"]? != nil) + json.field "published", published.to_unix json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale)) @@ -670,6 +672,7 @@ def content_to_comment_html(content, video_id : String? = "") end text = "<b>#{text}</b>" if run["bold"]? + text = "<s>#{text}</s>" if run["strikethrough"]? text = "<i>#{text}</i>" if run["italics"]? text diff --git a/src/invidious/helpers/serialized_yt_data.cr b/src/invidious/helpers/serialized_yt_data.cr index 635f0984..c1874780 100644 --- a/src/invidious/helpers/serialized_yt_data.cr +++ b/src/invidious/helpers/serialized_yt_data.cr @@ -74,6 +74,7 @@ struct SearchVideo json.field "author", self.author json.field "authorId", self.ucid json.field "authorUrl", "/channel/#{self.ucid}" + json.field "authorVerified", self.author_verified json.field "videoThumbnails" do Invidious::JSONify::APIv1.thumbnails(json, self.id) diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr index 72fdb187..500a2582 100644 --- a/src/invidious/helpers/utils.cr +++ b/src/invidious/helpers/utils.cr @@ -259,7 +259,7 @@ def get_referer(env, fallback = "/", unroll = true) end referer = referer.request_target - referer = "/" + referer.gsub(/[^\/?@&%=\-_.0-9a-zA-Z]/, "").lstrip("/\\") + referer = "/" + referer.gsub(/[^\/?@&%=\-_.:,0-9a-zA-Z]/, "").lstrip("/\\") if referer == env.request.path referer = fallback diff --git a/src/invidious/routes/account.cr b/src/invidious/routes/account.cr index d01aee56..8f69df94 100644 --- a/src/invidious/routes/account.cr +++ b/src/invidious/routes/account.cr @@ -203,7 +203,7 @@ module Invidious::Routes::Account referer = get_referer(env) if !user - return env.redirect referer + return env.redirect "/login?referer=#{URI.encode_path_segment(env.request.resource)}" end user = user.as(User) diff --git a/src/invidious/routes/api/v1/authenticated.cr b/src/invidious/routes/api/v1/authenticated.cr index 421355bb..6b935312 100644 --- a/src/invidious/routes/api/v1/authenticated.cr +++ b/src/invidious/routes/api/v1/authenticated.cr @@ -31,6 +31,29 @@ module Invidious::Routes::API::V1::Authenticated env.response.status_code = 204 end + def self.export_invidious(env) + env.response.content_type = "application/json" + user = env.get("user").as(User) + + return Invidious::User::Export.to_invidious(user) + end + + def self.import_invidious(env) + user = env.get("user").as(User) + + begin + if body = env.request.body + body = env.request.body.not_nil!.gets_to_end + else + body = "{}" + end + Invidious::User::Import.from_invidious(user, body) + rescue + end + + env.response.status_code = 204 + end + def self.feed(env) env.response.content_type = "application/json" diff --git a/src/invidious/routes/api/v1/channels.cr b/src/invidious/routes/api/v1/channels.cr index ca2b2734..bcb4db2c 100644 --- a/src/invidious/routes/api/v1/channels.cr +++ b/src/invidious/routes/api/v1/channels.cr @@ -89,6 +89,8 @@ module Invidious::Routes::API::V1::Channels json.field "descriptionHtml", channel.description_html json.field "allowedRegions", channel.allowed_regions + json.field "tabs", channel.tabs + json.field "authorVerified", channel.verified json.field "latestVideos" do json.array do diff --git a/src/invidious/routes/api/v1/misc.cr b/src/invidious/routes/api/v1/misc.cr index 43d360e6..e499f4d6 100644 --- a/src/invidious/routes/api/v1/misc.cr +++ b/src/invidious/routes/api/v1/misc.cr @@ -150,4 +150,31 @@ module Invidious::Routes::API::V1::Misc response end + + # resolve channel and clip urls, return the UCID + def self.resolve_url(env) + env.response.content_type = "application/json" + url = env.params.query["url"]? + + return error_json(400, "Missing URL to resolve") if !url + + begin + resolved_url = YoutubeAPI.resolve_url(url.as(String)) + endpoint = resolved_url["endpoint"] + pageType = endpoint.dig?("commandMetadata", "webCommandMetadata", "webPageType").try &.as_s || "" + if resolved_ucid = endpoint.dig?("watchEndpoint", "videoId") + elsif resolved_ucid = endpoint.dig?("browseEndpoint", "browseId") + elsif pageType == "WEB_PAGE_TYPE_UNKNOWN" + return error_json(400, "Unknown url") + end + rescue ex + return error_json(500, ex) + end + JSON.build do |json| + json.object do + json.field "ucid", resolved_ucid.try &.as_s || "" + json.field "pageType", pageType + end + end + end end diff --git a/src/invidious/routes/login.cr b/src/invidious/routes/login.cr index 99fc13a2..6454131a 100644 --- a/src/invidious/routes/login.cr +++ b/src/invidious/routes/login.cr @@ -6,14 +6,14 @@ module Invidious::Routes::Login user = env.get? "user" - return env.redirect "/feed/subscriptions" if user + referer = get_referer(env, "/feed/subscriptions") + + return env.redirect referer if user if !CONFIG.login_enabled return error_template(400, "Login has been disabled by administrator.") end - referer = get_referer(env, "/feed/subscriptions") - email = nil password = nil captcha = nil diff --git a/src/invidious/routes/subscriptions.cr b/src/invidious/routes/subscriptions.cr index 7b1fa876..0704c05e 100644 --- a/src/invidious/routes/subscriptions.cr +++ b/src/invidious/routes/subscriptions.cr @@ -104,33 +104,8 @@ module Invidious::Routes::Subscriptions if format == "json" env.response.content_type = "application/json" env.response.headers["content-disposition"] = "attachment" - playlists = Invidious::Database::Playlists.select_like_iv(user.email) - - return JSON.build do |json| - json.object do - json.field "subscriptions", user.subscriptions - json.field "watch_history", user.watched - json.field "preferences", user.preferences - json.field "playlists" do - json.array do - playlists.each do |playlist| - json.object do - json.field "title", playlist.title - json.field "description", html_to_content(playlist.description_html) - json.field "privacy", playlist.privacy.to_s - json.field "videos" do - json.array do - Invidious::Database::PlaylistVideos.select_ids(playlist.id, playlist.index, limit: 500).each do |video_id| - json.string video_id - end - end - end - end - end - end - end - end - end + + return Invidious::User::Export.to_invidious(user) else env.response.content_type = "application/xml" env.response.headers["content-disposition"] = "attachment" diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index 157e6de7..dca2f117 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -254,6 +254,9 @@ module Invidious::Routing get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences + get "/api/v1/auth/export/invidious", {{namespace}}::Authenticated, :export_invidious + post "/api/v1/auth/import/invidious", {{namespace}}::Authenticated, :import_invidious + get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions @@ -281,6 +284,7 @@ module Invidious::Routing get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes + get "/api/v1/resolveurl", {{namespace}}::Misc, :resolve_url {% end %} end end diff --git a/src/invidious/trending.cr b/src/invidious/trending.cr index 1f957081..134eb437 100644 --- a/src/invidious/trending.cr +++ b/src/invidious/trending.cr @@ -4,11 +4,12 @@ def fetch_trending(trending_type, region, locale) plid = nil - if trending_type == "Music" + case trending_type.try &.downcase + when "music" params = "4gINGgt5dG1hX2NoYXJ0cw%3D%3D" - elsif trending_type == "Gaming" + when "gaming" params = "4gIcGhpnYW1pbmdfY29ycHVzX21vc3RfcG9wdWxhcg%3D%3D" - elsif trending_type == "Movies" + when "movies" params = "4gIKGgh0cmFpbGVycw%3D%3D" else # Default params = "" diff --git a/src/invidious/user/exports.cr b/src/invidious/user/exports.cr new file mode 100644 index 00000000..b52503c9 --- /dev/null +++ b/src/invidious/user/exports.cr @@ -0,0 +1,35 @@ +struct Invidious::User + module Export + extend self + + def to_invidious(user : User) + playlists = Invidious::Database::Playlists.select_like_iv(user.email) + + return JSON.build do |json| + json.object do + json.field "subscriptions", user.subscriptions + json.field "watch_history", user.watched + json.field "preferences", user.preferences + json.field "playlists" do + json.array do + playlists.each do |playlist| + json.object do + json.field "title", playlist.title + json.field "description", html_to_content(playlist.description_html) + json.field "privacy", playlist.privacy.to_s + json.field "videos" do + json.array do + Invidious::Database::PlaylistVideos.select_ids(playlist.id, playlist.index, limit: CONFIG.playlist_length_limit).each do |video_id| + json.string video_id + end + end + end + end + end + end + end + end + end + end + end # module +end diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index d626c7d1..436ac82d 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -247,6 +247,12 @@ struct Video info["reason"]?.try &.as_s end + def music : Array(VideoMusic) + info["music"].as_a.map { |music_json| + VideoMusic.new(music_json["album"].as_s, music_json["artist"].as_s, music_json["license"].as_s) + } + end + # Macros defining getters/setters for various types of data private macro getset_string(name) diff --git a/src/invidious/videos/music.cr b/src/invidious/videos/music.cr new file mode 100644 index 00000000..402ae46f --- /dev/null +++ b/src/invidious/videos/music.cr @@ -0,0 +1,12 @@ +require "json" + +struct VideoMusic + include JSON::Serializable + + property album : String + property artist : String + property license : String + + def initialize(@album : String, @artist : String, @license : String) + end +end diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 5c323975..cf43f1be 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -311,6 +311,33 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any end end + # Music section + + music_list = [] of VideoMusic + music_desclist = player_response.dig?( + "engagementPanels", 1, "engagementPanelSectionListRenderer", + "content", "structuredDescriptionContentRenderer", "items", 2, + "videoDescriptionMusicSectionRenderer", "carouselLockups" + ) + + music_desclist.try &.as_a.each do |music_desc| + artist = nil + album = nil + music_license = nil + + music_desc.dig?("carouselLockupRenderer", "infoRows").try &.as_a.each do |desc| + desc_title = extract_text(desc.dig?("infoRowRenderer", "title")) + if desc_title == "ARTIST" + artist = extract_text(desc.dig?("infoRowRenderer", "defaultMetadata")) + elsif desc_title == "ALBUM" + album = extract_text(desc.dig?("infoRowRenderer", "defaultMetadata")) + elsif desc_title == "LICENSES" + music_license = extract_text(desc.dig?("infoRowRenderer", "expandedMetadata")) + end + end + music_list << VideoMusic.new(album.to_s, artist.to_s, music_license.to_s) + end + # Author infos author = video_details["author"]?.try &.as_s @@ -361,6 +388,8 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any "genre" => JSON::Any.new(genre.try &.as_s || ""), "genreUcid" => JSON::Any.new(genre_ucid.try &.as_s || ""), "license" => JSON::Any.new(license.try &.as_s || ""), + # Music section + "music" => JSON.parse(music_list.to_json), # Author infos "author" => JSON::Any.new(author || ""), "ucid" => JSON::Any.new(ucid || ""), diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index a6f2e524..666eb3b0 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -235,6 +235,28 @@ we're going to need to do it here in order to allow for translations. <hr> + <% if !video.music.empty? %> + <input id="music-desc-expansion" type="checkbox"/> + <label for="music-desc-expansion"> + <h3 id="music-description-title"> + <%= translate(locale, "Music in this video") %> + <span class="icon ion-ios-arrow-up"></span> + <span class="icon ion-ios-arrow-down"></span> + </h3> + </label> + + <div id="music-description-box"> + <% video.music.each do |music| %> + <div class="music-item"> + <p id="music-artist"><%= translate(locale, "Artist: ") %><%= music.artist %></p> + <p id="music-album"><%= translate(locale, "Album: ") %><%= music.album %></p> + <p id="music-license"><%= translate(locale, "License: ") %><%= music.license %></p> + </div> + <% end %> + </div> + <hr> + + <% end %> <div id="comments"> <% if nojs %> <%= comment_html %> |
