diff options
| author | Astounds <kirito@disroot.org> | 2026-03-22 20:50:03 -0500 |
|---|---|---|
| committer | Astounds <kirito@disroot.org> | 2026-03-22 20:50:03 -0500 |
| commit | 6a68f0664568cea6f9a12e8743f195fe0a41f3ce (patch) | |
| tree | 4ad12a70811a4821c0cc9dc94c19c1ccf2bca808 /youtube/templates | |
| parent | 84e1acaab8f7e4e7e36d19e3b6847a0ab6c33759 (diff) | |
| download | yt-local-0.4.0.tar.lz yt-local-0.4.0.tar.xz yt-local-0.4.0.zip | |
Release v0.4.0 - HD Thumbnails, YouTube 2024+ Support, and yt-dlp Integrationv0.4.0
Major Features:
- HD video thumbnails (hq720.jpg) with automatic fallback to lower qualities
- HD channel avatars (240x240 instead of 88x88)
- YouTube 2024+ lockupViewModel support for channel playlists
- youtubei/v1/browse API integration for channel playlist tabs
- yt-dlp integration for multi-language audio and subtitles
Bug Fixes:
- Fixed undefined `abort` import in playlist.py
- Fixed undefined functions in proto.py (encode_varint, bytes_to_hex, succinct_encode)
- Fixed missing `traceback` import in proto_debug.py
- Fixed blurry playlist thumbnails using default.jpg instead of HD versions
- Fixed channel playlists page using deprecated pbj=1 format
Improvements:
- Automatic thumbnail fallback system (hq720 → sddefault → hqdefault → mqdefault → default)
- JavaScript thumbnail_fallback() handler for 404 errors
- Better thumbnail quality across all pages (watch, channel, playlist, subscriptions)
- Consistent HD avatar display for all channel items
- Settings system automatically adds new settings without breaking user config
Files Modified:
- youtube/watch.py - HD thumbnails for related videos and playlist items
- youtube/channel.py - HD thumbnails for channel playlists, youtubei API integration
- youtube/playlist.py - HD thumbnails, fixed abort import
- youtube/util.py - HD thumbnail URLs, avatar HD upgrade, prefix_url improvements
- youtube/comments.py - HD video thumbnail
- youtube/subscriptions.py - HD thumbnails, fixed abort import
- youtube/yt_data_extract/common.py - lockupViewModel support, extract_lockup_view_model_info()
- youtube/yt_data_extract/everything_else.py - HD playlist thumbnails
- youtube/proto.py - Fixed undefined function references
- youtube/proto_debug.py - Added traceback import
- youtube/static/js/common.js - thumbnail_fallback() handler
- youtube/templates/*.html - Added onerror handlers for thumbnail fallback
- youtube/version.py - Bump to v0.4.0
Technical Details:
- All thumbnail URLs now use hq720.jpg (1280x720) when available
- Fallback handled client-side via JavaScript onerror handler
- Server-side avatar upgrade via regex in util.prefix_url()
- lockupViewModel parser extracts contentType, metadata, and first_video_id
- Channel playlist tabs now use youtubei/v1/browse instead of deprecated pbj=1
- Settings version system ensures backward compatibility
Diffstat (limited to 'youtube/templates')
| -rw-r--r-- | youtube/templates/base.html | 6 | ||||
| -rw-r--r-- | youtube/templates/channel.html | 4 | ||||
| -rw-r--r-- | youtube/templates/common_elements.html | 6 | ||||
| -rw-r--r-- | youtube/templates/watch.html | 42 |
4 files changed, 53 insertions, 5 deletions
diff --git a/youtube/templates/base.html b/youtube/templates/base.html index 95207fa..dd7c628 100644 --- a/youtube/templates/base.html +++ b/youtube/templates/base.html @@ -26,6 +26,12 @@ // @license-end </script> {% endif %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + // Image prefix for thumbnails + let settings_img_prefix = "{{ settings.img_prefix or '' }}"; + // @license-end + </script> </head> <body> diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html index c43f488..2c0a1a2 100644 --- a/youtube/templates/channel.html +++ b/youtube/templates/channel.html @@ -81,10 +81,10 @@ <!-- new--> <div id="links-metadata"> {% if current_tab in ('videos', 'shorts', 'streams') %} - {% set sorts = [('1', 'views'), ('2', 'oldest'), ('3', 'newest'), ('4', 'newest - no shorts'),] %} + {% set sorts = [('3', 'newest'), ('4', 'newest - no shorts')] %} <div id="number-of-results">{{ number_of_videos }} videos</div> {% elif current_tab == 'playlists' %} - {% set sorts = [('2', 'oldest'), ('3', 'newest'), ('4', 'last video added')] %} + {% set sorts = [('3', 'newest'), ('4', 'last video added')] %} {% if items %} <h2 class="page-number">Page {{ page_number }}</h2> {% else %} diff --git a/youtube/templates/common_elements.html b/youtube/templates/common_elements.html index bacc513..bd43761 100644 --- a/youtube/templates/common_elements.html +++ b/youtube/templates/common_elements.html @@ -23,11 +23,11 @@ <a class="thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> <div class="thumbnail {% if info['type'] == 'channel' %} channel {% endif %}"> {% if lazy_load %} - <img class="thumbnail-img lazy" alt=" " data-src="{{ info['thumbnail'] }}"> + <img class="thumbnail-img lazy" alt=" " data-src="{{ info['thumbnail'] }}" onerror="thumbnail_fallback(this)"> {% elif info['type'] == 'channel' %} - <img class="thumbnail-img channel" alt=" " src="{{ info['thumbnail'] }}"> + <img class="thumbnail-img channel" alt=" " src="{{ info['thumbnail'] }}" onerror="thumbnail_fallback(this)"> {% else %} - <img class="thumbnail-img" alt=" " src="{{ info['thumbnail'] }}"> + <img class="thumbnail-img" alt=" " src="{{ info['thumbnail'] }}" onerror="thumbnail_fallback(this)"> {% endif %} {% if info['type'] != 'channel' %} diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html index 0991457..7432bde 100644 --- a/youtube/templates/watch.html +++ b/youtube/templates/watch.html @@ -85,6 +85,16 @@ <option value='{"type": "pair", "index": {{ loop.index0}}}' {{ 'selected' if loop.index0 == pair_idx and using_pair_sources else '' }} >{{ src_pair['quality_string'] }}</option> {% endfor %} </select> + + {% if audio_tracks and audio_tracks|length > 1 %} + <select id="audio-language-select" autocomplete="off" title="Audio language"> + {% for track in audio_tracks %} + <option value="{{ track.get('track_id', track['language']) }}" {{ 'selected' if loop.index0 == 0 else '' }}> + 🔊 {{ track['language_name'] }}{% if track.get('is_default') %} (Default){% endif %} + </option> + {% endfor %} + </select> + {% endif %} {% endif %} </div> <input class="v-checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> @@ -246,6 +256,38 @@ let storyboard_url = {{ storyboard_url | tojson }}; // @license-end </script> + + <!-- Audio language selector handler --> + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + (function() { + 'use strict'; + const audioSelect = document.getElementById('audio-language-select'); + const qualitySelect = document.getElementById('quality-select'); + + if (audioSelect && qualitySelect) { + audioSelect.addEventListener('change', function() { + const selectedAudio = this.value; + const selectedQuality = qualitySelect.value; + + // Parse current quality selection + let qualityData; + try { + qualityData = JSON.parse(selectedQuality); + } catch(e) { + return; + } + + // Reload video with new audio language + const currentUrl = new URL(window.location.href); + currentUrl.searchParams.set('audio_lang', selectedAudio); + window.location.href = currentUrl.toString(); + }); + } + }()); + // @license-end + </script> + <script src="/youtube.com/static/js/common.js"></script> <script src="/youtube.com/static/js/transcript-table.js"></script> {% if settings.use_video_player == 2 %} |
