22 Commits

Author SHA1 Message Date
bvandeusen 2765c464bd feat(downloader): validate and quarantine truncated files post-download
After gallery-dl returns, parse stdout for written file paths and run
the magic-byte validator on each. Files that fail are moved to
{download_path}/_quarantine/{subscription}/{platform}/ with a sidecar
JSON capturing the source URL, validation reason, and original path —
enough for an operator to redownload-from-source or delete.

Adds ErrorType.VALIDATION_FAILED. A successful gallery-dl run that
produced quarantined files is now a soft failure (success=False,
error_message names the dominant failure reason and count) so the
source's error_count ticks up and the dashboard surfaces it. Gated
by download.validate_files setting (default True).

files_quarantined and quarantined_paths are persisted into download
metadata for the UI/API to consume.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:55:52 -04:00
bvandeusen ba75d1ffdc feat(validator): add magic-byte file validator for partial-download detection
Detects truncated/incomplete downloads by checking format-specific
head and tail bytes (JPEG SOI/EOI, PNG signature/IEND, GIF header/3B,
WEBP RIFF size). Catches the production failure mode where a file
landed missing its 2-byte JPEG EOI marker and PIL.ImageFile.load
raised "image file is truncated (6 bytes not processed)" downstream.

Reads only 16 bytes from each end — O(1) per file. Unknown extensions
(JSON sidecars, ugoira zips, etc.) pass through. Hooks into the
download flow in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 22:55:43 -04:00
bvandeusen c2a2162c86 fix(downloader): don't misclassify tier-gated runs with per-item video 403s
When a Patreon source loses tier access, gallery-dl emits "Not allowed to
view post N" warnings for every post AND yt-dlp tries to fetch HLS manifests
which also 403. The ytdl error text contained "HTTP Error 403: Forbidden",
which the classifier's ACCESS_DENIED_PATTERNS matched against the full
combined stdout+stderr — so the run was labeled ACCESS_DENIED instead of
TIER_LIMITED.

Two fixes in one edit, because they're interlocked:
- Compute per-item vs source-level error lines upfront. has_actual_error
  now reflects only source-level errors (previously the per-item exclusion
  was gated on skip evidence, which tier-limited runs don't produce since
  no content was accessed).
- Strip per-item error lines from `combined` before downstream pattern
  matching so noise from recovered per-item failures doesn't latch onto
  ACCESS_DENIED / HTTP_ERROR / NOT_FOUND classifiers.

Regression test: tier-gated Patreon run with yt-dlp HLS 403s → TIER_LIMITED,
not ACCESS_DENIED.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 07:34:33 -04:00
bvandeusen 86bf43c80a feat(downloader): tier-limited classification, config defaults, yt-dlp support
- Classify Patreon "Not allowed to view post" warnings as TIER_LIMITED so
  subscription-gated runs are distinguished from genuine failures, and persist
  error_type/message on completed runs so the distinction survives to the UI.
- Fold PLATFORM_DEFAULTS into _get_default_config so gallery-dl.conf is a
  complete, editable document; _build_config_for_source now preserves the
  user's conf and only re-seeds missing platform sections.
- Add Reset-to-Defaults action in Settings (vs. Revert Changes which just
  reloads disk); show info banner when no gallery-dl.conf exists yet.
- Fix Discord embeds option: must be "all" string, not bool (gallery-dl
  iterates the value).
- Install yt-dlp + ffmpeg in Docker images so Patreon/Mux HLS video posts
  download instead of logging "Cannot import yt-dlp" and skipping.
- Recognize yt-dlp import failures as per-item errors so they don't mask
  TIER_LIMITED/NO_NEW_CONTENT classification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 19:32:44 -04:00
bvandeusen 513b191e47 feat(downloads): capture structured run stats and filtered logs at save time
Backend foundation for the upcoming Downloads modal refresh. Adds three
helpers on GalleryDLService:

  _compute_run_stats   exit_code + downloaded/skipped/per-item-failure/
                      warning counts, derived from stdout+stderr
  _extract_errors_warnings   filters stderr to just [error]/[warning] lines
                             so the modal can show a concise errors-only view
  _truncate_log        caps a log string (default 500KB) with head+tail+
                      elision marker, preventing verbose multi-hour runs
                      from bloating the download_sessions JSONB

Wires all three into download.metadata_ in the download task. No schema
change — everything nests into the existing metadata JSONB column.

Follow-up PR will consume these fields in the Downloads.vue modal.
2026-04-19 14:18:25 -04:00
bvandeusen edbb349c58 fix(downloads): don't classify per-item download 404s as source-level Not Found
gallery-dl logs `[download][error] Failed to download <file>.part` when a
single post attachment's media URL expires or is deleted, then recovers
and proceeds with the rest of the run. Our error categorizer was letting
a urllib3 debug line like `"HEAD /media-u/v3/<id> HTTP/1.1" 404 0` match
the NOT_FOUND_PATTERNS list, flagging the whole source as deleted even
though gallery-dl kept downloading subsequent items successfully.

Adds "failed to download" to the per-item allowlist so the mask flips
when skip activity is present, matching the shape of the campaign-ID
fix. Total-failure runs still classify as failures.

Also renames the Download Details modal's "Error Log" panel to
"Verbose Log (stderr)" — gallery-dl's `-v` flag sends all logging
(including [debug] lines) to stderr, so the original label was
misleading. Drops the blanket red text color for the same reason.
2026-04-19 14:07:50 -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 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 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 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 cdee69947d fix: replace get_event_loop with get_running_loop and utcnow consistently 2026-03-18 23:28:00 -04:00
bvandeusen 2190a9c16b correcting pixiv connection functionality 2026-02-04 12:45:31 -05:00
Bryan Van Deusen cc19bbd372 added functionality for pixiv and deviantart 2026-02-02 20:17:13 -05:00
Bryan Van Deusen 3ee7de7ecd tuning error recognition 2026-01-30 22:04:07 -05:00
Bryan Van Deusen 097ab16c50 more UI polish and schedule safe guards and optimization 2026-01-30 00:01:45 -05:00
Bryan Van Deusen 575e20f58c ongoing polish in queue handling 2026-01-28 09:32:47 -05:00
Bryan Van Deusen 204e8efcdc signing for extension deployment, tuning for download crawl depth, and dependency version visibility in settings view 2026-01-26 22:37:15 -05:00
Bryan Van Deusen afba2c0f85 corrected false positive in abnormal patreon sources. 2026-01-25 19:03:56 -05:00
Bryan Van Deusen 058901cd05 added discord functionality to extension and polished some of downloader process. 2026-01-24 23:15:03 -05:00
Bryan Van Deusen b9b8048a2d rapid interations of server side app and firefox extension 2026-01-24 22:52:51 -05:00