192 lines
8.3 KiB
Python
192 lines
8.3 KiB
Python
import sys
|
|
import os
|
|
import traceback
|
|
import re
|
|
import logging
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
from collections import defaultdict
|
|
from aniworld.models import Anime, Episode
|
|
from aniworld.common import get_season_episode_count, get_movie_episode_count
|
|
from aniworld.search import search_anime
|
|
from Loader import download
|
|
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(funcName)s - %(message)s')
|
|
console_handler = logging.StreamHandler()
|
|
console_handler.setLevel(logging.INFO)
|
|
console_handler.setFormatter(logging.Formatter(
|
|
"%(asctime)s - %(levelname)s - %(funcName)s - %(message)s")
|
|
)
|
|
logging.getLogger().addHandler(console_handler)
|
|
logging.getLogger("urllib3.connectionpool").setLevel(logging.INFO)
|
|
logging.getLogger('charset_normalizer').setLevel(logging.INFO)
|
|
logging.getLogger().setLevel(logging.INFO)
|
|
|
|
error_logger = logging.getLogger("ErrorLog")
|
|
error_handler = logging.FileHandler("errors.log")
|
|
error_handler.setLevel(logging.ERROR)
|
|
error_logger.addHandler(error_handler)
|
|
|
|
noKeyFound_logger = logging.getLogger("NoKeyFound")
|
|
noKeyFound_handler = logging.FileHandler("NoKeyFound.log")
|
|
noKeyFound_handler.setLevel(logging.ERROR)
|
|
noKeyFound_logger.addHandler(noKeyFound_handler)
|
|
|
|
noGerFound_logger = logging.getLogger("noGerFound")
|
|
noGerFound_handler = logging.FileHandler("noGerFound.log")
|
|
noGerFound_handler.setLevel(logging.ERROR)
|
|
noGerFound_logger.addHandler(noGerFound_handler)
|
|
|
|
class NoKeyFoundException(Exception):
|
|
"""Exception raised when an anime key cannot be found."""
|
|
pass
|
|
class MatchNotFoundError(Exception):
|
|
"""Exception raised when an anime key cannot be found."""
|
|
pass
|
|
|
|
class Loader:
|
|
def __init__(self, basePath: str):
|
|
self.directory = basePath
|
|
logging.info(f"Initialized Loader with base path: {self.directory}")
|
|
|
|
def __find_mp4_files(self):
|
|
logging.info("Scanning for .mp4 files")
|
|
|
|
for root_folder_name in os.listdir(self.directory):
|
|
|
|
folder_data = defaultdict(list) # Dictionary to store MP4 files per folder
|
|
folder = os.path.join(self.directory, root_folder_name)
|
|
|
|
logging.info(f"Processing folder: {root_folder_name}")
|
|
|
|
# First pass: Scan all folders and collect MP4 file data
|
|
for root, dirs, files in os.walk(folder):
|
|
mp4_files = [file for file in files if file.endswith('.mp4')]
|
|
if mp4_files:
|
|
folder_data[root_folder_name].extend(mp4_files)
|
|
yield root_folder_name, folder_data[root_folder_name]
|
|
|
|
for dir in self.__find_empty_folders():
|
|
logging.info(f"Found no .mp4 files in {dir}")
|
|
yield dir, []
|
|
|
|
def __find_empty_folders(self):
|
|
"""Yield folder names that do not contain any mp4 files in a given directory."""
|
|
for folder in os.listdir(self.directory):
|
|
folder_path = os.path.join(self.directory, folder)
|
|
|
|
if os.path.isdir(folder_path): # Ensure it's a directory
|
|
has_mp4 = any(file.endswith(".mp4") for file in os.listdir(folder_path))
|
|
|
|
if not has_mp4:
|
|
yield folder # Yield the folder name if no mp4 files found
|
|
|
|
def __remove_year(self, input_string: str):
|
|
cleaned_string = re.sub(r'\(\d{4}\)', '', input_string).strip()
|
|
logging.debug(f"Removed year from '{input_string}' -> '{cleaned_string}'")
|
|
return cleaned_string
|
|
|
|
def __check_and_generate_key(self, folder_name: str):
|
|
folder_path = os.path.join(self.directory, folder_name)
|
|
key_file = os.path.join(folder_path, 'key')
|
|
|
|
if os.path.exists(key_file):
|
|
with open(key_file, 'r') as file:
|
|
key = file.read().strip()
|
|
logging.info(f"Key found for folder '{folder_name}': {key}")
|
|
return key
|
|
else:
|
|
try:
|
|
key = search_anime(folder_name, True)
|
|
if key:
|
|
key = key[0]['link']
|
|
with open(key_file, 'w') as file:
|
|
file.write(key)
|
|
logging.info(f"Generated new key for folder '{folder_name}': {key}")
|
|
return key
|
|
else:
|
|
raise NoKeyFoundException(f"No key found for folder '{folder_name}'")
|
|
except Exception as e:
|
|
raise NoKeyFoundException(f"Failed to retrieve key for folder '{folder_name}'") from e
|
|
|
|
def __GetEpisodeAndSeason(self, filename: str):
|
|
pattern = r'S(\d+)E(\d+)'
|
|
match = re.search(pattern, filename)
|
|
if match:
|
|
season = match.group(1)
|
|
episode = match.group(2)
|
|
logging.debug(f"Extracted season {season}, episode {episode} from '{filename}'")
|
|
return int(season), int(episode)
|
|
else:
|
|
logging.error(f"Failed to find season/episode pattern in '{filename}'")
|
|
raise MatchNotFoundError("Season and episode pattern not found in the filename.")
|
|
|
|
def __GetEpisodesAndSeasons(self, mp4_files: []):
|
|
episodes_dict = {}
|
|
|
|
for file in mp4_files:
|
|
season, episode = self.__GetEpisodeAndSeason(file)
|
|
|
|
if season in episodes_dict:
|
|
episodes_dict[season].append(episode)
|
|
else:
|
|
episodes_dict[season] = [episode]
|
|
|
|
return episodes_dict
|
|
|
|
def __GetMissingEpisodesAndSeason(self, key: str, mp4_files: []):
|
|
expected_dict = get_season_episode_count(key) # key season , value count of episodes
|
|
filedict = self.__GetEpisodesAndSeasons(mp4_files)
|
|
|
|
for season, expected_count in expected_dict.items():
|
|
existing_episodes = filedict.get(season, [])
|
|
missing_episodes = [ep for ep in range(1, expected_count + 1) if ep not in existing_episodes]
|
|
|
|
if missing_episodes:
|
|
yield season, missing_episodes
|
|
|
|
def LoadMissing(self):
|
|
logging.info("Starting process to load missing episodes")
|
|
result = self.__find_mp4_files()
|
|
|
|
def download_episode(folder, season, episode, key):
|
|
"""Helper function to download an individual episode."""
|
|
try:
|
|
folder_path = os.path.join(self.directory, folder, f"Season {season}")
|
|
anime = Anime(
|
|
episode_list=[Episode(slug=key, season=season, episode=episode)],
|
|
language="German Dub",
|
|
output_directory=folder_path
|
|
)
|
|
logging.info(f"Downloading anime {key} season {season} episode {episode}")
|
|
download(anime)
|
|
logging.info(f"Downloading completed anime {key} season {season} episode {episode}")
|
|
except KeyError as keye:
|
|
noGerFound_logger.error(f"Language not found for anime: {key} season: {season} episode: {episode}")
|
|
except Exception as e:
|
|
logging.error(f"Error downloading episode {episode} of season {season} for anime {key}: {e}")
|
|
|
|
# Using ThreadPoolExecutor to run downloads in parallel
|
|
with ThreadPoolExecutor(max_workers=2) as executor: # Adjust number of workers as needed
|
|
for folder, mp4_files in result:
|
|
try:
|
|
key = self.__check_and_generate_key(folder)
|
|
missings = self.__GetMissingEpisodesAndSeason(key, mp4_files)
|
|
for season, missing_episodes in missings:
|
|
logging.info(f"Missing episodes for {key}\nSeason {str(season)}: Episodes: " + ",".join(f"{''.join(str(v))}" for v in missing_episodes))
|
|
for episode in missing_episodes:
|
|
executor.submit(download_episode, folder, season, episode, key)
|
|
#download_episode(folder, season, episode, key)
|
|
except NoKeyFoundException as nkfe:
|
|
noKeyFound_logger.error(f"Error processing folder '{folder}': {nkfe}")
|
|
except Exception as e:
|
|
error_logger.error(f"Unexpected error processing folder '{folder}': {e} \n {traceback.format_exc()}")
|
|
continue
|
|
|
|
# Read the base directory from an environment variable
|
|
directory_to_search = os.getenv("ANIME_DIRECTORY", "\\\\sshfs.r\\ubuntu@192.168.178.43\\media\\serien\\Serien")
|
|
#directory_to_search = os.getenv("ANIME_DIRECTORY", "D:\sss")
|
|
|
|
loader = Loader(directory_to_search)
|
|
loader.LoadMissing() |