Skip to content

feat: separate deleted videos from missing ones - #950

Merged
dialmaster merged 4 commits into
devfrom
feat/670-deleted-and-missing-video-handling
Oct 19, 2026
Merged

dialmaster merged 4 commits into
devfrom
feat/670-deleted-and-missing-video-handling

Conversation

@dialmaster

@dialmaster dialmaster commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Every video without a file looked the same. A video deleted in the app, one removed by automatic cleanup, and one whose file disappeared from disk all showed the same red Missing state, and they piled up on the Videos page with no way to clear them out.

Deleted vs. missing

  • Youtarr now records why a video lost its file (removed_reason, removed_at, removed_detail) and shows it as Deleted (grey) or Missing (red). The chip tooltip and the video modal give the reason in plain words and say the video can be downloaded again. Videos removed before this change show as Deleted.
  • The Videos page hides deleted videos by default and remembers the choice in the browser. Channel and playlist pages still list both, so either kind can be re-downloaded. In hot-load mode the list reloads from page one after a delete or mark, since refetching a later page kept the removed rows around; classic pagination stays on its page.
  • Missing videos can be marked as deleted in three places:
    • A selection on the Videos page (sent in batches of 1000, showing the server's message if it's refused).
    • One video from the video modal, on any page.
    • All of them at once from Settings > Maintenance.
  • Maintenance also has "Empty trash...", which forgets the chosen videos and can remove their download archive entries too. Its dialog opens on the first choice that's available and disables archive removal up front while a download is running.
  • A purge that removes archive entries is refused while downloads are running, and new downloads wait until that kind of purge finishes.
  • Channel "Download all" and playlist downloads skip videos the archive lists without a database row. Before, they queued those videos on every run and yt-dlp skipped them every time.

Unmounted drives and shares

  • Before marking anything missing, Youtarr checks that the downloads folder and each __subfolder exist and aren't empty. If the downloads folder itself fails, every video is in doubt. If a subfolder fails, only the videos stored in it are. Every failing subfolder gets reported, and an unreadable one counts as worse than an empty one, so an unreadable share is never mistaken for a folder that was emptied on purpose. An empty subfolder with no videos stored in it (say, one a channel was moved out of) doesn't block anything.
  • Files and folders Youtarr creates itself (.youtarr_tmp, __playlists__, leftover health-check files) don't count, because a sweep or a restart creates them on the bare mount point while a share is down.
  • While the check puts videos in doubt:
    • The file check leaves those videos alone, and the Videos page shows a warning that can be dismissed.
    • The rescan fails if the downloads folder itself is the problem. If it's a subfolder, the rescan still runs but doesn't mark that subfolder's videos missing, and the run is recorded as partial.
    • Automatic cleanup and the orphan folder sweep record a skipped run if present videos are affected.
    • Manual deletes refuse each affected video with the same message, instead of marking it deleted while its file sits on the share.
    • Emptying missing videos from the trash is refused if any of them are affected.
  • The messages name a subfolder by its own name and never show the container path, since that path doesn't mean anything on the host.
  • For a folder that's empty on purpose (only when it's empty, never when it's gone or unreadable), the Maintenance card has two ways around the check:
    • "Rescan anyway" runs a manual rescan that skips the check once and marks the gone files missing. The scheduled rescan never skips the check, and the run record says when it was skipped.
    • "Mark all missing as deleted" goes ahead once the user confirms they removed the files themselves.

Smaller fixes

  • The file check only turns a present video missing and only restores a removed one, so a delete that happens mid-request keeps its reason. Job recovery leaves the removal columns alone when it can't find the file.
  • The channel page shows the file check's result on the same load. Its table view no longer shows a download format for a removed video whose old path is still stored.
  • A present file in the playlist modal reads as downloaded even when YouTube has removed the video.
  • On a touch screen, tapping an Availability chip opens its tooltip without also selecting or expanding the row underneath. Mouse clicks still go through to the row.
  • The Deleted filter chip looks neutral when it's at its default, and the File Deleted/Missing overlay is readable on any thumbnail and fits in a phone's player.

Known limitation, follow-up PR
A download that finishes while a share is down still creates channel folders on the bare mount point, and from then on the check can't tell the share is missing. I plan to fix that in a separate PR by holding downloads through the existing storage pause while the library looks unmounted. That PR will also keep library repair from recreating lost videos as missing while the folder looks unmounted, and make the archive write for single entries atomic.

Refs: #670

Every video without a file looked the same. A video deleted in the
app, one removed by automatic cleanup, and one whose file vanished
from disk all showed the same red Missing state, and they built up
on the Videos page with no way to clear them out.

- Record why a row lost its file (removed_reason, removed_at,
  removed_detail) and show it as Deleted (neutral) or Missing (red),
  with the reason in plain words in the chip tooltip and the video
  modal. Rows removed before this change show as Deleted.
