[2.2.15] - 2022-12-29
All checks were successful
ci/woodpecker/tag/tests Pipeline was successful
ci/woodpecker/tag/publish_release Pipeline was successful
ci/woodpecker/tag/publish_docker_arm64 Pipeline was successful
ci/woodpecker/tag/publish_docker_amd64 Pipeline was successful
ci/woodpecker/tag/publish_docker_manifest Pipeline was successful
ci/woodpecker/push/tests Pipeline was successful

## [2.2.15] - 2022-12-29

### Added

- `--warn` and `--loglevel` flags

### Removed

- Remove `--lean` and `--verbose` flags and remove custom log levels

### Changed

- Move from standard library logging to [loguru](https://loguru.readthedocs.io/en/stable/index.html)
- Move from standard library argparse to [click](https://click.palletsprojects.com/en/8.1.x/)
This commit is contained in:
Ivan Schaller 2022-12-29 19:45:26 +01:00
commit 5bdd54fc16
39 changed files with 518 additions and 685 deletions

View file

@ -1,4 +1,4 @@
python 3.9.13 3.10.5 3.8.13 3.7.13 3.6.15 python 3.9.13 3.10.5 3.8.13
shfmt 3.5.1 shfmt 3.5.1
shellcheck 0.8.0 shellcheck 0.8.0
just 1.2.0 just 1.2.0

View file

@ -14,7 +14,7 @@ clone:
when: when:
#branch: master #branch: master
event: tag event: tag
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
#branch: master #branch: master
event: tag event: tag
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -15,7 +15,7 @@ clone:
when: when:
#branch: master #branch: master
event: tag event: tag
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
#branch: master #branch: master
event: tag event: tag
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
branch: master branch: master
event: pull_request event: pull_request
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
branch: master branch: master
event: pull_request event: pull_request
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
branch: master branch: master
event: pull_request event: pull_request
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
branch: master branch: master
event: pull_request event: pull_request
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -14,7 +14,7 @@ clone:
when: when:
branch: master branch: master
event: pull_request event: pull_request
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -8,7 +8,7 @@ platform: linux/amd64
clone: clone:
git: git:
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git:v1.6.0
pipeline: pipeline:

View file

@ -9,6 +9,21 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Add support for more sites - Add support for more sites
## [2.2.15] - 2022-12-29
### Added
- `--warn` and `--loglevel` flags
### Removed
- Remove `--lean` and `--verbose` flags and remove custom log levels
### Changed
- Move from standard library logging to [loguru](https://loguru.readthedocs.io/en/stable/index.html)
- Move from standard library argparse to [click](https://click.palletsprojects.com/en/8.1.x/)
## [2.2.14] - 2022-10-06 ## [2.2.14] - 2022-10-06
### Changed ### Changed

View file

@ -89,30 +89,34 @@ See the docker [README](https://manga-dlp.ivn.sh/docker/)
## Options ## Options
```txt ```txt
usage: manga-dlp.py [-h] (-u URL_UUID | --read READ | -v) [-c CHAPTERS] [-p PATH] [-l LANG] [--list] [--format FORMAT] [--forcevol] [--wait WAIT] [--lean | --verbose | --debug] [--hook-manga-pre HOOK_MANGA_PRE] Usage: manga-dlp.py [OPTIONS]
[--hook-manga-post HOOK_MANGA_POST] [--hook-chapter-pre HOOK_CHAPTER_PRE] [--hook-chapter-post HOOK_CHAPTER_POST]
Script to download mangas from various sites Script to download mangas from various sites
optional arguments: Options:
-h, --help show this help message and exit --help Show this message and exit.
-u URL_UUID, --url URL_UUID, --uuid URL_UUID URL or UUID of the manga --version Show the version and exit.
--read READ Path of file with manga links to download. One per line
-v, --version Show version of manga-dlp and exit source: [mutually_exclusive, required]
-c CHAPTERS, --chapters CHAPTERS Chapters to download -u, --url, --uuid TEXT URL or UUID of the manga
-p PATH, --path PATH Download path. Defaults to "<script_dir>/downloads" --read FILE Path of file with manga links to download. One per line
-l LANG, --language LANG Manga language. Defaults to "en" --> english
--list List all available chapters. Defaults to false verbosity: [mutually_exclusive]
--format FORMAT Archive format to create. An empty string means dont archive the folder. Defaults to 'cbz' --loglevel INTEGER Custom log level [default: 20]
--warn Only log warnings and higher
--debug Debug logging. Log EVERYTHING
-c, --chapters TEXT Chapters to download
-p, --path PATH Download path [default: downloads]
-l, --language TEXT Manga language [default: en]
--list List all available chapters
--format TEXT Archive format to create. An empty string means dont archive the folder [default: cbz]
--forcevol Force naming of volumes. For mangas where chapters reset each volume --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 --wait FLOAT Time to wait for each picture to download in seconds(float) [default: 0.5]
--lean Lean logging. Minimal log output. Defaults to false --hook-manga-pre TEXT Commands to execute before the manga download starts
--verbose Verbose logging. More log output. Defaults to false --hook-manga-post TEXT Commands to execute after the manga download finished
--debug Debug logging. Most log output. Defaults to false --hook-chapter-pre TEXT Commands to execute before the chapter download starts
--hook-manga-pre HOOK_MANGA_PRE Commands to execute before the manga download starts --hook-chapter-post TEXT Commands to execute after the chapter download finished
--hook-manga-post HOOK_MANGA_POST Commands to execute after the manga download finished
--hook-chapter-pre HOOK_CHAPTER_PRE Commands to execute before the chapter download starts
--hook-chapter-post HOOK_CHAPTER_POST Commands to execute after the chapter download finished
``` ```
## Contribution / Bugs ## Contribution / Bugs

View file

@ -1,10 +1,14 @@
# application requirements # application requirements
requests>=2.24.0 requests>=2.28.0
loguru>=0.6.0
click>=8.1.3
click-option-group>=0.5.5
img2pdf>=0.4.4 img2pdf>=0.4.4
# dev and testing requirements # dev and testing requirements
hatch>=1.2.1 hatch>=1.6.0
hatchling>=1.4.1 hatchling>=1.11.0
pytest>=7.0.0 pytest>=7.0.0
coverage>=6.3.1 coverage>=6.3.1
black>=22.1.0 black>=22.1.0

View file

@ -1,4 +1,4 @@
FROM cr.44net.ch/baseimages/debian-s6:11.5.5-linux-amd64 FROM cr.44net.ch/baseimages/debian-s6:11.5-linux-amd64
# set version label # set version label
ARG BUILD_VERSION ARG BUILD_VERSION

View file

@ -1,4 +1,4 @@
FROM cr.44net.ch/baseimages/debian-s6:11.5.5-linux-arm64 FROM cr.44net.ch/baseimages/debian-s6:11.5-linux-arm64
# set version label # set version label
ARG BUILD_VERSION ARG BUILD_VERSION

View file

@ -1,8 +1,10 @@
# Docker container of manga-dlp # Docker container of manga-dlp
> Full docs: https://manga-dlp.ivn.sh/docker
## Quick start ## Quick start
> the pdf creation only works on amd64 images, as it unfortunately is incompatible with arm64. The pdf creation only works on amd64 images, as it unfortunately is incompatible with arm64.
```sh ```sh
# with docker-compose # with docker-compose
@ -13,135 +15,3 @@ docker-compose up -d
# with docker run # with docker run
docker run -v ./downloads:/app/downloads -v ./mangas.txt:/app/mangas.txt olofvndrhr/manga-dlp docker run -v ./downloads:/app/downloads -v ./mangas.txt:/app/mangas.txt olofvndrhr/manga-dlp
``` ```
### Change UID/GID
> The default UID and GID are 4444.
You can change the UID and GID of the container user simply with:
```yml
# docker-compose.yml
environment:
- PUID=<userid>
- PGID=<groupid>
```
```sh
docker run -e PUID=<userid> -e PGID=<groupid>
```
## Environment variables
You can configure the default schedule via environment variables. Don't forget to set `MDLP_GENERATE_SCHEDULE` to "true"
, else
it will not generate it (it will just use the default one).
For more info's about the options, you can look in the main scripts [README.md](../README.md)
| ENV Variable | Default | manga-dlp option | Info |
|:-----------------------|:----------------|:-------------------------|--------------------------------------------------------------------------|
| MDLP_GENERATE_SCHEDULE | false | none | Has to be set to "true" to generate the config via environment variables |
| MDLP_PATH | /app/downloads | --path | |
| MDLP_READ | /app/mangas.txt | --read | |
| MDLP_LANGUAGE | en | --language | |
| MDLP_CHAPTERS | all | --chapter | |
| MDLP_FILE_FORMAT | cbz | --format | |
| MDLP_WAIT | 0.5 | --wait | |
| MDLP_FORCEVOL | false | --forcevol | |
| MDLP_LOG_LEVEL | lean | --lean/--verbose/--debug | Can either be set to: "lean", "verbose" or "debug" |
## Run commands in container
> You don't need to use the full path of manga-dlp.py because `/app` already is the working directory
You can simply use the `docker exec` command to run the scripts like normal.
```sh
docker exec <container name> python3 manga-dlp.py <options>
```
## Run your own schedule
The default config runs `manga-dlp.py` once a day at 12:00 and fetches every chapter of the mangas listed in the file
`mangas.txt` in the root directory of this repo.
#### The default schedule:
```sh
#!/bin/bash
python3 /app/manga-dlp.py \
--path /app/downloads \
--read /app/mangas.txt \
--chapters all \
--wait 2 \
--lean
```
To use your own schedule you need to mount (override) the default schedule or add new ones to the crontab.
> Don't forget to add the cron entries for every new schedule
```yml
# docker-compose.yml
volumes:
- ./crontab:/etc/cron.d/mangadlp # overwrites the default crontab
- ./crontab2:/etc/cron.d/something # adds a new one crontab file
- ./schedule1.sh:/app/schedules/daily.sh # overwrites the default schedule
- ./schedule2.sh:/app/schedules/weekly.sh # adds a new schedule
```
```sh
docker run -v ./crontab:/etc/cron.d/mangadlp # overwrites the default crontab
docker run -v ./crontab2:/etc/cron.d/something # adds a new one crontab file
docker run -v ./schedule1.sh:/app/schedules/daily.sh # overwrites the default schedule
docker run -v ./schedule2.sh:/app/schedules/weekly.sh # adds a new schedule
```
#### The default crontab file:
```sh
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# default crontab to run manga-dlp once a day
# and get all (new) chapters of the mangas in
# the file mangas.txt
# "/proc/1/fd/1 2>&1" is to show the logs in the container
# "s6-setuidgid abc" is used to set the permissions
0 12 * * * root s6-setuidgid abc /app/schedules/daily.sh > /proc/1/fd/1 2>&1
```
## Add mangas to mangas.txt
If you use the default crontab you still need to add some mangas to mangas.txt. This is done almost identical to adding
your own cron schedule. If you use a custom cron schedule you need to mount the file you specified with `--read`.
```yml
# docker-compose.yml
volumes:
- ./mangas.txt:/app/mangas.txt
```
```sh
docker run -v ./mangas.txt:/app/mangas.txt
```
## Change download directory
Per default as in the script, it downloads everything to "downloads" in the scripts root directory. This data does not
persist with container recreation, so you need to mount it. This is already done in the quick start section. If you want
to change the path of the host, simply change `./downloads/` to a path of your choice.
```yml
# docker-compose.yml
volumes:
- ./downloads/:/app/downloads
```
```sh
docker run -v ./downloads/:/app/downloads
```

View file

@ -5,4 +5,4 @@ python3 /app/manga-dlp.py \
--read /app/mangas.txt \ --read /app/mangas.txt \
--chapters all \ --chapters all \
--wait 2 \ --wait 2 \
--lean --warn

View file

@ -12,4 +12,4 @@
: "${MDLP_FILE_FORMAT:=cbz}" : "${MDLP_FILE_FORMAT:=cbz}"
: "${MDLP_WAIT:=0.5}" : "${MDLP_WAIT:=0.5}"
: "${MDLP_FORCEVOL:=false}" : "${MDLP_FORCEVOL:=false}"
: "${MDLP_LOG_LEVEL:=lean}" : "${MDLP_LOG_LEVEL:=warn}"

View file

@ -14,17 +14,17 @@ elif grep -q -e "/app/schedules/daily\s" /etc/cron.d/mangadlp; then
CRON_FOUND=true CRON_FOUND=true
fi fi
# fix new .sh schedule if its not synced with the crontab # fix new .sh schedule if it's not synced with the crontab
if [[ "${CRON_SH_FOUND}" == "true" ]] && [[ "${DAILY_SH_FOUND}" != "true" ]]; then if [[ "${CRON_SH_FOUND}" == "true" ]] && [[ "${DAILY_SH_FOUND}" != "true" ]]; then
echo "Fixing new .sh schedule" echo "Fixing new .sh schedule"
echo "Adding symlink to daily.sh" echo "Adding symlink to daily.sh"
if ! ln -s /app/schedule/daily /app/schedule/daily.sh; then if ! ln -s /app/schedule/daily /app/schedule/daily.sh; then
echo "Cant fix schedule. Maybe the file is missing." echo "Can't fix schedule. Maybe the file is missing."
fi fi
elif [[ "${CRON_FOUND}" == "true" ]] && [[ "${DAILY_FOUND}" != "true" ]]; then elif [[ "${CRON_FOUND}" == "true" ]] && [[ "${DAILY_FOUND}" != "true" ]]; then
echo "Fixing new .sh schedule" echo "Fixing new .sh schedule"
echo "Adding symlink to daily" echo "Adding symlink to daily"
if ! ln -s /app/schedule/daily.sh /app/schedule/daily; then if ! ln -s /app/schedule/daily.sh /app/schedule/daily; then
echo "Cant fix schedule. Maybe the file is missing." echo "Can't fix schedule. Maybe the file is missing."
fi fi
fi fi

View file

@ -7,15 +7,15 @@ source /etc/cont-init.d/20-setenv.sh
function prepare_vars() { function prepare_vars() {
# set log level # set log level
case "${MDLP_LOG_LEVEL}" in case "${MDLP_LOG_LEVEL}" in
"lean") "warn")
MDLP_LOG_LEVEL_FLAG=" --lean" MDLP_LOG_LEVEL_FLAG=" --warn"
;;
"verbose")
MDLP_LOG_LEVEL_FLAG=" --verbose"
;; ;;
"debug") "debug")
MDLP_LOG_LEVEL_FLAG=" --debug" MDLP_LOG_LEVEL_FLAG=" --debug"
;; ;;
*)
MDLP_LOG_LEVEL_FLAG=" --loglevel ${MDLP_LOG_LEVEL}"
;;
esac esac
# check if forcevol should be used # check if forcevol should be used

View file

@ -40,7 +40,7 @@ it will not generate it (it will just use the default one).
For more info's about the options, you can look in the main scripts [README.md](../) For more info's about the options, you can look in the main scripts [README.md](../)
| ENV Variable | Default | manga-dlp option | Info | | ENV Variable | Default | manga-dlp option | Info |
|:-----------------------|:----------------|:-------------------------|--------------------------------------------------------------------------| |:-----------------------|:----------------|:------------------------------|--------------------------------------------------------------------------|
| MDLP_GENERATE_SCHEDULE | false | none | Has to be set to "true" to generate the config via environment variables | | MDLP_GENERATE_SCHEDULE | false | none | Has to be set to "true" to generate the config via environment variables |
| MDLP_PATH | /app/downloads | --path | | | MDLP_PATH | /app/downloads | --path | |
| MDLP_READ | /app/mangas.txt | --read | | | MDLP_READ | /app/mangas.txt | --read | |
@ -49,7 +49,7 @@ For more info's about the options, you can look in the main scripts [README.md](
| MDLP_FILE_FORMAT | cbz | --format | | | MDLP_FILE_FORMAT | cbz | --format | |
| MDLP_WAIT | 0.5 | --wait | | | MDLP_WAIT | 0.5 | --wait | |
| MDLP_FORCEVOL | false | --forcevol | | | MDLP_FORCEVOL | false | --forcevol | |
| MDLP_LOG_LEVEL | lean | --lean/--verbose/--debug | Can either be set to: "lean", "verbose" or "debug" | | MDLP_LOG_LEVEL | lean | --warn / --debug / --loglevel | Can either be set to: warn, debug or a custom loglevel integer |
## Run commands in container ## Run commands in container
@ -76,7 +76,7 @@ python3 /app/manga-dlp.py \
--read /app/mangas.txt \ --read /app/mangas.txt \
--chapters all \ --chapters all \
--wait 2 \ --wait 2 \
--lean --warn
``` ```
To use your own schedule you need to mount (override) the default schedule or add new ones to the crontab. To use your own schedule you need to mount (override) the default schedule or add new ones to the crontab.
@ -144,4 +144,3 @@ volumes:
```sh ```sh
docker run -v ./downloads/:/app/downloads docker run -v ./downloads/:/app/downloads
``` ```

View file

@ -3,7 +3,7 @@
## Available hooks ## Available hooks
You can run custom hooks with manga-dlp for specific events. You can run custom hooks with manga-dlp for specific events.
They are run with the `subproccess.call` function, so they get run directly by your operating system. They are run with the `subproccess.run` function, so they get run directly by your operating system.
The available hook events are: The available hook events are:

View file

@ -87,30 +87,34 @@ See the docker [README](docker/)
## Options ## Options
```txt ```txt
usage: manga-dlp.py [-h] (-u URL_UUID | --read READ | -v) [-c CHAPTERS] [-p PATH] [-l LANG] [--list] [--format FORMAT] [--forcevol] [--wait WAIT] [--lean | --verbose | --debug] [--hook-manga-pre HOOK_MANGA_PRE] Usage: manga-dlp.py [OPTIONS]
[--hook-manga-post HOOK_MANGA_POST] [--hook-chapter-pre HOOK_CHAPTER_PRE] [--hook-chapter-post HOOK_CHAPTER_POST]
Script to download mangas from various sites Script to download mangas from various sites
optional arguments: Options:
-h, --help show this help message and exit --help Show this message and exit.
-u URL_UUID, --url URL_UUID, --uuid URL_UUID URL or UUID of the manga --version Show the version and exit.
--read READ Path of file with manga links to download. One per line
-v, --version Show version of manga-dlp and exit source: [mutually_exclusive, required]
-c CHAPTERS, --chapters CHAPTERS Chapters to download -u, --url, --uuid TEXT URL or UUID of the manga
-p PATH, --path PATH Download path. Defaults to "<script_dir>/downloads" --read FILE Path of file with manga links to download. One per line
-l LANG, --language LANG Manga language. Defaults to "en" --> english
--list List all available chapters. Defaults to false verbosity: [mutually_exclusive]
--format FORMAT Archive format to create. An empty string means dont archive the folder. Defaults to 'cbz' --loglevel INTEGER Custom log level [default: 20]
--warn Only log warnings and higher
--debug Debug logging. Log EVERYTHING
-c, --chapters TEXT Chapters to download
-p, --path PATH Download path [default: downloads]
-l, --language TEXT Manga language [default: en]
--list List all available chapters
--format TEXT Archive format to create. An empty string means dont archive the folder [default: cbz]
--forcevol Force naming of volumes. For mangas where chapters reset each volume --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 --wait FLOAT Time to wait for each picture to download in seconds(float) [default: 0.5]
--lean Lean logging. Minimal log output. Defaults to false --hook-manga-pre TEXT Commands to execute before the manga download starts
--verbose Verbose logging. More log output. Defaults to false --hook-manga-post TEXT Commands to execute after the manga download finished
--debug Debug logging. Most log output. Defaults to false --hook-chapter-pre TEXT Commands to execute before the chapter download starts
--hook-manga-pre HOOK_MANGA_PRE Commands to execute before the manga download starts --hook-chapter-post TEXT Commands to execute after the chapter download finished
--hook-manga-post HOOK_MANGA_POST Commands to execute after the manga download finished
--hook-chapter-pre HOOK_CHAPTER_PRE Commands to execute before the chapter download starts
--hook-chapter-post HOOK_CHAPTER_POST Commands to execute after the chapter download finished
``` ```
## Contribution / Bugs ## Contribution / Bugs

View file

@ -1,4 +1,6 @@
from mangadlp.input import main import sys
import mangadlp.cli
if __name__ == "__main__": if __name__ == "__main__":
main() sys.exit(mangadlp.cli.main()) # pylint: disable=no-value-for-parameter

View file

@ -1 +1 @@
__version__ = "2.1.14" __version__ = "2.1.15"

View file

@ -1,4 +0,0 @@
from mangadlp.logger import prepare_logger
# prepare logger with default level INFO==20
prepare_logger()

View file

@ -1,6 +1,6 @@
import sys import sys
from mangadlp.input import main import mangadlp.cli
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main()) sys.exit(mangadlp.cli.main()) # pylint: disable=no-value-for-parameter

