Task 3.1: Standardize series identifiers in DownloadService
- Updated DownloadService to use 'serie_id' (provider key) for identification - Changed 'serie_folder' from Optional to required in models (DownloadItem, DownloadRequest) - Removed incorrect fallback logic that used serie_id as folder name - Enhanced docstrings to clarify purpose of each identifier field: * serie_id: Provider key (e.g., 'attack-on-titan') for lookups * serie_folder: Filesystem folder name (e.g., 'Attack on Titan (2013)') for file operations - Updated logging to reference 'serie_key' for clarity - Fixed all unit tests to include required serie_folder field - All 25 download service tests passing - All 47 download model tests passing - Updated infrastructure.md with detailed documentation - Marked Task 3.1 as completed in instructions.md Benefits: - Clear separation between provider identifier and filesystem path - Prevents confusion from mixing different identifier types - Consistent with broader series identifier standardization effort - Better error messages when required fields are missing
This commit is contained in:
parent
883f89b113
commit
e1c8b616a8
@ -1374,6 +1374,59 @@ Clients can subscribe to specific topics (rooms) to receive targeted updates:
|
|||||||
- Testing: Comprehensive unit tests in `tests/unit/test_download_service.py`
|
- Testing: Comprehensive unit tests in `tests/unit/test_download_service.py`
|
||||||
cover queue operations, persistence, retry logic, and error handling
|
cover queue operations, persistence, retry logic, and error handling
|
||||||
|
|
||||||
|
#### Series Identifier Standardization (November 2025)
|
||||||
|
|
||||||
|
**Task 3.1 Completed**: Updated DownloadService to use standardized series identifiers.
|
||||||
|
|
||||||
|
**Changes Made**:
|
||||||
|
|
||||||
|
- **serie_id Field**: Now explicitly documented as the provider key
|
||||||
|
(e.g., "attack-on-titan"). This is the unique, URL-safe identifier
|
||||||
|
used for all series lookups and identification throughout the system.
|
||||||
|
- **serie_folder Field**: Changed from Optional to required. This field
|
||||||
|
contains the filesystem folder name (e.g., "Attack on Titan (2013)")
|
||||||
|
and is used exclusively for filesystem operations.
|
||||||
|
- **Separation of Concerns**: Removed incorrect fallback logic that
|
||||||
|
used `serie_id` as a substitute for `serie_folder`. These fields now
|
||||||
|
serve distinct purposes and must both be provided.
|
||||||
|
- **Enhanced Documentation**: Updated docstrings in `add_to_queue()`
|
||||||
|
method and all Pydantic models to clarify the purpose and usage of
|
||||||
|
each identifier field.
|
||||||
|
- **Improved Logging**: Updated log statements to reference `serie_key`
|
||||||
|
for identification, making it clear which identifier is being used.
|
||||||
|
|
||||||
|
**Models Updated**:
|
||||||
|
|
||||||
|
- `DownloadItem` (src/server/models/download.py):
|
||||||
|
- `serie_id`: Required field with provider key
|
||||||
|
- `serie_folder`: Changed from Optional to required
|
||||||
|
- Both fields have enhanced field descriptions
|
||||||
|
- `DownloadRequest` (src/server/models/download.py):
|
||||||
|
- `serie_folder`: Changed from Optional to required
|
||||||
|
- Enhanced field descriptions for both identifiers
|
||||||
|
|
||||||
|
**Service Changes**:
|
||||||
|
|
||||||
|
- `DownloadService.add_to_queue()`: Updated to validate that
|
||||||
|
`serie_folder` is always provided and raises DownloadServiceError
|
||||||
|
if missing
|
||||||
|
- Removed fallback logic: `folder = item.serie_folder if item.serie_folder else item.serie_id`
|
||||||
|
- Added validation check before download execution
|
||||||
|
- Updated logging to use `serie_key` parameter name
|
||||||
|
|
||||||
|
**Testing**:
|
||||||
|
|
||||||
|
- Updated all test fixtures to include required `serie_folder` field
|
||||||
|
- All 25 download service tests passing
|
||||||
|
- All 47 download model tests passing
|
||||||
|
|
||||||
|
**Benefits**:
|
||||||
|
|
||||||
|
- Clear separation between provider identifier (key) and filesystem path (folder)
|
||||||
|
- Prevents confusion and potential bugs from mixing identifiers
|
||||||
|
- Consistent with broader series identifier standardization effort
|
||||||
|
- Better error messages when required fields are missing
|
||||||
|
|
||||||
### Download Queue API Endpoints (October 2025)
|
### Download Queue API Endpoints (October 2025)
|
||||||
|
|
||||||
Implemented comprehensive REST API endpoints for download queue management:
|
Implemented comprehensive REST API endpoints for download queue management:
|
||||||
|
|||||||
@ -171,38 +171,7 @@ For each task completed:
|
|||||||
|
|
||||||
### Phase 3: Service Layer
|
### Phase 3: Service Layer
|
||||||
|
|
||||||
#### Task 3.1: Update DownloadService to Use Key
|
✅ **Task 3.1 completed and committed to git (November 2025)**
|
||||||
|
|
||||||
**File:** [`src/server/services/download_service.py`](src/server/services/download_service.py)
|
|
||||||
|
|
||||||
**Objective:** Change `DownloadService` to use `key` as the series identifier instead of mixing `serie_id` and `serie_folder`.
|
|
||||||
|
|
||||||
**Steps:**
|
|
||||||
|
|
||||||
1. Open [`src/server/services/download_service.py`](src/server/services/download_service.py)
|
|
||||||
2. In `add_to_queue()` method:
|
|
||||||
- Rename parameter `serie_id` to `series_key` (or keep as `serie_id` but document it's the key)
|
|
||||||
- Keep `serie_folder` for filesystem operations
|
|
||||||
- Keep `serie_name` for display
|
|
||||||
- Update docstring to clarify: `serie_id` is the provider key
|
|
||||||
3. Update `DownloadItem` dataclass:
|
|
||||||
- Change `serie_id` to use `key`
|
|
||||||
- Keep `serie_folder` for file operations
|
|
||||||
4. Update all internal methods to use `key` consistently
|
|
||||||
5. Update logging to reference `key` instead of `folder`
|
|
||||||
|
|
||||||
**Success Criteria:**
|
|
||||||
|
|
||||||
- [ ] `add_to_queue()` uses `key` for identification
|
|
||||||
- [ ] `DownloadItem` uses `key` as identifier
|
|
||||||
- [ ] Filesystem operations still use `serie_folder`
|
|
||||||
- [ ] All download service tests pass
|
|
||||||
|
|
||||||
**Test Command:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
conda run -n AniWorld python -m pytest tests/unit/ -k "DownloadService" -v
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -1052,18 +1021,18 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
|
|||||||
|
|
||||||
### Completion Status
|
### Completion Status
|
||||||
|
|
||||||
- [ ] Phase 1: Core Models and Data Layer
|
- [x] Phase 1: Core Models and Data Layer
|
||||||
- [ ] Task 1.1: Update Serie Class
|
- [x] Task 1.1: Update Serie Class
|
||||||
- [ ] Task 1.2: Update SerieList
|
- [x] Task 1.2: Update SerieList
|
||||||
- [ ] Task 1.3: Update SerieScanner
|
- [x] Task 1.3: Update SerieScanner
|
||||||
- [ ] **Task 1.4: Update Provider Classes** ⭐ NEW
|
- [x] **Task 1.4: Update Provider Classes**
|
||||||
- [ ] Phase 2: Core Application Layer
|
- [x] Phase 2: Core Application Layer
|
||||||
- [ ] Task 2.1: Update SeriesApp
|
- [x] Task 2.1: Update SeriesApp
|
||||||
- [ ] Phase 3: Service Layer
|
- [ ] Phase 3: Service Layer
|
||||||
- [ ] Task 3.1: Update DownloadService
|
- [x] Task 3.1: Update DownloadService ✅ **Completed November 2025**
|
||||||
- [ ] Task 3.2: Update AnimeService
|
- [ ] Task 3.2: Update AnimeService
|
||||||
- [ ] **Task 3.3: Update ProgressService** ⭐ NEW
|
- [ ] **Task 3.3: Update ProgressService**
|
||||||
- [ ] **Task 3.4: Update ScanService** ⭐ NEW
|
- [ ] **Task 3.4: Update ScanService**
|
||||||
- [ ] Phase 4: API Layer
|
- [ ] Phase 4: API Layer
|
||||||
- [ ] Task 4.1: Update Anime API Endpoints
|
- [ ] Task 4.1: Update Anime API Endpoints
|
||||||
- [ ] Task 4.2: Update Download API Endpoints
|
- [ ] Task 4.2: Update Download API Endpoints
|
||||||
|
|||||||
@ -66,9 +66,19 @@ class DownloadItem(BaseModel):
|
|||||||
"""Represents a single download item in the queue."""
|
"""Represents a single download item in the queue."""
|
||||||
|
|
||||||
id: str = Field(..., description="Unique download item identifier")
|
id: str = Field(..., description="Unique download item identifier")
|
||||||
serie_id: str = Field(..., description="Series identifier (provider key)")
|
serie_id: str = Field(
|
||||||
serie_folder: Optional[str] = Field(
|
...,
|
||||||
None, description="Series folder name on disk"
|
description=(
|
||||||
|
"Series identifier - provider key "
|
||||||
|
"(e.g., 'attack-on-titan')"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
serie_folder: str = Field(
|
||||||
|
...,
|
||||||
|
description=(
|
||||||
|
"Series folder name on disk "
|
||||||
|
"(e.g., 'Attack on Titan (2013)')"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
serie_name: str = Field(..., min_length=1, description="Series name")
|
serie_name: str = Field(..., min_length=1, description="Series name")
|
||||||
episode: EpisodeIdentifier = Field(
|
episode: EpisodeIdentifier = Field(
|
||||||
@ -160,9 +170,19 @@ class QueueStats(BaseModel):
|
|||||||
class DownloadRequest(BaseModel):
|
class DownloadRequest(BaseModel):
|
||||||
"""Request to add episode(s) to the download queue."""
|
"""Request to add episode(s) to the download queue."""
|
||||||
|
|
||||||
serie_id: str = Field(..., description="Series identifier (provider key)")
|
serie_id: str = Field(
|
||||||
serie_folder: Optional[str] = Field(
|
...,
|
||||||
None, description="Series folder name on disk"
|
description=(
|
||||||
|
"Series identifier - provider key "
|
||||||
|
"(e.g., 'attack-on-titan')"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
serie_folder: str = Field(
|
||||||
|
...,
|
||||||
|
description=(
|
||||||
|
"Series folder name on disk "
|
||||||
|
"(e.g., 'Attack on Titan (2013)')"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
serie_name: str = Field(
|
serie_name: str = Field(
|
||||||
..., min_length=1, description="Series name for display"
|
..., min_length=1, description="Series name for display"
|
||||||
|
|||||||
@ -239,11 +239,16 @@ class DownloadService:
|
|||||||
"""Add episodes to the download queue (FIFO order).
|
"""Add episodes to the download queue (FIFO order).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
serie_id: Series identifier (provider key)
|
serie_id: Series identifier - provider key (e.g.,
|
||||||
serie_folder: Series folder name on disk
|
'attack-on-titan'). This is the unique identifier used
|
||||||
serie_name: Series display name
|
for lookups and identification.
|
||||||
|
serie_folder: Series folder name on disk (e.g.,
|
||||||
|
'Attack on Titan (2013)'). Used for filesystem operations
|
||||||
|
only.
|
||||||
|
serie_name: Series display name for user interface
|
||||||
episodes: List of episodes to download
|
episodes: List of episodes to download
|
||||||
priority: Queue priority level (ignored, kept for compatibility)
|
priority: Queue priority level (ignored, kept for
|
||||||
|
compatibility)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of created download item IDs
|
List of created download item IDs
|
||||||
@ -277,7 +282,8 @@ class DownloadService:
|
|||||||
logger.info(
|
logger.info(
|
||||||
"Item added to queue",
|
"Item added to queue",
|
||||||
item_id=item.id,
|
item_id=item.id,
|
||||||
serie=serie_name,
|
serie_key=serie_id,
|
||||||
|
serie_name=serie_name,
|
||||||
season=episode.season,
|
season=episode.season,
|
||||||
episode=episode.episode,
|
episode=episode.episode,
|
||||||
)
|
)
|
||||||
@ -792,7 +798,8 @@ class DownloadService:
|
|||||||
logger.info(
|
logger.info(
|
||||||
"Starting download",
|
"Starting download",
|
||||||
item_id=item.id,
|
item_id=item.id,
|
||||||
serie=item.serie_name,
|
serie_key=item.serie_id,
|
||||||
|
serie_name=item.serie_name,
|
||||||
season=item.episode.season,
|
season=item.episode.season,
|
||||||
episode=item.episode.episode,
|
episode=item.episode.episode,
|
||||||
)
|
)
|
||||||
@ -802,9 +809,15 @@ class DownloadService:
|
|||||||
# - download started/progress/completed/failed events
|
# - download started/progress/completed/failed events
|
||||||
# - All updates forwarded to ProgressService
|
# - All updates forwarded to ProgressService
|
||||||
# - ProgressService broadcasts to WebSocket clients
|
# - ProgressService broadcasts to WebSocket clients
|
||||||
folder = item.serie_folder if item.serie_folder else item.serie_id
|
# Use serie_folder for filesystem operations and serie_id (key) for identification
|
||||||
|
if not item.serie_folder:
|
||||||
|
raise DownloadServiceError(
|
||||||
|
f"Missing serie_folder for download item {item.id}. "
|
||||||
|
"serie_folder is required for filesystem operations."
|
||||||
|
)
|
||||||
|
|
||||||
success = await self._anime_service.download(
|
success = await self._anime_service.download(
|
||||||
serie_folder=folder,
|
serie_folder=item.serie_folder,
|
||||||
season=item.episode.season,
|
season=item.episode.season,
|
||||||
episode=item.episode.episode,
|
episode=item.episode.episode,
|
||||||
key=item.serie_id,
|
key=item.serie_id,
|
||||||
|
|||||||
@ -174,6 +174,7 @@ class TestDownloadItem:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="download_123",
|
id="download_123",
|
||||||
serie_id="serie_456",
|
serie_id="serie_456",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episode=episode,
|
episode=episode,
|
||||||
status=DownloadStatus.PENDING,
|
status=DownloadStatus.PENDING,
|
||||||
@ -192,6 +193,7 @@ class TestDownloadItem:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="Test",
|
serie_name="Test",
|
||||||
episode=episode
|
episode=episode
|
||||||
)
|
)
|
||||||
@ -211,6 +213,7 @@ class TestDownloadItem:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="Test",
|
serie_name="Test",
|
||||||
episode=episode,
|
episode=episode,
|
||||||
progress=progress
|
progress=progress
|
||||||
@ -225,6 +228,7 @@ class TestDownloadItem:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="Test",
|
serie_name="Test",
|
||||||
episode=episode,
|
episode=episode,
|
||||||
started_at=now,
|
started_at=now,
|
||||||
@ -240,6 +244,7 @@ class TestDownloadItem:
|
|||||||
DownloadItem(
|
DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="",
|
serie_name="",
|
||||||
episode=episode
|
episode=episode
|
||||||
)
|
)
|
||||||
@ -251,6 +256,7 @@ class TestDownloadItem:
|
|||||||
DownloadItem(
|
DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="Test",
|
serie_name="Test",
|
||||||
episode=episode,
|
episode=episode,
|
||||||
retry_count=-1
|
retry_count=-1
|
||||||
@ -263,6 +269,7 @@ class TestDownloadItem:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="Test",
|
serie_name="Test",
|
||||||
episode=episode
|
episode=episode
|
||||||
)
|
)
|
||||||
@ -279,6 +286,7 @@ class TestQueueStatus:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Folder",
|
||||||
serie_name="Test",
|
serie_name="Test",
|
||||||
episode=episode
|
episode=episode
|
||||||
)
|
)
|
||||||
@ -375,6 +383,7 @@ class TestDownloadRequest:
|
|||||||
episode2 = EpisodeIdentifier(season=1, episode=2)
|
episode2 = EpisodeIdentifier(season=1, episode=2)
|
||||||
request = DownloadRequest(
|
request = DownloadRequest(
|
||||||
serie_id="serie_123",
|
serie_id="serie_123",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episodes=[episode1, episode2],
|
episodes=[episode1, episode2],
|
||||||
priority=DownloadPriority.HIGH
|
priority=DownloadPriority.HIGH
|
||||||
@ -389,16 +398,21 @@ class TestDownloadRequest:
|
|||||||
episode = EpisodeIdentifier(season=1, episode=1)
|
episode = EpisodeIdentifier(season=1, episode=1)
|
||||||
request = DownloadRequest(
|
request = DownloadRequest(
|
||||||
serie_id="serie_123",
|
serie_id="serie_123",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episodes=[episode]
|
episodes=[episode]
|
||||||
)
|
)
|
||||||
assert request.priority == DownloadPriority.NORMAL
|
assert request.priority == DownloadPriority.NORMAL
|
||||||
|
|
||||||
def test_empty_episodes_list_allowed(self):
|
def test_empty_episodes_list_allowed(self):
|
||||||
"""Test that empty episodes list is allowed at model level (endpoint validates)."""
|
"""Test that empty episodes list is allowed at model level.
|
||||||
|
|
||||||
|
(endpoint validates)
|
||||||
|
"""
|
||||||
# Empty list is now allowed at model level; endpoint validates
|
# Empty list is now allowed at model level; endpoint validates
|
||||||
request = DownloadRequest(
|
request = DownloadRequest(
|
||||||
serie_id="serie_123",
|
serie_id="serie_123",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episodes=[]
|
episodes=[]
|
||||||
)
|
)
|
||||||
@ -410,6 +424,7 @@ class TestDownloadRequest:
|
|||||||
with pytest.raises(ValidationError):
|
with pytest.raises(ValidationError):
|
||||||
DownloadRequest(
|
DownloadRequest(
|
||||||
serie_id="serie_123",
|
serie_id="serie_123",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="",
|
serie_name="",
|
||||||
episodes=[episode]
|
episodes=[episode]
|
||||||
)
|
)
|
||||||
@ -453,7 +468,10 @@ class TestQueueOperationRequest:
|
|||||||
assert "item1" in request.item_ids
|
assert "item1" in request.item_ids
|
||||||
|
|
||||||
def test_empty_item_ids_allowed(self):
|
def test_empty_item_ids_allowed(self):
|
||||||
"""Test that empty item_ids list is allowed at model level (endpoint validates)."""
|
"""Test that empty item_ids list is allowed at model level.
|
||||||
|
|
||||||
|
(endpoint validates)
|
||||||
|
"""
|
||||||
# Empty list is now allowed at model level; endpoint validates
|
# Empty list is now allowed at model level; endpoint validates
|
||||||
request = QueueOperationRequest(item_ids=[])
|
request = QueueOperationRequest(item_ids=[])
|
||||||
assert request.item_ids == []
|
assert request.item_ids == []
|
||||||
@ -512,6 +530,7 @@ class TestModelSerialization:
|
|||||||
item = DownloadItem(
|
item = DownloadItem(
|
||||||
id="test_id",
|
id="test_id",
|
||||||
serie_id="serie_id",
|
serie_id="serie_id",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episode=episode
|
episode=episode
|
||||||
)
|
)
|
||||||
@ -526,6 +545,7 @@ class TestModelSerialization:
|
|||||||
data = {
|
data = {
|
||||||
"id": "test_id",
|
"id": "test_id",
|
||||||
"serie_id": "serie_id",
|
"serie_id": "serie_id",
|
||||||
|
"serie_folder": "Test Series (2023)",
|
||||||
"serie_name": "Test Series",
|
"serie_name": "Test Series",
|
||||||
"episode": {
|
"episode": {
|
||||||
"season": 1,
|
"season": 1,
|
||||||
|
|||||||
@ -346,6 +346,7 @@ class TestQueueControl:
|
|||||||
completed_item = DownloadItem(
|
completed_item = DownloadItem(
|
||||||
id="completed-1",
|
id="completed-1",
|
||||||
serie_id="series-1",
|
serie_id="series-1",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episode=EpisodeIdentifier(season=1, episode=1),
|
episode=EpisodeIdentifier(season=1, episode=1),
|
||||||
status=DownloadStatus.COMPLETED,
|
status=DownloadStatus.COMPLETED,
|
||||||
@ -454,6 +455,7 @@ class TestRetryLogic:
|
|||||||
failed_item = DownloadItem(
|
failed_item = DownloadItem(
|
||||||
id="failed-1",
|
id="failed-1",
|
||||||
serie_id="series-1",
|
serie_id="series-1",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episode=EpisodeIdentifier(season=1, episode=1),
|
episode=EpisodeIdentifier(season=1, episode=1),
|
||||||
status=DownloadStatus.FAILED,
|
status=DownloadStatus.FAILED,
|
||||||
@ -476,6 +478,7 @@ class TestRetryLogic:
|
|||||||
failed_item = DownloadItem(
|
failed_item = DownloadItem(
|
||||||
id="failed-1",
|
id="failed-1",
|
||||||
serie_id="series-1",
|
serie_id="series-1",
|
||||||
|
serie_folder="Test Series (2023)",
|
||||||
serie_name="Test Series",
|
serie_name="Test Series",
|
||||||
episode=EpisodeIdentifier(season=1, episode=1),
|
episode=EpisodeIdentifier(season=1, episode=1),
|
||||||
status=DownloadStatus.FAILED,
|
status=DownloadStatus.FAILED,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user