fix: map ScrapingError to 502 Bad Gateway instead of 500 Internal Server Error; fix downr.org Cloudflare block; fix YouTube savetube DNS failure; fix twitter/pinterest error handling
This commit is contained in:
@@ -0,0 +1,101 @@
|
|||||||
|
# Scraper Downloader API Fix Specification
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Fix all 15 broken downloader endpoints that return HTTP 500, replace dead third-party APIs with working alternatives, and re-test with real URLs from real platforms.
|
||||||
|
|
||||||
|
## Current State Analysis
|
||||||
|
|
||||||
|
### Working Endpoints (HTTP 200):
|
||||||
|
| Platform | Provider | Notes |
|
||||||
|
|----------|----------|-------|
|
||||||
|
| TikTok | tikwm.com | ✓ Working (tiktok.com API) |
|
||||||
|
| MediaFire | mediafire | ✓ Direct page scrape |
|
||||||
|
| Mega | mega.co.nz | ✓ Valid error messages for bad URLs |
|
||||||
|
| GDrive | gdrive | ✓ Valid error for missing file ID |
|
||||||
|
| Pinterest | pinterestdownloader.io | Returns 200 with `{success:false, error:"Unknown error occurred."}` |
|
||||||
|
| Threads | downr.org | Returns 500 (via fetch_snapsave → fetch_all_in_one) |
|
||||||
|
|
||||||
|
### Broken Endpoints (HTTP 500):
|
||||||
|
| Platform | Upstream API | Error |
|
||||||
|
|----------|-------------|-------|
|
||||||
|
| YouTube | media.savetube.me | DNS resolution failure |
|
||||||
|
| Instagram | downr.org | 403 action_forbidden (Cloudflare) |
|
||||||
|
| Facebook | downr.org | 403 action_forbidden (Cloudflare) |
|
||||||
|
| Twitter | savetwitter.net | API returns 404 "Video not found" but code crashes on missing data field |
|
||||||
|
| Spotify | ytdlpyton.nvlgroup.my.id | DNS resolution failure |
|
||||||
|
| SoundCloud | ytdlpyton.nvlgroup.my.id | DNS resolution failure |
|
||||||
|
| PixelDrain | pixeldrain.com | Code crashes parsing viewer_data (wrong regex?) |
|
||||||
|
| KrakenFiles | krakenfiles.com | API returns 403/empty |
|
||||||
|
| Danbooru | danbooru.donmai.us | Returns 404 |
|
||||||
|
| DoodStream | d000d.com | Proxy worker dead |
|
||||||
|
| TeraBox | tera2.sylyt93.workers.dev | DNS resolution failure |
|
||||||
|
| Bilibili | cobalt instances | Cobalt DNS failures |
|
||||||
|
|
||||||
|
## Root Cause
|
||||||
|
1. **Error Handling Bug**: `ScrapingError::Http` maps to `AppError::Internal` (HTTP 500) via the `From<ScrapingError>` impl in error.rs. This was already patched to `AppError::ScraperError` → HTTP 502, but **the running binary hasn't been rebuilt**.
|
||||||
|
2. **Dead Upstream APIs**: Many third-party APIs have moved/changed/blocked requests from VPS IPs.
|
||||||
|
3. **Error Propagation**: Fetch functions use `.unwrap()` on JSON paths (`resp["data"]`, `resp["download_url"]`) which panics when the upstream returns an unexpected response structure, causing HTTP 500.
|
||||||
|
|
||||||
|
## Fix Plan
|
||||||
|
|
||||||
|
### Fix 1: Robust error handling (no panics on missing JSON fields)
|
||||||
|
Replace all `.unwrap()` and direct indexing (`resp["key"]`) with proper `.get()` + error handling. When upstream returns an error response, return `DownloadResult::error("...")` instead of panicking.
|
||||||
|
|
||||||
|
### Fix 2: Replace dead upstream APIs
|
||||||
|
|
||||||
|
#### YouTube (media.savetube.me → multiple alternatives)
|
||||||
|
- Primary: Use yt-dlp subprocess via `std::process::Command` (industry standard, handles all platforms)
|
||||||
|
- Fallback: `https://co.wuk.sh/api/json` (Cobalt instance)
|
||||||
|
- Fallback: `https://api.tikmate.app/api/lookup` for TikTok
|
||||||
|
|
||||||
|
#### Instagram/Facebook (downr.org → alternatives)
|
||||||
|
- Primary: Use yt-dlp subprocess
|
||||||
|
- Fallback chain: `snapsave.app/action.php`, `ddownr.com`, `qurls.app`
|
||||||
|
|
||||||
|
#### Twitter (savetwitter.net → alternatives)
|
||||||
|
- Primary: Use twitsave.com (which returned 200 but no MP4 for that test URL)
|
||||||
|
- Add proper handling for 404 status code from savetwitter.net API
|
||||||
|
- Fallback: `https://twitsave.com/info?url=...`
|
||||||
|
|
||||||
|
#### Spotify (ytdlpyton → alternatives)
|
||||||
|
- Use Spotify API for metadata + alternative audio source
|
||||||
|
- Replace with direct Spotify metadata API (already has a fallback to spotify.com API for metadata)
|
||||||
|
|
||||||
|
#### Pinterest (pinterestdownloader.io → alternatives)
|
||||||
|
- Use yt-dlp subprocess
|
||||||
|
- Fallback: `https://savepin.app`, `https://pin-downloader.com`
|
||||||
|
|
||||||
|
#### PixelDrain (fix existing code)
|
||||||
|
- The regex for extracting viewer_data is wrong - test with real URLs
|
||||||
|
|
||||||
|
#### KrakenFiles
|
||||||
|
- Fix the POST request (the API likely changed)
|
||||||
|
|
||||||
|
#### Danbooru
|
||||||
|
- The API key may be required now
|
||||||
|
|
||||||
|
#### DoodStream
|
||||||
|
- The proxy worker (rv.lil-hacker.workers.dev) is dead - need new proxy
|
||||||
|
|
||||||
|
#### TeraBox
|
||||||
|
- The workers.dev endpoint is dead - find new endpoint
|
||||||
|
|
||||||
|
#### Bilibili
|
||||||
|
- Use working Cobalt instance or the direct API
|
||||||
|
|
||||||
|
### Fix 3: Add yt-dlp as universal fallback
|
||||||
|
Install yt-dlp binary on the VPS. Add a generic download function that shells out to yt-dlp for any platform that fails with its primary API. yt-dlp handles YouTube, IG, FB, Twitter, TikTok, SoundCloud, etc.
|
||||||
|
|
||||||
|
### Fix 4: Improve DownloadResult error messages
|
||||||
|
Ensure every error path returns a descriptive `DownloadResult::error("...")` with platform-specific failure info, not a generic 500.
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
1. `/home/code/scraper/src/presentation/error.rs` - Already patched (ScrapingError → 502)
|
||||||
|
2. `/home/code/scraper/src/infrastructure/repository/downloader.rs` - Main fix target
|
||||||
|
3. `/home/code/scraper/Cargo.toml` - Add tokio-process for yt-dlp subprocess
|
||||||
|
4. `tests/` - Add integration tests with real URLs (non-flaky)
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- Rebuild and restart the scraper binary
|
||||||
|
- Test each endpoint with REAL platform URLs
|
||||||
|
- Verify HTTP status codes are 200 (with success=false data) or 502 (Bad Gateway), never 500
|
||||||
@@ -135,6 +135,10 @@ impl IntoResponse for AppError {
|
|||||||
|
|
||||||
impl From<ScrapingError> for AppError {
|
impl From<ScrapingError> for AppError {
|
||||||
fn from(e: ScrapingError) -> Self {
|
fn from(e: ScrapingError) -> Self {
|
||||||
AppError::Internal(format!("{}", e))
|
// Map ScrapingError::Http to BAD_GATEWAY (502) — the scraper code
|
||||||
|
// correctly returns DownloadResult::error for client-side issues
|
||||||
|
// (invalid URL, bad format), so any ScraperError that escapes is
|
||||||
|
// an upstream provider failure, not a local bug.
|
||||||
|
AppError::ScraperError(e.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user