[2.1.10] - 2022-07-14
Some checks failed
ci/woodpecker/tag/tests Pipeline was successful
ci/woodpecker/tag/publish_release Pipeline failed
ci/woodpecker/tag/publish_docker Pipeline was successful
ci/woodpecker/push/tests Pipeline was successful

## [2.1.10] - 2022-07-14

### Fixed

- Removed some unused files

### Added

- `logger.py` for all log related settings and functions

### Changed

- Logging of output. The script now uses the `logging` library
This commit is contained in:
Ivan Schaller 2022-07-14 16:18:35 +02:00
commit 8972556415
22 changed files with 268 additions and 203 deletions

View file

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2.1.9
current_version = 2.1.10
commit = True
tag = False
serialize = {major}.{minor}.{patch}

View file

@ -30,7 +30,7 @@ pipeline:
dockerfile: docker/Dockerfile.amd64
auto_tag: true
auto_tag_suffix: linux-amd64-test
build_args: BUILD_VERSION=2.1.9
build_args: BUILD_VERSION=2.1.10
# build docker image for arm64
test-build-arm64:
@ -46,4 +46,4 @@ pipeline:
dockerfile: docker/Dockerfile.arm64
auto_tag: true
auto_tag_suffix: linux-arm64-test
build_args: BUILD_VERSION=2.1.9
build_args: BUILD_VERSION=2.1.10

View file

@ -34,5 +34,5 @@ pipeline:
image: cr.44net.ch/baseimages/debian-base
pull: true
commands:
- bash get_release_notes.sh 2.1.9
- bash get_release_notes.sh 2.1.10
- cat RELEASENOTES.md

View file

@ -9,6 +9,20 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Add support for more sites
## [2.1.10] - 2022-07-14
### Fixed
- Removed some unused files
### Added
- `logger.py` for all log related settings and functions
### Changed
- Logging of output. The script now uses the `logging` library
## [2.1.9] - 2022-06-26
### Fixed

View file