View file

@ -3,12 +3,9 @@ import sys
from time import sleep from time import sleep
import requests import requests
from loguru import logger as log
from mangadlp import utils from mangadlp import utils
from mangadlp.logger import Logger
# prepare logger
log = Logger(__name__)
class Mangadex: class Mangadex:
@ -57,7 +54,7 @@ class Mangadex:
# make initial request # make initial request
def get_manga_data(self) -> dict: def get_manga_data(self) -> dict:
log.verbose(f"Getting manga data for: {self.manga_uuid}") log.debug(f"Getting manga data for: {self.manga_uuid}")
counter = 1 counter = 1
while counter <= 3: while counter <= 3:
try: try:
@ -98,7 +95,7 @@ class Mangadex:
# get the title of the manga (and fix the filename) # get the title of the manga (and fix the filename)
def get_manga_title(self) -> str: def get_manga_title(self) -> str:
log.verbose(f"Getting manga title for: {self.manga_uuid}") log.debug(f"Getting manga title for: {self.manga_uuid}")
manga_data = self.manga_data manga_data = self.manga_data
try: try:
title = manga_data["data"]["attributes"]["title"][self.language] title = manga_data["data"]["attributes"]["title"][self.language]
@ -117,9 +114,7 @@ class Mangadex:
# check if chapters are available in requested language # check if chapters are available in requested language
def check_chapter_lang(self) -> int: def check_chapter_lang(self) -> int:
log.verbose( log.debug(f"Checking for chapters in specified language for: {self.manga_uuid}")
f"Checking for chapters in specified language for: {self.manga_uuid}"
)
r = requests.get( r = requests.get(
f"{self.api_base_url}/manga/{self.manga_uuid}/feed?limit=0&{self.api_additions}" f"{self.api_base_url}/manga/{self.manga_uuid}/feed?limit=0&{self.api_additions}"
) )
@ -139,7 +134,7 @@ class Mangadex:
# get chapter data like name, uuid etc # get chapter data like name, uuid etc
def get_chapter_data(self) -> dict: def get_chapter_data(self) -> dict:
log.verbose(f"Getting chapter data for: {self.manga_uuid}") log.debug(f"Getting chapter data for: {self.manga_uuid}")
api_sorting = "order[chapter]=asc&order[volume]=asc" api_sorting = "order[chapter]=asc&order[volume]=asc"
# check for chapters in specified lang # check for chapters in specified lang
total_chapters = self.check_chapter_lang() total_chapters = self.check_chapter_lang()
@ -197,7 +192,7 @@ class Mangadex:
# get images for the chapter (mangadex@home) # get images for the chapter (mangadex@home)
def get_chapter_images(self, chapter: str, wait_time: float) -> list: def get_chapter_images(self, chapter: str, wait_time: float) -> list:
log.verbose(f"Getting chapter images for: {self.manga_uuid}") log.debug(f"Getting chapter images for: {self.manga_uuid}")
athome_url = f"{self.api_base_url}/at-home/server" athome_url = f"{self.api_base_url}/at-home/server"
chapter_uuid = self.manga_chapter_data[chapter][0] chapter_uuid = self.manga_chapter_data[chapter][0]
@ -244,7 +239,7 @@ class Mangadex:
# create list of chapters # create list of chapters
def create_chapter_list(self) -> list: def create_chapter_list(self) -> list:
log.verbose(f"Creating chapter list for: {self.manga_uuid}") log.debug(f"Creating chapter list for: {self.manga_uuid}")
chapter_list = [] chapter_list = []
for chapter in self.manga_chapter_data.items(): for chapter in self.manga_chapter_data.items():
chapter_info = self.get_chapter_infos(chapter[0]) chapter_info = self.get_chapter_infos(chapter[0])

