Files
ohm_streaming/.sisyphus/plans/anime-sama-player-fallback.md
T
2026-02-28 09:22:57 +00:00

23 KiB

Anime-Sama Player Fallback System

TL;DR

Quick Summary: Implement automatic player fallback for Anime-Sama downloads to handle cases where the detected player fails

Deliverables:

  • get_download_link_with_fallback() method in AnimeSamaDownloader
  • Player success validation via chunk download test
  • Player caching for performance optimization

Estimated Effort: Medium Parallel Execution: NO - sequential implementation Critical Path: Test implementation → AnimeSamaDownloader update → Integration testing


Context

Original Request

User requested a new feature for Anime-Sama provider: ability to change video player on the site. When a player (like Lpayer) fails, the downloader should automatically test different players until finding one that works.

Interview Summary

Key Decisions:

  • Mode: Automatic - if Lpayer fails, try VidMoly, SendVid, Sibnet, etc. automatically
  • Success Criterion: Download test (download first 10KB chunk to verify URL works)
  • Priority Order: VidMoly → SendVid → Sibnet → Lpayer

Technical Requirements:

  • Test video URL by downloading small chunk (10KB)
  • If successful, consider player working
  • Cache working player per anime/series for future episodes
  • Automatic retry without user intervention

Work Objectives

Core Objective

Implement automatic player fallback in Anime-Sama downloader to handle failed extractions by trying alternative players sequentially.

Concrete Deliverables

  • AnimeSamaDownloader.get_download_link_with_fallback() - Main fallback method
  • _test_video_url() - Helper to validate video URL by downloading chunk
  • Player priority list with caching mechanism
  • Updated get_download_link() to use fallback by default

Definition of Done

  • Fallback method tries players in priority order
  • Video URL validated before returning (10KB download test)
  • Working player cached per anime for performance
  • All existing Anime-Sama functionality preserved

Must Have

  • Players tested sequentially: VidMoly → SendVid → Sibnet → Lpayer
  • Success detection via HTTP 200 + valid data download (10KB chunk)
  • Cache mechanism to avoid re-testing for same anime
  • Automatic integration with existing download flow

Must NOT Have (Guardrails)

  • NO frontend changes required (backend-only implementation)
  • NO manual player selection via API (automatic only)
  • NO changes to other anime sites (Anime-Sama only)
  • NO breaking changes to existing Anime-Sama functionality

Verification Strategy (MANDATORY)

ZERO HUMAN INTERVENTION — ALL verification is agent-executed. No exceptions.

Test Decision

  • Infrastructure exists: YES
  • Automated tests: Tests-after (unit tests for fallback logic)
  • Framework: pytest

QA Policy

Every task MUST include agent-executed QA scenarios (see TODO template below).

  • Unit Tests: pytest with mocked HTTP clients
  • Integration Tests: Test with real Anime-Sama URLs
  • Edge Cases: All players failing, first player working, cache invalidation

Execution Strategy

Sequential Implementation

Since this is a focused feature on a single file, implementation will be sequential:

Step 1: Add URL validation helper (can test independently)
  → _test_video_url(url) method

Step 2: Implement fallback logic
  → get_download_link_with_fallback() method

Step 3: Integrate with existing flow
  → Update get_download_link() to use fallback

Step 4: Add unit tests
  → Test fallback logic and URL validation

Step 5: Integration testing
  → Test with real Anime-Sama URLs (Frieren S2 E1)

Dependency Matrix

  • 1: — 2
  • 2: — 3
  • 3: — 4, 5
  • 4: — 5
  • 5: Final

Agent Dispatch Summary

  • 1: quick - Helper method
  • 2: quick - Main fallback logic
  • 3: quick - Integration
  • 4: quick - Unit tests
  • 5: quick - Integration testing

