iOS doesn't have hover, so the tooltip only ever showed on desktop —
where the share-arrow glyph is universally recognised anyway. Aria-
label stays for screen readers.
Web Share fails silently when iOS' share sheet refuses the payload,
typically because the file exceeds the platform's soft size limit
(~50–100 MB depending on iOS version). The previous patch logged to
the console but the user saw nothing — staring at a button that
'does nothing' is poor UX.
Adds two layers of feedback:
1. Pre-flight size check (SHARE_SIZE_WARN_BYTES = 80 MB, conservative
relative to iOS' actual limit) with a confirm() dialog before the
fetch. Avoids spending bandwidth pulling a 150 MB blob into the
browser only for navigator.canShare to reject it.
2. Surfaces canShare-rejection AND share()-failure as a visible
alert() suggesting the user fall back to the download link next
to the share button.
Tested locally with files from 0.7 MB up to 150.7 MB: small files
share unchanged, the 150 MB file now produces a pre-flight warning
the user can dismiss, and any subsequent rejection produces a clear
alert instead of silently no-op'ing.
Adds a share button to the completed-list action row that hands the
downloaded file off to the platform share sheet via navigator.share().
On iOS Safari/Chrome this surfaces the native Save-to-Photos / Save-to-
Files / AirDrop options for videos and images, and Files / 3rd-party
app targets for audio. On platforms without Web Share support (Desktop
Firefox/Chrome/Safari) the button hides itself; the existing download
link remains the universal fallback.
Implementation notes:
- canShareDownloads() requires both navigator.share AND navigator.canShare
(Desktop Safari has the former without the latter; we always intend
to share a file, not a URL)
- shareDownload() fetches the file via the existing buildDownloadLink()
helper, wraps it in a File, then runs canShare() before share() so we
can bail out cleanly on platforms that reject the MIME type
- AbortError (user dismisses sheet) is silenced; other errors logged
- Tooltip on the button explains the iOS behaviour briefly
Refs alexta69/metube#582 — addresses the 'add to Photos.app' request
without depending on the iOS Shortcut, which has had reliability issues
(cf #763).
GITHUB_TOKEN cannot push commits that contain changes to
.github/workflows/ files (GitHub security restriction).
Use a Classic PAT with 'repo' + 'workflow' scopes instead.
Also add explicit checkout with fetch-depth:0 and ref:master
so the Fork-Sync action has full history for merging.
Revert to actions-cool/issues-helper and aormsby/Fork-Sync-With-Upstream-action
(matching lobehub/lobehub upstream sync pattern) with pinned SHA for v3.7.6.
Uses GITHUB_TOKEN which works when repo Settings > Actions > Workflow permissions
is set to 'Read and write permissions'.
GITHUB_TOKEN cannot push to fork repos in scheduled workflows.
Remove the GITHUB_TOKEN fallback so the workflow fails explicitly
when UPSTREAM_TOKEN is missing, instead of silently 403-ing.
Also add issues:write permission and git user config for merges.
Replace actions-cool/issues-helper and aormsby/Fork-Sync-With-Upstream-action
with native git commands and actions/github-script. This fixes the
'Repository access blocked' error caused by GitHub restricting access
to third-party actions in fork repositories.
Replace actions-cool/issues-helper and aormsby/Fork-Sync-With-Upstream-action
with native git commands and actions/github-script. This fixes the
'Repository access blocked' error caused by GitHub restricting access
to third-party actions in fork repositories.
Bug fixes from full codebase review:
- clear() with DELETE_FILE_ON_TRASHCAN now also deletes chapter_files
and subtitle_files, not just the primary filename
- _PERSISTED_DOWNLOAD_FIELDS now includes 'subtitle_files' so subtitle
file info survives app restart
Tests:
- Added test_subtitle_lang_list_with_region_variant_input (zh-Hans)
- Added test_subtitle_lang_list_dedup_region_variant (pt-BR)
P1 fixes:
- _subtitle_lang_list now extracts base language from qualified tags
(e.g. zh-Hans → zh base) so regional variants are still added as
fallbacks. Original input is preserved as first priority.
- DownloadInfo.__init__ now explicitly initializes filename=None and
chapter_files=[] to avoid AttributeError on dynamic attribute access.
- __setstate__ adds filename=None migration for old persistent data.
- Frontend Download interface gains subtitle_files field; completed
downloads show each subtitle file with download link and CC icon.
P2 improvements:
- _LANGUAGE_REGION_VARIANTS expanded from 10 to 30 languages covering
it/ja/ko/hi/th/vi/id/pl/uk/ru/cs/sv/da/no/fi/tr/el/he/hu/bn/ta.
- Languages with no regional variants use empty tuple (they still get
-orig suffix when include_orig=True).
Update test expectations to match the new _subtitle_lang_list() output
which includes regional variants (e.g. fr-FR, en-GB) for better
subtitle matching.
DownloadInfo doesn't have a 'filename' attribute by default — it's only
set dynamically in update_status when a 'downloaded' status is received.
When captions are silently skipped by yt-dlp (no matching language), the
'downloaded' status is never emitted, so 'filename' doesn't exist on the
object at all. Using getattr(download.info, 'filename', None) instead of
direct attribute access prevents AttributeError.
Two fixes for captions downloads:
1. Language variant fallback: When user requests 'en' but the video only
has 'en-GB' (or other regional variants), yt-dlp silently skips
subtitle extraction because it uses exact language matching. Now the
subtitleslangs list includes common regional variants (e.g. en-US,
en-GB, en-AU, etc.) so yt-dlp can match the first available variant.
2. Error on no files: When yt-dlp completes successfully (ret=0) but
produces no subtitle files (e.g. no matching language at all), the
download was incorrectly shown as 'completed'. Now it's marked as
'error' with a descriptive message like 'No subtitles found for
language "en"'. Same treatment for thumbnail downloads.
- Reset download_phase when transitioning to paused (pause(), update_status(),
_post_download_cleanup()) so UI shows 'Paused' instead of stale phase label
- Reset download_phase when starting/resuming download (start() preparing state)
- Count 'postprocessing' as active download in updateMetrics() so dashboard
shows accurate counts during ffmpeg post-processing
- Add missing completedDownload Subject to DownloadsServiceStub in app.spec.ts
- Add Download type import to app.spec.ts
Fixes: TypeError Cannot read properties of undefined (reading 'pipe')
at app.ts:300 (completedDownload not in test stub)
- _download_phase_from_status now falls back to checking requested_formats
when top-level vcodec/acodec are both 'none' (common when yt-dlp uses
separate downloaders for video+audio streams)
- Clear download_phase on download completion/error (was persisting stale
phase value in finished state)
- Add debug logging in put_status for phase detection
- Update docker-compose.local.yml to match docker-compose.yml structure
with local image build (adds TARGETARCH build arg)
- Removed shadow from URL entry wrap in app.html for a cleaner look.
- Updated various icon files to improve visual consistency across the application.
- Add suffix to identify different files of same video
- Fixed invisible download for short link
Co-authored-by: Copilot <copilot@github.com>