Compare commits

..

No commits in common. "22ee445b7eb12f6eed8d23f9d161a2fefdfd00d1" and "aade02d7634788b0da861fa5f088aff66fe709d1" have entirely different histories.

11 changed files with 27 additions and 339 deletions

4
.gitmodules vendored
View File

@ -1,4 +0,0 @@
[submodule "AniWorld-Downloader"]
path = AniWorld-Downloader
url = https://github.com/lukaspupkalipinski/AniWorld-Downloader.git
branch = next

@ -1 +0,0 @@
Subproject commit 94e332f37b3f037d175e579ea370ab2be90534e4

View File

@ -11,7 +11,7 @@ from aniworld.parser import arguments
timeout = int(os.getenv("DOWNLOAD_TIMEOUT", 600)) timeout = int(os.getenv("DOWNLOAD_TIMEOUT", 600))
download_error_logger = logging.getLogger("DownloadErrors") download_error_logger = logging.getLogger("DownloadErrors")
download_error_handler = logging.FileHandler("../download_errors.log") download_error_handler = logging.FileHandler("download_errors.log")
download_error_handler.setLevel(logging.ERROR) download_error_handler.setLevel(logging.ERROR)
download_error_logger.addHandler(download_error_handler) download_error_logger.addHandler(download_error_handler)
@ -40,7 +40,7 @@ def download(anime: Anime): # pylint: disable=too-many-branches
"yt-dlp", "yt-dlp",
episode.get_direct_link(anime.provider, anime.language), episode.get_direct_link(anime.provider, anime.language),
"--fragment-retries", "infinite", "--fragment-retries", "infinite",
#"--concurrent-fragments", "4", "--concurrent-fragments", "4",
"-o", output_path, "-o", output_path,
"--quiet", "--quiet",
"--no-warnings" "--no-warnings"

Binary file not shown.

View File

@ -1,12 +1,14 @@
import sys
import os import os
import traceback import traceback
import re import re
import logging import logging
from concurrent.futures import ThreadPoolExecutor
from collections import defaultdict from collections import defaultdict
from aniworld.models import Anime, Episode from aniworld.models import Anime, Episode
from aniworld.common import get_season_episode_count from aniworld.common import get_season_episode_count, get_movie_episode_count
from aniworld.search import search_anime from aniworld.search import search_anime
from src.Loader import download from Loader import download
# Configure logging # Configure logging
@ -22,17 +24,17 @@ logging.getLogger('charset_normalizer').setLevel(logging.INFO)
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(logging.INFO)
error_logger = logging.getLogger("ErrorLog") error_logger = logging.getLogger("ErrorLog")
error_handler = logging.FileHandler("../errors.log") error_handler = logging.FileHandler("errors.log")
error_handler.setLevel(logging.ERROR) error_handler.setLevel(logging.ERROR)
error_logger.addHandler(error_handler) error_logger.addHandler(error_handler)
noKeyFound_logger = logging.getLogger("NoKeyFound") noKeyFound_logger = logging.getLogger("NoKeyFound")
noKeyFound_handler = logging.FileHandler("../NoKeyFound.log") noKeyFound_handler = logging.FileHandler("NoKeyFound.log")
noKeyFound_handler.setLevel(logging.ERROR) noKeyFound_handler.setLevel(logging.ERROR)
noKeyFound_logger.addHandler(noKeyFound_handler) noKeyFound_logger.addHandler(noKeyFound_handler)
noGerFound_logger = logging.getLogger("noGerFound") noGerFound_logger = logging.getLogger("noGerFound")
noGerFound_handler = logging.FileHandler("../noGerFound.log") noGerFound_handler = logging.FileHandler("noGerFound.log")
noGerFound_handler.setLevel(logging.ERROR) noGerFound_handler.setLevel(logging.ERROR)
noGerFound_logger.addHandler(noGerFound_handler) noGerFound_logger.addHandler(noGerFound_handler)
@ -165,23 +167,26 @@ class Loader:
except Exception as e: except Exception as e:
logging.error(f"Error downloading episode {episode} of season {season} for anime {key}: {e}") logging.error(f"Error downloading episode {episode} of season {season} for anime {key}: {e}")
for folder, mp4_files in result: # Using ThreadPoolExecutor to run downloads in parallel
try: with ThreadPoolExecutor(max_workers=2) as executor: # Adjust number of workers as needed
key = self.__check_and_generate_key(folder) for folder, mp4_files in result:
missings = self.__GetMissingEpisodesAndSeason(key, mp4_files) try:
for season, missing_episodes in missings: key = self.__check_and_generate_key(folder)
logging.info(f"Missing episodes for {key}\nSeason {str(season)}: Episodes: " + ",".join(f"{''.join(str(v))}" for v in missing_episodes)) missings = self.__GetMissingEpisodesAndSeason(key, mp4_files)
for episode in missing_episodes: for season, missing_episodes in missings:
download_episode(folder, season, episode, key) logging.info(f"Missing episodes for {key}\nSeason {str(season)}: Episodes: " + ",".join(f"{''.join(str(v))}" for v in missing_episodes))
except NoKeyFoundException as nkfe: for episode in missing_episodes:
noKeyFound_logger.error(f"Error processing folder '{folder}': {nkfe}") executor.submit(download_episode, folder, season, episode, key)
except Exception as e: #download_episode(folder, season, episode, key)
error_logger.error(f"Unexpected error processing folder '{folder}': {e} \n {traceback.format_exc()}") except NoKeyFoundException as nkfe:
continue 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 # 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", "\\\\sshfs.r\\ubuntu@192.168.178.43\\media\\serien\\Serien")
#directory_to_search = os.getenv("ANIME_DIRECTORY", "D:\\sss") #directory_to_search = os.getenv("ANIME_DIRECTORY", "D:\sss")
loader = Loader(directory_to_search) loader = Loader(directory_to_search)
loader.LoadMissing() loader.LoadMissing()

