Commit Graph

74 Commits

Author SHA1 Message Date
bvandeusen e9edaeb66d feat(ui): cap Dashboard Active and Recent Activity lists at 5 items
Both lists could grow long enough to dominate the viewport (21 queued
downloads was common). Limit each to 5 entries with a "+N more"
affordance: Active shows a "+N more queued" button that deep-links to
/downloads?status=queued, and Recent Activity folds the overflow count
into the existing "View all downloads" button. Seed Downloads.vue's
status filter from the URL query param so the deep-link actually filters
on arrival.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:47:38 -04:00
bvandeusen 97864efacb fix(downloads): treat recovered Patreon campaign-ID errors as non-fatal
When gallery-dl logs "[patreon][error] Failed to extract campaign ID"
but then recovers via its /cw/<vanity> fallback and skips every file
(all already archived), _categorize_error was flagging the [error] line
as fatal and returning UNKNOWN_ERROR. Add "failed to extract campaign
id" to the per-item error allowlist alongside "not allowed to view" and
"unable to get post" so skip detection proceeds and the run is
classified NO_NEW_CONTENT.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:23:41 -04:00
bvandeusen 659f1f64a4 feat(ui): use autocomplete for Downloads Source filter
Swaps v-select for v-autocomplete on the Source filter so users can
type-to-filter instead of scrolling a long list. Same data source, same
selection semantics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:12:24 -04:00
bvandeusen 0426e91a84 feat(ui): collapse Downloads filters into compact bar with popover
Replaces two stacked filter rows (4 fields + superseded switch) with a
single "Filters" button that opens a popover containing all controls.
Active filters surface as dismissable chips on the bar alongside a
"Clear all" button, so what's currently applied stays visible without
eating vertical space above the table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:10:29 -04:00
bvandeusen c5ebbe5a76 feat(ui): add legend and per-bar tooltip to Platform Health
The dual meaning of the progress bar (red = failing share, green = all
healthy) was not self-explanatory. Added a small legend beside the
section header and a hover tooltip on each bar that states "N of M
<platform> sources failing".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:08:11 -04:00
bvandeusen ae140dbb0a feat(ui): show relative timestamps on Dashboard and Downloads tables
Converts the Source Health last-check column and the Downloads created-at
column from locale date strings to human-relative times ("6 hours ago").
The absolute timestamp is preserved as a native browser tooltip so full
precision is still one hover away; detail modals keep the absolute form.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:07:19 -04:00
bvandeusen 9f8ce67084 feat(ui): group repeated failures by subscription on Dashboard recent activity
Consecutive failures on the same source no longer spam the feed as
separate rows. Failed downloads are now bucketed per subscription and
rendered as a single row with a failure count and the latest
timestamp, while successful/running entries still render individually.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 15:04:21 -04:00
bvandeusen b9c0d0efbf chore(tests): remove unused pytest import in retry tests 2026-04-18 14:44:41 -04:00
bvandeusen b57e93f1bf chore(downloads): log failed resolutions and tighten retry dataclass/tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 14:18:43 -04:00
bvandeusen 35589e996a feat(downloads): auto-heal patreon campaign-ID failures via resolver retry 2026-04-18 14:11:11 -04:00
bvandeusen 8e3da096ec feat(downloads): add patreon URL-rewrite and error-pattern helpers 2026-04-18 13:43:21 -04:00
bvandeusen 3642acc41c fix(patreon): declare aiohttp/yarl deps and use public yarl.URL import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:40:20 -04:00
bvandeusen ce8ba538b7 feat(patreon): add campaign-ID resolver service
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:22:36 -04:00
bvandeusen d819a184a1 test: remove dead anyio_backend fixture and unused pytest imports
The anyio_backend fixture is only consumed by pytest-anyio, which is not a
dependency of this project (we use pytest-asyncio with asyncio_mode=auto).
Also drop the unused pytest import from test_sanity.py.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 13:13:50 -04:00
bvandeusen 74f0086664 test: bootstrap backend pytest infrastructure with sanity tests 2026-04-18 13:07:20 -04:00
bvandeusen e70ff636ea docs: add patreon campaign-ID resolver implementation plan
Four bite-sized tasks: pytest bootstrap, resolver service, downloads.py
pure helpers, retry-hook integration. Full TDD with 35 tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:54:34 -04:00
bvandeusen 59c40ccdb9 docs: add patreon campaign-ID resolver design spec
Auto-heal "Failed to extract campaign ID" failures by resolving via
Patreon's campaigns API, caching the ID in Source.metadata_, and retrying
once within the same task run.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:46:00 -04:00
bvandeusen f3412dea57 fix(dashboard): guard failureThreshold against empty/invalid settings values
v-model.number on the Settings input returns an empty string when cleared,
which persists through the PATCH and then coerces error_count >= '' to
error_count >= 0 — flagging every source as failing. Coerce to Number and
fall back to 5 unless the value is a finite number >= 1.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:53:18 -04:00
bvandeusen 42192e82be fix(dashboard): add missing getPlatformColor helper for platform health bars
The platform-health row icon binds to getPlatformColor(row.platform) but the
helper was only present in Sources.vue/Subscriptions.vue/Credentials.vue/
Settings.vue — not copied into Dashboard.vue when the template was introduced,
so the icons next to each platform row rendered in Vuetify's default color.
Also collapse the double blank line left where sourcesWithErrors was removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:50:09 -04:00
bvandeusen f809fa552d feat(dashboard): replace recent-failures list with platform health bars + strict failing-source table 2026-04-17 23:44:42 -04:00
bvandeusen 7680f03560 refactor(dashboard): drop redundant PLATFORM_ORDER and misleading optional chain
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 23:31:29 -04:00
bvandeusen 62713cfec0 feat(dashboard): add failure-threshold computeds backed by settings store 2026-04-17 23:29:02 -04:00
bvandeusen 4ee78ef859 feat(settings-ui): expose dashboard failure threshold 2026-04-17 23:24:13 -04:00
bvandeusen 14682fc20c feat(settings): add dashboard.failure_threshold default (5) 2026-04-17 23:06:12 -04:00
bvandeusen 9ab94eb1f9 docs: add failure display implementation plan
4-task plan: backend default setting, Settings page input,
Dashboard script computeds, Dashboard template replacement.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:01:09 -04:00
bvandeusen 38bade0320 docs: add failure display design spec
Defines "failing source" as error_count >= configurable threshold
(default 5), adds per-platform health bars and a strict failing-source
list to the Dashboard, surfaces the threshold on the Settings page.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 22:56:23 -04:00
bvandeusen a11861197c fix(deps): pin gallery-dl>=1.31.10 to resolve Patreon 426
Earlier container rolled 1.31.5, which fails Patreon fetches with
HTTP 426 Upgrade Required. 1.31.10 restores the OAuth flow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 22:43:21 -04:00
bvandeusen b03fd215a0 feat(ui): configured vs unconfigured visual treatment on Credentials cards 2026-03-19 18:33:24 -04:00
bvandeusen 0f8dbfe5c2 feat(ui): add exclude superseded failures filter toggle to Downloads
Adds a new filter toggle to the Downloads view that allows users to hide
superseded failed downloads. The filter is enabled by default and passes
exclude_superseded=true to the API when active.