TODOs

  • 1. Add video URL validation helper method

    What to do:

    • Add _test_video_url(url: str) -> bool method to AnimeSamaDownloader
    • Download first 10KB of video using self.client
    • Return True if HTTP 200 and valid data received, False otherwise
    • Include timeout handling (10 seconds for 10KB)
    • Log validation results for debugging

    Must NOT do:

    • Download entire video
    • Change existing player extraction logic

    Recommended Agent Profile:

    Category: quick

    • Reason: Simple helper method, focused task
    • Skills: None needed
    • Skills Evaluated but Omitted:
      • No additional skills needed

    Parallelization:

    • Can Run In Parallel: NO - Sequential
    • Parallel Group: Sequential
    • Blocks: Task 2
    • Blocked By: None

    References (CRITICAL):

    The executor has NO context from your interview. References are their ONLY guide. Each reference must answer: "What should I look at and WHY?"

    Pattern References (existing code to follow):

    • app/downloaders/anime_sites/animesama.py:120-150 - Existing video URL extraction methods
    • app/downloaders/anime_sites/animesama.py:402-445 - Existing Lplayer extraction pattern

    API/Type References (contracts to implement against):

    • httpx.AsyncClient.stream() - For downloading chunks efficiently

    External References (libraries and frameworks):

    • httpx docs: https://www.python-httpx.org/advanced/#streaming-responses - Chunked downloads

    WHY Each Reference Matters:

    • Existing extraction methods show how video URLs are currently handled
    • httpx streaming allows efficient chunk download without loading full video

    Acceptance Criteria:

    AGENT-EXECUTABLE VERIFICATION ONLY — No human action permitted. Every criterion MUST be verifiable by running a command or using a tool.

    • _test_video_url() method added to AnimeSamaDownloader
    • Downloads first 10KB chunk with 10s timeout
    • Returns True if HTTP 200 and data > 0 bytes
    • Returns False if timeout, error, or empty response
    • Logs validation results (success/failure)

    QA Scenarios (MANDATORY — task is INCOMPLETE without these):

    Scenario: Valid video URL returns 200 OK
      Tool: Bash (python3)
      Preconditions: Mock a video URL that returns 200 with data
      Steps:
        1. python3 -c "from app.downloaders.anime_sites.animesama import AnimeSamaDownloader; d = AnimeSamaDownloader(); result = d._test_video_url('https://example.com/video.mp4'); print(f'Result: {result}')"
      Expected Result: Returns True
      Evidence: .sisyphus/evidence/task-1-valid-url.txt
    
    Scenario: Invalid video URL times out
      Tool: Bash (python3)
      Preconditions: Mock a video URL that times out
      Steps:
        1. python3 -c "from app.downloaders.anime_sites.animesama import AnimeSamaDownloader; d = AnimeSamaDownloader(); result = d._test_video_url('https://httpbin.org/delay/20'); print(f'Result: {result}')"
      Expected Result: Returns False (timeout)
      Evidence: .sisyphus/evidence/task-1-timeout-url.txt
    

    Evidence to Capture:

    • Each evidence file named: task-{N}-{scenario-slug}.txt
    • Contains test results with True/False output

    Commit: YES

    • Message: feat(anime-sama): add video URL validation helper method
    • Files: app/downloaders/anime_sites/animesama.py

  • 2. Implement player fallback logic with priority list

    What to do:

    • Add get_download_link_with_fallback(url, target_filename=None, anime_page_url=None, episode_title=None) method
    • Define player priority list: ['vidmoly', 'sendvid', 'sibnet', 'lpayer']
    • For each player in priority order:
      • Try existing extraction methods (_extract_from_vidmoly, etc.)
      • If extraction succeeds, validate URL with _test_video_url()
      • If validation succeeds, return (video_url, filename)
    • Add player caching: self._working_players = {} dict to cache working player per anime URL
    • If cached player exists for anime, try it first
    • Log each attempted player with success/failure

    Must NOT do:

    • Modify existing extract_from* methods
    • Break existing Anime-Sama download flow

    Recommended Agent Profile:

    Category: quick

    • Reason: Sequential logic implementation, clear requirements
    • Skills: None needed
    • Skills Evaluated but Omitted:
      • No additional skills needed

    Parallelization:

    • Can Run In Parallel: NO - Depends on Task 1
    • Parallel Group: Sequential
    • Blocks: Task 3
    • Blocked By: Task 1

    References (CRITICAL):

    Pattern References (existing code to follow):

    • app/downloaders/anime_sites/animesama.py:95-170 - VidMoly extraction pattern
    • app/downloaders/anime_sites/animesama.py:280-320 - SendVid extraction pattern
    • app/downloaders/anime_sites/animesama.py:250-280 - Sibnet extraction pattern
    • app/downloaders/anime_sites/animesama.py:402-445 - Lpayer extraction pattern
    • app/downloaders/anime_sites/animesama.py:117-120 - Player detection logic

    WHY Each Reference Matters:

    • Existing extraction methods show the interface each player uses
    • Player detection logic shows how to identify which player URL to extract
    • Need to understand the signature of each extraction method

    Acceptance Criteria:

    • get_download_link_with_fallback() method added
    • Player priority list defined: vidmoly → sendvid → sibnet → lpayer
    • Tries each player in order if previous fails
    • Validates video URL with _test_video_url() before returning
    • Caches working player per anime_page_url
    • Logs each player attempt (success/failure)
    • Returns (video_url, filename) on first success
    • Raises exception if all players fail

    QA Scenarios (MANDATORY — task is INCOMPLETE without these):

    Scenario: First player (VidMoly) works
      Tool: Bash (python3)
      Preconditions: Mock VidMoly URL that passes validation
      Steps:
        1. python3 -c "
    

