Add series key metadata to callback contexts
This commit is contained in:
parent
41a53bbf8f
commit
883f89b113
@ -811,6 +811,12 @@ A comprehensive callback system for real-time progress reporting, error handling
|
||||
- `ErrorContext`: Error details, recoverability, retry information
|
||||
- `CompletionContext`: Success status, results, and statistics
|
||||
|
||||
All context dataclasses expose a `key` field (provider identifier) plus an
|
||||
optional `folder` field used purely for display metadata. This keeps the
|
||||
callback contract aligned with the broader series identifier standardization
|
||||
work: downstream consumers rely on `key` for lookups while still showing a
|
||||
user-friendly folder name when needed.
|
||||
|
||||
#### Enums
|
||||
|
||||
- `OperationType`: SCAN, DOWNLOAD, SEARCH, INITIALIZATION
|
||||
@ -893,6 +899,10 @@ Implemented a comprehensive progress callback system for real-time operation tra
|
||||
- Coverage for all callback types
|
||||
- Exception handling verification
|
||||
- Multiple callback registration tests
|
||||
5. **Identifier Support (Nov 2025)**:
|
||||
- Added `key` + optional `folder` fields to every context object
|
||||
- Docstrings now clarify that `key` is the canonical lookup identifier
|
||||
- Tests updated to guarantee both fields serialize correctly
|
||||
|
||||
### Core Logic Enhancement (October 2025)
|
||||
|
||||
|
||||
@ -169,36 +169,6 @@ For each task completed:
|
||||
|
||||
---
|
||||
|
||||
#### Task 2.2: Update Callback Interfaces to Use Key
|
||||
|
||||
**File:** [`src/core/interfaces/callbacks.py`](src/core/interfaces/callbacks.py)
|
||||
|
||||
**Objective:** Ensure callback interfaces use `key` in all progress and event contexts.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Open [`src/core/interfaces/callbacks.py`](src/core/interfaces/callbacks.py)
|
||||
2. Review `ProgressContext` and other context classes
|
||||
3. Ensure context classes include `key` field where series is referenced
|
||||
4. Add `folder` field as optional metadata
|
||||
5. Update docstrings to clarify `key` vs `folder` usage
|
||||
6. Ensure backward compatibility where needed
|
||||
|
||||
**Success Criteria:**
|
||||
|
||||
- [ ] Context classes include `key` field
|
||||
- [ ] `folder` included as optional metadata
|
||||
- [ ] Docstrings clearly document field usage
|
||||
- [ ] All callback tests pass
|
||||
|
||||
**Test Command:**
|
||||
|
||||
```bash
|
||||
conda run -n AniWorld python -m pytest tests/unit/test_callbacks.py -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Service Layer
|
||||
|
||||
#### Task 3.1: Update DownloadService to Use Key
|
||||
@ -1089,7 +1059,6 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
|
||||
- [ ] **Task 1.4: Update Provider Classes** ⭐ NEW
|
||||
- [ ] Phase 2: Core Application Layer
|
||||
- [ ] Task 2.1: Update SeriesApp
|
||||
- [ ] **Task 2.2: Update Callback Interfaces** ⭐ NEW
|
||||
- [ ] Phase 3: Service Layer
|
||||
- [ ] Task 3.1: Update DownloadService
|
||||
- [ ] Task 3.2: Update AnimeService
|
||||
|
||||
@ -47,6 +47,8 @@ class ProgressContext:
|
||||
percentage: Completion percentage (0.0 to 100.0)
|
||||
message: Human-readable progress message
|
||||
details: Additional context-specific details
|
||||
key: Provider-assigned series identifier (None when not applicable)
|
||||
folder: Optional folder metadata for display purposes only
|
||||
metadata: Extra metadata for specialized use cases
|
||||
"""
|
||||
|
||||
@ -58,6 +60,8 @@ class ProgressContext:
|
||||
percentage: float
|
||||
message: str
|
||||
details: Optional[str] = None
|
||||
key: Optional[str] = None
|
||||
folder: Optional[str] = None
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
@ -71,6 +75,8 @@ class ProgressContext:
|
||||
"percentage": round(self.percentage, 2),
|
||||
"message": self.message,
|
||||
"details": self.details,
|
||||
"key": self.key,
|
||||
"folder": self.folder,
|
||||
"metadata": self.metadata,
|
||||
}
|
||||
|
||||
@ -87,6 +93,8 @@ class ErrorContext:
|
||||
message: Human-readable error message
|
||||
recoverable: Whether the error is recoverable
|
||||
retry_count: Number of retry attempts made
|
||||
key: Provider-assigned series identifier (None when not applicable)
|
||||
folder: Optional folder metadata for display purposes only
|
||||
metadata: Additional error context
|
||||
"""
|
||||
|
||||
@ -96,6 +104,8 @@ class ErrorContext:
|
||||
message: str
|
||||
recoverable: bool = False
|
||||
retry_count: int = 0
|
||||
key: Optional[str] = None
|
||||
folder: Optional[str] = None
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
@ -108,6 +118,8 @@ class ErrorContext:
|
||||
"message": self.message,
|
||||
"recoverable": self.recoverable,
|
||||
"retry_count": self.retry_count,
|
||||
"key": self.key,
|
||||
"folder": self.folder,
|
||||
"metadata": self.metadata,
|
||||
}
|
||||
|
||||
@ -124,6 +136,8 @@ class CompletionContext:
|
||||
message: Human-readable completion message
|
||||
result_data: Result data from the operation
|
||||
statistics: Operation statistics (duration, items processed, etc.)
|
||||
key: Provider-assigned series identifier (None when not applicable)
|
||||
folder: Optional folder metadata for display purposes only
|
||||
metadata: Additional completion context
|
||||
"""
|
||||
|
||||
@ -133,6 +147,8 @@ class CompletionContext:
|
||||
message: str
|
||||
result_data: Optional[Any] = None
|
||||
statistics: Dict[str, Any] = field(default_factory=dict)
|
||||
key: Optional[str] = None
|
||||
folder: Optional[str] = None
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
@ -143,6 +159,8 @@ class CompletionContext:
|
||||
"success": self.success,
|
||||
"message": self.message,
|
||||
"statistics": self.statistics,
|
||||
"key": self.key,
|
||||
"folder": self.folder,
|
||||
"metadata": self.metadata,
|
||||
}
|
||||
|
||||
|
||||
@ -34,6 +34,8 @@ class TestProgressContext(unittest.TestCase):
|
||||
percentage=50.0,
|
||||
message="Downloading...",
|
||||
details="Episode 5",
|
||||
key="attack-on-titan",
|
||||
folder="Attack on Titan (2013)",
|
||||
metadata={"series": "Test"}
|
||||
)
|
||||
|
||||
@ -45,6 +47,8 @@ class TestProgressContext(unittest.TestCase):
|
||||
self.assertEqual(context.percentage, 50.0)
|
||||
self.assertEqual(context.message, "Downloading...")
|
||||
self.assertEqual(context.details, "Episode 5")
|
||||
self.assertEqual(context.key, "attack-on-titan")
|
||||
self.assertEqual(context.folder, "Attack on Titan (2013)")
|
||||
self.assertEqual(context.metadata, {"series": "Test"})
|
||||
|
||||
def test_progress_context_to_dict(self):
|
||||
@ -69,6 +73,8 @@ class TestProgressContext(unittest.TestCase):
|
||||
self.assertEqual(result["percentage"], 100.0)
|
||||
self.assertEqual(result["message"], "Scan complete")
|
||||
self.assertIsNone(result["details"])
|
||||
self.assertIsNone(result["key"])
|
||||
self.assertIsNone(result["folder"])
|
||||
self.assertEqual(result["metadata"], {})
|
||||
|
||||
def test_progress_context_default_metadata(self):
|
||||
@ -100,6 +106,8 @@ class TestErrorContext(unittest.TestCase):
|
||||
message="Download failed",
|
||||
recoverable=True,
|
||||
retry_count=2,
|
||||
key="jujutsu-kaisen",
|
||||
folder="Jujutsu Kaisen",
|
||||
metadata={"attempt": 3}
|
||||
)
|
||||
|
||||
@ -109,6 +117,8 @@ class TestErrorContext(unittest.TestCase):
|
||||
self.assertEqual(context.message, "Download failed")
|
||||
self.assertTrue(context.recoverable)
|
||||
self.assertEqual(context.retry_count, 2)
|
||||
self.assertEqual(context.key, "jujutsu-kaisen")
|
||||
self.assertEqual(context.folder, "Jujutsu Kaisen")
|
||||
self.assertEqual(context.metadata, {"attempt": 3})
|
||||
|
||||
def test_error_context_to_dict(self):
|
||||
@ -131,6 +141,8 @@ class TestErrorContext(unittest.TestCase):
|
||||
self.assertEqual(result["message"], "Scan error occurred")
|
||||
self.assertFalse(result["recoverable"])
|
||||
self.assertEqual(result["retry_count"], 0)
|
||||
self.assertIsNone(result["key"])
|
||||
self.assertIsNone(result["folder"])
|
||||
self.assertEqual(result["metadata"], {})
|
||||
|
||||
|
||||
@ -146,6 +158,8 @@ class TestCompletionContext(unittest.TestCase):
|
||||
message="Download completed successfully",
|
||||
result_data={"file": "episode.mp4"},
|
||||
statistics={"size": 1024, "time": 60},
|
||||
key="bleach",
|
||||
folder="Bleach (2004)",
|
||||
metadata={"quality": "HD"}
|
||||
)
|
||||
|
||||
@ -155,6 +169,8 @@ class TestCompletionContext(unittest.TestCase):
|
||||
self.assertEqual(context.message, "Download completed successfully")
|
||||
self.assertEqual(context.result_data, {"file": "episode.mp4"})
|
||||
self.assertEqual(context.statistics, {"size": 1024, "time": 60})
|
||||
self.assertEqual(context.key, "bleach")
|
||||
self.assertEqual(context.folder, "Bleach (2004)")
|
||||
self.assertEqual(context.metadata, {"quality": "HD"})
|
||||
|
||||
def test_completion_context_to_dict(self):
|
||||
@ -173,6 +189,8 @@ class TestCompletionContext(unittest.TestCase):
|
||||
self.assertFalse(result["success"])
|
||||
self.assertEqual(result["message"], "Scan failed")
|
||||
self.assertEqual(result["statistics"], {})
|
||||
self.assertIsNone(result["key"])
|
||||
self.assertIsNone(result["folder"])
|
||||
self.assertEqual(result["metadata"], {})
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user