Changes:
- Add filterExcludeSuperseded ref initialized to true
- Add filterExcludeSuperseded to watch dependencies
- Include exclude_superseded param in loadDownloads() when true
- Add v-switch toggle UI control below date filters

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:32:41 -04:00
bvandeusen b039f82255 fix(ui): use bg-surface on expanded subscription rows for dark mode compat 2026-03-19 18:32:03 -04:00
bvandeusen 294ccdc748 feat(ui): consistent empty states and running pulse on Dashboard
- Updated empty state messages to include descriptive icons, main text (body-1), and helper text (caption)
- Active Downloads: shows idle status with check-circle icon
- Recent Activity: shows history icon with context about failures/new downloads
- Sources Needing Attention: shows check-all icon confirming 7-day health check
- Added pulse animation to running downloads with left border animation
- Consistent styling across all empty states with py-8 padding and secondary color icons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 18:31:03 -04:00
bvandeusen b419ee8b32 feat(ui): number-forward stat cards and split action bar on Dashboard 2026-03-19 18:29:19 -04:00
bvandeusen f132c115c9 fix(ui): use div instead of button for WS status indicator 2026-03-19 18:28:14 -04:00
bvandeusen 34d4482f6d feat(ui): lift nav branding and add WebSocket status indicator 2026-03-19 18:26:46 -04:00
bvandeusen 8b189d72f4 feat(ui): replace palette with slate/navy monitoring dashboard theme 2026-03-19 18:25:19 -04:00
bvandeusen a25c332cda docs: add UI improvement pass implementation plan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 17:51:00 -04:00
bvandeusen 728a8bb529 docs: fix spec review issues in UI improvement pass spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 17:45:22 -04:00
bvandeusen fc79996468 docs: add UI improvement pass design spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:50:36 -04:00
bvandeusen 9db27ddaae chore: sane ignore files, update summary, bump base images
- Add .claude/ to .gitignore and untrack settings.local.json
- Create .dockerignore to exclude docs, extension, analysis, scripts,
  node_modules, and other noise from the Docker build context
