feature/hls: Add HLS playback support, and refactors documentation for better usability and maintainability. (#1)
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 32s
CI / test (push) Successful in 46s

## 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:
2026-04-20 01:22:55 -04:00
committed by heckyel
parent 62a028968e
commit a0f315be51
46 changed files with 4109 additions and 687 deletions

View File

@@ -1,4 +1,18 @@
from youtube import util
from youtube.i18n_strings import (
AUTO,
AUTO_HLS_PREFERRED,
ENGLISH,
ESPANOL,
FORCE_DASH,
FORCE_HLS,
NEWEST,
PLAYBACK_MODE,
RANKING_1,
RANKING_2,
RANKING_3,
TOP,
)
import ast
import re
import os
@@ -139,8 +153,8 @@ For security reasons, enabling this is not recommended.''',
'comment': '''0 to sort by top
1 to sort by newest''',
'options': [
(0, 'Top'),
(1, 'Newest'),
(0, TOP),
(1, NEWEST),
],
}),
@@ -159,18 +173,32 @@ For security reasons, enabling this is not recommended.''',
}),
('default_resolution', {
'type': int,
'default': 720,
'type': str,
'default': 'auto',
'comment': '',
'options': [
(144, '144p'),
(240, '240p'),
(360, '360p'),
(480, '480p'),
(720, '720p'),
(1080, '1080p'),
(1440, '1440p'),
(2160, '2160p'),
('auto', AUTO),
('144', '144p'),
('240', '240p'),
('360', '360p'),
('480', '480p'),
('720', '720p'),
('1080', '1080p'),
('1440', '1440p'),
('2160', '2160p'),
],
'category': 'playback',
}),
('playback_mode', {
'type': str,
'default': 'auto',
'label': PLAYBACK_MODE,
'comment': 'HLS uses hls.js (multi-audio). DASH uses av-merge (single audio).',
'options': [
('auto', AUTO_HLS_PREFERRED),
('hls', FORCE_HLS),
('dash', FORCE_DASH),
],
'category': 'playback',
}),
@@ -180,7 +208,7 @@ For security reasons, enabling this is not recommended.''',
'default': 1,
'label': 'AV1 Codec Ranking',
'comment': '',
'options': [(1, '#1'), (2, '#2'), (3, '#3')],
'options': [(1, RANKING_1), (2, RANKING_2), (3, RANKING_3)],
'category': 'playback',
}),
@@ -189,7 +217,7 @@ For security reasons, enabling this is not recommended.''',
'default': 2,
'label': 'VP8/VP9 Codec Ranking',
'comment': '',
'options': [(1, '#1'), (2, '#2'), (3, '#3')],
'options': [(1, RANKING_1), (2, RANKING_2), (3, RANKING_3)],
'category': 'playback',
}),
@@ -198,7 +226,7 @@ For security reasons, enabling this is not recommended.''',
'default': 3,
'label': 'H.264 Codec Ranking',
'comment': '',
'options': [(1, '#1'), (2, '#2'), (3, '#3')],
'options': [(1, RANKING_1), (2, RANKING_2), (3, RANKING_3)],
'category': 'playback',
'description': (
'Which video codecs to prefer. Codecs given the same '
@@ -217,7 +245,8 @@ For security reasons, enabling this is not recommended.''',
(2, 'Always'),
],
'category': 'playback',
'description': 'If set to Prefer or Always and the default resolution is set to 360p or 720p, uses the unified (integrated) video files which contain audio and video, with buffering managed by the browser. If set to prefer not, uses the separate audio and video files through custom buffer management in av-merge via MediaSource unless they are unavailable.',
'hidden': True,
'description': 'Deprecated: HLS is now used exclusively for all playback.',
}),
('use_video_player', {
@@ -232,10 +261,20 @@ For security reasons, enabling this is not recommended.''',
'category': 'interface',
}),
('native_player_storyboard', {
'type': bool,
'default': False,
'label': 'Storyboard preview (native)',
'comment': '''Show thumbnail preview on hover (native player modes).
Positioning is heuristic; may misalign in Firefox/Safari.
Works best on Chromium browsers.
No effect in Plyr.''',
'category': 'interface',
}),
('use_video_download', {
'type': int,
'default': 0,
'comment': '',
'options': [
(0, 'Disabled'),
(1, 'Enabled'),
@@ -301,8 +340,8 @@ Archive: https://archive.ph/OZQbN''',
'default': 'en',
'comment': 'Interface language',
'options': [
('en', 'English'),
('es', 'Español'),
('en', ENGLISH),
('es', ESPANOL),
],
'category': 'interface',
}),
@@ -442,7 +481,7 @@ upgrade_functions = {
def log_ignored_line(line_number, message):
print("WARNING: Ignoring settings.txt line " + str(node.lineno) + " (" + message + ")")
print('WARNING: Ignoring settings.txt line ' + str(line_number) + ' (' + message + ')')
if os.path.isfile("settings.txt"):
@@ -470,25 +509,29 @@ else:
else:
# parse settings in a safe way, without exec
current_settings_dict = {}
# Python 3.8+ uses ast.Constant; older versions use ast.Num, ast.Str, ast.NameConstant
attributes = {
ast.Constant: 'value',
ast.NameConstant: 'value',
ast.Num: 'n',
ast.Str: 's',
}
try:
attributes[ast.Num] = 'n'
attributes[ast.Str] = 's'
attributes[ast.NameConstant] = 'value'
except AttributeError:
pass # Removed in Python 3.12+
module_node = ast.parse(settings_text)
for node in module_node.body:
if type(node) != ast.Assign:
log_ignored_line(node.lineno, "only assignments are allowed")
if not isinstance(node, ast.Assign):
log_ignored_line(node.lineno, 'only assignments are allowed')
continue
if len(node.targets) > 1:
log_ignored_line(node.lineno, "only simple single-variable assignments allowed")
log_ignored_line(node.lineno, 'only simple single-variable assignments allowed')
continue
target = node.targets[0]
if type(target) != ast.Name:
log_ignored_line(node.lineno, "only simple single-variable assignments allowed")
if not isinstance(target, ast.Name):
log_ignored_line(node.lineno, 'only simple single-variable assignments allowed')
continue
if target.id not in acceptable_targets:
@@ -522,7 +565,7 @@ else:
globals().update(current_settings_dict)
if route_tor:
if globals().get('route_tor', False):
print("Tor routing is ON")
else:
print("Tor routing is OFF - your YouTube activity is NOT anonymous")
@@ -542,7 +585,7 @@ def add_setting_changed_hook(setting, func):
def set_img_prefix(old_value=None, value=None):
global img_prefix
if value is None:
value = proxy_images
value = globals().get('proxy_images', False)
if value:
img_prefix = '/'
else: