mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-12 15:50:01 -08:00
feat: cleanup
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
"""
|
||||
Integration services for synchronizing watch history and download tracking.
|
||||
"""
|
||||
|
||||
from .sync import HistoryDownloadSync
|
||||
|
||||
__all__ = ["HistoryDownloadSync"]
|
||||
@@ -1,301 +0,0 @@
|
||||
"""
|
||||
Synchronization service between watch history and download tracking.
|
||||
|
||||
This module provides functionality to keep watch history and download status
|
||||
in sync, enabling features like offline availability markers and smart
|
||||
download suggestions based on viewing patterns.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import List, Optional
|
||||
|
||||
from ....libs.api.types import MediaItem
|
||||
from ..downloads.manager import DownloadManager
|
||||
from ..watch_history.manager import WatchHistoryManager
|
||||
from ..watch_history.types import WatchHistoryEntry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HistoryDownloadSync:
|
||||
"""
|
||||
Service to synchronize watch history and download tracking.
|
||||
|
||||
Provides bidirectional synchronization between viewing history and
|
||||
download status, enabling features like offline availability and
|
||||
smart download recommendations.
|
||||
"""
|
||||
|
||||
def __init__(self, watch_manager: WatchHistoryManager, download_manager: DownloadManager):
|
||||
self.watch_manager = watch_manager
|
||||
self.download_manager = download_manager
|
||||
|
||||
def sync_download_status(self, media_id: int) -> bool:
|
||||
"""
|
||||
Update watch history with download availability status.
|
||||
|
||||
Args:
|
||||
media_id: The media ID to sync
|
||||
|
||||
Returns:
|
||||
True if sync was successful
|
||||
"""
|
||||
try:
|
||||
# Get download record
|
||||
download_record = self.download_manager.get_download_record(media_id)
|
||||
if not download_record:
|
||||
return False
|
||||
|
||||
# Get watch history entry
|
||||
watch_entry = self.watch_manager.get_entry_by_media_id(media_id)
|
||||
if not watch_entry:
|
||||
return False
|
||||
|
||||
# Check if any episodes are downloaded
|
||||
has_downloads = any(
|
||||
ep.is_completed for ep in download_record.episodes.values()
|
||||
)
|
||||
|
||||
# Check if current/next episode is available offline
|
||||
current_episode = watch_entry.last_watched_episode
|
||||
next_episode = current_episode + 1
|
||||
|
||||
offline_available = (
|
||||
current_episode in download_record.episodes and
|
||||
download_record.episodes[current_episode].is_completed
|
||||
) or (
|
||||
next_episode in download_record.episodes and
|
||||
download_record.episodes[next_episode].is_completed
|
||||
)
|
||||
|
||||
# Update watch history entry
|
||||
updated_entry = watch_entry.model_copy(update={
|
||||
"has_downloads": has_downloads,
|
||||
"offline_available": offline_available
|
||||
})
|
||||
|
||||
return self.watch_manager.save_entry(updated_entry)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to sync download status for media {media_id}: {e}")
|
||||
return False
|
||||
|
||||
def mark_episodes_offline_available(self, media_id: int, episodes: List[int]) -> bool:
|
||||
"""
|
||||
Mark specific episodes as available offline in watch history.
|
||||
|
||||
Args:
|
||||
media_id: The media ID
|
||||
episodes: List of episode numbers that are available offline
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
try:
|
||||
watch_entry = self.watch_manager.get_entry_by_media_id(media_id)
|
||||
if not watch_entry:
|
||||
return False
|
||||
|
||||
# Check if current or next episode is in the available episodes
|
||||
current_episode = watch_entry.last_watched_episode
|
||||
next_episode = current_episode + 1
|
||||
|
||||
offline_available = (
|
||||
current_episode in episodes or
|
||||
next_episode in episodes or
|
||||
len(episodes) > 0 # Any episodes available
|
||||
)
|
||||
|
||||
updated_entry = watch_entry.model_copy(update={
|
||||
"has_downloads": len(episodes) > 0,
|
||||
"offline_available": offline_available
|
||||
})
|
||||
|
||||
return self.watch_manager.save_entry(updated_entry)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to mark episodes offline available for media {media_id}: {e}")
|
||||
return False
|
||||
|
||||
def suggest_downloads_for_watching(self, media_id: int, lookahead: int = 3) -> List[int]:
|
||||
"""
|
||||
Suggest episodes to download based on watch history.
|
||||
|
||||
Args:
|
||||
media_id: The media ID
|
||||
lookahead: Number of episodes ahead to suggest
|
||||
|
||||
Returns:
|
||||
List of episode numbers to download
|
||||
"""
|
||||
try:
|
||||
watch_entry = self.watch_manager.get_entry_by_media_id(media_id)
|
||||
if not watch_entry or watch_entry.status != "watching":
|
||||
return []
|
||||
|
||||
download_record = self.download_manager.get_download_record(media_id)
|
||||
if not download_record:
|
||||
return []
|
||||
|
||||
# Get currently downloaded episodes
|
||||
downloaded_episodes = set(
|
||||
ep_num for ep_num, ep in download_record.episodes.items()
|
||||
if ep.is_completed
|
||||
)
|
||||
|
||||
# Suggest next episodes
|
||||
current_episode = watch_entry.last_watched_episode
|
||||
total_episodes = watch_entry.media_item.episodes or 999
|
||||
|
||||
suggestions = []
|
||||
for i in range(1, lookahead + 1):
|
||||
next_episode = current_episode + i
|
||||
if (next_episode <= total_episodes and
|
||||
next_episode not in downloaded_episodes):
|
||||
suggestions.append(next_episode)
|
||||
|
||||
return suggestions
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to suggest downloads for media {media_id}: {e}")
|
||||
return []
|
||||
|
||||
def suggest_downloads_for_completed(self, limit: int = 5) -> List[MediaItem]:
|
||||
"""
|
||||
Suggest anime to download based on completed watch history.
|
||||
|
||||
Args:
|
||||
limit: Maximum number of suggestions
|
||||
|
||||
Returns:
|
||||
List of MediaItems to consider for download
|
||||
"""
|
||||
try:
|
||||
# Get completed anime from watch history
|
||||
completed_entries = self.watch_manager.get_entries_by_status("completed")
|
||||
|
||||
suggestions = []
|
||||
for entry in completed_entries[:limit]:
|
||||
# Check if not already fully downloaded
|
||||
download_record = self.download_manager.get_download_record(entry.media_item.id)
|
||||
|
||||
if not download_record:
|
||||
suggestions.append(entry.media_item)
|
||||
elif download_record.completion_percentage < 100:
|
||||
suggestions.append(entry.media_item)
|
||||
|
||||
return suggestions
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to suggest downloads for completed anime: {e}")
|
||||
return []
|
||||
|
||||
def sync_all_entries(self) -> int:
|
||||
"""
|
||||
Sync download status for all watch history entries.
|
||||
|
||||
Returns:
|
||||
Number of entries successfully synced
|
||||
"""
|
||||
try:
|
||||
watch_entries = self.watch_manager.get_all_entries()
|
||||
synced_count = 0
|
||||
|
||||
for entry in watch_entries:
|
||||
if self.sync_download_status(entry.media_item.id):
|
||||
synced_count += 1
|
||||
|
||||
logger.info(f"Synced download status for {synced_count}/{len(watch_entries)} entries")
|
||||
return synced_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to sync all entries: {e}")
|
||||
return 0
|
||||
|
||||
def update_watch_progress_from_downloads(self, media_id: int) -> bool:
|
||||
"""
|
||||
Update watch progress based on downloaded episodes.
|
||||
|
||||
Useful when episodes are watched outside the app but files exist.
|
||||
|
||||
Args:
|
||||
media_id: The media ID to update
|
||||
|
||||
Returns:
|
||||
True if successful
|
||||
"""
|
||||
try:
|
||||
download_record = self.download_manager.get_download_record(media_id)
|
||||
if not download_record:
|
||||
return False
|
||||
|
||||
watch_entry = self.watch_manager.get_entry_by_media_id(media_id)
|
||||
if not watch_entry:
|
||||
# Create new watch entry if none exists
|
||||
watch_entry = WatchHistoryEntry(
|
||||
media_item=download_record.media_item,
|
||||
status="watching"
|
||||
)
|
||||
|
||||
# Find highest downloaded episode
|
||||
downloaded_episodes = [
|
||||
ep_num for ep_num, ep in download_record.episodes.items()
|
||||
if ep.is_completed
|
||||
]
|
||||
|
||||
if downloaded_episodes:
|
||||
max_downloaded = max(downloaded_episodes)
|
||||
|
||||
# Only update if we have more episodes downloaded than watched
|
||||
if max_downloaded > watch_entry.last_watched_episode:
|
||||
updated_entry = watch_entry.model_copy(update={
|
||||
"last_watched_episode": max_downloaded,
|
||||
"watch_progress": 1.0, # Assume completed if downloaded
|
||||
"has_downloads": True,
|
||||
"offline_available": True
|
||||
})
|
||||
|
||||
return self.watch_manager.save_entry(updated_entry)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update watch progress from downloads for media {media_id}: {e}")
|
||||
return False
|
||||
|
||||
def get_offline_watchable_anime(self) -> List[WatchHistoryEntry]:
|
||||
"""
|
||||
Get list of anime that can be watched offline.
|
||||
|
||||
Returns:
|
||||
List of watch history entries with offline episodes available
|
||||
"""
|
||||
try:
|
||||
watch_entries = self.watch_manager.get_all_entries()
|
||||
offline_entries = []
|
||||
|
||||
for entry in watch_entries:
|
||||
if entry.offline_available:
|
||||
offline_entries.append(entry)
|
||||
else:
|
||||
# Double-check by looking at downloads
|
||||
download_record = self.download_manager.get_download_record(entry.media_item.id)
|
||||
if download_record:
|
||||
next_episode = entry.last_watched_episode + 1
|
||||
if (next_episode in download_record.episodes and
|
||||
download_record.episodes[next_episode].is_completed):
|
||||
offline_entries.append(entry)
|
||||
|
||||
return offline_entries
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get offline watchable anime: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def create_sync_service(watch_manager: WatchHistoryManager,
|
||||
download_manager: DownloadManager) -> HistoryDownloadSync:
|
||||
"""Factory function to create a synchronization service."""
|
||||
return HistoryDownloadSync(watch_manager, download_manager)
|
||||
Reference in New Issue
Block a user