many changes

This commit is contained in:
2025-09-28 20:03:18 +02:00
parent f455918ef4
commit 4d0d412d38
8 changed files with 632 additions and 297 deletions

View File

@@ -1,15 +1,18 @@
import os
import re
from komconfig import KomConfig
from pathlib import Path
import shutil
import subprocess
import jaro
import loguru
import sys
import time
from komgapi import komgapi
from pathlib import Path
import jaro
import loguru
from komcache import KomCache
from komconfig import KomConfig
from komconfig.config import Library
from komgapi import komgapi
from komgapi.schemas import Series
cfg = KomConfig()
@@ -21,7 +24,7 @@ config = KomConfig()
komga = komgapi(cfg.komga.user, cfg.komga.password, cfg.komga.url)
def rename(folder: Path = config.komgrabber.download_location) -> None:
def rename(folder: Path = config.komgrabber.tag_location) -> None:
"""Rename the files in a folder according to the template.
Template: [Name] v[nr] #[nr].ext (e.g. "The Flash v1 #1.cbz").
@@ -61,14 +64,14 @@ def rename_recursive(folder: str) -> None:
rename(Path(f"{root}/{dir}"))
def tag_folder(folder: Path = config.komgrabber.download_location) -> None:
def tag_folder(folder: Path = config.komgrabber.tag_location) -> None:
"""
Recursively tags all the .cbz files in the folder using ComicTagger
Parameters
----------
folder : Path, optional
The path that will be used to tag, by default Path(config.komgrabber.download_location)
The path that will be used to tag, by default Path(config.komgrabber.tag_location)
"""
# Get the files in the folder
if "~" in str(folder):
@@ -88,7 +91,7 @@ def tag_folder(folder: Path = config.komgrabber.download_location) -> None:
)
def move(src: Path, dest: Path = Path(config.komga.media_path)) -> None:
def move(src: Path, library_path: str) -> None:
"""
Moves the files from the source folder to the destination folder.
If the folder already exists in the destination, only move the new files.
@@ -97,12 +100,13 @@ def move(src: Path, dest: Path = Path(config.komga.media_path)) -> None:
----------
src : Path
The source folder
dest : Path, optional
The destination folder used by Komga, by default Path(config.komga.media_path)
dest : str
The destination folder used by Komga for the library, set in config file, defaults to "Manga"
"""
# Get the files in the folder
# +move the folders from src to disc, if folder already exists, only move new files
dest = Path(config.komga.media_path, library_path)
folders = os.listdir(src)
for folder in folders:
if not os.path.exists(f"{dest}/{folder}"):
@@ -116,6 +120,9 @@ def move(src: Path, dest: Path = Path(config.komga.media_path)) -> None:
else:
files = os.listdir(f"{src}/{folder}")
for file in files:
if file.startswith("."):
log.debug(f"Skipping hidden file {file}")
continue
if not os.path.exists(f"{dest}/{folder}/{file}"):
log.info(f"Moving {file} to {dest}/{folder}")
shutil.move(f"{src}/{folder}/{file}", f"{dest}/{folder}")
@@ -123,7 +130,7 @@ def move(src: Path, dest: Path = Path(config.komga.media_path)) -> None:
remove_empty_folders(src)
def remove_empty_folders(src):
def remove_empty_folders(src: Path):
"""
Recursively removes empty folders in the source folder
@@ -140,36 +147,67 @@ def remove_empty_folders(src):
log.info(f"Removing {folder}")
os.rmdir(f"{src}/{folder}")
else:
remove_empty_folders(f"{src}/{folder}")
newPath = Path(f"{src}/{folder}")
remove_empty_folders(newPath)
def detect_chapters(src: Path = config.komgrabber.download_location) -> None:
def detect_chapters(
src: Path = config.komgrabber.tag_location, valid_extension: str = "cbz|epub"
) -> None:
"""
Detects and deletes any non-volume file in the source folder
Parameters
----------
src : Path, optional
The Path to be checked, by default Path(config.komgrabber.download_location)
The Path to be checked, by default Path(config.komgrabber.tag_location)
"""
log.info(f"Checking {src} for chapters")
regex = re.compile(rf"^.* v(\d+) #(\d+(?:-\d+)?)\.({valid_extension})$")
for folder in os.listdir(src):
if os.path.isdir(f"{src}/{folder}"):
files = os.listdir(f"{src}/{folder}")
for file in files:
if os.path.isdir(f"{src}/{folder}/{file}"):
folder_files = os.listdir(f"{src}/{folder}/{file}")
for folder_file in folder_files:
# check for regex "v(d) #(d)" in the file name
if regex.search(folder_file):
log.debug(f"File {folder_file} is a Volume")
else:
log.info(f"Deleting chapter {folder_file}")
if os.path.isfile(f"{src}/{folder}/{file}/{folder_file}"):
os.remove(f"{src}/{folder}/{file}/{folder_file}")
else:
shutil.rmtree(f"{src}/{folder}/{file}/{folder_file}")
# check for regex "v(d) #(d)" in the file name
regex = re.compile(r"^.* v(\d+) #(\d+(?:-\d+)?)\.cbz$")
if regex.search(file):
log.debug(f"File {file} is a Volume")
else:
log.debug(f"Deleting chapter {file}")
if os.path.isdir(f"{src}/{folder}/{file}"):
shutil.rmtree(f"{src}/{folder}/{file}")
else:
log.info(f"Deleting chapter {file}")
if os.path.isfile(f"{src}/{folder}/{file}"):
os.remove(f"{src}/{folder}/{file}")
else:
if os.path.isdir(f"{src}/{folder}/{file}"):
for subfile in os.listdir(f"{src}/{folder}/{file}"):
if regex.search(subfile):
log.debug(f"File {subfile} is a Volume")
else:
log.info(f"Deleting chapter {subfile}")
if os.path.isfile(
f"{src}/{folder}/{file}/{subfile}"
):
os.remove(f"{src}/{folder}/{file}/{subfile}")
else:
shutil.rmtree(
f"{src}/{folder}/{file}/{subfile}"
)
else:
shutil.rmtree(f"{src}/{folder}/{file}")
def folder_similarity(folder1, folder2) -> float:
def folder_similarity(folder1: str, folder2: str) -> float:
"""
Calculate the similarity between two folder names using Jaro-Winkler distance.
@@ -184,7 +222,9 @@ def folder_similarity(folder1, folder2) -> float:
return similarity
def rename_folder(src=config.komgrabber.download_location, series=None) -> bool:
def rename_folder(
src: Path = config.komgrabber.tag_location, series: Series = None
) -> bool:
renamer_regex = r"(\s*\([^)]*\))+$"
for folder in os.listdir(src):
if os.path.isdir(f"{src}/{folder}"):
@@ -235,11 +275,16 @@ def calculate_new_volumes(
present_volumes: list[int], new_volumes: list[int]
) -> list[int]:
if len(new_volumes) == 1:
if max(new_volumes) > max(present_volumes):
if len(present_volumes) == 0:
return new_volumes
if max(new_volumes) > max(present_volumes):
# return any new volume that is not in present volumes
return [v for v in new_volumes if v not in present_volumes]
else:
return []
else:
if len(present_volumes) == 0:
return new_volumes
new_volumes = sorted(new_volumes)
new_volumes = [i for i in new_volumes if i > max(present_volumes)]
if len(new_volumes) == 0:
@@ -283,3 +328,138 @@ def get_series_update_date(series_name: str) -> str:
args=(series_name,),
)
print(update_date)
def process_manga(download_path: Path, library: Library, serie: Series) -> None:
"""Process the downloaded manga: rename files, detect chapters, tag, rename folder, and move to library."""
rename(download_path)
if not config.komgrabber.get_chapters:
detect_chapters(download_path, "|".join(library.valid_extensions))
tag_folder(download_path)
if rename_folder(series=serie, src=download_path):
move(
download_path,
library.media_path,
)
def process_novel(
download_path: Path, library: Library, serie: Series, copy: bool = False
) -> None:
"""Process the downloaded novel: rename files, tag, rename folder, and move to library."""
# rename the folder to the series name
folder = os.listdir(download_path)[0]
series_name = serie.name
# remove all files that are not valid extensions
valid_extensions = library.valid_extensions
# flatten subfolders and subsubfolders
for root, dirs, files in os.walk(f"{download_path}/{folder}"):
for dir in dirs:
for file in os.listdir(f"{root}/{dir}"):
if file.startswith("."):
log.debug(f"Skipping hidden file {file}")
continue
log.info(f"Moving {file} to {download_path}/{folder}")
shutil.move(f"{root}/{dir}/{file}", f"{download_path}/{folder}")
os.rmdir(f"{root}/{dir}")
# removing invalid extensions
for file in os.listdir(f"{download_path}/{folder}"):
if not any(file.endswith(ext) for ext in valid_extensions):
log.info(f"Removing {file} as it is not a valid extension")
if os.path.isfile(f"{download_path}/{folder}/{file}"):
os.remove(f"{download_path}/{folder}/{file}")
else:
shutil.rmtree(f"{download_path}/{folder}/{file}")
# rename files to remove all [] and text within
for file in os.listdir(f"{download_path}/{folder}"):
filename = file.split(".")[0]
if f"{series_name} - Volume" in filename:
log.debug(f"Skipping {file}, already renamed")
continue
# extract the volume number, may be a float, either v1, v1.5, v01, v01.5, vol.1, vol.01, vol.1.5, vol.01.5, Vol.1, Vol.01, Vol.1.5, Vol.01.5, Volume 1, Volume 01, Volume 1.5, Volume 01.5
regex_volume_pattern = r"(v|vol\.|Vol\.|Volume\s)(\d+(\.\d+)?)"
match = re.search(regex_volume_pattern, file, re.IGNORECASE)
# from the match, get the volume number
volume = match.group(2) if match else None
# rename the file to series name v(volume).ext
ext = file.split(".")[-1]
# if volume is not null and less than 10, pad with a 0
if volume and float(volume) < 10:
volume = f"0{volume}"
if volume and "00" in volume:
volume = volume.replace("00", "0")
fixed = (
f"{series_name} - Volume {volume}.{ext}"
if volume
else f"{series_name}.{ext}"
)
log.debug(f"Renaming {file} to {fixed}")
os.rename(
f"{download_path}/{folder}/{file}", f"{download_path}/{folder}/{fixed}"
)
# flatten subfolders
os.rename(f"{download_path}/{folder}", f"{download_path}/{series_name}")
dest = Path(config.komga.media_path, library.media_path)
folders = os.listdir(download_path)
log.info(f"Moving {folders} to {dest}")
for folder in folders:
log.info(f"Processing folder {folder}")
time.sleep(1)
if not os.path.exists(f"{dest}/{folder}"):
log.info(f"Moving {folder} to {dest}")
os.mkdir(f"{dest}/{folder}")
files = os.listdir(f"{download_path}/{folder}")
for file in files:
time.sleep(1)
log.debug(f"Moving {file} to {dest}/{folder}")
if copy:
# copy file to komgrabber tag location
copy_location = config.komgrabber.copy_location
if not os.path.exists(f"{copy_location}"):
os.mkdir(f"{copy_location}")
shutil.copy(
f"{download_path}/{folder}/{file}",
f"{copy_location}/{file}",
)
log.debug(
f"Copied from {download_path}/{folder}/{file} to {copy_location}/{file}"
)
shutil.move(f"{download_path}/{folder}/{file}", f"{dest}/{folder}")
# shutil.move(f"{src}/{folder}", dest)
else:
files = os.listdir(f"{download_path}/{folder}")
for file in files:
time.sleep(1)
log.debug(f"Processing file {file}")
if file.startswith("."):
log.debug(f"Skipping hidden file {file}")
continue
if not os.path.exists(f"{dest}/{folder}/{file}"):
log.debug(f"Moving {file} to {dest}/{folder}")
if copy:
# copy file to komgrabber tag location
copy_location = config.komgrabber.copy_location
if not os.path.exists(f"{copy_location}/{folder}"):
os.mkdir(f"{copy_location}")
shutil.copy(
f"{download_path}/{folder}/{file}",
f"{copy_location}/{file}",
)
log.debug(
f"Copied from {download_path}/{folder}/{file} to {copy_location}/{file}"
)
shutil.move(f"{download_path}/{folder}/{file}", f"{dest}/{folder}")
log.info("Finished moving files, removing empty folders")
remove_empty_folders(download_path)
if __name__ == "__main__":
print(folder_similarity("Dr. STONE (2018-2023) (Digital) (1r0n)", "Dr. STONE"))