from app.downloaders.anime_sites.animesama import AnimeSamaDownloader d = AnimeSamaDownloader()

Mock _test_video_url to return True

original_test = d._test_video_url d._test_video_url = lambda url: True

Call fallback

video_url, filename = d.get_download_link_with_fallback('test_url|anime_page|Ep1', episode_title='Episode 1') print(f'Video URL: {video_url[:50] if video_url else None}') print(f'Filename: {filename}') " Expected Result: Returns VidMoly URL, logs "VidMoly player succeeded" Evidence: .sisyphus/evidence/task-2-first-works.txt

Scenario: First player fails, second works Tool: Bash (python3) Preconditions: Mock VidMoly to fail, SendVid to succeed Steps: 1. python3 -c " from app.downloaders.anime_sites.animesama import AnimeSamaDownloader d = AnimeSamaDownloader() call_count = [0] def mock_extract(*args, **kwargs): call_count[0] += 1 if call_count[0] == 1: # VidMoly call raise Exception('VidMoly failed') elif call_count[0] == 2: # SendVid call return ('https://sendvid.com/video.mp4', 'sendvid_video.mp4')

Mock extraction methods

d._extract_from_vidmoly = lambda *a, **kw: mock_extract(*a, **kw) d._extract_from_sendvid = lambda *a, **kw: mock_extract(*a, **kw) d._test_video_url = lambda url: True

Call fallback

video_url, filename = d.get_download_link_with_fallback('test_url|anime_page|Ep1') print(f'Video URL: {video_url}') print(f'Used player: {"SendVid" if "sendvid" in video_url else "Unknown"}') " Expected Result: Returns SendVid URL (VidMoly failed, SendVid succeeded) Evidence: .sisyphus/evidence/task-2-second-works.txt

Scenario: All players fail Tool: Bash (python3) Preconditions: Mock all extractions to fail Steps: 1. python3 -c " from app.downloaders.anime_sites.animesama import AnimeSamaDownloader d = AnimeSamaDownloader() def mock_fail(*args, **kwargs): raise Exception('Player failed')

Mock all extraction methods

d._extract_from_vidmoly = mock_fail d._extract_from_sendvid = mock_fail d._extract_from_sibnet = mock_fail d._extract_from_lpayer = mock_fail