- Dockerfile: Node 20→22-alpine, Python 3.11→3.13-slim, DEBIAN_FRONTEND
  set, pip upgrade before install
- Track frontend/package-lock.json for reproducible builds
- Update summary.md: date, db.py in project structure, superseded column
  in schema, two new key patterns (12/13), full 2026-03-19 changelog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 08:20:24 -04:00
bvandeusen 8cd0ce4ecb fix: restore AsyncSession import and fix dead regex patterns in NOT_FOUND_PATTERNS
- Added missing 'from sqlalchemy.ext.asyncio import AsyncSession' import in downloads.py
  This fixes the NameError at module load time that was breaking all download tasks

- Replaced regex patterns with plain-string equivalents in NOT_FOUND_PATTERNS:
  * 'account.*deleted' → 'account deleted' (. and * are literal in substring search)
  * 'profile.*not.*exist' → 'profile does not exist' (same issue)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 23:43:01 -04:00
bvandeusen 48d10ec21e fix: count only absolute path lines in _count_downloaded_files 2026-03-18 23:39:45 -04:00
bvandeusen 080a1254c7 fix: wrap AsyncSession in context manager across all API blueprints
Replace all bare `session = AsyncSession(bind=conn)` assignments with
`async with AsyncSession(bind=conn) as session:` in downloads.py,
credentials.py, settings.py, sources.py, and subscriptions.py to
ensure sessions are properly closed on all code paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 23:38:58 -04:00
bvandeusen dc146f4a16 refactor: extract error patterns to constants, add debug logging, fix rc1 no_new_content
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 23:37:04 -04:00
bvandeusen 6432210a36 refactor: extract shared DB helpers to tasks/db.py
Extract identical get_async_session() and cleanup_engine() functions
from downloads.py and maintenance.py into a shared db.py module.
This eliminates code duplication and improves maintainability.

db.py imports only from app.config and SQLAlchemy to avoid circular
dependencies with the task modules that import from it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 23:36:42 -04:00
bvandeusen 4eac527f0f perf: parallelize checkAllSources with Promise.allSettled 2026-03-18 23:36:33 -04:00
bvandeusen 0f2901ef58 feat: mark prior failures as superseded on successful download 2026-03-18 23:32:51 -04:00
bvandeusen 2c95374edc feat: replace _not_superseded_condition subquery with superseded column filter 2026-03-18 23:32:06 -04:00
bvandeusen 7268d06b4f feat: migration to add superseded column with backfill and partial index 2026-03-18 23:31:17 -04:00
bvandeusen f84b20269a feat: add superseded column to Download model 2026-03-18 23:30:35 -04:00
bvandeusen cdee69947d fix: replace get_event_loop with get_running_loop and utcnow consistently 2026-03-18 23:28:00 -04:00