View file

@ -4,13 +4,11 @@ import sys
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from loguru import logger as log
from mangadlp import downloader, utils from mangadlp import downloader, utils
from mangadlp.api.mangadex import Mangadex from mangadlp.api.mangadex import Mangadex
from mangadlp.hooks import Hooks from mangadlp.hooks import run_hook
from mangadlp.logger import Logger
# prepare logger
log = Logger(__name__)
class MangaDLP: class MangaDLP:
@ -53,14 +51,12 @@ class MangaDLP:
self.forcevol: bool = forcevol self.forcevol: bool = forcevol
self.download_path: str = download_path self.download_path: str = download_path
self.download_wait: float = download_wait self.download_wait: float = download_wait
# hooks self.manga_pre_hook_cmd: str = manga_pre_hook_cmd
self.hook: Hooks = Hooks( self.manga_post_hook_cmd: str = manga_post_hook_cmd
manga_pre_hook_cmd, self.chapter_pre_hook_cmd: str = chapter_pre_hook_cmd
manga_post_hook_cmd, self.chapter_post_hook_cmd: str = chapter_post_hook_cmd
chapter_pre_hook_cmd,
chapter_post_hook_cmd,
)
self.hook_infos: dict = {} self.hook_infos: dict = {}
# prepare everything # prepare everything
self._prepare() self._prepare()
@ -136,7 +132,7 @@ class MangaDLP:
print_divider = "=========================================" print_divider = "========================================="
# show infos # show infos
log.info(f"{print_divider}") log.info(f"{print_divider}")
log.lean(f"Manga Name: {self.manga_title}") log.info(f"Manga Name: {self.manga_title}")
log.info(f"Manga UUID: {self.manga_uuid}") log.info(f"Manga UUID: {self.manga_uuid}")
log.info(f"Total chapters: {self.manga_total_chapters}") log.info(f"Total chapters: {self.manga_total_chapters}")
@ -144,7 +140,7 @@ class MangaDLP:
if self.list_chapters: if self.list_chapters:
log.info(f"Available Chapters: {', '.join(self.manga_chapter_list)}") log.info(f"Available Chapters: {', '.join(self.manga_chapter_list)}")
log.info(f"{print_divider}\n") log.info(f"{print_divider}\n")
sys.exit(0) return
# check chapters to download if not all # check chapters to download if not all
if self.chapters.lower() == "all": if self.chapters.lower() == "all":
@ -155,7 +151,7 @@ class MangaDLP:
) )
# show chapters to download # show chapters to download
log.lean(f"Chapters selected: {', '.join(chapters_to_download)}") log.info(f"Chapters selected: {', '.join(chapters_to_download)}")
log.info(f"{print_divider}") log.info(f"{print_divider}")
# create manga folder # create manga folder
@ -179,7 +175,12 @@ class MangaDLP:
) )
# start manga pre hook # start manga pre hook
self.hook.run("manga_pre", {"status": "starting"}, self.hook_infos) run_hook(
command=self.manga_pre_hook_cmd,
hook_type="manga_pre",
status="starting",
**self.hook_infos,
)
# get chapters # get chapters
for chapter in chapters_to_download: for chapter in chapters_to_download:
@ -201,24 +202,34 @@ class MangaDLP:
log.info(f"Done with chapter '{chapter}'\n") log.info(f"Done with chapter '{chapter}'\n")
# start chapter post hook # start chapter post hook
self.hook.run("chapter_post", {"status": "successful"}, self.hook_infos) run_hook(
command=self.chapter_post_hook_cmd,
hook_type="chapter_post",
status="successful",
**self.hook_infos,
)
# done with manga # done with manga
log.info(f"{print_divider}") log.info(f"{print_divider}")
log.lean(f"Done with manga: {self.manga_title}") log.info(f"Done with manga: {self.manga_title}")
# filter skipped list # filter skipped list
skipped_chapters = list(filter(None, skipped_chapters)) skipped_chapters = list(filter(None, skipped_chapters))
if len(skipped_chapters) >= 1: if len(skipped_chapters) >= 1:
log.lean(f"Skipped chapters: {', '.join(skipped_chapters)}") log.info(f"Skipped chapters: {', '.join(skipped_chapters)}")
# filter error list # filter error list
error_chapters = list(filter(None, error_chapters)) error_chapters = list(filter(None, error_chapters))
if len(error_chapters) >= 1: if len(error_chapters) >= 1:
log.lean(f"Chapters with errors: {', '.join(error_chapters)}") log.info(f"Chapters with errors: {', '.join(error_chapters)}")
# start manga post hook # start manga post hook
self.hook.run("manga_post", {"status": "successful"}, self.hook_infos) run_hook(
command=self.manga_post_hook_cmd,
hook_type="manga_post",
status="successful",
**self.hook_infos,
)
log.info(f"{print_divider}\n") log.info(f"{print_divider}\n")
@ -242,8 +253,12 @@ class MangaDLP:
f"No images: Skipping Vol. {chapter_infos['volume']} Ch.{chapter_infos['chapter']}" f"No images: Skipping Vol. {chapter_infos['volume']} Ch.{chapter_infos['chapter']}"
) )
self.hook.run( run_hook(
"chapter_pre", {"status": "skipped", "reason": "No images"}, {} command=self.chapter_pre_hook_cmd,
hook_type="chapter_pre",
status="skipped",
reason="No images",
**self.hook_infos,
) )
# add to skipped chapters list # add to skipped chapters list
@ -271,8 +286,12 @@ class MangaDLP:
if chapter_archive_path.exists(): if chapter_archive_path.exists():
log.info(f"'{chapter_archive_path}' already exists. Skipping") log.info(f"'{chapter_archive_path}' already exists. Skipping")
self.hook.run( run_hook(
"chapter_pre", {"status": "skipped", "reason": "Existing"}, {} command=self.chapter_pre_hook_cmd,
hook_type="chapter_pre",
status="skipped",
reason="Existing",
**self.hook_infos,
) )
# add to skipped chapters list # add to skipped chapters list
@ -289,10 +308,10 @@ class MangaDLP:
chapter_path.mkdir(parents=True, exist_ok=True) chapter_path.mkdir(parents=True, exist_ok=True)
# verbose log # verbose log
log.verbose(f"Chapter UUID: {chapter_infos['uuid']}") log.debug(f"Chapter UUID: {chapter_infos['uuid']}")
log.verbose(f"Filename: '{chapter_archive_path.name}'") log.debug(f"Filename: '{chapter_archive_path.name}'")
log.verbose(f"File path: '{chapter_archive_path}'") log.debug(f"File path: '{chapter_archive_path}'")
log.verbose(f"Image URLS:\n{chapter_image_urls}") log.debug(f"Image URLS:\n{chapter_image_urls}")
# create dict with all variables for the hooks # create dict with all variables for the hooks
self.hook_infos.update( self.hook_infos.update(
@ -308,10 +327,15 @@ class MangaDLP:
) )
# start chapter pre hook # start chapter pre hook
self.hook.run("chapter_pre", {"status": "starting"}, self.hook_infos) run_hook(
command=self.chapter_pre_hook_cmd,
hook_type="chapter_pre",
status="starting",
**self.hook_infos,
)
# log # log
log.lean(f"Downloading: '{chapter_filename}'") log.info(f"Downloading: '{chapter_filename}'")
# download images # download images
try: try:
@ -324,8 +348,13 @@ class MangaDLP:
except Exception: except Exception:
log.error(f"Cant download: '{chapter_filename}'. Skipping") log.error(f"Cant download: '{chapter_filename}'. Skipping")
self.hook.run( # run chapter post hook
"chapter_post", {"status": "error", "reason": "Download error"}, {} run_hook(
command=self.chapter_post_hook_cmd,
hook_type="chapter_post",
status="starting",
reason="Download error",
**self.hook_infos,
) )
# add to skipped chapters list # add to skipped chapters list
@ -340,13 +369,13 @@ class MangaDLP:
else: else:
# Done with chapter # Done with chapter
log.lean(f"Successfully downloaded: '{chapter_filename}'") log.info(f"Successfully downloaded: '{chapter_filename}'")
return {"chapter_path": chapter_path} return {"chapter_path": chapter_path}
# create an archive of the chapter if needed # create an archive of the chapter if needed
def archive_chapter(self, chapter_path: Path) -> dict: def archive_chapter(self, chapter_path: Path) -> dict:
log.lean(f"Creating archive '{chapter_path}{self.file_format}'") log.info(f"Creating archive '{chapter_path}{self.file_format}'")
try: try:
# check if image folder is existing # check if image folder is existing
if not chapter_path.exists(): if not chapter_path.exists():

