Prevent memory leaks by ensuring the search EventSource connection is
closed when the modal unmounts and setTimeout handles are cleared in
copy-to-clipboard flows across modal components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add _fallback_parse() tried when TITLE_RE.match() returns None, using two
regex patterns to extract episode numbers from formats the main regex misses:
- digits before [ bracket (issues #876, #910)
- compound [02(57)] format (issue #773)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 tests covering the full bug chain:
- raw_parser misparses leading number as episode
- TitleParser.raw_parser returns None for unparseable titles
- add_title_alias rejects None and empty string
- _get_aliases_list filters null values from JSON
- get_all_title_patterns skips None title_raw
- match_torrent and match_list handle corrupted data
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Titles like "29 岁单身冒险家的日常" cause the regex to match the leading
number as episode, leaving title_raw as None. This cascades into storing
null aliases and crashing match_torrent with TypeError.
- Fall back to title_jp when title_en and title_zh are both None
- Return None from raw_parser when no title can be extracted
- Reject None/empty aliases in add_title_alias
- Filter null values from parsed title_aliases JSON
- Skip None title_raw in get_all_title_patterns
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow users to drag bangumi cards from the "Unknown" section into weekday
columns in the calendar view. Manual assignments are locked so calendar
refresh from Bangumi.tv doesn't overwrite them. A reset button lets users
unlock and send cards back to Unknown.
Backend:
- Add weekday_locked field to Bangumi model (migration v9)
- Add PATCH /api/v1/bangumi/{id}/weekday endpoint
- Skip locked items in refresh_calendar()
Frontend:
- Add vuedraggable for smooth drag-and-drop
- Pin indicator and unpin button on manually-assigned cards
- Drop zone highlighting during drag
- i18n strings for drag/pin/unpin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The BangumiDatabase.search_all() uses a module-level TTL cache that
persists across tests using different in-memory SQLite databases.
This caused test_migrate_preserves_existing_data and test_migrate_idempotent
to return stale cached results (1 bangumi instead of 2).
Add an autouse fixture in conftest.py to clear the cache before and after
each test.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Skip episode offset for episode 0 (specials/OVAs) to prevent overwriting
regular episodes (fixes#977)
- Catch re.PatternError in RSS filter compilation and fall back to literal
matching when user filter contains invalid regex chars (fixes#974)
- Remove Aria2 and Transmission from README supported downloaders list
(addresses #987)
- Add regression tests for issues #974, #976, #977, #986
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- useAuth: replace watcher with explicit router.replace on login/logout
- useMyI18n: create single i18n instance at module level (avoid dupes)
- usePasskey: detect WebAuthn support synchronously (no onMounted)
- main.ts: import i18n from hook module instead of calling composable
- ab-add-rss: hoist useApi composables outside functions to avoid
recreating them on each call
- calendar: prevent duplicate refreshes when already refreshing
- downloader: guard interval polling against stale activation state
- router: only mark setupChecked on successful status check
- log store: stop SSE log updates on logout
- i18n: add missing "edit" translation key (en + zh-CN)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Aria2: add stub methods for full duck-typing compatibility
- MockDownloader: add verify parameter to rename_file signature
- DownloadClient: raise ConnectionError on auth failure
- Path: fallback bangumi_name from torrent_name when path is flat
- Renamer: remove unused check_pool and dead compare_ep_version
- Pass torrent_name to _path_to_bangumi for better name resolution
- Remove check_pool unit test (feature removed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Database:
- Add error handling and per-step version tracking in migrations
- Enable SQLite foreign keys via PRAGMA on connect
- Fix SQLAlchemy .is_(None) usage, add session.merge() for detached
- Batch commit for semantic alias merges
- Quote table/field names in fill-null-defaults SQL
- Guard against empty user data in migration
Parsers:
- TMDB: bounded LRU cache (512), asyncio.gather for parallel season
fetches, fix season regex \d -> \d+, null-safe year, fix id shadowing
- Raw parser: re.escape() for group/prefix regex, None guard on match
- OpenAI: handle Pydantic model_dump, catch ValueError
Network:
- Null-safe get_html() return
- Error handling per RSS item in mikan parser
- Progressive retry delays (5/15/45/120/300s) with specific exceptions
- Platform detection via sys.platform instead of path heuristic
- Move filter cache to instance variable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use per-task stop events instead of shared stop_event to prevent
stopping one task from killing all others
- Track running state via _tasks_started flag instead of stop_event
- Add error handling in RSS, rename, scan, and calendar loops
- Make restart() resilient to stop failures (catch and continue)
- Cache downloader status check with 60s TTL
- Fix _startup_done set before start() completes (race condition)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Persist JWT secret to config/.jwt_secret (survives restarts)
- Change active_user from list to dict with timestamps
- Extract username from cookie token instead of list index
- Add SSRF protection (_validate_url) for setup test endpoints
- Mask sensitive config fields (password, api_key, token, secret)
- Add auth guards to notification test endpoints
- Fix path traversal in /posters endpoint using resolved path check
- Add CORS middleware with empty allow_origins
- WebAuthn: add challenge TTL (300s), max capacity (100), cleanup
- Remove hardcoded default password from User model
- Use timezone-aware datetime in passkey models
- Adapt unit tests for active_user dict and cookie-based auth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
67 tests across 11 phases exercising the full AutoBangumi workflow
against Docker infrastructure (qBittorrent + mock RSS server).
Covers setup wizard, auth, config, RSS CRUD, bangumi, downloader,
program lifecycle, log, search, notification, and credential updates
with both happy paths and error conditions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a bangumi was deleted, its associated Torrent records remained in
the database. This prevented re-downloading the same torrents if the
user re-added the anime, because check_new() deduplicates by URL and
would filter out the orphaned records.
Now delete_rule() removes Torrent records before deleting the Bangumi,
so re-adding the same anime correctly treats those torrents as new.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 13 new test cases across three parser test files:
- raw_parser: Chinese 第二季, 2160p resolution, bracketed Season N, multi-group with Chinese punctuation, English-only title
- torrent_parser: EP format, tc/zh-tw subtitle, no-language subtitle (ValidationError), multi-level path, [NNvN] version suffix
- path_parser: season=2/no-offset, large positive offset, offset yielding exactly Season 1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace non-existent ab-dialog with ab-popup for proper modal behavior,
use ab-button/ab-label components and consistent spacing/styles matching
other settings panels (search-provider, proxy).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: Episode 0 incorrectly renamed to E01 (#977)
fix: NoneType error in match_list when title_raw is null (#976)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add NotificationProvider base class with send() and test() methods
- Add NotificationManager for handling multiple providers simultaneously
- Add new providers: Discord, Gotify, Pushover, generic Webhook
- Migrate existing providers (Telegram, Bark, Server Chan, WeChat Work) to new architecture
- Add API endpoints for testing providers (/notification/test, /notification/test-config)
- Auto-migrate legacy single-provider configs to new multi-provider format
- Update WebUI with card-based multi-provider settings UI
- Add test button for each provider in settings
- Generic webhook supports template variables: {{title}}, {{season}}, {{episode}}, {{poster_url}}
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes IndexError when parsing torrent names that don't follow the
standard [Group] format. Now returns empty string instead of crashing.
Fixes#973
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add batch offset lookup to reduce N database connections to 1-3 per cycle
- Add search_by_qb_hashes() and search_ids() for batch queries
- Throttle pending rename cache cleanup to once per minute max
- Use exponential backoff for rename verification (0.1s->0.2s->0.4s)
- Skip verification for subtitle renames to reduce latency
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add complete Japanese translation for all documentation pages including:
- Home and about pages
- Deployment guides (Docker CLI, Docker Compose, DSM, Local)
- Configuration pages (RSS, Downloader, Parser, Notifier, Manager, Proxy, Experimental)
- Feature documentation (RSS Management, Bangumi, Calendar, Rename, Search)
- FAQ and troubleshooting
- API reference
- Changelogs (2.6, 3.0, 3.1, 3.2)
- Developer guide
Also updates VitePress config to add Japanese locale with full sidebar navigation.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Fix qBittorrent rename verification (verify file actually renamed)
- Add pending rename cooldown to prevent spam when rename delayed
- Add torrent tagging API for accurate offset lookup
- Add auto calendar refresh every 24 hours
- Fix frontend error handling (don't logout on server errors)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(docs): use absolute paths for images in public folder
Relative paths (../image/) don't work on Vercel since the image/
folder at docs root is not tracked in git. Only public/image/ is
tracked. Using absolute paths (/image/) correctly references the
public folder assets.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update README image paths to public folder
The images are tracked at docs/public/image/, not docs/image/.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The workflow was using the full PR title (e.g., 'Release 3.2.1') as the
version string, causing Docker tags and release creation to fail.
Now extracts just the version number (X.Y.Z or X.Y.Z-suffix) from the
PR title, handling formats like 'Release 3.2.1', 'v3.2.1', or '3.2.1'.
Images referenced with absolute paths in VitePress frontmatter (e.g.,
/image/icons/rss.png) must be in the public folder to be served correctly.
This fixes missing feature icons on the homepage.
A symlink is created at docs/image -> public/image to maintain compatibility
with relative paths used in markdown files.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Configure VitePress locales with zh-CN as root and en-US as /en/
- Translate all documentation to Chinese (31 files)
- Create English documentation under /en/ directory
- Add Chinese UI labels for navigation and pagination
- Language switcher now available in site header
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Previously, the rename log message was printed before checking if the
qBittorrent API call succeeded. This caused log spam when rename
operations failed (e.g., due to 409 conflicts or network errors) since
the same file would be attempted again on the next cycle.
Now the log message is only printed after confirming the rename
succeeded, reducing noise in the logs.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
The warning "Episode offset 0 would result in negative episode" was
misleading and caused log spam. The actual issue was either:
1. Parsed episode was 0 or negative (parsing failure or special episode)
2. A negative offset would make a valid episode negative
Changes:
- Differentiate between parsing issues vs offset issues in log messages
- Use debug level for parsed episode issues (likely special episodes)
- Keep warning level only for actual offset problems
- Include original episode value in warning for better debugging
- Handle edge case where parsed episode is 0 by falling back to 1
Fixes#962
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
When users have a SOCKS proxy configured (via HTTP_PROXY or ALL_PROXY
environment variables), httpx's AsyncClient automatically tries to use
it but fails without the socksio package installed.
Changed httpx dependency from httpx>=0.25.0 to httpx[socks]>=0.25.0 to
include the socksio package as an extra dependency.
Fixes#961
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>