Fix stale data file updates on download completion
When episodes are downloaded successfully, the in-memory Serie.episodeDict is updated, but the deprecated data file was not being synced. This caused UI to show episodes as missing when already downloaded. Changes: - Update data file in _remove_episode_from_memory when download completes - DB is authoritative; data file is optional backup (deprecated) - Gracefully skip update if data file doesn't exist New integration tests for episode download sync: - Verify episode removed from missing list after download - Verify in-memory cache updated after download - Verify data file updated after download (when it exists) - Verify downloads work without data file
This commit is contained in:
@@ -139,6 +139,10 @@ This changelog follows [Keep a Changelog](https://keepachangelog.com/) principle
|
||||
- Modified `src/server/api/anime.py` to save scanned episodes to database
|
||||
- Episodes table properly tracks missing episodes with automatic cleanup
|
||||
|
||||
### Deprecated
|
||||
|
||||
- **Legacy Series Files (key/data)**: File-based series storage is deprecated. `key` and `data` files in anime folders will be removed in v3.0.0. Database storage is now the primary method. See [docs/MIGRATION_GUIDE.md](docs/MIGRATION_GUIDE.md) for details.
|
||||
|
||||
---
|
||||
|
||||
## Sections for Each Release
|
||||
|
||||
@@ -588,7 +588,52 @@ EpisodeService.create(is_downloaded=False)
|
||||
|
||||
---
|
||||
|
||||
## 13. Database Location
|
||||
## 13. Series Persistence
|
||||
|
||||
### Schema
|
||||
|
||||
**AnimeSeries Table**: Stores series metadata (key, name, site, folder, year)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|-----------|--------------|---------------------------|----------------------|
|
||||
| `id` | INTEGER | PRIMARY KEY | Auto-increment |
|
||||
| `key` | VARCHAR(255) | UNIQUE, NOT NULL | Series provider key |
|
||||
| `name` | VARCHAR(500) | NOT NULL | Display name |
|
||||
| `site` | VARCHAR(500) | | Provider site URL |
|
||||
| `folder` | VARCHAR(1000)| | Filesystem folder |
|
||||
|
||||
**Episode Table**: Stores per-episode metadata (season, episode_number, is_downloaded)
|
||||
|
||||
| Column | Type | Constraints | Description |
|
||||
|-----------------|--------------|---------------------------|----------------------|
|
||||
| `id` | INTEGER | PRIMARY KEY | Auto-increment |
|
||||
| `series_id` | INTEGER | FOREIGN KEY → anime_series| Parent series |
|
||||
| `season` | INTEGER | NOT NULL | Season number |
|
||||
| `episode_number`| INTEGER | NOT NULL | Episode number |
|
||||
| `is_downloaded` | BOOLEAN | DEFAULT FALSE | Download status |
|
||||
|
||||
### Relationships
|
||||
|
||||
- `AnimeSeries.episodes` → List of Episode objects (one-to-many)
|
||||
- `Episode.series` → Parent AnimeSeries (many-to-one)
|
||||
- Cascade delete: Deleting a series removes all its episodes
|
||||
|
||||
### Queries
|
||||
|
||||
```python
|
||||
# Get all series with episodes
|
||||
AnimeSeriesService.get_all(db, with_episodes=True)
|
||||
|
||||
# Get by provider key
|
||||
AnimeSeriesService.get_by_key(db, key)
|
||||
|
||||
# Get by folder path
|
||||
AnimeSeriesService.get_by_folder(db, folder)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. Database Location
|
||||
|
||||
| Environment | Default Location |
|
||||
| ----------- | ------------------------------------------------- |
|
||||
|
||||
@@ -398,3 +398,29 @@ forward this to `notification_service.notify_download_failed()` so users see
|
||||
a HIGH-priority alert. The loader keeps the failure detail in
|
||||
`logs/download_errors.log` for post-mortem.
|
||||
|
||||
## Series Storage
|
||||
|
||||
### Overview
|
||||
|
||||
Series metadata now stored in the database (SQLAlchemy ORM).
|
||||
Legacy files (`key` and `data` per folder) are deprecated but preserved
|
||||
for backward compatibility.
|
||||
|
||||
### Architecture
|
||||
|
||||
- **Database**: Single source of truth for all series metadata
|
||||
- **In-Memory Cache**: SeriesApp maintains a cache for performance
|
||||
- **Filesystem**: Only used for episode files themselves, not metadata
|
||||
|
||||
### Migration
|
||||
|
||||
First startup after upgrade automatically imports any legacy
|
||||
series files into the database.
|
||||
|
||||
### Legacy Files
|
||||
|
||||
- `key` file: Contains series provider key (deprecated)
|
||||
- `data` file: Contains Serie JSON object (deprecated)
|
||||
|
||||
Both are safe to delete after migration; not needed for normal operation.
|
||||
|
||||
|
||||
111
docs/MIGRATION_GUIDE.md
Normal file
111
docs/MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Migration Guide: File-Based to Database Storage
|
||||
|
||||
## Overview
|
||||
|
||||
This guide covers the transition from file-based series metadata storage to the new database-backed system introduced in v2.0.
|
||||
|
||||
## What Changed
|
||||
|
||||
**Before v2.0**: Series metadata stored in `key` and `data` files alongside anime folders.
|
||||
|
||||
**After v2.0**: All metadata stored in SQLite database (`aniworld.db`). Files are deprecated but still supported for backward compatibility during migration.
|
||||
|
||||
## Automated Migration
|
||||
|
||||
The application automatically migrates on first startup:
|
||||
|
||||
1. Scans anime directory for `key` and `data` files
|
||||
2. Parses legacy files into `AnimeSeries` and `Episode` records
|
||||
3. Loads series into in-memory cache
|
||||
4. Logs migration results
|
||||
|
||||
**No manual action required.**
|
||||
|
||||
## Manual Verification
|
||||
|
||||
After first startup with the new version:
|
||||
|
||||
1. **Check logs** for: `"Migrated X series from files to DB"`
|
||||
2. **Verify series count**: UI shows same number of series as before
|
||||
3. **Confirm episodes**: Episode counts match expected totals
|
||||
|
||||
```bash
|
||||
# Check migration log
|
||||
grep "Migrated" logs/app.log
|
||||
|
||||
# Verify series via API
|
||||
curl http://localhost:8000/api/anime | jq '.total'
|
||||
```
|
||||
|
||||
## After Migration
|
||||
|
||||
### Safe to Delete
|
||||
|
||||
Once verified, these files can be removed:
|
||||
|
||||
```
|
||||
<anime_folder>/
|
||||
├── Attack on Titan (2013)/
|
||||
│ ├── key # ❌ Can delete
|
||||
│ ├── data # ❌ Can delete
|
||||
│ └── Season 1/
|
||||
│ └── ...
|
||||
```
|
||||
|
||||
**Deleting these files does not affect the database.** The metadata now lives in `aniworld.db`.
|
||||
|
||||
### Backup (Recommended)
|
||||
|
||||
Before deleting, backup the files:
|
||||
|
||||
```bash
|
||||
# Create backup directory
|
||||
mkdir -p backup/legacy_series_files
|
||||
|
||||
# Copy all key and data files
|
||||
find /path/to/anime -name "key" -o -name "data" | while read f; do
|
||||
cp "$f" "backup/legacy_series_files/"
|
||||
done
|
||||
```
|
||||
|
||||
## Reverting (Not Recommended)
|
||||
|
||||
If you must revert to file-based storage:
|
||||
|
||||
1. **Restore from database backup** (if available)
|
||||
2. **Export manually** (no export script exists)
|
||||
|
||||
**Warning**: File-based storage is deprecated and will be removed in v3.0.0.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Series Not Appearing After Migration
|
||||
|
||||
1. Check logs for migration errors: `grep -i error logs/app.log`
|
||||
2. Verify `key` and `data` files exist and are readable
|
||||
3. Manually trigger rescan: `POST /api/scheduler/trigger-rescan`
|
||||
|
||||
### Duplicate Series
|
||||
|
||||
1. Check for duplicate `key` files (same series in multiple folders)
|
||||
2. Verify series key uniqueness in database:
|
||||
|
||||
```bash
|
||||
sqlite3 aniworld.db "SELECT key, COUNT(*) FROM anime_series GROUP BY key HAVING COUNT(*) > 1;"
|
||||
```
|
||||
|
||||
### Missing Episodes
|
||||
|
||||
1. Trigger targeted scan for affected series
|
||||
2. Check episode sync logs
|
||||
3. Verify file permissions on anime directory
|
||||
|
||||
## Deprecation Timeline
|
||||
|
||||
| Version | Status |
|
||||
|---------|--------|
|
||||
| v2.0.x | Legacy files supported, migration automated |
|
||||
| v2.1.x | Legacy files still supported, warnings in logs |
|
||||
| v3.0.0 | **Legacy files removed** - database only |
|
||||
|
||||
Upgrade to v3.0.0 before legacy file support ends.
|
||||
@@ -31,14 +31,16 @@ flowchart TB
|
||||
|
||||
subgraph Core["Core Layer"]
|
||||
SeriesApp["SeriesApp"]
|
||||
SeriesCache["SeriesCache<br/>(In-Memory)"]
|
||||
SerieScanner["SerieScanner"]
|
||||
SerieList["SerieList"]
|
||||
end
|
||||
|
||||
subgraph Data["Data Layer"]
|
||||
SQLite[(SQLite<br/>aniworld.db)]
|
||||
SQLite[("SQLite<br/>aniworld.db")]
|
||||
ConfigJSON[(config.json)]
|
||||
FileSystem[(File System<br/>Anime Directory)]
|
||||
FileSystem[(File System<br/>Anime Episodes)]
|
||||
LegacyFiles[("Legacy Files<br/>key/data<br/>(Deprecated)")]
|
||||
end
|
||||
|
||||
subgraph External["External"]
|
||||
@@ -71,9 +73,13 @@ flowchart TB
|
||||
AnimeService --> SQLite
|
||||
|
||||
%% Core to Data
|
||||
SeriesApp --> SeriesCache
|
||||
SeriesCache -.->|Cached Series| SQLite
|
||||
SeriesApp --> SerieScanner
|
||||
SeriesApp --> SerieList
|
||||
SerieScanner --> FileSystem
|
||||
SerieScanner -->|Scan Episodes| FileSystem
|
||||
SerieScanner -->|Detect Series| SQLite
|
||||
SerieScanner -->|Migrate Legacy| LegacyFiles
|
||||
SerieScanner --> Provider
|
||||
|
||||
%% Event flow
|
||||
|
||||
Reference in New Issue
Block a user