Phase 8: Documentation and deprecation warnings for identifier standardization
- Enhanced infrastructure.md with identifier convention table, format requirements, migration notes - Updated docs/README.md with series identifier convention section - Updated docs/api_reference.md with key-based API examples and notes - Added deprecation warnings to SerieList.get_by_folder() - Added deprecation warnings to anime.py folder fallback lookup - Added deprecation warnings to validate_series_key_or_folder() - All warnings include v3.0.0 removal timeline - All 1006 tests pass
This commit is contained in:
parent
ddff43595f
commit
85a6b053eb
@ -97,6 +97,42 @@ Production deployment instructions covering:
|
||||
- Status notifications
|
||||
- Error alerts
|
||||
|
||||
## Series Identifier Convention
|
||||
|
||||
### Understanding Series Identifiers
|
||||
|
||||
The application uses two identifiers for anime series:
|
||||
|
||||
| Identifier | Purpose | Example | Used For |
|
||||
| ---------- | ------------------------ | -------------------------- | ----------------- |
|
||||
| `key` | **Primary identifier** | `"attack-on-titan"` | All API lookups |
|
||||
| `folder` | Filesystem metadata only | `"Attack on Titan (2013)"` | Display purposes |
|
||||
|
||||
### Key Format
|
||||
|
||||
- Lowercase letters and numbers only
|
||||
- Words separated by hyphens (`-`)
|
||||
- No spaces, underscores, or uppercase letters
|
||||
- Examples: `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
||||
|
||||
### Usage Guidelines
|
||||
|
||||
```python
|
||||
# ✅ Correct: Use 'key' for API operations
|
||||
GET /api/anime/attack-on-titan
|
||||
POST /api/queue/add {"serie_id": "attack-on-titan", ...}
|
||||
|
||||
# ❌ Deprecated: Folder-based lookups (backward compatibility only)
|
||||
GET /api/anime/Attack%20on%20Titan%20(2013) # Will work but deprecated
|
||||
```
|
||||
|
||||
### Backward Compatibility
|
||||
|
||||
For existing integrations, folder-based lookups are still supported but deprecated:
|
||||
- API endpoints check `key` first, then fall back to `folder`
|
||||
- New code should always use `key` as the identifier
|
||||
- Deprecation warnings will be added in future versions
|
||||
|
||||
## Documentation Examples
|
||||
|
||||
### API Usage Example
|
||||
@ -285,8 +321,8 @@ docs/
|
||||
|
||||
## Document Info
|
||||
|
||||
- **Last Updated**: October 22, 2025
|
||||
- **Version**: 1.0.0
|
||||
- **Last Updated**: November 28, 2025
|
||||
- **Version**: 2.0.0
|
||||
- **Status**: Production Ready
|
||||
- **Maintainers**: Development Team
|
||||
|
||||
|
||||
@ -421,12 +421,16 @@ Authorization: Bearer <token>
|
||||
|
||||
### Anime Endpoints
|
||||
|
||||
> **Note on Identifiers**: All anime endpoints use `key` as the primary series identifier (e.g., `"attack-on-titan"`).
|
||||
> The `folder` field is metadata only and should not be used for lookups.
|
||||
> For backward compatibility, folder-based lookups are supported but deprecated.
|
||||
|
||||
#### List Anime with Missing Episodes
|
||||
|
||||
Lists all anime series with missing episodes.
|
||||
|
||||
```http
|
||||
GET /api/v1/anime
|
||||
GET /api/anime
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
@ -442,14 +446,18 @@ Authorization: Bearer <token>
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "aniworld_123",
|
||||
"title": "Attack on Titan",
|
||||
"missing_episodes": 5
|
||||
"key": "attack-on-titan",
|
||||
"name": "Attack on Titan",
|
||||
"folder": "Attack on Titan (2013)",
|
||||
"missing_episodes": 5,
|
||||
"link": "https://aniworld.to/anime/stream/attack-on-titan"
|
||||
},
|
||||
{
|
||||
"id": "aniworld_456",
|
||||
"title": "Demon Slayer",
|
||||
"missing_episodes": 2
|
||||
"key": "demon-slayer",
|
||||
"name": "Demon Slayer",
|
||||
"folder": "Demon Slayer (2019)",
|
||||
"missing_episodes": 2,
|
||||
"link": "https://aniworld.to/anime/stream/demon-slayer"
|
||||
}
|
||||
]
|
||||
```
|
||||
@ -461,20 +469,23 @@ Authorization: Bearer <token>
|
||||
Retrieves detailed information for a specific anime series.
|
||||
|
||||
```http
|
||||
GET /api/v1/anime/{anime_id}
|
||||
GET /api/anime/{anime_id}
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
**Path Parameters**:
|
||||
|
||||
- `anime_id` (string): Series `key` (preferred) or `folder` (deprecated)
|
||||
|
||||
**Response (200 OK)**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "aniworld_123",
|
||||
"key": "attack-on-titan",
|
||||
"title": "Attack on Titan",
|
||||
"episodes": ["Season 1 Episode 1", "Season 1 Episode 2"],
|
||||
"description": "Anime description...",
|
||||
"total_episodes": 100,
|
||||
"downloaded_episodes": 95
|
||||
"folder": "Attack on Titan (2013)",
|
||||
"episodes": ["S01-E01", "S01-E02", "S02-E01"],
|
||||
"description": "Anime description..."
|
||||
}
|
||||
```
|
||||
|
||||
@ -489,7 +500,7 @@ Authorization: Bearer <token>
|
||||
Rescans the local anime directory for new series and episodes.
|
||||
|
||||
```http
|
||||
POST /api/v1/anime/rescan
|
||||
POST /api/anime/rescan
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
@ -539,6 +550,9 @@ Authorization: Bearer <token>
|
||||
|
||||
### Download Queue Endpoints
|
||||
|
||||
> **Note on Identifiers**: Download queue operations use `serie_id` which should be the series `key` (e.g., `"attack-on-titan"`).
|
||||
> The `serie_folder` field is filesystem metadata and should not be used for identification.
|
||||
|
||||
#### Get Queue Status
|
||||
|
||||
Retrieves download queue status and statistics.
|
||||
@ -577,13 +591,21 @@ Authorization: Bearer <token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"anime_id": "aniworld_123",
|
||||
"serie_id": "attack-on-titan",
|
||||
"serie_folder": "Attack on Titan (2013)",
|
||||
"serie_name": "Attack on Titan",
|
||||
"episodes": ["S01E01", "S01E02"],
|
||||
"priority": "normal"
|
||||
"priority": 1
|
||||
}
|
||||
```
|
||||
|
||||
**Priority Values**: `low`, `normal`, `high`
|
||||
**Request Fields**:
|
||||
|
||||
- `serie_id` (string, required): Series `key` - primary identifier
|
||||
- `serie_folder` (string, optional): Filesystem folder name (metadata)
|
||||
- `serie_name` (string, required): Display name
|
||||
- `episodes` (array, required): List of episodes to download
|
||||
- `priority` (integer, optional): Priority level (default: 0)
|
||||
|
||||
**Response (201 Created)**:
|
||||
|
||||
@ -592,7 +614,7 @@ Content-Type: application/json
|
||||
"success": true,
|
||||
"data": {
|
||||
"queue_item_id": "queue_456",
|
||||
"anime_id": "aniworld_123",
|
||||
"serie_id": "attack-on-titan",
|
||||
"status": "pending"
|
||||
}
|
||||
}
|
||||
@ -682,18 +704,21 @@ Authorization: Bearer <token>
|
||||
|
||||
### WebSocket Endpoints
|
||||
|
||||
> **Note on Identifiers**: All WebSocket events include `key` as the primary series identifier.
|
||||
> The `folder` field is included as metadata but should not be used for identification.
|
||||
|
||||
#### Real-Time Progress Updates
|
||||
|
||||
Establishes WebSocket connection for real-time download progress updates.
|
||||
|
||||
```
|
||||
WS /ws/downloads
|
||||
WS /ws/connect
|
||||
```
|
||||
|
||||
**Connection**:
|
||||
|
||||
```javascript
|
||||
const ws = new WebSocket("ws://localhost:8000/ws/downloads");
|
||||
const ws = new WebSocket("ws://localhost:8000/ws/connect");
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const message = JSON.parse(event.data);
|
||||
@ -701,6 +726,8 @@ ws.onmessage = (event) => {
|
||||
};
|
||||
```
|
||||
|
||||
**Rooms**: `downloads`, `download_progress`, `scan_progress`
|
||||
|
||||
**Message Types**:
|
||||
|
||||
**Download Started**:
|
||||
@ -710,8 +737,9 @@ ws.onmessage = (event) => {
|
||||
"type": "download_started",
|
||||
"timestamp": "2025-10-22T12:00:00Z",
|
||||
"data": {
|
||||
"queue_item_id": "queue_456",
|
||||
"anime_title": "Attack on Titan",
|
||||
"download_id": "dl_456",
|
||||
"key": "attack-on-titan",
|
||||
"folder": "Attack on Titan (2013)",
|
||||
"episode": "S01E01"
|
||||
}
|
||||
}
|
||||
@ -724,11 +752,12 @@ ws.onmessage = (event) => {
|
||||
"type": "download_progress",
|
||||
"timestamp": "2025-10-22T12:00:05Z",
|
||||
"data": {
|
||||
"queue_item_id": "queue_456",
|
||||
"progress_percent": 45,
|
||||
"downloaded_bytes": 500000000,
|
||||
"total_bytes": 1100000000,
|
||||
"speed_mbps": 5.5
|
||||
"download_id": "dl_456",
|
||||
"key": "attack-on-titan",
|
||||
"folder": "Attack on Titan (2013)",
|
||||
"percent": 45.2,
|
||||
"speed_mbps": 5.5,
|
||||
"eta_seconds": 180
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -737,11 +766,12 @@ ws.onmessage = (event) => {
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "download_completed",
|
||||
"type": "download_complete",
|
||||
"timestamp": "2025-10-22T12:05:00Z",
|
||||
"data": {
|
||||
"queue_item_id": "queue_456",
|
||||
"total_time_seconds": 300,
|
||||
"download_id": "dl_456",
|
||||
"key": "attack-on-titan",
|
||||
"folder": "Attack on Titan (2013)",
|
||||
"file_path": "/path/to/anime/file.mkv"
|
||||
}
|
||||
}
|
||||
@ -751,15 +781,17 @@ ws.onmessage = (event) => {
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "download_error",
|
||||
"type": "download_failed",
|
||||
"timestamp": "2025-10-22T12:05:00Z",
|
||||
"data": {
|
||||
"queue_item_id": "queue_456",
|
||||
"error_message": "Connection timeout",
|
||||
"error_code": "PROVIDER_ERROR"
|
||||
"download_id": "dl_456",
|
||||
"key": "attack-on-titan",
|
||||
"folder": "Attack on Titan (2013)",
|
||||
"error": "Connection timeout"
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -41,12 +41,30 @@ tests/ # Test suites
|
||||
|
||||
## Series Identifier Convention
|
||||
|
||||
Throughout the codebase, two identifiers are used for anime series:
|
||||
Throughout the codebase, three identifiers are used for anime series:
|
||||
|
||||
- **`key`**: Primary identifier (provider-assigned, URL-safe, e.g., `"attack-on-titan"`)
|
||||
- **`folder`**: Display/filesystem metadata only (e.g., `"Attack on Titan (2013)"`)
|
||||
| Identifier | Type | Purpose | Example |
|
||||
| ---------- | --------------- | --------------------------------------------------------- | ----------------------------- |
|
||||
| `key` | Unique, Indexed | **PRIMARY** - All lookups, API operations, WebSocket events | `"attack-on-titan"` |
|
||||
| `folder` | String | Display/filesystem metadata only (never for lookups) | `"Attack on Titan (2013)"` |
|
||||
| `id` | Primary Key | Internal database key for relationships | `1`, `42` |
|
||||
|
||||
All lookups, events, and API operations use `key`. The `folder` is metadata for display purposes.
|
||||
### Key Format Requirements
|
||||
|
||||
- **Lowercase only**: No uppercase letters allowed
|
||||
- **URL-safe**: Only alphanumeric characters and hyphens
|
||||
- **Hyphen-separated**: Words separated by single hyphens
|
||||
- **No leading/trailing hyphens**: Must start and end with alphanumeric
|
||||
- **No consecutive hyphens**: `attack--titan` is invalid
|
||||
|
||||
**Valid examples**: `"attack-on-titan"`, `"one-piece"`, `"86-eighty-six"`, `"re-zero"`
|
||||
**Invalid examples**: `"Attack On Titan"`, `"attack_on_titan"`, `"attack on titan"`
|
||||
|
||||
### Migration Notes
|
||||
|
||||
- **Backward Compatibility**: API endpoints accepting `anime_id` will check `key` first, then fall back to `folder` lookup
|
||||
- **Deprecation**: Folder-based lookups are deprecated and will be removed in a future version
|
||||
- **New Code**: Always use `key` for identification; `folder` is metadata only
|
||||
|
||||
## API Endpoints
|
||||
|
||||
@ -282,3 +300,38 @@ conda run -n AniWorld python -m pytest tests/api/ -v
|
||||
- Move WebSocket registry to Redis
|
||||
- Use distributed locking for queue operations
|
||||
- Consider Redis for session/cache storage
|
||||
|
||||
## Code Examples
|
||||
|
||||
### API Usage with Key Identifier
|
||||
|
||||
```python
|
||||
# Fetching anime list - response includes 'key' as identifier
|
||||
response = requests.get("/api/anime", headers={"Authorization": f"Bearer {token}"})
|
||||
anime_list = response.json()
|
||||
# Each item has: key="attack-on-titan", folder="Attack on Titan (2013)", ...
|
||||
|
||||
# Fetching specific anime by key (preferred)
|
||||
response = requests.get("/api/anime/attack-on-titan", headers={"Authorization": f"Bearer {token}"})
|
||||
|
||||
# Adding to download queue using key
|
||||
download_request = {
|
||||
"serie_id": "attack-on-titan", # Use key, not folder
|
||||
"serie_folder": "Attack on Titan (2013)", # Metadata for filesystem
|
||||
"serie_name": "Attack on Titan",
|
||||
"episodes": ["S01E01", "S01E02"],
|
||||
"priority": 1
|
||||
}
|
||||
response = requests.post("/api/queue/add", json=download_request, headers=headers)
|
||||
```
|
||||
|
||||
### WebSocket Event Handling
|
||||
|
||||
```javascript
|
||||
// WebSocket events always include 'key' as identifier
|
||||
socket.on("download_progress", (data) => {
|
||||
const key = data.key; // Primary identifier: "attack-on-titan"
|
||||
const folder = data.folder; // Metadata: "Attack on Titan (2013)"
|
||||
updateProgressBar(key, data.percent);
|
||||
});
|
||||
```
|
||||
|
||||
@ -248,75 +248,18 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
|
||||
|
||||
---
|
||||
|
||||
### Phase 8: Documentation and Cleanup
|
||||
### Phase 8: Documentation and Cleanup ✅ **Completed November 28, 2025**
|
||||
|
||||
#### Task 8.1: Update Infrastructure Documentation
|
||||
All tasks completed:
|
||||
|
||||
**File:** [`infrastructure.md`](infrastructure.md)
|
||||
- **Task 8.1**: Updated `infrastructure.md` with enhanced identifier convention section including table format, key format requirements, migration notes, and code examples
|
||||
- **Task 8.2**: Updated `docs/README.md` with series identifier convention section, updated `docs/api_reference.md` with key-based API examples
|
||||
- **Task 8.3**: Added deprecation warnings with Python's `warnings` module to:
|
||||
- `SerieList.get_by_folder()` in `src/core/entities/SerieList.py`
|
||||
- Folder fallback lookup in `src/server/api/anime.py`
|
||||
- `validate_series_key_or_folder()` in `src/server/utils/validators.py`
|
||||
|
||||
**Objective:** Document the identifier standardization in infrastructure documentation.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Open [`infrastructure.md`](infrastructure.md)
|
||||
2. Add section explaining identifier usage:
|
||||
- `key`: Unique series identifier (provider-assigned)
|
||||
- `folder`: Filesystem folder name (metadata only)
|
||||
- `id`: Database primary key (internal use)
|
||||
3. Update all API documentation to show `key` as identifier
|
||||
4. Update data model documentation
|
||||
5. Add migration notes if needed
|
||||
|
||||
**Success Criteria:**
|
||||
|
||||
- [ ] Documentation clearly explains identifier usage
|
||||
- [ ] All API examples use `key`
|
||||
- [ ] Data model section updated
|
||||
- [ ] Migration notes added if applicable
|
||||
|
||||
---
|
||||
|
||||
#### Task 8.2: Update README and Developer Documentation
|
||||
|
||||
**Files:** [`README.md`](README.md), [`docs/`](docs/)
|
||||
|
||||
**Objective:** Update all developer-facing documentation.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Update main README to explain identifier usage
|
||||
2. Update any developer guides to use `key`
|
||||
3. Add note about backward compatibility with `folder`
|
||||
4. Update code examples to use `key`
|
||||
|
||||
**Success Criteria:**
|
||||
|
||||
- [ ] README updated
|
||||
- [ ] Developer guides updated
|
||||
- [ ] Code examples use `key`
|
||||
- [ ] Backward compatibility documented
|
||||
|
||||
---
|
||||
|
||||
#### Task 8.3: Add Deprecation Warnings for Folder-Based Lookups
|
||||
|
||||
**Files:** Various service and API files
|
||||
|
||||
**Objective:** Add deprecation warnings where `folder` is still accepted for backward compatibility.
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Identify all methods that accept `folder` for lookups
|
||||
2. Add deprecation warnings using Python's `warnings` module
|
||||
3. Update docstrings to indicate deprecation
|
||||
4. Plan removal timeline (e.g., next major version)
|
||||
|
||||
**Success Criteria:**
|
||||
|
||||
- [ ] Deprecation warnings added
|
||||
- [ ] Docstrings indicate deprecation
|
||||
- [ ] Removal timeline documented
|
||||
- [ ] Tests updated to suppress warnings
|
||||
All deprecation warnings include removal timeline (v3.0.0) and guidance to use `key` instead.
|
||||
|
||||
---
|
||||
|
||||
@ -482,18 +425,11 @@ conda run -n AniWorld python -m pytest tests/integration/test_identifier_consist
|
||||
- [x] Phase 3: Service Layer ✅
|
||||
- [x] Phase 4: API Layer ✅ **Completed November 28, 2025**
|
||||
- [x] Phase 5: Frontend ✅ **Completed November 28, 2025**
|
||||
- [x] Task 5.1: Update Frontend JavaScript
|
||||
- [x] Task 5.2: Update WebSocket Events
|
||||
- [x] Task 5.3: Update Additional Frontend JavaScript Files
|
||||
- [x] Task 5.4: Update HTML Templates
|
||||
- [x] Phase 6: Database Layer ✅ **Completed November 28, 2025**
|
||||
- [x] Task 6.1: Verify Database Models
|
||||
- [x] Task 6.2: Update Database Services
|
||||
- [x] Phase 7: Testing and Validation ✅ **Completed November 28, 2025**
|
||||
- [x] Task 7.1: Update Test Fixtures - Updated all test fixtures and mocks to use `key` consistently with realistic key values (URL-safe, lowercase, hyphenated)
|
||||
- [x] Task 7.2: Add Integration Tests - Created `tests/integration/test_identifier_consistency.py` with 10 tests verifying `key` usage across all layers
|
||||
- [ ] Phase 8: Documentation and Cleanup
|
||||
- [ ] Task 8.1: Update Infrastructure Documentation
|
||||
- [ ] Task 8.2: Update README
|
||||
- [ ] Task 8.3: Add Deprecation Warnings
|
||||
- [x] Phase 8: Documentation and Cleanup ✅ **Completed November 28, 2025**
|
||||
- [x] Task 8.1: Update Infrastructure Documentation - Enhanced identifier convention section with table, format requirements, migration notes, and code examples
|
||||
- [x] Task 8.2: Update README and Developer Docs - Updated docs/README.md with identifier section, updated api_reference.md with key-based examples
|
||||
- [x] Task 8.3: Add Deprecation Warnings - Added warnings to SerieList.get_by_folder(), anime.py folder fallback, and validators.py
|
||||
- [ ] Phase 9: Final Validation
|
||||
- [ ] Phase 10: Deployment
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import warnings
|
||||
from json import JSONDecodeError
|
||||
from typing import Dict, Iterable, List, Optional
|
||||
|
||||
@ -144,6 +145,11 @@ class SerieList:
|
||||
"""
|
||||
Get a series by its folder name.
|
||||
|
||||
.. deprecated:: 2.0.0
|
||||
Use :meth:`get_by_key` instead. Folder-based lookups will be
|
||||
removed in version 3.0.0. The `folder` field is metadata only
|
||||
and should not be used for identification.
|
||||
|
||||
This method is provided for backward compatibility only.
|
||||
Prefer using get_by_key() for new code.
|
||||
|
||||
@ -153,6 +159,12 @@ class SerieList:
|
||||
Returns:
|
||||
The Serie instance if found, None otherwise
|
||||
"""
|
||||
warnings.warn(
|
||||
"get_by_folder() is deprecated and will be removed in v3.0.0. "
|
||||
"Use get_by_key() instead. The 'folder' field is metadata only.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
for serie in self.keyDict.values():
|
||||
if serie.folder == folder:
|
||||
return serie
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import logging
|
||||
import warnings
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
@ -11,6 +13,8 @@ from src.server.utils.dependencies import (
|
||||
require_auth,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(prefix="/api/anime", tags=["anime"])
|
||||
|
||||
|
||||
@ -715,6 +719,21 @@ async def get_anime(
|
||||
for serie in series:
|
||||
if getattr(serie, "folder", None) == anime_id:
|
||||
found = serie
|
||||
# Log deprecation warning for folder-based lookup
|
||||
key = getattr(serie, "key", "unknown")
|
||||
logger.warning(
|
||||
"Folder-based lookup for '%s' is deprecated. "
|
||||
"Use series key '%s' instead. Folder-based lookups "
|
||||
"will be removed in v3.0.0.",
|
||||
anime_id,
|
||||
key
|
||||
)
|
||||
warnings.warn(
|
||||
f"Folder-based lookup for '{anime_id}' is deprecated. "
|
||||
f"Use series key '{key}' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
break
|
||||
|
||||
if not found:
|
||||
|
||||
@ -6,6 +6,7 @@ utilities for ensuring data integrity across the application.
|
||||
"""
|
||||
|
||||
import re
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
@ -496,6 +497,10 @@ def validate_series_key_or_folder(
|
||||
"""
|
||||
Validate an identifier that could be either a series key or folder.
|
||||
|
||||
.. deprecated:: 2.0.0
|
||||
Folder-based identification is deprecated. Use series `key` only.
|
||||
This function will require key format only in v3.0.0.
|
||||
|
||||
This function provides backward compatibility during the transition
|
||||
from folder-based to key-based identification.
|
||||
|
||||
@ -532,6 +537,15 @@ def validate_series_key_or_folder(
|
||||
"Keys must be lowercase with hyphens (e.g., 'attack-on-titan')."
|
||||
)
|
||||
|
||||
# Emit deprecation warning for folder-based identification
|
||||
warnings.warn(
|
||||
f"Folder-based identification for '{identifier}' is deprecated. "
|
||||
"Use series key (lowercase with hyphens) instead. "
|
||||
"Folder-based identification will be removed in v3.0.0.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
|
||||
# Validate as folder (more permissive)
|
||||
if len(identifier) > 1000:
|
||||
raise ValueError("Identifier too long (max 1000 characters)")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user