Call fallback

try: video_url, filename = d.get_download_link_with_fallback('test_url|anime_page|Ep1') print('ERROR: Should have raised exception') except Exception as e: print(f'Exception raised: {e}') " Expected Result: Raises exception "All video players failed" Evidence: .sisyphus/evidence/task-2-all-fail.txt


**Evidence to Capture**:
- [ ] Each evidence file contains video URL and logs output
- [ ] Test confirms fallback logic works correctly

**Commit**: YES
- Message: `feat(anime-sama): add player fallback logic with priority retry`
- Files: `app/downloaders/anime_sites/animesama.py`

---

- [x] 3. Integrate fallback into existing get_download_link() method

**What to do**:
- Update `get_download_link()` to use `get_download_link_with_fallback()` by default
- Maintain backward compatibility: if direct video URL detected, skip fallback
- Pass anime_page_url and episode_title from pipe-separated URL format
- Keep existing player detection and direct extraction flow for simple cases

**Must NOT do**:
- Remove existing extraction methods
- Change existing player detection logic

**Recommended Agent Profile**:
> **Category**: `quick`
  - Reason: Integration task, minimal changes
- **Skills**: None needed
- **Skills Evaluated but Omitted**:
  - No additional skills needed

**Parallelization**:
- **Can Run In Parallel**: NO - Depends on Task 2
- **Parallel Group**: Sequential
- **Blocks**: Task 4, 5
- **Blocked By**: Task 2

**References** (CRITICAL):

**Pattern References** (existing code to follow):
- `app/downloaders/anime_sites/animesama.py:93-120` - Current get_download_link implementation

**WHY Each Reference Matters**:
- Need to understand current logic to integrate fallback without breaking it
- Player detection and pipe URL parsing must be preserved

**Acceptance Criteria**:

- [ ] `get_download_link()` calls `get_download_link_with_fallback()` for complex URLs
- [ ] Direct video URLs (no pipe format) skip fallback (performance)
- [ ] Pipe-separated URLs trigger fallback with anime_page_url and episode_title
- [ ] Existing Anime-Sama functionality preserved (VidMoly, SendVid, Sibnet, Lpayer)
- [ ] Backward compatible with existing download flow

**QA Scenarios (MANDATORY — task is INCOMPLETE without these):**

Scenario: Pipe URL triggers fallback Tool: Bash (python3) Preconditions: Anime-Sama downloader with fallback method Steps: 1. python3 -c " from app.downloaders.anime_sites.animesama import AnimeSamaDownloader d = AnimeSamaDownloader()

Mock to test that fallback is called

fallback_called = [False] original_fallback = d.get_download_link_with_fallback def mock_fallback(*args, **kwargs): fallback_called[0] = True return original_fallback(*args, **kw) d.get_download_link_with_fallback = mock_fallback

Call with pipe URL

d.get_download_link('https://vidmoly.to/vid|https://anime-sama.si/cat/naruto/s1|Episode+1') print(f'Fallback called: {fallback_called[0]}') " Expected Result: Fallback method is called (True) Evidence: .sisyphus/evidence/task-3-pipe-url.txt

Scenario: Direct video URL skips fallback Tool: Bash (python3) Preconditions: Anime-Sama downloader with fallback method Steps: 1. python3 -c " from app.downloaders.anime_sites.animesama import AnimeSamaDownloader d = AnimeSamaDownloader()

Mock to test that fallback is NOT called

fallback_called = [False] def mock_fallback(*args, **kwargs): fallback_called[0] = True return ('https://video.mp4', 'video.mp4') d.get_download_link_with_fallback = mock_fallback

Call with direct URL (no pipe)

d.get_download_link('https://vidmoly.to/vid') print(f'Fallback called: {fallback_called[0]}') " Expected Result: Fallback method is NOT called (False) - direct extraction used Evidence: .sisyphus/evidence/task-3-direct-url.txt


