Skip to content

feat(175): channels as tv shows - #915

Draft
dialmaster wants to merge 8 commits into
devfrom
feat/175-channels-as-tv-shows
Draft

dialmaster wants to merge 8 commits into
devfrom
feat/175-channels-as-tv-shows

Conversation

@dialmaster

@dialmaster dialmaster commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Work in progress: NOT ready for merge or testing yet :)

First phase of saving channels as TV shows. Nothing changes for users
yet: every library folder still reports the videos layout, so the TV
code paths only run in tests.

- Video deletion and the orphan sweep choose their cleanup by the
  library folder's layout. In a TV folder they remove an emptied
  season folder, then the show folder once only metadata and art
  remain, and skip a folder they cannot list.
- The post-processor writes the channel poster and backdrop to the
  folder the move wrote to, instead of working it out from folder
  depth.
- Add sanitizeFilenameLikeYtDlp, a port of yt-dlp's title
  sanitization that cuts to a byte limit between whole characters,
  with fixtures generated from the dev image's yt-dlp.
- Add tvShows modules for library layouts, episode file names, date
  numbering (UTC MMDDHHMM, bumped on collision, adopting "Plex TV
  Series" preset names) and routing, plus a corpus of real channel
  titles for title-pattern shows.

Refs: #175
Plex playlist sync could not find videos stored in a TV (show)
library section, such as files named with the Plex TV Series
preset, so those videos were left out of the synced playlists. The
file lookup listed show sections without a type filter, which
appears to return only show-level items with no file paths. Watch
status sync already lists show sections with type=4 (episodes).

Request type=4 for show sections when resolving playlist files
too. An episode listing covers every show on the server and is
fetched again on each resolve round, so show sections are only
listed when a file in the batch is named like an episode (SxxEyy).
Batches of movie-style files skip them, which keeps a large
unrelated TV library from being listed episode by episode.

Refs: #175
Each library folder (the main downloads folder and every __subfolder)
now has a layout, videos or tv. A channel that downloads to a TV folder
is saved as one show, <show>/Season <year>/SxxEyy - Title [id].ext,
numbered by upload time, with an episode NFO and a tvshow.nfo, so a
Plex, Jellyfin or Emby TV library can index it.

- Store subfolder layouts on the subfolders table and the main
  folder's in config (mainFolderLayout); store shows and episode
  numbers in the new tv_shows and video_classifications tables. A
  show's folder is pinned when it is created, so an uploader rename or
  a default-folder change does not start a second show.
- Guard layout changes: a folder or channel that already holds
  downloaded videos cannot switch between videos and TV, nothing
  switches while a download runs, and TV folders are video-only. Saved
  MP3 settings are refused; a download-time MP3 type is downgraded to
  video for videos whose predicted folder is TV, and an MP3 that still
  reaches a TV folder is placed as the episode, with a warning, rather
  than movie-style.
- Add GET/PUT /api/library-folders, GET /api/channels/:id/tv and
  PUT /api/channels/:id/tv/layout, a layout filter on /getchannels,
  and episode details on the video listings and video metadata.
- Settings gains a library folder list, Channel Settings a TV Show
  section that switches a channel (creating a TV folder when none
  exists), and the subscription, channel and video pages show TV and
  episode chips.
- Replace episode files through a .part staging file and an atomic
  rename. fs-extra's overwrite removes the destination before a
  cross-device copy, so a copy that failed on a full disk could have
  left no copy of the episode at all.

Refs: #175
Switching a channel, a library folder or the default subfolder between
Videos and TV no longer refuses when downloads exist. The change opens
a dry run of every file move (paths, episode numbers, new shows,
missing files, collisions, watch state at risk), and confirming it
moves the files in the background, so existing downloads become
episodes, or movie-style videos again, without downloading them again.

- Record each reorganize in tv_reorganize_operations and
  tv_reorganize_items, so a restart resumes it and failed videos can
  be retried. A revision token over the planned moves refuses a stale
  preview. The settings change applies when the move starts and is
  undone only if no video could be moved.
- Hold download jobs in the queue while files move, and refuse the
  rescan, library repair, auto-removal, watch status sync and yt-dlp
  update, scheduled runs included. Deletes and channel settings saves
  of the videos and channels being moved get a 409.
- Move files without replacing anything at the destination, through a
  staging copy when it is on another filesystem, and move earlier
  files back when a later one fails. NFOs, art, tvshow.nfo, the paths
  stored in jobs/info, channel and playlist m3u files and media server
  playlists follow the moved files.
- Keep Youtarr's watch state while media servers show the moved files
  as new, unwatched items: watch_status_holds snapshots it before the
  move, watch status sync does not lower a held row, and the state is
  pushed back to the Plex owner account, Jellyfin and Emby once they
  index the new path. Restores that don't land within 14 days are
  listed under Watch Status with Retry and Dismiss.
- Name videos leaving a TV folder the way a new download would be
  named, rendered in batches by yt-dlp from their stored info.json.
- Add POST /api/tv/reorganize/preview, POST /api/tv/reorganize and the
  /api/tv/operations and /api/tv/holds endpoints. The library folder,
  channel layout, channel settings and default subfolder routes answer
  a reorganizeRequired 409 that names the change to preview.
- Add the review and progress dialog to Channel Settings, the library
  folder list and Settings, and a banner on every page while a
  reorganize runs.

Refs: #175
Setting up a TV folder meant guessing whether the media server
library for it was right. Settings and Channel Settings now show, per
library folder and per configured server, which libraries hold the
folder and what in their setup works against Youtarr's files: no TV
library yet, a library of the wrong type, the Plex Series or a legacy
agent, a Jellyfin or Emby library that saves NFO files or looks items
up online, another library showing the same files again, a library
nested inside another on Jellyfin or Emby (which leaves it empty), a
mount under another name, or a missing Plex refresh mapping. Server
paths differ from the container's, so folders are matched by a
location ending in __name and by sampling a few files from each
library and pairing them with Youtarr's downloads by YouTube id.

- Add GET /api/library-folders/check (optionally for given folders,
  checked as a given layout) and PUT /api/library-folders/plex-mapping.
  Channel Settings adds the Plex refresh mapping itself when exactly
  one Plex TV library holds the channel's folder; the folder list
  offers it as a button. The saved mapping reaches an open Settings
  page without discarding its unsaved edits, including pending edits
  to other mappings.
- Show the check in the move review for the TV folders videos move
  into, evaluated as TV folders even while they are still saved as
  Videos folders, and only for the move being previewed.
- List only the libraries that may hold Youtarr's files when Plex
  watch sync and playlist sync and Jellyfin/Emby push-back read a
  server, so an unrelated TV library is no longer listed episode by
  episode. A library whose sample says nothing (empty, unreadable, or
  YouTube downloads Youtarr can't place) stays in scope, as do the
  configured and mapped Plex sections.
- Re-sync media server playlists 5 and 15 minutes after a reorganize:
  a move that keeps file names first resolved to the item at the old
  path. Remove Youtarr's tvshow.nfo from a channel folder that is
  movie-style again; a Jellyfin Mixed library kept showing the channel
  as a series.
- Recheck a move review blocked by a running task every 5 seconds,
  after a failed recheck too, so Move enables without reopening the
  dialog. Word the default-subfolder confirmation for a layout change,
  which moves the channels' videos.
- Document the TV library settings for Plex, Jellyfin and Emby with
  the servers' own labels, one library per folder on Jellyfin and
  Emby, and advise against Mixed libraries.

Refs: #175
A channel that uploads several series (Hermitcraft seasons, one
cartoon in four languages) could only be saved as a single show
numbered by upload date. Title shows are series inside a channel,
defined by title patterns and numbered from the titles: season and
episode taken from the title, a fixed season, the next number in the
season, or the upload year. Each show has its own folder in a TV
folder; videos no show takes follow the channel's layout.

- Edit shows in Channel Settings > TV Show: patterns in a simple
  syntax ({season}, {episode}, {title}, *) or as Python regular
  expressions, matched by Python's re as yt-dlp does, exclude terms,
  season names and the TV folder, with a live preview of episodes,
  duplicates, gaps, unmatched titles and compilations or parts not
  supported yet.
- When two uploads claim one number, the stored holder keeps it, else
  the earliest available upload. A duplicate that isn't downloaded is
  ignored and its complete.list line written, deferred while a
  download runs; episode_conflicts records what Youtarr did, so a
  release undoes only that. A downloaded duplicate stays where it is.
  "Use this copy instead", a manual episode and "Not an episode"
  override the classification.
- A save that changes a downloaded episode's show, number or file
  name opens the move review and runs as a reorganize, which restores
  the previous shows and rows when no video moves. Channel and folder
  layout switches leave title show episodes in place, and a folder
  holding title shows can't switch to Videos.
- Classify new downloads at finalize and new uploads on listing
  refresh. An optional switch limits channel downloads and Download
  All to show episodes, with one --match-filter per show.
- Write a youtarr uniqueid and <namedseason> to tvshow.nfo, and
  season.nfo to named season folders.
- Add a show filter, a Missing episodes view and planned episode
  chips to the channel page, and an "N shows" chip to Subscriptions
  and the channel header.
- Show a file sitting where a destination folder belongs as a
  collision in the move review instead of failing the preview, and
  say when a video's files moved but finishing it failed.
- Add tv_show_patterns, tv_show_seasons, episode_conflicts and
  channels.tv_show_only_downloads; the routes under
  /api/channels/:channelId/tv/shows, preview, show-only, conflicts
  and recheck; and GET/PUT /api/videos/:youtubeId/episode. Document
  title shows in the usage and folder structure guides.

Refs: #175
@dialmaster dialmaster linked an issue Oct 5, 2026 that may be closed by this pull request
@dialmaster
dialmaster changed the base branch from main to dev October 5, 2026 14:30
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

📊 Test Coverage Report

Backend Coverage

Type Coverage Status
Lines 92.24% 🟢
Statements 91.69% 🟢
Functions 91.88% 🟢
Branches 84.73% 🟢

Frontend Coverage

Type Coverage Status
Lines 89.71% 🟢
Statements 88.01% 🟢
Functions 82.16% 🟢
Branches 81.80% 🟢

Coverage Requirements

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

Coverage report generated for commit 23fd4ac

Follow-up fixes from the local checks of title shows and the
reorganize:

- The per-page file check showed videos as missing when their file
  had been renamed in place (for example to a TV episode name) but
  kept its [id]. It now also looks in the stored folder for a media
  file carrying the video's id, skips macOS AppleDouble copies, and
  lists each folder once per batch.
- Gaps and Missing episodes listed long runs of missing numbers for
  seasons numbered by upload year. Those seasons are skipped, as
  date-numbered seasons already were.
- A show's duplicate count comes from its recorded conflicts, so a
  TV channel's episode that lost a title claim, and kept its
  channel-show row, is counted.
- The reorganize preview notes MP3 files moving from a Videos folder
  into a TV folder, where TV libraries don't show them.
- After a move applies a default subfolder change, Settings asks to
  save again only when the form still has other unsaved changes.

Refs: #175
Follow-up fixes from the review of title shows and the reorganize:

- Renaming a show folder only in case or accents saved without
  moving its episodes, so later downloads could land in a second
  folder. Such renames now move the files. On case-insensitive
  filesystems the move no longer reports the source as a collision
  or deletes the episode NFO it just wrote, and a destination that
  differs only in case or accents is never taken for a finished
  copy of the source.
- A restart between creating a title show change's shows and
  recording the reorganize as applied could leave recovery and
  Retry refused on every attempt. Both are now written in one
  transaction.
- Regex patterns with numbered backreferences or conditional groups
  could match differently in channel downloads than in
  classification. They are refused; three-digit octal escapes such
  as \123 still work.
- A job started from an idle queue could begin while complete.list
  was being updated for duplicate episodes. Every job start now
  waits for that work, and a failed read of the pending updates no
  longer fails a save that has already committed.
- Listing refreshes could record a new video as a duplicate of a
  video they left out, spend order numbers on rows they did not
  write, and clear classification errors of videos they did not
  check.
- The subfolder delete guard did not count title shows of channels
  that download to a Videos folder.
- Undoing a title show change dropped the pattern link of rows in
  retired shows. Drafts naming another channel's show, the same
  show twice, or a folder another show gives up in the same change
  are refused.
- Moved title episodes lost their stored time source and were
  missing from the review's download-time and upload-day notes, and
  videos without a date were numbered before dated ones.
- The reorganize API docs now list the titleShows change.

Refs: #175

This branch has not been deployed

No deployments
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.

Support Channels as TV Shows.

1 participant