feature/hls: Add HLS playback support, and refactors documentation for better usability and maintainability. (#1)
## Overview This PR introduces HLS playback support, improves the player experience, and refactors documentation for better usability and maintainability. ## Key Features ### HLS Playback Support - Add HLS integration via new JavaScript assets: - `hls.min.js` - `plyr.hls.start.js` - `watch.hls.js` - Separate DASH and HLS logic: - `plyr-start.js` → `plyr.dash.start.js` - `watch.js` → `watch.dash.js` - Update templates (`embed.html`, `watch.html`) for conditional player loading ### Native Storyboard Preview - Add `native_player_storyboard` setting in `settings.py` - Implement hover thumbnail preview for native player modes - Add `storyboard-preview.js` ### UI and Player Adjustments - Update templates and styles (`custom_plyr.css`) - Modify backend modules to support new player modes: - `watch.py`, `channel.py`, `util.py`, and related components ### Internationalization - Update translation files: - `messages.po` - `messages.pot` ### Testing and CI - Add and update tests: - `test_shorts.py` - `test_util.py` - Minor CI and release script improvements ## Documentation ### OpenRC Service Guide Rewrite - Restructure `docs/basic-script-openrc/README.md` into: - Prerequisites - Installation - Service Management - Verification - Troubleshooting - Add admonition blocks: - `[!NOTE]`, `[!TIP]`, `[!IMPORTANT]`, `[!WARNING]`, `[!CAUTION]` - Fix log inspection command: ```bash doas tail -f /var/log/ytlocal.log ```` * Add path placeholders and clarify permission requirements * Remove legacy and duplicate content Reviewed-on: #1 Co-authored-by: Astounds <kirito@disroot.org> Co-committed-by: Astounds <kirito@disroot.org>
This commit was merged in pull request #1.
This commit is contained in:
@@ -11,8 +11,7 @@ import pytest
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
import youtube.proto as proto
|
||||
from youtube.yt_data_extract.common import (
|
||||
extract_item_info, extract_items, extract_shorts_lockup_view_model_info,
|
||||
extract_approx_int,
|
||||
extract_item_info, extract_items,
|
||||
)
|
||||
|
||||
|
||||
@@ -58,6 +57,59 @@ class TestChannelCtokenV5:
|
||||
assert t_shorts != t_streams
|
||||
assert t_videos != t_streams
|
||||
|
||||
def test_include_shorts_false_adds_filter(self):
|
||||
"""Test that include_shorts=False adds the shorts filter (field 104)."""
|
||||
# Token with shorts included (default)
|
||||
t_with_shorts = self.channel_ctoken_v5('UCtest', '1', '3', 'videos', include_shorts=True)
|
||||
# Token with shorts excluded
|
||||
t_without_shorts = self.channel_ctoken_v5('UCtest', '1', '3', 'videos', include_shorts=False)
|
||||
|
||||
# The tokens should be different because of the shorts filter
|
||||
assert t_with_shorts != t_without_shorts
|
||||
|
||||
# Decode and verify the filter is present
|
||||
raw_with_shorts = base64.urlsafe_b64decode(t_with_shorts + '==')
|
||||
raw_without_shorts = base64.urlsafe_b64decode(t_without_shorts + '==')
|
||||
|
||||
# Parse the outer protobuf structure
|
||||
import youtube.proto as proto
|
||||
outer_fields_with = list(proto.read_protobuf(raw_with_shorts))
|
||||
outer_fields_without = list(proto.read_protobuf(raw_without_shorts))
|
||||
|
||||
# Field 80226972 contains the inner data
|
||||
inner_with = [v for _, fn, v in outer_fields_with if fn == 80226972][0]
|
||||
inner_without = [v for _, fn, v in outer_fields_without if fn == 80226972][0]
|
||||
|
||||
# Parse the inner data - field 3 contains percent-encoded base64 data
|
||||
inner_fields_with = list(proto.read_protobuf(inner_with))
|
||||
inner_fields_without = list(proto.read_protobuf(inner_without))
|
||||
|
||||
# Get field 3 data (the encoded inner which is percent-encoded base64)
|
||||
encoded_inner_with = [v for _, fn, v in inner_fields_with if fn == 3][0]
|
||||
encoded_inner_without = [v for _, fn, v in inner_fields_without if fn == 3][0]
|
||||
|
||||
# The inner without shorts should contain field 104
|
||||
# Decode the percent-encoded base64 data
|
||||
import urllib.parse
|
||||
decoded_with = urllib.parse.unquote(encoded_inner_with.decode('ascii'))
|
||||
decoded_without = urllib.parse.unquote(encoded_inner_without.decode('ascii'))
|
||||
|
||||
# Decode the base64 data
|
||||
decoded_with_bytes = base64.urlsafe_b64decode(decoded_with + '==')
|
||||
decoded_without_bytes = base64.urlsafe_b64decode(decoded_without + '==')
|
||||
|
||||
# Parse the decoded protobuf data
|
||||
fields_with = list(proto.read_protobuf(decoded_with_bytes))
|
||||
fields_without = list(proto.read_protobuf(decoded_without_bytes))
|
||||
|
||||
field_numbers_with = [fn for _, fn, _ in fields_with]
|
||||
field_numbers_without = [fn for _, fn, _ in fields_without]
|
||||
|
||||
# The 'with' version should NOT have field 104
|
||||
assert 104 not in field_numbers_with
|
||||
# The 'without' version SHOULD have field 104
|
||||
assert 104 in field_numbers_without
|
||||
|
||||
|
||||
# --- shortsLockupViewModel parsing ---
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ class NewIdentityState():
|
||||
self.new_identities_till_success -= 1
|
||||
|
||||
def fetch_url_response(self, *args, **kwargs):
|
||||
cleanup_func = (lambda r: None)
|
||||
def cleanup_func(response):
|
||||
return None
|
||||
if self.new_identities_till_success == 0:
|
||||
return MockResponse(), cleanup_func
|
||||
return MockResponse(body=html429, status=429), cleanup_func
|
||||
|
||||
Reference in New Issue
Block a user