243
mangadlp/cli.py Normal file
View file

@ -0,0 +1,243 @@
from pathlib import Path
import click
from click_option_group import (
MutuallyExclusiveOptionGroup,
RequiredMutuallyExclusiveOptionGroup,
optgroup,
)
from loguru import logger as log
from mangadlp import app
from mangadlp.__about__ import __version__
from mangadlp.logger import prepare_logger
# read in the list of links from a file
def readin_list(_, __, value) -> list:
if not value:
return []
list_file = Path(value)
click.echo(f"Reading in file '{list_file}'")
try:
url_str = list_file.read_text(encoding="utf-8")
url_list = url_str.splitlines()
except Exception as exc:
raise click.BadParameter("Can't get links from the file") from exc
# filter empty lines and remove them
filtered_list = list(filter(len, url_list))
log.info(f"Mangas from list: {filtered_list}")
return filtered_list
@click.command(context_settings={"max_content_width": 150})
@click.help_option()
@click.version_option(version=__version__, package_name="manga-dlp")
# manga selection
@optgroup.group("source", cls=RequiredMutuallyExclusiveOptionGroup)
@optgroup.option(
"-u",
"--url",
"--uuid",
"url_uuid",
type=str,
default=None,
show_default=True,
help="URL or UUID of the manga",
)
@optgroup.option(
"--read",
"read_mangas",
is_eager=True,
callback=readin_list,
type=click.Path(exists=True, dir_okay=False),
default=None,
show_default=True,
help="Path of file with manga links to download. One per line",
)
# logging options
@optgroup.group("verbosity", cls=MutuallyExclusiveOptionGroup)
@optgroup.option(
"--loglevel",
"verbosity",
type=int,
default=20,
show_default=True,
help="Custom log level",
)
@optgroup.option(
"--warn",
"verbosity",
flag_value=25,
default=20,
show_default=False,
help="Only log warnings and higher",
)
@optgroup.option(
"--debug",
"verbosity",
flag_value=10,
default=20,
show_default=False,
help="Debug logging. Log EVERYTHING",
)
# other options
@click.option(
"-c",
"--chapters",
"chapters",
type=str,
default=None,
required=False,
show_default=True,
help="Chapters to download",
)
@click.option(
"-p",
"--path",
"path",
type=click.Path(exists=False),
default="downloads",
required=False,
show_default=True,
help="Download path",
)
@click.option(
"-l",
"--language",
"lang",
type=str,
default="en",
required=False,
show_default=True,
help="Manga language",
)
@click.option(
"--list",
"list_chapters",
is_flag=True,
default=False,
required=False,
show_default=True,
help="List all available chapters",
)
@click.option(
"--format",
"chapter_format",
type=str,
default="cbz",
required=False,
show_default=True,
help="Archive format to create. An empty string means dont archive the folder",
)
@click.option(
"--forcevol",
"forcevol",
is_flag=True,
default=False,
required=False,
show_default=True,
help="Force naming of volumes. For mangas where chapters reset each volume",
)
@click.option(
"--wait",
"wait_time",
type=float,
default=0.5,
required=False,
show_default=True,
help="Time to wait for each picture to download in seconds(float)",
)
# hook options
@click.option(
"--hook-manga-pre",
"hook_manga_pre",
type=str,
default=None,
required=False,
show_default=True,
help="Commands to execute before the manga download starts",
)
@click.option(
"--hook-manga-post",
"hook_manga_post",
type=str,
default=None,
required=False,
show_default=True,
help="Commands to execute after the manga download finished",
)
@click.option(
"--hook-chapter-pre",
"hook_chapter_pre",
type=str,
default=None,
required=False,
show_default=True,
help="Commands to execute before the chapter download starts",
)
@click.option(
"--hook-chapter-post",
"hook_chapter_post",
type=str,
default=None,
required=False,
show_default=True,
help="Commands to execute after the chapter download finished",
)
@click.pass_context
def main(
ctx: click.Context,
url_uuid: str,
read_mangas: list,
verbosity: int,
chapters: str,
path: str,
lang: str,
list_chapters: bool,
chapter_format: str,
forcevol: bool,
wait_time: float,
hook_manga_pre: str,
hook_manga_post: str,
hook_chapter_pre: str,
hook_chapter_post: str,
): # pylint: disable=too-many-locals
"""
Script to download mangas from various sites
"""
# set loglevel and log format
prepare_logger(verbosity)
# list all params
log.debug(ctx.params)
# all request mangas
requested_mangas = [url_uuid] if url_uuid else read_mangas
for manga in requested_mangas:
mdlp = app.MangaDLP(
url_uuid=manga,
language=lang,
chapters=chapters,
list_chapters=list_chapters,
file_format=chapter_format,
forcevol=forcevol,
download_path=path,
download_wait=wait_time,
manga_pre_hook_cmd=hook_manga_pre,
manga_post_hook_cmd=hook_manga_post,
chapter_pre_hook_cmd=hook_chapter_pre,
chapter_post_hook_cmd=hook_chapter_post,
)
mdlp.get_manga()
if __name__ == "__main__":
main() # pylint: disable=no-value-for-parameter