**Evidence to Capture**:
- [ ] Evidence files show fallback called/not-called correctly
- [ ] Integration preserves existing functionality

**Commit**: YES
- Message: `feat(anime-sama): integrate fallback into get_download_link()`
- Files: `app/downloaders/anime_sites/animesama.py`

---

- [x] 4. Add unit tests for fallback logic

**What to do**:
- Create `tests/test_anime_sama_fallback.py`
- Test 1: Fallback tries players in priority order
- Test 2: Caching mechanism stores working player
- Test 3: All players failing raises exception
- Test 4: _test_video_url() returns True/False correctly
- Use pytest with mock_httpx_client fixture

**Must NOT do**:
- Make real HTTP requests in tests (use mocks)
- Test other anime sites (Anime-Sama only)

**Recommended Agent Profile**:
> **Category**: `quick`
  - Reason: Unit tests are straightforward
- **Skills**: None needed
- **Skills Evaluated but Omitted**:
  - No additional skills needed

**Parallelization**:
- **Can Run In Parallel**: NO - Depends on Task 3
- **Parallel Group**: Sequential
- **Blocks**: Task 5
- **Blocked By**: Task 3

**References** (CRITICAL):

**Test References** (testing patterns to follow):
- `tests/test_downloaders.py:40-70` - Mock pattern for downloaders
- `tests/conftest.py:40-50` - Mock HTTP client fixture

**WHY Each Reference Matters**:
- Mocking patterns show how to simulate HTTP responses without network calls
- Conftest fixtures provide reusable test setup

**Acceptance Criteria**:

- [ ] `tests/test_anime_sama_fallback.py` file created
- [ ] Test priority order: VidMoly → SendVid → Sibnet → Lpayer
- [ ] Test caching: working player reused for same anime
- [ ] Test _test_video_url: returns True/False correctly
- [ ] Test all players fail: exception raised
- [ ] All tests pass with pytest

**QA Scenarios (MANDATORY — task is INCOMPLETE without these):**

Scenario: Run all fallback unit tests Tool: Bash Preconditions: Tests implemented in test_anime_sama_fallback.py Steps: 1. pytest tests/test_anime_sama_fallback.py -v --tb=short Expected Result: All tests pass Failure Indicators: Any test fails, pytest exit code non-zero Evidence: .sisyphus/evidence/task-4-tests-run.txt


**Evidence to Capture**:
- [ ] pytest output shows all tests passed
- [ ] Evidence file contains test summary

**Commit**: YES
- Message: `test(anime-sama): add unit tests for player fallback logic`
- Files: `tests/test_anime_sama_fallback.py`

---

- [x] 5. Integration testing with real Anime-Sama URLs

**What to do**:
- Test Frieren S2 E1 download with fallback enabled
- Verify that fallback tries multiple players if first fails
- Check logs to see which player succeeded
- Validate that downloaded video is playable
- Test with different Anime-Sama URLs to ensure general functionality

**Must NOT do**:
- Only test with Frieren (test variety)
- Modify production code during testing

**Recommended Agent Profile**:
> **Category**: `quick`
  - Reason: Integration testing with real data
- **Skills**: `playwright` (may be needed for Lpayer)
- **Skills Evaluated but Omitted**:
  - `git-master`: Not needed for testing

**Parallelization**:
- **Can Run In Parallel**: NO - Depends on Task 4
- **Parallel Group**: Sequential
- **Blocks**: Final Verification
- **Blocked By**: Task 4

**References** (CRITICAL):

**API/Type References** (contracts to implement against):
- `/api/anime/download` - Download endpoint
- `/api/downloads` - List downloads endpoint

**WHY Each Reference Matters**:
- Need to know how to trigger downloads and check status

**Acceptance Criteria**:

- [ ] Frieren S2 E1 download completes successfully
- [ ] Logs show multiple players tried if first fails
- [ ] Downloaded video file is valid (not empty, correct extension)
- [ ] Fallback logic works without errors