View File

@ -1,3 +1,4 @@
aniworld
requests requests
beautifulsoup4 beautifulsoup4
lxml lxml

View File

@ -1,7 +0,0 @@
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

View File

@ -1,160 +0,0 @@
import os
import re
import logging
from collections import defaultdict
import pickle
from Serie import Serie
import json
import traceback
from GlobalLogger import setupLogger, error_logger, noKeyFound_logger, noGerFound_logger
from Exceptions import NoKeyFoundException, MatchNotFoundError
import requests
from aniworld.common import get_season_episode_count
class FolderLookup:
def __init__(self, basePath: str):
self.directory = basePath
self.folderDict = dict[str, list[Serie]]
logging.info(f"Initialized Loader with base path: {self.directory}")
self.__init()
def is_null_or_whitespace(self, s):
return s is None or s.strip() == ""
def __init(self):
logging.info("Starting process to load missing episodes")
result = self.__find_mp4_files()
for folder, mp4_files in result:
try:
serie = self.__ReadDataFromFile(folder)
if (serie != None and not self.is_null_or_whitespace(serie.key)):
missings, site = self.__GetMissingEpisodesAndSeason(serie.key, mp4_files)
serie.episodeDict = missings
self.__SaveData(serie, folder)
if folder not in self.folderDict:
self.folderDict[folder] = []
self.folderDict[folder].append(serie)
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
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 __ReadDataFromFile(self, folder_name: str):
folder_path = os.path.join(self.directory, folder_name)
key = None
key_file = os.path.join(folder_path, 'key')
serie_file = os.path.join(folder_path, 'data')
if os.path.exists(key_file):
with open(key_file, 'r') as file:
logging.info(f"Key found for folder '{folder_name}': {key}")
key = file.read().strip()
return Serie(key, "", "aniworld.to" ,folder_name)
if os.path.exists(serie_file):
with open(serie_file, "rb") as file:
logging.info(f"load serie_file from '{folder_name}': {serie_file}")
return pickle.load(file)
return None
def __SaveData(self, serie: Serie, folder_name: str):
"""Saves a Serie object to a file using JSON."""
folder_path = os.path.join(self.directory, folder_name)
serie_file = os.path.join(folder_path, 'data')
with open(serie_file, "w", encoding="utf-8") as file:
json.dump(serie, file, indent=4)
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)
episodes_dict = {}
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:
episodes_dict[season] = [missing_episodes]
return episodes_dict, "aniworld.to"
#gg = FolderLookup("\\\\sshfs.r\\ubuntu@192.168.178.43\\media\\serien\\Serien")
base_url = f"https://aniworld.to/anime/stream/aico-incarnation"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
"Accept": "*/*",
"Referer": "https://www.example.com"
}
session = requests.Session()
response = requests.get(base_url, headers=headers, timeout=30)
c = response.content;

View File

@ -1,40 +0,0 @@
import logging
console_handler = None
error_logger = None
noKeyFound_logger = None
noGerFound_logger = None
def setupLogger():
global console_handler, error_logger, noKeyFound_logger, noGerFound_logger
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(funcName)s - %(message)s')
if (console_handler is None):
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)
if (error_logger is None):
error_logger = logging.getLogger("ErrorLog")
error_handler = logging.FileHandler("../errors.log")
error_handler.setLevel(logging.ERROR)
error_logger.addHandler(error_handler)
if (noKeyFound_logger is None):
noKeyFound_logger = logging.getLogger("NoKeyFound")
noKeyFound_handler = logging.FileHandler("../NoKeyFound.log")
noKeyFound_handler.setLevel(logging.ERROR)
noKeyFound_logger.addHandler(noKeyFound_handler)
if (noGerFound_logger is None):
noGerFound_logger = logging.getLogger("noGerFound")
noGerFound_handler = logging.FileHandler("../noGerFound.log")
noGerFound_handler.setLevel(logging.ERROR)
noGerFound_logger.addHandler(noGerFound_handler)
setupLogger()

View File

@ -1,47 +0,0 @@
class Serie:
def __init__(self, key: str, name: str, site: str, folder: str):
self._key = key
self._name = name
self._site = site
self._folder = folder
self._episodeDict = dict[int, list[int]]
@property
def key(self) -> str:
return self._key
@key.setter
def key(self, value: str):
self._key = value
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, value: str):
self._name = value
@property
def site(self) -> str:
return self._site
@site.setter
def site(self, value: str):
self._site = value
@property
def folder(self) -> str:
return self._folder
@folder.setter
def folder(self, value: str):
self._folder = value
@property
def episodeDict(self) -> dict[int, list[int]]:
return self._episodeDict
@episodeDict.setter
def episodeDict(self, value: dict[int, list[int]]):
self._episodeDict = value

View File

@ -1,59 +0,0 @@
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 ConsoleLoader:
def __init__(self, basePath: str):
self.directory = basePath
logging.info(f"Initialized Loader with base path: {self.directory}")
# 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")
loader = Loader(directory_to_search)
loader.LoadMissing()