View file

@ -6,12 +6,9 @@ from time import sleep
from typing import Union from typing import Union
import requests import requests
from loguru import logger as log
from mangadlp import utils from mangadlp import utils
from mangadlp.logger import Logger
# prepare logger
log = Logger(__name__)
# download images # download images
@ -29,7 +26,7 @@ def download_chapter(
# show progress bar for default log level # show progress bar for default log level
if logging.root.level == logging.INFO: if logging.root.level == logging.INFO:
utils.progress_bar(image_num, total_img) utils.progress_bar(image_num, total_img)
log.verbose(f"Downloading image {image_num}/{total_img}") log.debug(f"Downloading image {image_num}/{total_img}")
counter = 1 counter = 1
while counter <= 3: while counter <= 3:

View file

@ -1,71 +1,40 @@
import os import os
import subprocess import subprocess
from mangadlp.logger import Logger from loguru import logger as log
# prepare logger
log = Logger(__name__)
class Hooks: def run_hook(command: str, hook_type: str, **kwargs) -> int:
"""Pre- and post-hooks for each download. """
Args: Args:
cmd_manga_pre (str): Commands to execute before the manga download starts command (str): command to run
cmd_manga_post (str): Commands to execute after the manga download finished hook_type (str): type of the hook
cmd_chapter_pre (str): Commands to execute before the chapter download starts kwargs: key value pairs of env vars to set
cmd_chapter_post (str): Commands to execute after the chapter download finished
Returns:
exit_code (int): exit code of command
""" """
def __init__(
self,
cmd_manga_pre: str,
cmd_manga_post: str,
cmd_chapter_pre: str,
cmd_chapter_post: str,
) -> None:
self.cmd_manga_pre = cmd_manga_pre
self.cmd_manga_post = cmd_manga_post
self.cmd_chapter_pre = cmd_chapter_pre
self.cmd_chapter_post = cmd_chapter_post
def run(self, hook_type: str, hook_status: dict, hook_info: dict) -> int:
if hook_type == "manga_pre":
hook_cmd_str = self.cmd_manga_pre
elif hook_type == "manga_post":
hook_cmd_str = self.cmd_manga_post
elif hook_type == "chapter_pre":
hook_cmd_str = self.cmd_chapter_pre
elif hook_type == "chapter_post":
hook_cmd_str = self.cmd_chapter_post
else:
log.error(f"Hook type '{hook_type}' is not valid. Not running")
return 1
# check if hook commands are empty # check if hook commands are empty
if not hook_cmd_str or hook_cmd_str == "None": if not command or command == "None":
log.verbose(f"Hook '{hook_type}' empty. Not running") log.debug(f"Hook '{hook_type}' empty. Not running")
return 2 return 2
hook_cmd_list = hook_cmd_str.split(" ") command_list = command.split(" ")
# setting env vars # setting env vars
hook_info["hook_type"] = hook_type for key, value in kwargs.items():
hook_info["status"] = hook_status.get("status")
hook_info["reason"] = hook_status.get("reason")
for key, value in hook_info.items():
os.environ[f"MDLP_{key.upper()}"] = str(value) os.environ[f"MDLP_{key.upper()}"] = str(value)
# running command # running command
log.info(f"Hook '{hook_type}' - running command: '{hook_cmd_str}'") log.info(f"Hook '{hook_type}' - running command: '{command}'")
ecode = subprocess.call(hook_cmd_list) proc = subprocess.run(command_list, check=False, timeout=15, encoding="utf8")
exit_code = proc.returncode
if ecode == 0: if exit_code == 0:
log.verbose("Hook returned status code 0. All good") log.debug("Hook returned status code 0. All good")
else: else:
log.warning(f"Hook returned status code {ecode}. Possible error") log.warning(f"Hook returned status code {exit_code}. Possible error")
# return exit code of command # return exit code of command
return ecode return exit_code

View file

@ -1,268 +0,0 @@
import argparse
import sys
from pathlib import Path
from mangadlp import app, logger
from mangadlp.__about__ import __version__
from mangadlp.logger import Logger
# prepare logger
log = Logger(__name__)
def check_args(args):
# set logger formatting
logger.format_logger(args.verbosity)
# check if --version was used
if args.version:
print(f"manga-dlp version: {__version__}")
sys.exit(0)
# check if a readin list was provided
if not args.read:
# single manga, no readin list
call_app(args)
else:
# multiple mangas
url_list = readin_list(args.read)
for url in url_list:
args.url_uuid = url
call_app(args)
# read in the list of links from a file
def readin_list(readlist: str) -> list:
list_file = Path(readlist)
log.verbose(f"Reading in list '{str(list_file)}'")
try:
url_str = list_file.read_text(encoding="utf-8")
url_list = url_str.splitlines()
except Exception as exc:
raise IOError from exc
# filter empty lines and remove them
filtered_list = list(filter(len, url_list))
log.verbose(f"Mangas from list: {filtered_list}")
return filtered_list
def call_app(args):
# call main function with all input arguments
mdlp = app.MangaDLP(
url_uuid=args.url_uuid,
language=args.lang,
chapters=args.chapters,
list_chapters=args.list,
file_format=args.format,
forcevol=args.forcevol,
download_path=args.path,
download_wait=args.wait,
manga_pre_hook_cmd=args.hook_manga_pre,
manga_post_hook_cmd=args.hook_manga_post,
chapter_pre_hook_cmd=args.hook_chapter_pre,
chapter_post_hook_cmd=args.hook_chapter_post,
)
mdlp.get_manga()
def get_input():
print(f"manga-dlp version: {__version__}")
print("Enter details of the manga you want to download:")
while True:
try:
url_uuid = str(input("Url or UUID: "))
readlist = str(input("List with links (optional): "))
language = str(input("Language: ")) or "en"
list_chapters = str(input("List chapters? y/N: "))
if list_chapters.lower() in {"y", "yes"}:
chapters = str(input("Chapters: "))
except KeyboardInterrupt:
sys.exit(1)
except Exception:
continue
else:
break
args = [
"-l",
language,
"-c",
chapters,
]
if url_uuid:
args.extend(("-u", url_uuid))
if readlist:
args.extend(("--read", readlist))
if list_chapters.lower() in {"y", "yes"}:
args.append("--list")
# start script again with the arguments
sys.argv.extend(args)
log.info(f"Args: {sys.argv}")
get_args()
def get_args():
parser = argparse.ArgumentParser(
description="Script to download mangas from various sites"
)
action = parser.add_mutually_exclusive_group(required=True)
verbosity = parser.add_mutually_exclusive_group(required=False)
# selection options
action.add_argument(
"-u",
"--url",
"--uuid",
dest="url_uuid",
required=False,
help="URL or UUID of the manga",
action="store",
)
action.add_argument(
"--read",
dest="read",
required=False,
help="Path of file with manga links to download. One per line",
action="store",
)
action.add_argument(
"-v",
"--version",
dest="version",
required=False,
help="Show version of manga-dlp and exit",
action="store_true",
)
# base options
parser.add_argument(
"-c",
"--chapters",
dest="chapters",
required=False,
help="Chapters to download",
action="store",
)
parser.add_argument(
"-p",
"--path",
dest="path",
required=False,
help='Download path. Defaults to "<script_dir>/downloads"',
action="store",
default="downloads",
)
parser.add_argument(
"-l",
"--language",
dest="lang",
required=False,
help='Manga language. Defaults to "en" --> english',
action="store",
default="en",
)
parser.add_argument(
"--list",
dest="list",
required=False,
help="List all available chapters. Defaults to false",
action="store_true",
)
parser.add_argument(
"--format",
dest="format",
required=False,
help="Archive format to create. An empty string means dont archive the folder. Defaults to 'cbz'",
action="store",
default="cbz",
)
parser.add_argument(
"--forcevol",
dest="forcevol",
required=False,
help="Force naming of volumes. For mangas where chapters reset each volume",
action="store_true",
)
parser.add_argument(
"--wait",
dest="wait",
required=False,
type=float,
default=0.5,
help="Time to wait for each picture to download in seconds(float). Defaults 0.5",
)
# logging options
verbosity.add_argument(
"--lean",
dest="verbosity",
required=False,
help="Lean logging. Minimal log output. Defaults to false",
action="store_const",
const=25,
default=20,
)
verbosity.add_argument(
"--verbose",
dest="verbosity",
required=False,
help="Verbose logging. More log output. Defaults to false",
action="store_const",
const=15,
default=20,
)
verbosity.add_argument(
"--debug",
dest="verbosity",
required=False,
help="Debug logging. Most log output. Defaults to false",
action="store_const",
const=10,
default=20,
)
# hook options
parser.add_argument(
"--hook-manga-pre",
dest="hook_manga_pre",
required=False,
help="Commands to execute before the manga download starts",
action="store",
)
parser.add_argument(
"--hook-manga-post",
dest="hook_manga_post",
required=False,
help="Commands to execute after the manga download finished",
action="store",
)
parser.add_argument(
"--hook-chapter-pre",
dest="hook_chapter_pre",
required=False,
help="Commands to execute before the chapter download starts",
action="store",
)
parser.add_argument(
"--hook-chapter-post",
dest="hook_chapter_post",
required=False,
help="Commands to execute after the chapter download finished",
action="store",
)
# parser.print_help()
args = parser.parse_args()
check_args(args)
def main():
if len(sys.argv) > 1:
get_args()
else:
get_input()
if __name__ == "__main__":
main()

View file

@ -1,71 +1,35 @@
import logging import logging
import sys
DATE_FMT = "%Y-%m-%dT%H:%M:%S%z" from loguru import logger
LOGGING_FMT: str = (
"%(asctime)s | (D) [%(levelname)-7s] [%(name)-10s] [%(funcName)-20s]: %(message)s"
)
LOGURU_FMT: str = "{time:%Y-%m-%dT%H:%M:%S%z} | (C) <level>[{level: <7}]</level> [{name: <10}] [{function: <20}]: {message}"
# prepare custom levels and default config of logger def enable_default_logger(loglevel: int) -> None:
def prepare_logger(): logging.root.handlers = []
logging.basicConfig( logging.basicConfig(
format="%(asctime)s | [%(levelname)s] [%(name)s]: %(message)s", format=LOGGING_FMT,
datefmt=DATE_FMT, datefmt="%Y-%m-%dT%H:%M:%S%z",
level=20, level=loglevel,
handlers=[logging.StreamHandler()], handlers=[logging.StreamHandler()],
) )
logging.addLevelName(level=15, levelName="VERBOSE")
logging.addLevelName(level=25, levelName="LEAN")
# set log message format # create config for a normal stderr logger
def format_logger(verbosity: int): def prepare_logger(loglevel: int) -> None:
logging.getLogger().setLevel(verbosity) config: dict = {
"handlers": [
# dont show log level name on default/lean logging {
if verbosity >= 20: "sink": sys.stdout,
logging.basicConfig( "level": loglevel,
format="%(asctime)s | %(message)s", "format": LOGURU_FMT,
datefmt=DATE_FMT, },
force=True, ],
) }
else: logger.configure(**config)
logging.basicConfig( enable_default_logger(loglevel)
format="%(asctime)s | [%(levelname)s] [%(name)s]: %(message)s",
datefmt=DATE_FMT,
force=True,
)
class Logger:
"""Default logger for manga-dlp.
Args:
name (str): Name of the logger
"""
def __init__(self, name: str):
self.name = name
# create logger
self.log = logging.getLogger(self.name)
# custom log levels
def verbose(self, message: str):
self.log.log(level=15, msg=message)
def lean(self, message: str):
self.log.log(level=25, msg=message)
# default log levels
def critical(self, message: str):
self.log.critical(msg=message)
def error(self, message: str):
self.log.error(msg=message)
def warning(self, message: str):
self.log.warning(msg=message)
def info(self, message: str):
self.log.info(msg=message)
def debug(self, message: str):
self.log.debug(msg=message)