- Hide deleted videos on the Videos page by default and remember the
  choice in the browser. Channel and playlist pages keep listing
  both, so either can still be re-downloaded.
- Add "Mark as deleted" for selected missing videos, plus "Mark all
  missing as deleted" and "Empty trash..." in Settings > Maintenance.
  Emptying the trash forgets the chosen videos and can also remove
  their download archive entries.
- Check that the downloads folder and each __subfolder exist and are
  not empty before marking anything missing. An unmounted drive or
  share could mark the whole library missing; now the file check
  holds those rows back and warns, the rescan records a failed or
  partial run, and actions over all missing videos are refused.
- Leave videos the archive lists without a row out of channel
  "Download all" and playlist download selection, which otherwise
  queued them for yt-dlp to skip on every run.
- Refuse a purge that removes archive entries while downloads are
  running, and hold new downloads until such a purge finishes.

Refs: #670
@github-actions

github-actions Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

📊 Test Coverage Report

Backend Coverage

Type Coverage Status
Lines 91.24% 🟢
Statements 91.11% 🟢
Functions 91.08% 🟢
Branches 85.11% 🟢

Frontend Coverage

Type Coverage Status
Lines 89.47% 🟢
Statements 87.95% 🟢
Functions 81.42% 🟢
Branches 82.42% 🟢

Coverage Requirements

  • Minimum threshold: 70% line coverage
  • Backend: ✅ Passes
  • Frontend: ✅ Passes

Coverage report generated for commit 006bb04

While a drive or share was down, the downloads folder could still
look mounted once Youtarr had created its own temp folder, playlists
folder, or an interrupted write test on the bare mount point, and the
page-load check and rescan then marked the whole library missing. The
check now ignores the entries Youtarr creates itself, reports an
unreadable subfolder on its own so the rescan keeps going, and is
consulted by automatic cleanup, the orphan folder sweep, and manual
deletes, which could otherwise stamp videos as deleted while their
files sat on the unmounted share.

- Add "Rescan anyway" to the Maintenance card: a manual rescan that
  skips the check once, for a downloads folder emptied on purpose.
  The scheduled rescan never skips it and the run record says when
  it was skipped.
- Guard the file check's writes so a delete that lands mid-request
  keeps its reason, leave the removal columns alone when job recovery
  cannot find the file, and pass plain rows to the channel page's
  file check so its results show on the same load.
- Reload from page one after a delete only in hot-load mode, where a
  later page's refetch kept the removed rows; classic pagination
  stays on its page.
- Send Mark as deleted in batches of 1000 and show the server's
  message when it is refused.
- Open Empty trash on the first available choice, disable it when
  nothing can be purged, and disable archive removal up front while
  a download is running.
- Show a present file as downloaded in the playlist modal even when
  YouTube has removed the video.
- Derive the filter chips' aria-pressed from the resting state.

Refs: #670
One failing __subfolder stopped the check, and with any present video
on record it then held back the rescan, automatic cleanup, the orphan
sweep, and the "all missing" trash actions for the whole library. An
empty subfolder nothing was stored in (one a channel had been moved
out of, say) looked like an unmounted share for good, and the check's
messages named the container path, which means nothing on the host.

The check now reports every failing subfolder, with the strictest
reason winning so an unreadable share is never looked past as emptied
on purpose, and callers ask whether the failure covers any stored
video before acting on it. The rescan and the page-load file check
hold back only the videos stored under a failing subfolder, cleanup
and the sweep skip only while present videos are affected, the
removed-summary reports a failure that covers no present or missing
video as passing, and messages name a subfolder by its own name only.

- Let Mark all missing as deleted look past the check, with an
  explicit acknowledgement in the Maintenance card, when the folder
  is only empty, never when it is gone or unreadable, so Rescan
  anyway is no longer a dead end.
- Add Mark as deleted for one missing video to the video modal, on
  every page.
- Make the Videos page unmounted-folder banner dismissible and offer
  Maintenance only for the empty reasons.
- Open an Availability chip's tooltip on a touch-screen tap without
  reaching the row underneath; mouse clicks still pass through.
- Show the Deleted filter chip as neutral at rest, keep the File
  Deleted/Missing overlay readable on any thumbnail and within a
  phone's player, and say in every removal sentence that the video
  can be downloaded again.

Refs: #670
- Say "file" instead of "files" in the Mark all missing as deleted
  dialog when only one video is missing.
- Reword the Videos page banner and Maintenance card warnings so they
  read correctly when the message names more than one subfolder:
  "Until that changes" replaces "until it is back".
- Show "The downloads folder check was skipped." on the Maintenance
  page's Last rescan line after a Rescan anyway, as the Scheduling
  page already does.
- Keep the label on the video window's Mark as deleted button on
  phones, where it showed only an icon and acted on the first tap,
  and let the phone action row wrap so the label is not cut off.

Refs: #670
@dialmaster
dialmaster merged commit f9f3cac into dev Oct 19, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant