feat: cleanup

This commit is contained in:
Benexl
2025-07-20 19:34:19 +03:00
parent ac3c6801d7
commit 725fe4875d
6 changed files with 0 additions and 308 deletions

View File

@@ -1,7 +0,0 @@
"""
Integration services for synchronizing watch history and download tracking.
"""
from .sync import HistoryDownloadSync
__all__ = ["HistoryDownloadSync"]

View File

@@ -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)