View file

@ -4,15 +4,12 @@ from pathlib import Path
from typing import Any from typing import Any
from zipfile import ZipFile from zipfile import ZipFile
from mangadlp.logger import Logger from loguru import logger as log
# prepare logger
log = Logger(__name__)
# create an archive of the chapter images # create an archive of the chapter images
def make_archive(chapter_path: Path, file_format: str) -> None: def make_archive(chapter_path: Path, file_format: str) -> None:
zip_path = Path(f"{chapter_path}.zip") zip_path: Path = Path(f"{chapter_path}.zip")
try: try:
# create zip # create zip
with ZipFile(zip_path, "w") as zipfile: with ZipFile(zip_path, "w") as zipfile:
@ -26,13 +23,13 @@ def make_archive(chapter_path: Path, file_format: str) -> None:
def make_pdf(chapter_path: Path) -> None: def make_pdf(chapter_path: Path) -> None:
try: try:
import img2pdf import img2pdf # pylint: disable=import-outside-toplevel
except Exception as exc: except Exception as exc:
log.error("Cant import img2pdf. Please install it first") log.error("Cant import img2pdf. Please install it first")
raise ImportError from exc raise ImportError from exc
pdf_path = Path(f"{chapter_path}.pdf") pdf_path: Path = Path(f"{chapter_path}.pdf")
images = [] images: list[str] = []
for file in chapter_path.iterdir(): for file in chapter_path.iterdir():
images.append(str(file)) images.append(str(file))
try: try:
@ -85,6 +82,9 @@ def get_chapter_list(chapters: str, available_chapters: list) -> list:
# remove illegal characters etc # remove illegal characters etc
def fix_name(filename: str) -> str: def fix_name(filename: str) -> str:
filename = filename.encode(encoding="ascii", errors="ignore").decode(
encoding="utf8"
)
# remove illegal characters # remove illegal characters
filename = re.sub(r'[/\\<>:;|?*!@"]', "", filename) filename = re.sub(r'[/\\<>:;|?*!@"]', "", filename)
# remove multiple dots # remove multiple dots