**QA Scenarios (MANDATORY — task is INCOMPLETE without these):**

Scenario: Download Frieren S2 E1 with fallback Tool: Bash (curl) + Playwright Preconditions: Server running, fallback implemented Steps: 1. curl -s "http://localhost:3000/api/anime/episodes?url=https://anime-sama.si/catalogue/frieren-s1/vostfr/&lang=vostfr" | python3 -m json.tool 2. Extract first episode URL 3. curl -X POST "http://localhost:3000/api/anime/download" -H "Content-Type: application/json" -d '{"url": "EPISODE_URL|PAGE_URL|Episode+1"}' 4. curl -s "http://localhost:3000/api/downloads" | python3 -m json.tool 5. Wait for download to complete (status COMPLETED) 6. ls -lh downloads/Frieren*.mp4 2>&1 Expected Result: Download completes with status COMPLETED, video file exists with > 1MB Failure Indicators: Status FAILED, no video file, file size < 1MB Evidence: .sisyphus/evidence/task-5-frieren-download.txt


**Evidence to Capture**:
- [ ] Evidence file contains download status
- [ ] Video file exists and is playable

**Commit**: YES (if successful)
- Message: `test(anime-sama): verify fallback works with Frieren S2 E1`
- Files: `downloads/` (test artifacts)

---

## Final Verification Wave

- [ ] F1. **Unit Test Coverage** — `pytest`
Run pytest on anime-sama tests to ensure fallback logic is covered.
- Run: `pytest tests/test_anime_sama_fallback.py -v --cov=app.downloaders.anime_sites.animesama`
- Verify: All tests pass, coverage > 80% for new methods
Output: `Tests [N/N pass] | Coverage [%] | VERDICT`

- [ ] F2. **Real Download Test** — `curl` + `Bash`
Test actual download with Anime-Sama fallback enabled.
- Trigger: Download Frieren S2 E1 via API
- Verify: Download completes, fallback logs visible, file valid
Output: `Download [COMPLETE/FAILED] | Player [name] | File [size] | VERDICT`

- [ ] F3. **Log Analysis** — `Bash`
Check server logs for fallback behavior.
- Run: `tail -100 /tmp/uvicorn.log | grep -E "(LPAYER|fallback|player)"`
- Verify: Multiple player attempts logged when first fails
Output: `Attempts [N] | Success [True/False] | VERDICT`

- [ ] F4. **No Regressions** — `pytest`
Ensure existing Anime-Sama functionality still works.
- Run: `pytest tests/test_anime_sama.py -v -k "not fallback"`
- Verify: All existing tests pass
Output: `Tests [N/N pass] | VERDICT`

---

## Commit Strategy

- **1**: `feat(anime-sama): add video URL validation helper method` — `app/downloaders/anime_sites/animesama.py`
- **2**: `feat(anime-sama): add player fallback logic with priority retry` — `app/downloaders/anime_sites/animesama.py`
- **3**: `feat(anime-sama): integrate fallback into get_download_link()` — `app/downloaders/anime_sites/animesama.py`
- **4**: `test(anime-sama): add unit tests for player fallback logic` — `tests/test_anime_sama_fallback.py`
- **5**: `test(anime-sama): verify fallback works with real downloads` — `downloads/` (test artifacts)

---

## Success Criteria

### Verification Commands
```bash
# Unit tests
pytest tests/test_anime_sama_fallback.py -v

# Integration test
curl -X POST "http://localhost:3000/api/anime/download" \
-H "Content-Type: application/json" \
-d '{"url": "URL|PAGE|TITLE"}'

# Check logs
tail -50 /tmp/uvicorn.log | grep fallback

Final Checklist

  • Fallback method tries players in priority order
  • Video URLs validated before returning (10KB download test)
  • Working player cached per anime for performance
  • All unit tests pass
  • Real download test succeeds
  • No regressions in existing Anime-Sama functionality
  • All "Must Have" present
  • All "Must NOT Have" absent