Commit Graph

176 Commits

Author SHA1 Message Date
tjelite1986 75fe1f0c11 feat: shift-click to select a range of rows (closes #525)
Clicking a row's checkbox with Shift held takes every row between it and
the last one toggled to the state the click produced, so a stretch of a
long queue can be cancelled without ticking each entry.

The range follows the order the rows are rendered in, which for the
Completed list is the sort the user picked rather than the order the map
holds; the master checkbox takes that order as an input. An anchor whose
row has since left the list (a download finishing moves it to Completed)
falls back to a plain toggle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 12:49:39 +02:00
Alex Shnitman aac9c63a36 feat: let a subscription carry clip bounds (closes #1049)
A subscription already carries every other download option and applies it to
each video it queues. Clip bounds were the one exception: 4f83174 added them
for one-off downloads and carved subscriptions out, rejecting the fields in the
subscribe route and stripping them in the UI before the request was built. So
the fields sat visible in the shared advanced-options panel while quietly doing
nothing for a subscription.

Carry them like the rest: two fields on SubscriptionInfo, threaded through the
check flow to add_entry. Stored records that predate the fields take the
defaults, since _from_stored filters by field name.

One thing does not carry over. parse_download_options reads a YouTube t=
timestamp from the URL and turns it into a clip start, which is right when you
paste a link to a moment in a video you want. A subscription URL is a channel
or a playlist, so a timestamp left on it says nothing about the videos that
feed will yield, and honouring it would silently truncate every future
download. The subscribe route therefore takes clip bounds only when the caller
sent the fields explicitly; the t= param is still stripped from the stored URL.

Worth knowing when using this: the range is a fixed offset applied to every
video, so it suits feeds with a consistent shape - a standing intro, a fixed
sponsor read - and will cut in the wrong place on a feed without one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 09:25:03 +02:00
Alex Shnitman d66b04ccf5 feat: let subscriptions be renamed from the list (#1044)
A subscription's name is captured once at subscribe time from the feed's own
title, so playlists — particularly the UULF-prefixed channel-uploads playlists
— all come back named "Videos" and stay that way. Adds an inline editor on the
Name cell, mirroring the existing title-filter edit next to it.

The update route already whitelisted `name` and update_subscription already
applied it, so this is mostly the missing UI. The backend side is validation:
the old `str(changes["name"])` accepted any type, any length and any
whitespace, for a value that is persisted and broadcast to every connected
client. validate_subscription_name now requires a string, collapses interior
whitespace to keep the label single-line, and caps it at 200 characters.

The name is display-only — it is used for the subscription list and log lines,
never for download paths — so renaming cannot move where files land.
2026-07-27 22:25:51 +03:00
Alex Shnitman ff1b73a576 refactor: make POST /retry take a singular id
The endpoint accepted {ids: [x]} and then rejected anything but exactly one
id, so the schema advertised a batch it never supported. Retry is genuinely
singular: unlike the /delete, /start and /cancel batches, which act on local
state and can't meaningfully fail for one id and not another, each retry
re-extracts the URL and the caller removes that item's done record only once
it is confirmed re-queued. A real batch form would need per-id results in the
response for the caller to know which records to remove; that only becomes
worth designing alongside moving done-record deletion server-side.

/retry has not shipped yet, so there is no compatibility cost.
2026-07-27 21:19:01 +03:00
jahruz67 1839e5484d feat: add retry functionality for failed downloads 2026-07-24 10:21:06 -07:00
Alex Shnitman 3ea4732c5d fix: harden download lifecycle, subscriptions, and UI robustness
Addresses a full-project review. Backend correctness and availability:

- ytdl: cancel() only SIGKILLs the child's process group when the child
  actually became its own group leader, so a race (or failed setpgrp)
  can no longer kill the whole server; kill the group on cancel and on
  shutdown to avoid orphaned ffmpeg children
- ytdl: dedicated ThreadPoolExecutor for download supervision so active
  downloads can't starve extract_info / live probes on the default pool
- ytdl/main/subscriptions: route fire-and-forget tasks through a
  bg_tasks helper that keeps a strong ref and logs failures
- subscriptions: run flat-playlist extraction in an executor and check
  feeds with bounded concurrency so one slow feed can't block the loop;
  set last_checked on failure so broken feeds aren't retried every 60s
- main: validate ids on /start & /delete and numeric env vars at startup;
  return 400 (not 500) on bad subscriptions/update input; serve /history
  from memory; move get_custom_dirs off the event loop; restrict t=
  stripping to YouTube hosts; drop double percent-decode in state guard
- dl_formats/ytdl: enforce requested caption format via
  FFmpegSubtitlesConvertor and strip VTT header metadata only in the
  pre-cue region so real dialogue is preserved
- ytdl: throttle progress events, dedup adds against pending, clear
  filename/size on error and reject out-of-dir trashcan deletes, pin
  fork start-method on Linux only

Frontend:

- retry deletes the done record only after a successful re-add
- surface HTTP errors for delete/start and reset the deleting flag
- ignore late 'updated' events for rows no longer in the queue
- track table rows by map key; FileSizePipe uses base-1024

Also: HTTPS-aware Docker healthcheck, dead-code removal, and shared
helpers for path-containment and yt-dlp option merging. Adds/updates
unit tests throughout (250 backend tests passing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 08:08:14 +03:00
Alex Shnitman 51fd203b71 upgrade to Angular 22 2026-06-28 21:12:20 +03:00
Alex Shnitman 37f7af0555 fix batch download (closes #1008) 2026-06-16 21:37:05 +03:00
Alex Shnitman 5aa7d033e2 review fixes 2026-06-16 21:35:07 +03:00
Alex Shnitman 5429200fba support live streams (closes #302, closes #752, closes #978) 2026-06-13 17:39:14 +03:00
Alex Shnitman ee20512410 add option for following nightly yt-dlp releases (closes #999) 2026-06-06 09:42:26 +03:00
Alex Shnitman 897d52cd0d styling improvements 2026-05-30 15:35:52 +03:00
Alex Shnitman 66d8fa570b Merge branch 'pr-977' 2026-05-29 14:14:05 +03:00
Alex Shnitman cf2d2dd465 review fixes 2026-05-29 14:13:47 +03:00
Alex Shnitman 56c0ad3b5f fix catch 2026-05-29 13:23:25 +03:00
Helmut ad92607a21 fix(ui): drop redundant tooltip on share button
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.
2026-05-29 05:24:49 +02:00
Helmut 6ff364aacf feat(ui): warn before share + surface failures for large files
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.
2026-05-29 04:56:52 +02:00
Helmut 39a8948976 feat(ui): add iOS Web Share button next to download link
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).
2026-05-28 07:34:13 +02:00
Sean McCollum f0348581c2 remove circle and make labels with help text have an underline 2026-05-25 00:13:12 -07:00
Sean McCollum e2773db65a make ui more mobile mobile-friendly
Remove popovers and replace with question icons you have to click
Replace combobox in output > Download Folder with an autocomplete
2026-05-04 10:42:17 -07:00
Alex Shnitman 5d96a581b9 allow filtering out members-only videos in subscriptions (closes #971) 2026-04-28 22:02:05 +03:00
Alex Shnitman 4f83174d05 implement time-clipped downloads (closes #969, replaces #907) 2026-04-26 23:07:50 +03:00
Alex Shnitman 91ee8312bf title filter for subscriptions (closes #968) 2026-04-26 22:51:48 +03:00
Alex Shnitman 23de9824f0 cr fixes 2026-04-21 16:13:58 +03:00
rdiaz738 0ea934c08f Updated import and fixed race condition 2026-04-20 17:24:16 -07:00
Alex Shnitman dd0f98d12f change option presets to be multi-select 2026-04-04 10:25:46 +03:00
Alex Shnitman d41bdf61e2 finalize custom options (closes #563, #482, #261, #681) 2026-04-03 13:20:37 +03:00
copilot-swe-agent[bot] a02abf5853 Keep override controls on dedicated row
Agent-Logs-Url: https://github.com/alexta69/metube/sessions/aef158da-f919-4a3d-a5ee-b71df51c124d

Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
2026-04-03 09:21:44 +00:00
copilot-swe-agent[bot] b16e597125 Fix frontend test typing for override flag
Agent-Logs-Url: https://github.com/alexta69/metube/sessions/31b4274d-cf48-4260-b73b-633cbcd2bb09

Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
2026-04-03 09:07:34 +00:00
copilot-swe-agent[bot] 6e9b2dd7b3 Gate manual yt-dlp overrides behind flag
Agent-Logs-Url: https://github.com/alexta69/metube/sessions/31b4274d-cf48-4260-b73b-633cbcd2bb09

Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
2026-04-03 09:05:19 +00:00
copilot-swe-agent[bot] 565a715037 feat: add per-download yt-dlp presets and overrides
Agent-Logs-Url: https://github.com/alexta69/metube/sessions/8a3119fc-63d1-4508-a196-8c50ff248812

Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com>
2026-04-03 06:16:12 +00:00
Alex Shnitman 483575d24a add subscriptions; change persistence file format to JSON (closes #901, #76, #113, #170, #242, #444, #503, #555, #566) 2026-04-01 14:33:24 +03:00
Alex Shnitman a1f2fe3e73 implement tests 2026-03-20 13:12:31 +02:00
Alex Shnitman 7fa1fc7938 code review fixes 2026-03-15 20:53:13 +02:00
Alex Shnitman 475aeb91bf add status indicator when adding a URL 2026-03-13 19:49:18 +02:00
Alex Shnitman 5c321bfaca reoganize quality and codec selections 2026-03-13 19:47:36 +02:00
CyCl0ne 56826d33fd Add video codec selector and codec/quality columns in done list
Allow users to prefer a specific video codec (H.264, H.265, AV1, VP9)
when adding downloads. The selector filters available formats via
yt-dlp format strings, falling back to best available if the preferred
codec is not found. The completed downloads table now shows Quality
and Codec columns.
2026-03-09 08:59:01 +01:00
CyCl0ne 3bbe1e8424 Add "Downloaded" timestamp column to completed downloads list
Display the completion time for each download in the done list.
The backend already stores a nanosecond timestamp on DownloadInfo;                                     this wires it up to the frontend using Angular's DatePipe.
2026-03-08 14:56:16 +01:00
Alex Shnitman 54e25484c5 some fixes in cookie upload functionality 2026-03-06 14:20:16 +02:00
ddmoney420 7cfb0c3a1d Add cookie file upload for authenticated downloads 2026-03-04 13:29:43 -07:00
Alex Shnitman 58c317f7cd enhance playlist addition cancellation and improve error handling UI 2026-03-02 20:21:04 +02:00
ddmoney420 880eda8435 feat: cancel playlist adding mid-operation (closes #840) 2026-03-01 19:11:29 -07:00
ddmoney420 fd3aaea9d9 feat: expandable error details with copy-to-clipboard (closes #143) 2026-03-01 19:06:58 -07:00
ddmoney420 da84753e20 feat: sort completed downloads by newest first (closes #610) 2026-03-01 19:02:04 -07:00
Alex Shnitman 053e41cf52 code review fixes 2026-02-27 12:58:50 +02:00
vitaliibudnyi 8dff6448b2 add "text only" as another advanced option for captions format 2026-02-27 12:46:23 +02:00
vitaliibudnyi dd4e05325a change delaut captions type to .srt 2026-02-27 12:46:23 +02:00
vitaliibudnyi ce9703cd04 add advanced options for captions download format 2026-02-27 12:46:23 +02:00
vitaliibudnyi 973a87ffc6 add "captions" as download format 2026-02-27 12:46:23 +02:00
Adam Fendley ffe1112dc6 Fix __add_entry parameters; remove duplicated code between channel and playlist processing 2026-02-07 17:30:40 +02:00