Commit Graph

874 Commits

Author SHA1 Message Date
caa37e55fa refactor: improve download dropdown UX and styling
All checks were successful
CI / test (pull_request) Successful in 46s
CI / test (push) Successful in 1m35s
- Use lightweight fade‑in hover
2026-04-20 04:30:35 -05:00
7a3c92820c refactor: convert download dropdown to valid W3C table
All checks were successful
CI / test (push) Successful in 46s
CI / test (pull_request) Successful in 45s
- Replace ul/li structure with semantic table (thead/tbody)
- Add 6 columns: Ext, Video, Audio, Size, Codecs, Link
- Show "N/A" for missing codec data
- Add 0.5rem spacing between dropdown and description
2026-04-20 04:13:56 -05:00
13bdab41db chore(release): update to v0.5.0
All checks were successful
CI / test (push) Successful in 47s
CI / test (pull_request) Successful in 47s
Bump version in `version.py` to v0.5.0
2026-04-20 03:26:55 -05:00
3fe256ce29 feat: add native player storyboard preview option
All checks were successful
CI / test (push) Successful in 45s
- Introduced `native_player_storyboard` setting with default disabled.
- Updated templates to conditionally load the storyboard preview script only for native players.
- Added descriptive comments for the new setting and template blocks.
2026-04-20 02:43:41 -05:00
d6190a2d0b security: harden code against command injection and path traversal
Core changes:

* enforce HTTPS URLs and remove shell usage in generate_release.py
* replace os.system calls with subprocess across the codebase
* validate external inputs (playlist names, video IDs)

Improvements and fixes:

* settings.py: fix typo (node.lineno → line_number); use isinstance() over type()
* youtube/get_app_version: improve git detection using subprocess.DEVNULL
* youtube/util.py: add cleanup helpers; use shutil.which for binary resolution

YouTube modules:

* watch.py: detect and flag HLS streams; remove unused audio_track_sources
* comments.py: return early when comments are disabled; add error handling
* local_playlist.py: validate playlist names to prevent path traversal
* subscriptions.py: replace asserts with proper error handling; validate video IDs

Cleanup:

