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
open() never closed the previous EventSource, so re-searching leaked
the old stream: it kept appending the same results (duplicate
variants/tags in the list), its onerror closed the NEW stream's
handle, and the leaked stream auto-reconnected forever — an idle
spinner even with an empty input.
- close the previous stream before opening a new one
- stale-stream handlers close themselves and never touch shared state
- de-duplicate appended results by rss_link as a safety net
- clearing the input stops an in-flight search
- eventSource must be a shallowRef: ref() proxies the EventSource and
breaks the same-stream identity check
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
Backend: add network.tmdb_api_key (empty = fall back to the built-in
shared key, read live like tmdb_base_url); the config API masks it
automatically.
WebUI: the network section existed backend-only since #1042 — expose
it as a Network settings panel (TMDB API URL, TMDB API key,
Bangumi API URL) with types, initConfig defaults, i18n, and mocks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
New bangumi_manage.track_orphans toggle (default on = current
behavior). When off, refresh_rss no longer persists unmatched torrents
(bangumi_id NULL orphan records): they are re-matched in memory each
tick (cheap, no network — the analyser dedups via bangumi rules, not
torrent records), and a rule added later can retroactively pick up
episodes still in the feed, which persisted orphans used to block
forever. Season-collect flows still persist their NULL records for
feed dedup and are not gated.
WebUI: "Track Unmatched Torrents" switch in Settings -> Manage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
Post-rebase rework addressing review findings on #1051 (the branch
predated the component-system migration):
- Replace retired ab-popup confirm with the useConfirm() promise hook
- Replace NButton and bare <button> with ab-button / ab-icon-button;
badge pills with ab-tag / ab-badge marks; empty state with ab-empty;
drop hardcoded colors for theme variables
- Make the per-bangumi page's route param reactive with a NaN guard,
and remount the list on param change
- Add a "View torrents" entry point in the rule edit dialog
- Reload the torrent list in finally so partial batch-delete failures
don't leave stale rows
- Align the orphan card title across locales; regenerate dts files
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
Port zhushanwen321's torrent-management feature (#1020) from the 3.2 sync
architecture to the 3.3 async design.
Backend:
- TorrentDatabase: search_orphans / count_orphans / delete_orphans / delete_obj
- bangumi API: list/delete endpoints for per-bangumi torrents and orphan
torrents (bangumi_id IS NULL); literal /torrents/orphans routes registered
before /{bangumi_id}/torrents
WebUI:
- Others card on the bangumi list with orphan count badge
- Torrent list pages (per-bangumi and orphans) with multi-select,
batch delete, and clear-all confirm dialog
The PR's mikan_parser and rss/analyser fixes are not ported: 3.3-dev
already handles failed poster downloads and missing elements.
Co-authored-by: zhushanwen321 <zhushanwen321@users.noreply.github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
Review findings on #1056, verified against upstream NexusPHP source:
- torrentrss.php only recognizes per-category flags (cat402=1); the
bare cat=402 form was silently ignored. Emit cat<ID>=1 and accept
comma-separated IDs.
- Validate that category IDs are numeric, with a warning and Apply
gating in the dialog.
- Stock NexusPHP hardcodes $searchstr = null (search unsupported), so
the template only filters on sites/forks that kept search support —
the hint now tells users to verify this on their site instead of
implying it always works.
- Mask the passkey in the dialog's URL preview (stored URL unchanged).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
- The login button was type="button", so the form had no submit button
and Enter in the password field did nothing (no implicit submission
with two inputs). The button is now the form's submit button and the
handler guards re-entry.
- Auto-trigger the passkey prompt on the login page for browsers that
have successfully used a passkey before (hasPasskey localStorage
flag, set on passkey login/registration). A cancelled or unavailable
silent ceremony (DOMException) disarms the flag so a stale entry
can't doom every visit; API errors keep it (interceptor already
toasts). An explicit logout suppresses the next auto-prompt via
sessionStorage so a reflexive Touch ID confirm can't log straight
back in.
- Extract the shared withSetup test helper instead of a third copy.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
Verified against qB source at release tags 4.6.7/5.0.0/5.1.0/5.2.0:
- torrents/add: accept qB 5.2 JSON response (200/202) — partial success
and pending URL fetches count as ADDED, matching legacy "Ok."
semantics; 409 Conflict is DUPLICATE for synchronous file uploads and
hash-confirmed magnets, otherwise raised for retry. Send both paused
and stopped form params (renamed in 5.0, each era ignores the other).
- torrents/pause|resume: renamed to stop|start in qB 5.0 with no
aliases; try the modern name first, fall back on 404, remember which
era worked.
- torrents/info: filter=paused was renamed and unknown filter values
silently mean All — filter paused/stopped client-side by state.
- torrents/delete, torrents/renameFile: treat any 2xx as success
(qB 5.2 returns 204 for empty-body responses).
- webui: map qB 5.0+ stoppedDL/stoppedUP states to the paused label.
- .gitignore: exclude local runtime data (backend/data), agent
workspace (.claude), and task notes (tasks).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pz1EmcTPzjgEpkGQWzb8AQ
Add PT (private-tracker) support via the existing RSS-based search
pipeline. The Add Search Provider dialog gains a "PT Site (NexusPHP)"
mode that builds a torrentrss.php search template from base URL +
passkey + optional category ID (e.g. Audiences.me anime = 402), so
search -> subscribe -> auto-download -> rename works end-to-end on
NexusPHP trackers with no backend changes.
- webui/src/utils/nexusphp.ts: URL-template builder (vitest-covered)
- config-search-provider.vue: segmented custom/NexusPHP add dialog
with live URL preview; en + zh-CN strings
- backend test_nexusphp_rss.py: regression tests proving NexusPHP
torrentrss output (enclosure and linktype=dl variants) parses
through the generic rss_parser path
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JhTduCYBGeHETU7dSWoh37
- Nest ab-edit-rule's delete confirmation inside the main ab-modal so
headlessui suspends the outer dialog's Escape/outside-click while the
inner one is open (clicking the delete-files checkbox no longer
closes both layers); regression-tested with a nested-modal vitest.
- Restore autocomplete passthrough (ab-input prop + ab-setting wiring)
so downloader/proxy password fields keep autocomplete=off.
- Guarantee 44px touch targets for ab-button sm on touch; repoint or
remove the dead .n-button deep selectors in wizard/config/downloader/
log left over from the sweep.
- a11y labels: modal X and search closes announce 'Close', tag remove
announces 'Remove' (new common.close/common.remove i18n pairs);
ab-field renders label[for] only when the control adopts the id,
span + aria-labelledby otherwise.
- Convert the last NPopconfirms (passkey/search-provider/update-card)
to useConfirm with danger icon buttons; drop legacy
.notification-action CSS that fought ab-button variants.
- Fix missing homepage.rule.offset key (→ episode_offset), normalize
test names to the should-convention, run prettier over the branch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
- ab-status: back to the ring form (thin border circle, lamp inside)
per review; keeps the 4-state API and pulse.
- Select widths: drop the competing 140px floors in ab-setting and
ab-select; ab-field's 200px desktop floor now applies, so config
dropdowns stop collapsing to arrow-width.
- Modal actions standardized into ab-modal's footer slot (right-
aligned, consistent padding): search-provider, notification,
passkey dialogs; cancel buttons demoted from danger to secondary.
- ab-add-rss and ab-edit-rule converted from hand-rolled dialogs to
ab-modal (focus trap, unified header/footer; stray divider above
the add button gone); edit-rule's inner delete dialog is a nested
ab-modal, enable-rule dialog gains a proper footer.
- ab-modal: showClose prop + bare mode — bodyless confirms render
compact (no empty body strip, no doubled hairlines, no X); the
notification provider row uses ab-icon-button with a red remove.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
Migrate the remaining surfaces: all seven ab-popup call sites to
ab-modal (footer slots, sm sizing), every ab-label to ab-field, all
raw ab-input-shortcut inputs to the ab-input component (textareas get
local Soft Ink styles), rss mobile list to ab-list with loading
skeleton, and de-tint the hand-rolled warning callouts (hairline +
ink, per the no-tinted-boxes rule). Wire aria-live into pull-refresh.
Delete dead code: ab-popup, ab-adaptive-modal, ab-label, ab-data-list,
ab-add, ab-button-multi, ab-image, ab-rule, ab-swipe-container (+
orphaned stories); drop the ab-input/input-error UnoCSS shortcuts.
Add stories for the full universal set and document the final
22-component catalog in DESIGN.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs
Sweep every NButton and styled bare button to ab-button /
ab-icon-button (variant map: primary→primary, secondary→secondary,
error→danger with hover escalation); route the five NPopconfirm sites
and the config leave-guard through useConfirm(); migrate ab-tag call
sites to semantic types (primary→info, active→success, warn→danger,
inactive→neutral); wire ab-badge into the notification bell and
ab-progress into the downloader table; first two ab-popup call sites
moved to ab-modal.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAxVyRwrY7z5NotTtgnwVs