View file

@ -1,5 +1,5 @@
[build-system] [build-system]
requires = ["hatchling>=1.4.1"] requires = ["hatchling>=1.11.0"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[project] [project]
@ -26,7 +26,10 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
] ]
dependencies = [ dependencies = [
"requests>=2.24.0", "requests>=2.28.0",
"loguru>=0.6.0",
"click>=8.1.3",
"click-option-group>=0.5.5",
] ]
[project.urls] [project.urls]
@ -36,8 +39,8 @@ Tracker = "https://github.com/olofvndrhr/manga-dlp/issues"
Source = "https://github.com/olofvndrhr/manga-dlp" Source = "https://github.com/olofvndrhr/manga-dlp"
[project.scripts] [project.scripts]
mangadlp = "mangadlp.input:main" mangadlp = "mangadlp.cli:main"
manga-dlp = "mangadlp.input:main" manga-dlp = "mangadlp.cli:main"
[tool.hatch.version] [tool.hatch.version]
path = "mangadlp/__about__.py" path = "mangadlp/__about__.py"
@ -53,9 +56,13 @@ packages = ["mangadlp"]
[tool.hatch.envs.default] [tool.hatch.envs.default]
dependencies = [ dependencies = [
"requests>=2.28.0",
"loguru>=0.6.0",
"click>=8.1.3",
"click-option-group>=0.5.5",
"img2pdf>=0.4.4", "img2pdf>=0.4.4",
"hatch>=1.2.1", "hatch>=1.6.0",
"hatchling>=1.4.1", "hatchling>=1.11.0",
"pytest>=7.0.0", "pytest>=7.0.0",
"coverage>=6.3.1", "coverage>=6.3.1",
"black>=22.1.0", "black>=22.1.0",
@ -121,7 +128,7 @@ ignore_errors = true
py-version = "3.9" py-version = "3.9"
[tool.pylint.logging] [tool.pylint.logging]
logging-modules = ["logging"] logging-modules = ["logging", "loguru"]
disable = "C0301, C0114, C0116, W0703, R0902, R0913" disable = "C0301, C0114, C0116, W0703, R0902, R0913, E0401, W1203"
good-names = "r" good-names = "r"
#logging-format-style = "fstr" logging-format-style = "new"

View file

@ -1,3 +1,6 @@
requests>=2.24.0 requests>=2.28.0
loguru>=0.6.0
click>=8.1.3
click-option-group>=0.5.5
img2pdf>=0.4.4 img2pdf>=0.4.4

View file

@ -3,7 +3,7 @@ from pathlib import Path
import pytest import pytest
import mangadlp.input as mdlpinput import mangadlp.cli as mdlpinput
def test_read_and_url(): def test_read_and_url():
@ -54,7 +54,7 @@ def test_no_volume():
def test_readin_list(): def test_readin_list():
list_file = "tests/test_list.txt" list_file = "tests/test_list.txt"
test_list = mdlpinput.readin_list(list_file) test_list = mdlpinput.readin_list(None, None, list_file)
assert test_list == [ assert test_list == [
"https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu", "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu",