Compare commits
2 Commits
aade02d763
...
22ee445b7e
| Author | SHA1 | Date | |
|---|---|---|---|
| 22ee445b7e | |||
| cb6e74199b |
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
[submodule "AniWorld-Downloader"]
|
||||
path = AniWorld-Downloader
|
||||
url = https://github.com/lukaspupkalipinski/AniWorld-Downloader.git
|
||||
branch = next
|
||||
1
AniWorld-Downloader
Submodule
1
AniWorld-Downloader
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 94e332f37b3f037d175e579ea370ab2be90534e4
|
||||
Binary file not shown.
@ -1,4 +1,3 @@
|
||||
aniworld
|
||||
requests
|
||||
beautifulsoup4
|
||||
lxml
|
||||
|
||||
7
src/Exceptions.py
Normal file
7
src/Exceptions.py
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
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
|
||||
160
src/FolderLookup.py
Normal file
160
src/FolderLookup.py
Normal file
@ -0,0 +1,160 @@
|
||||
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;
|
||||
40
src/GlobalLogger.py
Normal file
40
src/GlobalLogger.py
Normal file
@ -0,0 +1,40 @@
|
||||
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()
|
||||
@ -11,7 +11,7 @@ from aniworld.parser import arguments
|
||||
timeout = int(os.getenv("DOWNLOAD_TIMEOUT", 600))
|
||||
|
||||
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_logger.addHandler(download_error_handler)
|
||||
|
||||
@ -40,7 +40,7 @@ def download(anime: Anime): # pylint: disable=too-many-branches
|
||||
"yt-dlp",
|
||||
episode.get_direct_link(anime.provider, anime.language),
|
||||
"--fragment-retries", "infinite",
|
||||
"--concurrent-fragments", "4",
|
||||
#"--concurrent-fragments", "4",
|
||||
"-o", output_path,
|
||||
"--quiet",
|
||||
"--no-warnings"
|
||||
47
src/Serie.py
Normal file
47
src/Serie.py
Normal file
@ -0,0 +1,47 @@
|
||||
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
|
||||
59
src/console.py
Normal file
59
src/console.py
Normal file
@ -0,0 +1,59 @@
|
||||
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()
|
||||
@ -1,14 +1,12 @@
|
||||
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.common import get_season_episode_count
|
||||
from aniworld.search import search_anime
|
||||
from Loader import download
|
||||
from src.Loader import download
|
||||
|
||||
|
||||
# Configure logging
|
||||
@ -24,17 +22,17 @@ 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 = 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 = 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 = logging.FileHandler("../noGerFound.log")
|
||||
noGerFound_handler.setLevel(logging.ERROR)
|
||||
noGerFound_logger.addHandler(noGerFound_handler)
|
||||
|
||||
@ -167,8 +165,6 @@ class Loader:
|
||||
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)
|
||||
@ -176,8 +172,7 @@ class Loader:
|
||||
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)
|
||||
download_episode(folder, season, episode, key)
|
||||
except NoKeyFoundException as nkfe:
|
||||
noKeyFound_logger.error(f"Error processing folder '{folder}': {nkfe}")
|
||||
except Exception as e:
|
||||
@ -186,7 +181,7 @@ class Loader:
|
||||
|
||||
# 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")
|
||||
#directory_to_search = os.getenv("ANIME_DIRECTORY", "D:\\sss")
|
||||
|
||||
loader = Loader(directory_to_search)
|
||||
loader.LoadMissing()
|
||||
Loading…
x
Reference in New Issue
Block a user