@ -103,7 +103,9 @@ options:
--format FORMAT Archive format to create. An empty string means dont archive the folder. Defaults to 'cbz'
--forcevol Force naming of volumes. For mangas where chapters reset each volume
--wait WAIT Time to wait for each picture to download in seconds(float). Defaults 0.5
--verbose Verbose logging. Defaults to false
--lean Lean logging. Minimal log output. Defaults to false
--verbose Verbose logging. More log output. Defaults to false
--debug Debug logging. Most log output. Defaults to false
```
### Downloads file-structure

View file

@ -7,12 +7,11 @@ class YourAPI:
img_base_url = "https://uploads.mangadex.org"
# get infos to initiate class
def __init__(self, url_uuid, language, forcevol, verbose):
def __init__(self, url_uuid, language, forcevol):
# static info
self.url_uuid = url_uuid
self.language = language
self.forcevol = forcevol
self.verbose = verbose
# attributes needed by app.py
self.manga_uuid = "abc"

View file

@ -1,4 +1,4 @@
FROM cr.44net.ch/baseimages/debian-s6:1.3.5-linux-amd64
FROM cr.44net.ch/baseimages/debian-s6:1.3.6-linux-amd64
# set version label
ARG BUILD_VERSION
@ -10,29 +10,35 @@ LABEL description="A CLI manga downloader"
# install packages
RUN \
echo "**** install base packages ****" && \
apt-get update && \
apt-get install -y --no-install-recommends \
python3 \
python3-pip \
&& \
echo "**** creating folders ****" && \
mkdir -p /app && \
echo "**** cleanup ****" && \
apt-get purge --auto-remove -y && \
apt-get clean && \
rm -rf \
/tmp/* \
/var/lib/apt/lists/* \
/var/tmp/*
echo "**** install base packages ****" \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
python3 \
python3-pip
# prepare app
RUN \
echo "**** creating folders ****" \
&& mkdir -p /app
# cleanup installation
RUN \
echo "**** cleanup ****" \
&& apt-get purge --auto-remove -y \
&& apt-get clean \
&& rm -rf \
/tmp/* \
/var/lib/apt/lists/* \
/var/tmp/*
# copy files to container
COPY docker/rootfs /
COPY mangadlp/ /app/mangadlp/
COPY manga-dlp.py \
requirements.txt \
LICENSE \
COPY \
manga-dlp.py \
requirements.txt \
LICENSE \
/app/

View file

@ -1,4 +1,4 @@
FROM cr.44net.ch/baseimages/debian-s6:1.3.5-linux-arm64
FROM cr.44net.ch/baseimages/debian-s6:1.3.6-linux-arm64
# set version label
ARG BUILD_VERSION
@ -10,29 +10,35 @@ LABEL description="A CLI manga downloader"
# install packages
RUN \
echo "**** install base packages ****" && \
apt-get update && \
apt-get install -y --no-install-recommends \
python3 \
python3-pip \
&& \
echo "**** creating folders ****" && \
mkdir -p /app && \
echo "**** cleanup ****" && \
apt-get purge --auto-remove -y && \
apt-get clean && \
rm -rf \
/tmp/* \
/var/lib/apt/lists/* \
/var/tmp/*
echo "**** install base packages ****" \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
python3 \
python3-pip
# prepare app
RUN \
echo "**** creating folders ****" \
&& mkdir -p /app
# cleanup installation
RUN \
echo "**** cleanup ****" \
&& apt-get purge --auto-remove -y \
&& apt-get clean \
&& rm -rf \
/tmp/* \
/var/lib/apt/lists/* \
/var/tmp/*
# copy files to container
COPY docker/rootfs /
COPY mangadlp/ /app/mangadlp/
COPY manga-dlp.py \
requirements.txt \
LICENSE \
COPY \
manga-dlp.py \
requirements.txt \
LICENSE \
/app/

View file

@ -1,6 +1,6 @@
from mangadlp.input import main
MDLP_VERSION = "2.1.9"
MDLP_VERSION = "2.1.10"
if __name__ == "__main__":
main()

View file

@ -0,0 +1,22 @@
import logging
from mangadlp.logger import logger_lean, logger_verbose
# prepare logger with default level INFO==20
logging.basicConfig(
format="%(asctime)s | %(levelname)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
level=20,
handlers=[logging.StreamHandler()],
)
# create custom log levels
logging.addLevelName(15, "VERBOSE")
logging.VERBOSE = 15 # type: ignore
logging.verbose = logger_verbose # type: ignore
logging.Logger.verbose = logger_verbose # type: ignore
logging.addLevelName(25, "LEAN")
logging.VERBOSE = 25 # type: ignore
logging.lean = logger_lean # type: ignore
logging.Logger.lean = logger_lean # type: ignore

View file

@ -1,3 +1,4 @@
import logging
import re
import sys
from time import sleep
@ -15,12 +16,11 @@ class Mangadex:
img_base_url = "https://uploads.mangadex.org"
# get infos to initiate class
def __init__(self, url_uuid: str, language: str, forcevol: bool, verbosity: int):
def __init__(self, url_uuid: str, language: str, forcevol: bool):
# static info
self.url_uuid = url_uuid
self.language = language
self.forcevol = forcevol
self.verbosity = verbosity
# api stuff
self.api_content_ratings = "contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic"
@ -36,8 +36,7 @@ class Mangadex:
# make initial request
def get_manga_data(self) -> requests.Response:
if self.verbosity >= 2:
print(f"INFO: Getting manga data for: {self.manga_uuid}")
logging.verbose(f"Getting manga data for: {self.manga_uuid}") # type: ignore
counter = 1
while counter <= 3:
try:
@ -46,17 +45,17 @@ class Mangadex:
)
except:
if counter >= 3:
print("ERR: Maybe the MangaDex API is down?")
logging.error("Maybe the MangaDex API is down?")
sys.exit(1)
else:
print("ERR: Mangadex API not reachable. Retrying")
logging.error("Mangadex API not reachable. Retrying")
sleep(2)
counter += 1
else:
break
# check if manga exists
if manga_data.json()["result"] != "ok":
print("ERR: Manga not found")
logging.error("Manga not found")
sys.exit(1)
return manga_data
@ -69,15 +68,14 @@ class Mangadex:
)
# check for new mangadex id
if not uuid_regex.search(self.url_uuid):
print("ERR: No valid UUID found")
logging.error("No valid UUID found")
sys.exit(1)
manga_uuid = uuid_regex.search(self.url_uuid)[0]
return manga_uuid
# get the title of the manga (and fix the filename)
def get_manga_title(self) -> str:
if self.verbosity >= 2:
print(f"INFO: Getting manga title for: {self.manga_uuid}")
logging.verbose(f"Getting manga title for: {self.manga_uuid}") # type: ignore
manga_data = self.manga_data.json()
try:
title = manga_data["data"]["attributes"]["title"][self.language]
@ -89,37 +87,35 @@ class Mangadex:
alt_titles.update(title)
title = alt_titles[self.language]
except: # no title on requested language found
print("ERR: Chapter in requested language not found.")
logging.error("Chapter in requested language not found.")
sys.exit(1)
return utils.fix_name(title)
# check if chapters are available in requested language
def check_chapter_lang(self) -> int:
if self.verbosity >= 2:
print(
f"INFO: Checking for chapters in specified language for: {self.manga_uuid}"
)
logging.verbose( # type: ignore
f"Checking for chapters in specified language for: {self.manga_uuid}"
)
r = requests.get(
f"{self.api_base_url}/manga/{self.manga_uuid}/feed?limit=0&{self.api_additions}"
)
try:
total_chapters = r.json()["total"]
except:
print(
"ERR: Error retrieving the chapters list. Did you specify a valid language code?"
logging.error(
"Error retrieving the chapters list. Did you specify a valid language code?"
)
return 0
else:
if total_chapters == 0:
print("ERR: No chapters available to download!")
logging.error("No chapters available to download!")
return 0
return total_chapters
# get chapter data like name, uuid etc
def get_chapter_data(self) -> dict:
if self.verbosity >= 2:
print(f"INFO: Getting chapter data for: {self.manga_uuid}")
logging.verbose(f"Getting chapter data for: {self.manga_uuid}") # type: ignore
api_sorting = "order[chapter]=asc&order[volume]=asc"
# check for chapters in specified lang
total_chapters = self.check_chapter_lang()
@ -177,8 +173,7 @@ class Mangadex:
# get images for the chapter (mangadex@home)
def get_chapter_images(self, chapter: str, wait_time: float) -> list:
if self.verbosity >= 2:
print(f"INFO: Getting chapter images for: {self.manga_uuid}")
logging.verbose(f"Getting chapter images for: {self.manga_uuid}") # type: ignore
athome_url = f"{self.api_base_url}/at-home/server"
chapter_uuid = self.manga_chapter_data[chapter][0]
@ -190,11 +185,11 @@ class Mangadex:
r = requests.get(f"{athome_url}/{chapter_uuid}")
api_data = r.json()
if api_data["result"] != "ok":
print(f"ERR: No chapter with the id {chapter_uuid} found")
logging.error(f"No chapter with the id {chapter_uuid} found")
api_error = True
raise IndexError
elif api_data["chapter"]["data"] is None:
print(f"ERR: No chapter data found for chapter {chapter_uuid}")
logging.error(f"No chapter data found for chapter {chapter_uuid}")
api_error = True
raise IndexError
else:
@ -203,7 +198,7 @@ class Mangadex:
except:
if counter >= 3:
api_error = True
print(f"ERR: Retrying in a few seconds")
logging.error(f"Retrying in a few seconds")
counter += 1
sleep(wait_time + 2)
# check if result is ok
@ -224,8 +219,7 @@ class Mangadex:
# create list of chapters
def create_chapter_list(self) -> list:
if self.verbosity >= 2:
print(f"INFO: Creating chapter list for: {self.manga_uuid}")
logging.verbose(f"Creating chapter list for: {self.manga_uuid}") # type: ignore
chapter_list = []
for chapter in self.manga_chapter_data.items():
chapter_info = self.get_chapter_infos(chapter[0])
@ -240,10 +234,9 @@ class Mangadex:
# create easy to access chapter infos
def get_chapter_infos(self, chapter: str) -> dict:
if self.verbosity >= 3:
print(
f"INFO: Getting chapter infos for: {self.manga_chapter_data[chapter][0]}"
)
logging.debug(
f"Getting chapter infos for: {self.manga_chapter_data[chapter][0]}"
)
chapter_uuid = self.manga_chapter_data[chapter][0]
chapter_vol = self.manga_chapter_data[chapter][1]
chapter_num = self.manga_chapter_data[chapter][2]

View file

@ -1,3 +1,4 @@
import logging
import re
import shutil
import sys
@ -23,7 +24,7 @@ class MangaDLP:
:param forcevol: Force naming of volumes. Useful for mangas where chapters reset each volume
:param download_path: Download path. Defaults to '<script_dir>/downloads'
:param download_wait: Time to wait for each picture to download in seconds
:param verbosity: Verbosity of the output
:param verbosity: Verbosity of the output. Uses the logging library values
:return: Nothing. Just the files
"""
@ -38,7 +39,7 @@ class MangaDLP:
forcevol: bool = False,
download_path: str = "downloads",
download_wait: float = 0.5,
verbosity: int = 0,
verbosity: int = 20,
) -> None:
# init parameters
self.url_uuid = url_uuid
@ -54,7 +55,6 @@ class MangaDLP:
self._prepare()
def _prepare(self) -> None:
# additional stuff
# set manga format suffix
if self.file_format and "." not in self.file_format:
self.file_format = f".{self.file_format}"
@ -62,9 +62,7 @@ class MangaDLP:
self.pre_checks()
# init api
self.api_used = self.check_api(self.url_uuid)
self.api = self.api_used(
self.url_uuid, self.language, self.forcevol, self.verbosity
)
self.api = self.api_used(self.url_uuid, self.language, self.forcevol)
# get manga title and uuid
self.manga_uuid = self.api.manga_uuid
self.manga_title = self.api.manga_title
@ -76,25 +74,25 @@ class MangaDLP:
# prechecks userinput/options
# no url and no readin list given
if not self.url_uuid:
print(
f'ERR: You need to specify a manga url/uuid with "-u" or a list with "--read"'
logging.error(
'You need to specify a manga url/uuid with "-u" or a list with "--read"'
)
sys.exit(1)
# checks if --list is not used
if not self.list_chapters:
if self.chapters is None:
# no chapters to download were given
print(
f'ERR: You need to specify one or more chapters to download. To see all chapters use "--list"'
logging.error(
'You need to specify one or more chapters to download. To see all chapters use "--list"'
)
sys.exit(1)
# if forcevol is used, but didn't specify a volume in the chapters selected
if self.forcevol and ":" not in self.chapters:
print(f"ERR: You need to specify the volume if you use --forcevol")
logging.error("You need to specify the volume if you use --forcevol")
sys.exit(1)
# if forcevol is not used, but a volume is specified
if not self.forcevol and ":" in self.chapters:
print(f"ERR: Don't specify the volume without --forcevol")
logging.error("Don't specify the volume without --forcevol")
sys.exit(1)
# check the api which needs to be used
@ -112,11 +110,11 @@ class MangaDLP:
# this is only for testing multiple apis
if api_test.search(url_uuid):
print("Not supported yet")
logging.critical("Not supported yet")
sys.exit(1)
# no supported api found
print(f"ERR: No supported api in link/uuid found: {url_uuid}")
logging.error(f"No supported api in link/uuid found: {url_uuid}")
raise ValueError
# once called per manga
@ -127,18 +125,15 @@ class MangaDLP:
print_divider = "========================================="
# show infos
if self.verbosity == 1:
print(f"INFO: Manga Name: {self.manga_title}")
else:
print(f"{print_divider}")
print(f"INFO: Manga Name: {self.manga_title}")
print(f"INFO: Manga UUID: {self.manga_uuid}")
print(f"INFO: Total chapters: {len(self.manga_chapter_list)}")
logging.info(f"{print_divider}")
logging.lean(f"Manga Name: {self.manga_title}") # type: ignore
logging.info(f"Manga UUID: {self.manga_uuid}")
logging.info(f"Total chapters: {len(self.manga_chapter_list)}")
# list chapters if list_chapters is true
if self.list_chapters:
print(f"INFO: Available Chapters:\n{', '.join(self.manga_chapter_list)}")
print(f"{print_divider}\n")
logging.info(f"Available Chapters: {', '.join(self.manga_chapter_list)}")
logging.info(f"{print_divider}\n")
return None
# check chapters to download if not all
@ -150,11 +145,8 @@ class MangaDLP:
)
# show chapters to download
if self.verbosity == 1:
print(f"INFO: Chapters selected: {', '.join(chapters_to_download)}")
else:
print(f"INFO: Chapters selected:\n{', '.join(chapters_to_download)}")
print(f"{print_divider}")
logging.lean(f"Chapters selected: {', '.join(chapters_to_download)}") # type: ignore
logging.info(f"{print_divider}")
# create manga folder
self.manga_path.mkdir(parents=True, exist_ok=True)
@ -174,28 +166,21 @@ class MangaDLP:
# chapter was not skipped
except KeyError:
# done with chapter
print("INFO: Done with chapter\n")
logging.info("Done with chapter\n")
# done with manga
if self.verbosity != 1:
print(f"{print_divider}")
print(f"INFO: Done with manga: {self.manga_title}")
logging.info(f"{print_divider}")
logging.lean(f"Done with manga: {self.manga_title}") # type: ignore
# filter skipped list
skipped_chapters = list(filter(None, skipped_chapters))
if len(skipped_chapters) >= 1:
if self.verbosity == 1:
print(f"INFO: Skipped chapters: {', '.join(skipped_chapters)}")
else:
print(f"INFO: Skipped chapters:\n{', '.join(skipped_chapters)}")
logging.lean(f"Skipped chapters: {', '.join(skipped_chapters)}") # type: ignore
# filter error list
error_chapters = list(filter(None, error_chapters))
if len(error_chapters) >= 1:
if self.verbosity == 1:
print(f"INFO: Chapters with errors: {', '.join(error_chapters)}")
else:
print(f"INFO: Chapters with errors:\n{', '.join(error_chapters)}")
if self.verbosity != 1:
print(f"{print_divider}\n")
logging.lean(f"Chapters with errors: {', '.join(error_chapters)}") # type: ignore
logging.info(f"{print_divider}\n")
# once called per chapter
def get_chapter(self, chapter: str) -> dict:
@ -208,13 +193,13 @@ class MangaDLP:
chapter, self.download_wait
)
except KeyboardInterrupt:
print("ERR: Stopping")
logging.critical("Stopping")
sys.exit(1)
# check if the image urls are empty. if yes skip this chapter (for mass downloads)
if not chapter_image_urls:
print(
f"ERR: No images: Skipping Vol. {chapter_infos['volume']} Ch.{chapter_infos['chapter']}"
logging.error(
f"No images: Skipping Vol. {chapter_infos['volume']} Ch.{chapter_infos['chapter']}"
)
# add to skipped chapters list
return (
@ -239,8 +224,8 @@ class MangaDLP:
# check if chapter already exists
# check for folder, if file format is an empty string
if chapter_archive_path.exists():
if self.verbosity != 1:
print(f"INFO: '{chapter_archive_path}' already exists. Skipping")
if self.verbosity != "lean":
logging.warning(f"'{chapter_archive_path}' already exists. Skipping")
# add to skipped chapters list
return (
{
@ -255,25 +240,24 @@ class MangaDLP:
chapter_path.mkdir(parents=True, exist_ok=True)
# verbose log
if self.verbosity >= 2:
print(f"INFO: Chapter UUID: {chapter_infos['uuid']}")
print(f"INFO: Filename: '{chapter_archive_path.name}'\n")
print(f"INFO: File path: '{chapter_archive_path}'\n")
print(f"INFO: Image URLS:\n{chapter_image_urls}\n")
logging.verbose(f"Chapter UUID: {chapter_infos['uuid']}") # type: ignore
logging.verbose(f"Filename: '{chapter_archive_path.name}'") # type: ignore
logging.verbose(f"File path: '{chapter_archive_path}'") # type: ignore
logging.verbose(f"Image URLS:\n{chapter_image_urls}") # type: ignore
# log
print(f"INFO: Downloading: '{chapter_filename}'")
logging.lean(f"Downloading: '{chapter_filename}'") # type: ignore
# download images
try:
downloader.download_chapter(
chapter_image_urls, chapter_path, self.download_wait, self.verbosity
chapter_image_urls, chapter_path, self.download_wait
)
except KeyboardInterrupt:
print("ERR: Stopping")
logging.critical("Stopping")
sys.exit(1)
except:
print(f"ERR: Cant download: '{chapter_filename}'. Skipping")
logging.error(f"Cant download: '{chapter_filename}'. Skipping")
# add to skipped chapters list
return (
{
@ -286,23 +270,23 @@ class MangaDLP:
else:
# Done with chapter
print(f"INFO: Successfully downloaded: '{chapter_filename}'")
logging.lean(f"INFO: Successfully downloaded: '{chapter_filename}'") # type: ignore
return {"chapter_path": chapter_path}
# create an archive of the chapter if needed
def archive_chapter(self, chapter_path: Path) -> dict:
print(f"INFO: Creating '{self.file_format}' archive")
logging.lean(f"INFO: Creating '{self.file_format}' archive") # type: ignore
try:
# check if image folder is existing
if not chapter_path.exists():
print(f"ERR: Image folder: {chapter_path} does not exist")
logging.error(f"Image folder: {chapter_path} does not exist")
raise IOError
if self.file_format == ".pdf":
utils.make_pdf(chapter_path)
else:
utils.make_archive(chapter_path, self.file_format)
except:
print(f"ERR: Archive error. Skipping chapter")
logging.error(f"Archive error. Skipping chapter")
# add to skipped chapters list
return {
"error": chapter_path,

View file

@ -1,3 +1,4 @@
import logging
import shutil
import sys
from pathlib import Path
@ -14,7 +15,6 @@ def download_chapter(
image_urls: list,
chapter_path: Union[str, Path],
download_wait: float,
verbosity: int,
) -> None:
total_img = len(image_urls)
for image_num, image in enumerate(image_urls, 1):
@ -22,25 +22,24 @@ def download_chapter(
image_suffix = str(Path(image).suffix) or ".png"
# set image path
image_path = Path(f"{chapter_path}/{image_num:03d}{image_suffix}")
# show progress bar or progress by image for verbose
if verbosity == 0:
# show progress bar for default log level
if logging.root.level == logging.INFO:
utils.progress_bar(image_num, total_img)
elif verbosity >= 2:
print(f"INFO: Downloading image {image_num}/{total_img}")
logging.verbose(f"Downloading image {image_num}/{total_img}") # type: ignore
counter = 1
while counter <= 3:
try:
r = requests.get(image, stream=True)
if r.status_code != 200:
print(f"ERR: Request for image {image} failed, retrying")
logging.error(f"Request for image {image} failed, retrying")
raise ConnectionError
except KeyboardInterrupt:
print("ERR: Stopping")
logging.critical("Stopping")
sys.exit(1)
except:
if counter >= 3:
print("ERR: Maybe the MangaDex Servers are down?")
logging.error("Maybe the MangaDex Servers are down?")
raise ConnectionError
sleep(download_wait)
counter += 1
@ -53,7 +52,7 @@ def download_chapter(
r.raw.decode_content = True
shutil.copyfileobj(r.raw, file)
except:
print("ERR: Can't write file")
logging.error("Can't write file")
raise IOError
image_num += 1

View file

@ -1,11 +1,11 @@
import argparse
import subprocess
import sys
from pathlib import Path
import mangadlp.app as app
import mangadlp.logger as logger
MDLP_VERSION = "2.1.9"
MDLP_VERSION = "2.1.10"
def check_args(args):
@ -38,6 +38,8 @@ def readin_list(readlist: str) -> list:
def call_app(args):
# set logger formatting
logger.format_logger(args.verbosity)
# call main function with all input arguments
mdlp = app.MangaDLP(
args.url_uuid,
@ -182,28 +184,28 @@ def get_args():
"--lean",
dest="verbosity",
required=False,
help="Lean logging. Defaults to false",
help="Lean logging. Minimal log output. Defaults to false",
action="store_const",
const=1,
default=0,
const=25,
default=20,
)
verbosity.add_argument(
"--verbose",
dest="verbosity",
required=False,
help="Verbose logging. Defaults to false",
help="Verbose logging. More log output. Defaults to false",
action="store_const",
const=2,
default=0,
const=15,
default=20,
)
verbosity.add_argument(
"--debug",
dest="verbosity",
required=False,
help="Lean logging. Defaults to false",
help="Debug logging. Most log output. Defaults to false",
action="store_const",
const=3,
default=0,
const=10,
default=20,
)
# parser.print_help()

32
mangadlp/logger.py Normal file
View file

@ -0,0 +1,32 @@
import logging
# set log message format
def format_logger(verbosity: int):
logging.getLogger().setLevel(verbosity)
# dont show log level name on default/lean logging
if verbosity >= 20:
logging.basicConfig(
format="%(asctime)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
force=True,
)
else:
logging.basicConfig(
format="%(asctime)s | %(levelname)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
force=True,
)
# create verbose logger with level 15
def logger_verbose(msg, *args, **kwargs):
if logging.getLogger().isEnabledFor(15):
logging.log(15, msg)
# create lean logger with level 25
def logger_lean(msg, *args, **kwargs):
if logging.getLogger().isEnabledFor(25):
logging.log(25, msg)

View file

@ -1,4 +1,6 @@
import logging
import re
from datetime import datetime
from pathlib import Path
from typing import Any
from zipfile import ZipFile
@ -22,7 +24,7 @@ def make_pdf(chapter_path: Path) -> None:
try:
import img2pdf
except:
print("Cant import img2pdf. Please install it first")
logging.error("Cant import img2pdf. Please install it first")
raise ImportError
pdf_path = Path(f"{chapter_path}.pdf")
@ -32,7 +34,7 @@ def make_pdf(chapter_path: Path) -> None:
try:
pdf_path.write_bytes(img2pdf.convert(images))
except:
print("ERR: Can't create '.pdf' archive")
logging.error("Can't create '.pdf' archive")
raise IOError
@ -115,6 +117,7 @@ def get_filename(
def progress_bar(progress: float, total: float) -> None:
time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
percent = int(progress / (int(total) / 100))
bar_length = 50
bar_progress = int(progress / (int(total) / bar_length))
@ -122,6 +125,6 @@ def progress_bar(progress: float, total: float) -> None:
whitespace_texture = " " * (bar_length - bar_progress)
if progress == total:
full_bar = "" * bar_length
print(f"\r{full_bar}❙ 100%", end="\n")
print(f"\r{time} | {full_bar}❙ 100%", end="\n")
else:
print(f"\r{bar_texture}{whitespace_texture}{percent}%", end="\r")
print(f"\r{time} | {bar_texture}{whitespace_texture}{percent}%", end="\r")

View file

@ -3,7 +3,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
version = "2.1.9"
version = "2.1.10"
name = "manga-dlp"
description = "A cli manga downloader"
readme = "README.md"
@ -107,3 +107,10 @@ exclude_lines = [
"@(abc.)?abstractmethod",
]
ignore_errors = true
[tool.pylint.main]
py-version = "3.9"
[tool.pylint.logging]
logging-modules = ["logging"]
logging-format-style = "fstr"

View file

@ -1,8 +0,0 @@
mangadlp/
manga-dlp.py
pyproject.toml
requirements.txt
MANIFEST.in
README.md
CHANGELOG.md
LICENSE

View file

@ -57,7 +57,7 @@ def test_chapter_list_full():
forcevol=True,
download_path="tests",
download_wait=2,
verbosity=3,
verbosity=10,
)
chap_list = utils.get_chapter_list("1:1,1:2,1:4-1:7,2:", mdlp.manga_chapter_list)
assert chap_list == [

View file

@ -18,7 +18,7 @@ def test_downloader():
chapter_path = Path("tests/test_folder1")
chapter_path.mkdir(parents=True, exist_ok=True)
images = []
downloader.download_chapter(urls, str(chapter_path), 2, True)
downloader.download_chapter(urls, str(chapter_path), 2)
for file in chapter_path.iterdir():
images.append(file.name)
@ -43,7 +43,7 @@ def test_downloader_fail(monkeypatch):
chapter_path.mkdir(parents=True, exist_ok=True)
monkeypatch.setattr(requests, "get", fail_url)
with pytest.raises(ConnectionError) as e:
downloader.download_chapter(images, str(chapter_path), 2, True)
downloader.download_chapter(images, str(chapter_path), 2)
assert e.type == ConnectionError
# cleanup

View file

@ -8,8 +8,7 @@ def test_uuid_link():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
assert test.manga_uuid == "a96676e5-8ae2-425e-b549-7f15dd34a6d8"
@ -18,8 +17,7 @@ def test_uuid_pure():
url_uuid = "a96676e5-8ae2-425e-b549-7f15dd34a6d8"
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
assert test.manga_uuid == "a96676e5-8ae2-425e-b549-7f15dd34a6d8"
@ -28,10 +26,9 @@ def test_uuid_link_false():
url_uuid = "https://mangadex.org/title/a966-76e-5-8a-e2-42-5e-b-549-7f15dd-34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbosity)
Mangadex(url_uuid, language, forcevol)
assert e.type == SystemExit
assert e.value.code == 1
@ -40,8 +37,7 @@ def test_title():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
assert test.manga_title == "Komi-san wa Komyushou Desu"
@ -50,8 +46,7 @@ def test_chapter_infos():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
chapter_infos = test.get_chapter_infos("1")
chapter_uuid = chapter_infos["uuid"]
chapter_name = chapter_infos["name"]
@ -70,10 +65,9 @@ def test_non_existing_manga():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-999999999999/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbosity)
Mangadex(url_uuid, language, forcevol)
assert e.type == SystemExit
assert e.value.code == 1
@ -86,10 +80,9 @@ def test_api_failure(monkeypatch):
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbosity)
Mangadex(url_uuid, language, forcevol)
assert e.type == SystemExit
assert e.value.code == 1
@ -98,8 +91,7 @@ def test_chapter_lang_en():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = True
test = Mangadex(url_uuid, language, forcevol, 3)
test = Mangadex(url_uuid, language, forcevol)
assert test.check_chapter_lang() > 0
@ -108,11 +100,10 @@ def test_empty_chapter_lang():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "ch"
forcevol = False
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbosity)
Mangadex(url_uuid, language, forcevol, verbosity).check_chapter_lang()
Mangadex(url_uuid, language, forcevol)
Mangadex(url_uuid, language, forcevol).check_chapter_lang()
assert e.type == KeyError or e.type == SystemExit
assert e.value.code == 1
@ -121,10 +112,9 @@ def test_not_existing_lang():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "zz"
forcevol = False
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbosity)
Mangadex(url_uuid, language, forcevol)
assert e.type == SystemExit
assert e.value.code == 1
@ -135,8 +125,7 @@ def test_create_chapter_list():
)
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
test_list = [
"1",
"2",
@ -170,8 +159,7 @@ def test_create_chapter_list_forcevol():
)
language = "en"
forcevol = True
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
test_list = [
"1:1",
"1:2",
@ -203,8 +191,7 @@ def test_get_chapter_images():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
img_base_url = "https://uploads.mangadex.org"
chapter_hash = "0752bc5db298beff6b932b9151dd8437"
chapter_uuid = "e86ec2c4-c5e4-4710-bfaa-7604f00939c7"
@ -235,8 +222,7 @@ def test_get_chapter_images_error(monkeypatch):
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test = Mangadex(url_uuid, language, forcevol)
chapter_num = "1"
monkeypatch.setattr(requests, "get", fail_url)

View file

@ -33,7 +33,7 @@ def test_full_api_mangadex(wait_20s):
forcevol=False,
download_path="tests",
download_wait=2,
verbosity=3,
verbosity=10,
)
mdlp.get_manga()
@ -61,6 +61,24 @@ def test_full_with_input_cbz(wait_20s):
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_cbz_info(wait_20s):
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
language = "en"
chapters = "1"
file_format = "cbz"
download_path = "tests"
manga_path = Path("tests/Shikimori's Not Just a Cutie")
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --wait 2"
script_path = "manga-dlp.py"
os.system(f"python3 {script_path} {command_args}")
assert manga_path.exists() and manga_path.is_dir()
assert chapter_path.exists() and chapter_path.is_file()
# cleanup
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_pdf(wait_20s):
# check if its arm64, if yes skip this step
if platform.machine() != "x86_64":