* remove unused imports across modules (playlist, search, channel)
* reorganize package imports in youtube/**init**.py
* simplify test imports and fix cleanup_func in tests

Tests:

* tests/test_shorts.py: simplify imports
* tests/test_util.py: fix cleanup_func definition
2026-04-20 00:39:35 -05:00
5577e9e1f2 feat(channels): fix pagination for "Sorted by newest - no shorts"
Some checks failed
CI / test (push) Has been cancelled
Replace UU-uploads playlist workaround (proto field 104) with direct
requests to the channel Videos tab API (tab="videos"), aligning with
Invidious content-type handling. This restores proper continuation
tokens and stable pagination (~30 videos per page).

Update display logic:
- Show channel total upload count as an upper-bound while continuation
  tokens exist.
- On final page, display exact fetched video count.
- Ensure page number never falls below current page (fix page reset to "1").

Maintain separate handling:
- Shorts and streams tabs continue using tab-specific continuation tokens.

Add test:
- TestChannelCtokenV5::test_include_shorts_false_adds_filter

Fixes issue where channels with many Shorts (e.g., Celine Dept) showed
only a few videos and broken pagination under "no shorts" sorting.
2026-04-19 22:34:14 -05:00
3795d9e4ff fix(playlists): make playlist parsing robust against filename and formatting issues
All checks were successful
CI / test (push) Successful in 53s
- Use glob lookup to find playlist files even with trailing spaces in filenames
- Sanitize lines (strip whitespace) before JSON parsing to ignore trailing spaces/empty lines
- Handle JSONDecodeError gracefully to prevent 500 errors from corrupt entries
- Return empty list on FileNotFoundError in read_playlist instead of crashing
- Extract _find_playlist_path and _parse_playlist_lines helpers for reuse
2026-04-05 18:47:21 -05:00
13a0e6ceed fix(hls): improve audio track selection and auto-detect "Original"
- Auto-select "Original" audio track by default in both native and Plyr HLS players
- Fix native HLS audio selector to use numeric indices instead of string matching
- Robustly detect "original" track by checking both `name` and `lang` attributes
- Fix audio track change handler to correctly switch between available tracks
2026-04-05 18:31:35 -05:00
e8e2aa93d6 fix(channel): fix shorts/streams pagination using continuation tokens
- Add continuation_token_cache to store ctokens between page requests
- Use cached ctoken for page 2+ instead of generating fresh tokens
- Switch shorts/streams to Next/Previous buttons (no page numbers)
- Show "N+ videos" indicator when more pages are available
- Fix UnboundLocalError when page_call was undefined for shorts/streams

The issue was that YouTube's InnerTube API requires continuation tokens
for pagination on shorts/streams tabs, but the code was generating a new
ctoken each time, always returning the same 30 videos.
2026-04-05 18:19:05 -05:00
8403e30b3a Many fixes to i18n 2026-04-05 17:43:01 -05:00
f0649be5de Add HLS support to multi-audio 2026-04-05 14:56:51 -05:00
f7bbf3129a update ios client
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 14s
CI / test (push) Successful in 53s
2026-04-04 15:05:33 -05:00
688521f8d6 bump to v0.4.5
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 50s
2026-04-01 11:54:46 -05:00
a374f90f6e fix: add support for YouTube Shorts tab on channel pages
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 56s
- Rewrite channel_ctoken_v5 with correct protobuf field numbers per tab
  (videos=15, shorts=10, streams=14) based on Invidious source
- Replace broken pbj=1 endpoint with youtubei browse API for shorts/streams
- Add shortsLockupViewModel parser to extract video data from new YT format
- Fix channel metadata not loading (get_metadata now uses browse API)
- Fix metadata caching: skip caching when channel_name is absent
- Show actual item count instead of UU playlist count for shorts/streams
- Format view counts with spaced suffixes (7.1 K, 1.2 M, 3 B)
2026-04-01 11:43:46 -05:00
bed14713ad bump to v0.4.4
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 45s
2026-03-31 21:48:46 -05:00
06051dd127 fix: support YouTube 2024+ data formats for playlists, podcasts and channels
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 51s
- Add PODCAST content type support in lockupViewModel extraction
- Extract thumbnails and episode count from thumbnail overlay badges
- Migrate playlist page fetching from pbj=1 to innertube API (youtubei/v1/browse)
- Support new pageHeaderRenderer format in playlist metadata extraction
- Fix subscriber count extraction when YouTube returns handle instead of count
- Hide "None subscribers" in template when data is unavailable
2026-03-31 21:38:51 -05:00
1aa344c7b0 bump to v0.4.3
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 46s
2026-03-28 16:09:23 -05:00
fa7273b328 fix: race condition in os.makedirs causing worker crashes
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 47s
Replace check-then-create pattern with exist_ok=True to prevent
FileExistsError when multiple workers initialize simultaneously.

Affects:
- subscriptions.py: open_database()
- watch.py: save_decrypt_cache()
- local_playlist.py: add_to_playlist()
- util.py: fetch_url(), get_visitor_data()
- settings.py: initialization

Fixes Gunicorn worker startup failures in multi-worker deployments.
2026-03-28 16:06:47 -05:00
a46cfda029 bump to v0.4.2
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 12s
CI / test (push) Successful in 46s
2026-03-27 21:26:08 -05:00
e03f40d728 fix error handling, null URLs in templates, and Radio playlist support
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 49s
- Global error handler: friendly messages for 429, 502, 403, 400
  instead of raw tracebacks. Filter FetchError from Flask logger.
- Fix None URLs in templates: protect href/src in common_elements,
  playlist, watch, and comments templates against None values.
- Radio playlists (RD...): redirect /playlist?list=RD... to
  /watch?v=...&list=RD... since YouTube only supports them in player.
- Wrap player client fallbacks (ios, tv_embedded) in try/catch so
  a failed fallback doesn't crash the whole page.
2026-03-27 21:23:03 -05:00
22c72aa842 remove yt-dlp, fix captions PO Token issue, fix 429 retry logic
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 52s
- Remove yt-dlp entirely (modules, routes, settings, dependency)
  Was blocking page loads by running synchronously in gevent
- Fix captions: use Android client caption URLs (no PO Token needed)
  instead of web timedtext URLs that YouTube now blocks
- Fix 429 retry: fail immediately without Tor (same IP = pointless retry)
  Was causing ~27s delays with exponential backoff
- Accept ytdlp_enabled as legacy setting to avoid warning on startup
2026-03-27 20:47:44 -05:00
56ecd6cb1b fix: use YouTube-provided thumbnail URLs instead of hardcoded hq720.jpg
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 15s
CI / test (push) Successful in 58s
Videos without hq720.jpg thumbnails caused mass 404 errors.
Now preserves the actual thumbnail URL from YouTube's API response,
falls back to hqdefault.jpg only when no thumbnail is provided.
Also picks highest quality thumbnail from API (thumbnails[-1])
and adds progressive fallback for subscription/download functions.
2026-03-27 19:22:12 -05:00
f629565e77 bump to v0.4.1
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 13s
CI / test (push) Successful in 48s
2026-03-22 21:27:50 -05:00
1f8c13adff feat: improve 429 handling with Tor support and clean CI
All checks were successful
git-sync-with-mirror / git-sync (push) Successful in 11s
CI / test (push) Successful in 50s
- Retry with new Tor identity on 429
- Improve error logging
- Remove .build.yml and .drone.yml
2026-03-22 21:25:57 -05:00
6a68f06645 Release v0.4.0 - HD Thumbnails, YouTube 2024+ Support, and yt-dlp Integration
Some checks failed
CI / test (push) Failing after 1m19s
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
2026-03-22 20:50:03 -05:00
84e1acaab8 yt-dlp 2026-03-22 14:17:23 -05:00
Jesus
ed4b05d9b6 Bump version to v0.3.2 2025-03-08 16:41:58 -05:00
Jesus
6f88b1cec6 Refactor extract_info in watch.py to improve client flexibility
Introduce primary_client, fallback_client, and last_resort_client variables for better configurability.
Replace hardcoded 'android_vr' with primary_client in fetch_player_response call.
2025-03-08 16:40:51 -05:00
Jesus
03451fb8ae fix: prevent error when closing avMerge if not a function 2025-03-08 16:39:37 -05:00
Jesus
e45c3fd48b Add styles error in player 2025-03-08 16:38:31 -05:00
Jesus
1153ac8f24 Fix NoneType inside comments.py
Bug:

Traceback (most recent call last):
  File "/home/rusian/yt-local/youtube/comments.py", line 180, in video_comments
    post_process_comments_info(comments_info)
  File "/home/rusian/yt-local/youtube/comments.py", line 81, in post_process_comments_info
    comment['author'] = strip_non_ascii(comment['author'])
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/rusian/yt-local/youtube/util.py", line 843, in strip_non_ascii
    stripped = (c for c in string if 0 < ord(c) < 127)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not iterable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run
  File "/home/rusian/yt-local/youtube/comments.py", line 195, in video_comments
    comments_info['error'] = 'YouTube blocked the request. IP address: %s' % e.ip
                                                                             ^^^^
AttributeError: 'TypeError' object has no attribute 'ip'
2025-03-08T01:25:47Z <Greenlet at 0x7f251e5279c0: video_comments('hcm55lU9knw', 0, lc='')> failed with AttributeError
2025-03-08 16:37:33 -05:00
Jesus
c256a045f9 Bump version to v0.3.1 2025-03-08 16:34:29 -05:00
Jesus
98603439cb Improve buffer management for different platforms
- Introduced `BUFFER_CONFIG` to define buffer sizes for various systems (webOS, Samsung Tizen, Android TV, desktop).
- Added `detectSystem()` function to determine the platform based on `navigator.userAgent`.
- Updated `Stream` constructor to use platform-specific buffer sizes dynamically.
- Added console log for debugging detected system and applied buffer size.
2025-03-08 16:32:26 -05:00
Jesus
a6ca011202 version v0.3.0 2025-03-08 16:28:39 -05:00
Jesus
114c2572a4 Renew plyr UI and simplify elements 2025-03-08 16:28:27 -05:00
f64b362603 update logic plyr-start.js 2025-03-03 08:20:41 +08:00
2fd7910194 version 0.2.21 2025-03-02 06:24:03 +08:00
c2986f3b14 Refactoring get_app_version 2025-03-01 04:06:11 +08:00
57854169f4 minor fix deprecation warning
tests/test_util.py: 14 warnings
  /home/runner/work/yt-local/youtube-local/youtube/util.py:321: DeprecationWarning: HTTPResponse.getheader() is deprecated and will be removed in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).
    response.getheader('Content-Encoding', default='identity'))
2025-03-01 01:12:09 +08:00
3217305f9f version 0.2.20 2025-02-28 11:04:06 +08:00
639aadd2c1 Remove gather_googlevideo_domains setting
This was an old experiment to collect googlevideo domains to see
if there was a pattern that could correlate to IP address to
look for workarounds for 403 errors

Can bug out if enabled and if failed to get any vidoe urls,
so remove since it is obsolete and some people are enabling it

See #218
2025-02-28 10:58:29 +08:00
7157df13cd Remove params to fetch_player_response 2025-02-28 10:58:15 +08:00
630e0137e0 Increase playlist count to 1000 by default if cannot get video count
This way, buttons will still appear even if there is a failure
to read playlist metadata

Fixes #220# Please enter the commit message for your changes. Lines starting
2025-02-28 10:51:51 +08:00
a0c51731af channel.py: Catch FetchError
Should catch this error to fail gracefully

See #227
2025-02-28 10:51:29 +08:00
d361996fc0 util: use visitorData for api request
watch: use android_vr client to get player data
2025-02-28 10:43:14 +08:00
Jesus
4ef7dda14a version 0.2.19 2024-10-11 11:25:12 +08:00
Jesus
ee31cedae0 Revert "Refactoring code and reuse INNERTUBE_CLIENTS"
This reverts commit 8af98968dd.
2024-10-11 11:22:36 +08:00
09a437f7fb v0.2.18 2024-07-09 13:10:10 +08:00
Jesus
62418f8e95 Switch to android test suite client by default
Invidious' solution to the destruction of the android client:
https://github.com/iv-org/invidious/pull/4650

Fixes #207
2024-06-11 10:46:25 +08:00
bfd3760969 Release v0.2.17 2024-04-29 01:00:13 +08:00