Empty sensitive fields were masked to '********' in GET /config, so the
UI showed a phantom password where none was set. Worse, 3.2.3/3.2.4
masked without restoring on save, so users who saved config on those
versions had the literal sentinel written into stored proxy/downloader
credentials, breaking authentication until manually cleared.
- _sanitize_dict now masks only non-empty values.
- Settings._migrate_old_config scrubs literal '********' sentinels from
sensitive fields on load (original values are unrecoverable), logging
a warning so users know to re-enter real secrets.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WTfJZ3EzCpY8SLhzXT6Sf
BangumiDatabase.delete_all() issued a bare DELETE FROM bangumi while
torrent.bangumi_id and aria2_gid.bangumi_id rows still referenced it;
with PRAGMA foreign_keys=ON the whole reset rolled back with
IntegrityError, and the surviving torrent dedup rows made re-added
bangumi skip episodes as already downloaded. Delete bangumi-linked
torrent records (same semantics as delete_rule) and detach aria2_gid
mappings before the table wipe.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WTfJZ3EzCpY8SLhzXT6Sf
Subtitle groups sometimes release TV specials without an episode number
or any special/movie marker (e.g. LoliHouse's ONE PIECE HEROINES). The
Preview engine's admission policy already accepts such evidence-backed
single releases as eps_collect bangumi, but the classic engine's legacy
Episode projection rejected them outright, so default installs silently
skipped the torrent.
Align classic admission with the Preview persistence policy for exactly
this shape (no episode, unknown media type, admissible under
persistence_target). The frozen legacy Episode contract and its golden
corpus stay untouched; numbered legacy rejections (e.g. fractional
episodes) keep their behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WTfJZ3EzCpY8SLhzXT6Sf
Download-complete notifications were silently dropped when sendPhoto
failed: post_form returned None on any error and the provider crashed
on resp.status_code, with no text fallback. HTTP rejections were also
logged as "Cannot connect", hiding the real cause.
- save_image now records the poster's source URL in a .url sidecar;
Telegram prefers sendPhoto with that URL (server-side fetch), falls
back to local multipart upload, then to a plain text message.
- load_image returns None instead of raising when the cached file is
missing.
- post_url/post_form log HTTP status and response body separately from
connection errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WTfJZ3EzCpY8SLhzXT6Sf
Deleting a bangumi disables its orphan standalone RSS feed (#1053), but
re-subscribing the same feed hit the duplicate-URL early return in
RSSDatabase.add() and never reset enabled, so the rebuilt bangumi
silently stopped updating. Re-enable the existing row and report it as
a successful add.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WTfJZ3EzCpY8SLhzXT6Sf
Replaces the monolithic regex-based parser with a multi-stage tokenizer
pipeline (normalize, tokenize, classify, resolve). Fixes multiple parsing
bugs: Atlas format (#986), VTuber/Lv2 title splits (#798, #766),
Pre-episode format (#794), western S01E05 format (#764), and separator
dash leaking into titles (#819).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add detection and handling for anime movies (剧场版/劇場版) as a
separate entity from TV series. Previously, movies in RSS feeds were
either parsed as episode 0 or skipped entirely.
- Add TokenKind.MOVIE and movie keyword/pattern detection in tokenizer
- Add is_movie flag to Episode dataclass
- Create Movie SQLModel, MovieDatabase with CRUD + TTL cache
- Add migration v10 to create movie table
- Route TitleParser.raw_parser() to return Movie or Bangumi
- Update RSS analyser/engine for movie-aware parsing and downloads
- Add movie save path (no Season subfolder) and rename logic
- Add Movie REST API endpoints (get/update/delete/disable/enable)
- Add 11 tests for movie marker detection and non-movie regression
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add anibt.net as a first-class default search provider alongside
mikan/nyaa/dmhy, using its RSS search endpoint
(https://anibt.net/rss/magnets.xml?q=%s) with the tmdb parser.
load_provider() now merges newly added built-in sites into an
already-saved config/search_provider.json, so existing installs pick
up new default providers on restart while keeping user-customized
URLs and parsers untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YUaTRVgAxPq6zEiwJHnbiC
qB <=5.1 answers every rejected add with an opaque 200 "Fails.". For
magnet adds we already confirm "actually a duplicate" via the btih
hash, but Mikan torrents are uploaded as .torrent files, so the check
never matched and the add was classified FAILED — and since failed
adds are deliberately not persisted (kept for retry), the same
torrents error-looped every RSS tick ("Fails." ... "no matching
torrent found") forever, e.g. after purging orphan records while the
torrents still exist in qB.
Compute the v1 infohash (SHA-1 of the bencoded info dict, minimal
cursor — no new dependency) from the uploaded bytes and confirm
existence in qB; corrupt files still fail loudly and keep retrying.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
Codex review follow-up: a soft-deleted exact duplicate still orphaned
torrents (add() refuses the insert, the deleted row was refused for
linking, and download proceeded with no id) and the collect path
could link torrents to a still-disabled row. Explicitly subscribing
or collecting a disabled rule can only mean the user wants it back:
restore_one() re-enables the row (keeping its tuned config) and
torrents link to it. eps_complete is unaffected (not_complete already
filters deleted rows).
Also document the ab:renamed tag semantics (top-level media files;
subtitles follow in the same tick, nested extras never rename).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
- renamer: drop retroactive sanitization in gen_path — titles parsed
from on-disk files are already legal; sanitizing them would batch-
rename existing libraries containing ":" on upgrade. Sanitization
stays in gen_save_path (new rules only), with an "Unknown Bangumi"
fallback when a title is all reserved characters.
- renamer: tag failures no longer break the rename flow (notification
still returned, cycle continues); collections with unparseable
media files are not marked ab:renamed.
- collector: duplicate resolution no longer clobbers user-tuned
fields (eps_collect is set via a targeted update); failed collects
roll back the freshly inserted row; soft-deleted rules are never
linked; stale ids pointing at removed rows are cleared.
- database: find_duplicate is deterministic (oldest row) and warns on
legacy duplicate keys.
- api: auto_tag_torrents matches ab:<digits> exactly so the
ab:renamed marker doesn't block re-linking.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