[2.1.9] - 2022-06-26
All checks were successful
ci/woodpecker/tag/tests Pipeline was successful
ci/woodpecker/tag/publish_release Pipeline was successful
ci/woodpecker/tag/publish_docker Pipeline was successful

## [2.1.9] - 2022-06-26

### Fixed

- Timeouts in tests, due to api limitations. Now added a wait time between tests
- Pytest path

### Added

- `--lean` flag for less output
- [justfile](https://github.com/casey/just) for setting up a dev environment and testing the code
- [asdf](https://github.com/asdf-vm/asdf) for version management
- Dev requirements in [contrib/requirements_dev.txt](contrib/requirements_dev.txt)
- `README` in [contrib](contrib)

### Changed

- Handling of verbosity and logging. Now there are 4 types of verbosity: `normal`, `lean`, `verbose` and `debug`
- CI/CD pipeline for testing and releases
- Coverage testing now also done with `tox`
- Default verbosity of docker container is now `--lean`
- Reorganised [pyproject.toml](pyproject.toml)
This commit is contained in:
Ivan Schaller 2022-06-26 21:16:39 +02:00
commit b4d636a845
32 changed files with 512 additions and 251 deletions

View file

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

1
.envrc Normal file
View file

@ -0,0 +1 @@
use asdf

5
.tool-versions Normal file
View file

@ -0,0 +1,5 @@
python 3.9.13 3.10.5 3.8.13 3.7.13 3.6.15
shfmt 3.5.1
shellcheck 0.8.0
just 1.2.0
direnv 2.32.1

View file

@ -1,42 +0,0 @@
#############
# build app #
#############
# branch: master
# event: tag
depends_on:
- tests
clone:
git:
when:
#branch: master
event: tag
image: woodpeckerci/plugin-git
pipeline:
# build wheel and dist
build-pypi:
when:
#branch: master
event: tag
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- hatch build
# release pypi
release-pypi:
when:
#branch: master
event: tag
image: cr.44net.ch/ci-plugins/tests
pull: true
secrets:
- source: pypi_username
target: HATCH_PYPI_USER
- source: pypi_token
target: HATCH_PYPI_AUTH
commands:
- hatch publish

View file

@ -16,15 +16,15 @@ clone:
pipeline:
# create release tar
create-release-tar:
# build wheel and dist
build-pypi:
when:
#branch: master
event: tag
image: cr.44net.ch/baseimages/debian-base
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- tar -czf manga-dlp-${CI_COMMIT_TAG}.tar.gz --files-from=release-files.txt
- hatch build
# create release-notes
create-release-notes:
@ -47,7 +47,7 @@ pipeline:
api_key:
from_secret: gitea-olofvndrhr-token
base_url: https://git.44net.ch
files: manga-dlp-${CI_COMMIT_TAG}.tar.gz
files: dist/*
title: ${CI_COMMIT_TAG}
note: RELEASENOTES.md
@ -61,6 +61,21 @@ pipeline:
settings:
api_key:
from_secret: github-olofvndrhr-token
files: manga-dlp-${CI_COMMIT_TAG}.tar.gz
files: dist/*
title: ${CI_COMMIT_TAG}
note: RELEASENOTES.md
# release pypi
release-pypi:
when:
#branch: master
event: tag
image: cr.44net.ch/ci-plugins/tests
pull: true
secrets:
- source: pypi_username
target: HATCH_PYPI_USER
- source: pypi_token
target: HATCH_PYPI_AUTH
commands:
- hatch publish

View file

@ -1,47 +0,0 @@
##################
# test build app #
##################
# branch: master
# event: pull_request
depends_on:
- tests
clone:
git:
when:
branch: master
event: pull_request
image: woodpeckerci/plugin-git
pipeline:
# build wheel and dist
test-build-pypi:
when:
branch: master
event: pull_request
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- hatch build
# # test code with different python versions - amd64
# test-tox-amd64:
# when:
# branch: master
# event: pull_request
# image: cr.44net.ch/ci-plugins/tests:1-linux-amd64
# pull: true
# commands:
# - tox
#
# # test code with different python versions - arm64
# test-tox-arm64:
# when:
# branch: master
# event: pull_request
# image: cr.44net.ch/ci-plugins/tests:1-linux-arm64
# pull: true
# commands:
# - tox

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.8
build_args: BUILD_VERSION=2.1.9
# 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.8
build_args: BUILD_VERSION=2.1.9

View file

@ -1,6 +1,6 @@
########################
# test publish release #
########################
################
# test release #
################
# branch: master
# event: pull_request
@ -16,16 +16,15 @@ clone:
pipeline:
# create release tar
test-create-release-tar:
# build wheel and dist
test-build-pypi:
when:
branch: master
event: pull_request
image: cr.44net.ch/baseimages/debian-base
image: cr.44net.ch/ci-plugins/tests
pull: true
commands:
- tar -czf manga-dlp-2.1.8.tar.gz --files-from=release-files.txt
- tar -tzf manga-dlp-2.1.8.tar.gz
- hatch build
# create release-notes
test-create-release-notes:
@ -35,5 +34,5 @@ pipeline:
image: cr.44net.ch/baseimages/debian-base
pull: true
commands:
- bash get_release_notes.sh 2.1.8
- bash get_release_notes.sh 2.1.9
- cat RELEASENOTES.md

View file

@ -0,0 +1,27 @@
##################
# test tox amd64 #
##################
# branch: master
# event: pull_request
depends_on:
- tests
clone:
git:
when:
branch: master
event: pull_request
image: woodpeckerci/plugin-git
pipeline:
# test code with different python versions - amd64
test-tox-amd64:
when:
branch: master
event: pull_request
image: cr.44net.ch/ci-plugins/multipy:1-linux-amd64
pull: true
commands:
- tox

View file

@ -0,0 +1,30 @@
##################
# test tox arm64 #
##################
# branch: master
# event: pull_request
depends_on:
- tests
clone:
git:
when:
branch: master
event: pull_request
image: woodpeckerci/plugin-git
pipeline:
# test code with different python versions - arm64
test-tox-arm64:
when:
branch: master
event: pull_request
image: cr.44net.ch/ci-plugins/multipy:1-linux-arm64
pull: true
commands:
- grep -v img2pdf contrib/requirements_dev.txt > contrib/requirements_dev_arm64.txt
- rm -f contrib/requirements_dev.txt
- mv contrib/requirements_dev_arm64.txt contrib/requirements_dev.txt
- tox

View file

@ -38,20 +38,30 @@ pipeline:
commands:
- mypy --install-types --non-interactive mangadlp/
# test code and generate coverage report
test-coverage-pytest:
image: cr.44net.ch/ci-plugins/tests
# test code with different python versions
test-tox-pytest:
when:
event: [ push ]
image: cr.44net.ch/ci-plugins/multipy:1-linux-amd64
pull: true
commands:
- pip install -r requirements.txt
- coverage erase
- coverage run
- coverage xml -i
- tox -e basic
# generate coverage report
test-tox-coverage:
when:
branch: master
event: [ pull_request ]
image: cr.44net.ch/ci-plugins/multipy:1-linux-amd64
pull: true
commands:
- tox -e coverage
# analyse code with sonarqube and upload it
sonarqube-analysis:
when:
branch: master
event: [ pull_request ]
image: cr.44net.ch/ci-plugins/sonar-scanner
pull: true
settings:

View file

@ -9,6 +9,29 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Add support for more sites
## [2.1.9] - 2022-06-26
### Fixed
- Timeouts in tests, due to api limitations. Now added a wait time between tests
- Pytest path
### Added
- `--lean` flag for less output
- [justfile](https://github.com/casey/just) for setting up a dev environment and testing the code
- [asdf](https://github.com/asdf-vm/asdf) for version management
- Dev requirements in [contrib/requirements_dev.txt](contrib/requirements_dev.txt)
- `README` in [contrib](contrib)
### Changed
- Handling of verbosity and logging. Now there are 4 types of verbosity: `normal`, `lean`, `verbose` and `debug`
- CI/CD pipeline for testing and releases
- Coverage testing now also done with `tox`
- Default verbosity of docker container is now `--lean`
- Reorganised [pyproject.toml](pyproject.toml)
## [2.1.8] - 2022-06-22
### Fixed

View file

@ -224,10 +224,12 @@ This will download the chapter and save it as a zip archive.
For suggestions for improvement, just open a pull request.
If you want to add support for a new site, there is an api [template file](./contrib/api_template.py) which you can use.
And more infos and tools in the contrib [README.md](contrib/README.md)
Otherwise you can open a issue with the name of the site which you want support for. (not guaranteed to be implemented)
Otherwise, you can open am issue with the name of the site which you want support for. (not guaranteed to be
implemented)
If you encounter any bugs, also just open a issue with a description of the problem.
If you encounter any bugs, also just open an issue with a description of the problem.
## TODO's

16
contrib/README.md Normal file
View file

@ -0,0 +1,16 @@
# contribute
### install dev requirements
```sh
python3 -m pip install -r contrib/requirements_dev.txt
```
### setup asdf with all needed tools and versions
> you may need to install just first: [link](https://github.com/casey/just)
```sh
just prepare_workspace
```

View file

@ -0,0 +1,13 @@
# application requirements
requests>=2.24.0
img2pdf>=0.4.4
# dev and testing requirements
pytest>=7.0.0
coverage>=6.3.1
black>=22.1.0
isort>=5.10.0
pylint>=2.13.0
mypy>=0.940
tox>=3.24.5
hatch>=1.0.0

View file

@ -55,7 +55,8 @@ python3 /app/manga-dlp.py \
--path /app/downloads \
--read /app/mangas.txt \
--chapters all \
--wait 2
--wait 2 \
--lean
```
To use your own schedule you need to mount (override) the default schedule or add new ones to the crontab.

View file

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

137
justfile Executable file
View file

@ -0,0 +1,137 @@
#!/usr/bin/env just --justfile
default: show_receipts
set shell := ["bash", "-uc"]
set dotenv-load := true
#set export
# aliases
alias s := show_receipts
alias i := show_system_info
alias p := prepare_workspace
alias l := lint
alias t := tests
alias f := tests_full
# variables
export asdf_version := "v0.10.2"
# default recipe to display help information
show_receipts:
@just --list
show_system_info:
@echo "=================================="
@echo "os : {{os()}}"
@echo "arch: {{arch()}}"
@echo "home: ${HOME}"
@echo "project dir: {{justfile_directory()}}"
@echo "=================================="
check_asdf:
@if ! asdf --version; then \
just install_asdf \
;else \
echo "asdf already installed" \
;fi
just install_asdf_bins
install_asdf:
@echo "installing asdf"
@echo "asdf version: ${asdf_version}"
@git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch "${asdf_version}"
@echo "adding asdf to .bashrc"
@if ! grep -q ".asdf/asdf.sh" "${HOME}/.bashrc"; then \
echo -e '\n# source asdf' >> "${HOME}/.bashrc" \
;echo 'source "${HOME}/.asdf/asdf.sh"' >> "${HOME}/.bashrc" \
;echo -e 'source "${HOME}/.asdf/completions/asdf.bash"\n' >> "${HOME}/.bashrc" \
;fi
@echo "to load asdf either restart your shell or do: 'source \${HOME}/.bashrc'"
setup_asdf:
@echo "installing asdf bins"
# add plugins
@if ! asdf plugin add python; then :; fi
@if ! asdf plugin add shfmt; then :; fi
@if ! asdf plugin add shellcheck; then :; fi
@if ! asdf plugin add just https://github.com/franklad/asdf-just; then :; fi
@if ! asdf plugin add direnv; then :; fi
# install bins
@if ! asdf install; then :; fi
# setup direnv
@if ! asdf direnv setup --shell bash --version latest; then :; fi
create_venv:
@echo "creating venv"
@python3 -m pip install --upgrade pip setuptools wheel
@python3 -m venv venv
test_shfmt:
@find . -type f \( -name "**.sh" -and -not -path "./venv/*" -and -not -path "./.tox/*" \) -exec shfmt -d -i 4 -bn -ci -sr "{}" \+;
test_black:
@python3 -m black --check --diff .
test_isort:
@python3 -m isort --check-only --diff .
test_mypy:
@python3 -m mypy --install-types --non-interactive mangadlp/
test_pytest:
@python3 -m tox -e basic
test_tox:
@python3 -m tox
test_tox_coverage:
@python3 -m tox -e coverage
test_build:
@python3 -m hatch build
test_ci_conf:
@woodpecker-cli lint .woodpecker/
test_docker_build:
@docker build . -f docker/Dockerfile.amd64 -t manga-dlp:test
# install dependecies and set everything up
prepare_workspace:
just show_system_info
just check_asdf
just setup_asdf
just create_venv
lint:
just show_system_info
-just test_ci_conf
just test_shfmt
just test_black
just test_isort
just test_mypy
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"
tests:
just show_system_info
-just test_ci_conf
just test_shfmt
just test_black
just test_isort
just test_mypy
just test_pytest
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"
tests_full:
just show_system_info
-just test_ci_conf
just test_shfmt
just test_black
just test_isort
just test_mypy
just test_build
just test_tox
just test_tox_coverage
just test_docker_build
@echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n"

View file

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

View file

@ -15,12 +15,12 @@ class Mangadex:
img_base_url = "https://uploads.mangadex.org"
# get infos to initiate class
def __init__(self, url_uuid: str, language: str, forcevol: bool, verbose: bool):
def __init__(self, url_uuid: str, language: str, forcevol: bool, verbosity: int):
# static info
self.url_uuid = url_uuid
self.language = language
self.forcevol = forcevol
self.verbose = verbose
self.verbosity = verbosity
# api stuff
self.api_content_ratings = "contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic"
@ -36,7 +36,7 @@ class Mangadex:
# make initial request
def get_manga_data(self) -> requests.Response:
if self.verbose:
if self.verbosity >= 2:
print(f"INFO: Getting manga data for: {self.manga_uuid}")
counter = 1
while counter <= 3:
@ -76,7 +76,7 @@ class Mangadex:
# get the title of the manga (and fix the filename)
def get_manga_title(self) -> str:
if self.verbose:
if self.verbosity >= 2:
print(f"INFO: Getting manga title for: {self.manga_uuid}")
manga_data = self.manga_data.json()
try:
@ -95,7 +95,7 @@ class Mangadex:
# check if chapters are available in requested language
def check_chapter_lang(self) -> int:
if self.verbose:
if self.verbosity >= 2:
print(
f"INFO: Checking for chapters in specified language for: {self.manga_uuid}"
)
@ -118,7 +118,7 @@ class Mangadex:
# get chapter data like name, uuid etc
def get_chapter_data(self) -> dict:
if self.verbose:
if self.verbosity >= 2:
print(f"INFO: Getting chapter data for: {self.manga_uuid}")
api_sorting = "order[chapter]=asc&order[volume]=asc"
# check for chapters in specified lang
@ -177,7 +177,7 @@ class Mangadex:
# get images for the chapter (mangadex@home)
def get_chapter_images(self, chapter: str, wait_time: float) -> list:
if self.verbose:
if self.verbosity >= 2:
print(f"INFO: Getting chapter images for: {self.manga_uuid}")
athome_url = f"{self.api_base_url}/at-home/server"
chapter_uuid = self.manga_chapter_data[chapter][0]
@ -224,7 +224,7 @@ class Mangadex:
# create list of chapters
def create_chapter_list(self) -> list:
if self.verbose:
if self.verbosity >= 2:
print(f"INFO: Creating chapter list for: {self.manga_uuid}")
chapter_list = []
for chapter in self.manga_chapter_data.items():
@ -240,7 +240,7 @@ class Mangadex:
# create easy to access chapter infos
def get_chapter_infos(self, chapter: str) -> dict:
if self.verbose:
if self.verbosity >= 3:
print(
f"INFO: Getting chapter infos for: {self.manga_chapter_data[chapter][0]}"
)

View file

@ -23,7 +23,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 verbose: If verbose logging is enabled
:param verbosity: Verbosity of the output
:return: Nothing. Just the files
"""
@ -38,7 +38,7 @@ class MangaDLP:
forcevol: bool = False,
download_path: str = "downloads",
download_wait: float = 0.5,
verbose: bool = False,
verbosity: int = 0,
) -> None:
# init parameters
self.url_uuid = url_uuid
@ -49,7 +49,7 @@ class MangaDLP:
self.forcevol = forcevol
self.download_path = download_path
self.download_wait = download_wait
self.verbose = verbose
self.verbosity = verbosity
# prepare everything
self._prepare()
@ -63,7 +63,7 @@ class MangaDLP:
# init api
self.api_used = self.check_api(self.url_uuid)
self.api = self.api_used(
self.url_uuid, self.language, self.forcevol, self.verbose
self.url_uuid, self.language, self.forcevol, self.verbosity
)
# get manga title and uuid
self.manga_uuid = self.api.manga_uuid
@ -125,9 +125,12 @@ class MangaDLP:
skipped_chapters: list[Any] = []
error_chapters: list[Any] = []
# show infos
print_divider = "========================================="
print(f"\n{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)}")
@ -147,8 +150,11 @@ 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}\n")
print(f"{print_divider}")
# create manga folder
self.manga_path.mkdir(parents=True, exist_ok=True)
@ -171,16 +177,24 @@ class MangaDLP:
print("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}")
# 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)}")
# 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")
# once called per chapter
@ -225,6 +239,7 @@ 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")
# add to skipped chapters list
return (
@ -240,7 +255,7 @@ class MangaDLP:
chapter_path.mkdir(parents=True, exist_ok=True)
# verbose log
if self.verbose:
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")
@ -252,7 +267,7 @@ class MangaDLP:
# download images
try:
downloader.download_chapter(
chapter_image_urls, chapter_path, self.download_wait, self.verbose
chapter_image_urls, chapter_path, self.download_wait, self.verbosity
)
except KeyboardInterrupt:
print("ERR: Stopping")

View file

@ -14,7 +14,7 @@ def download_chapter(
image_urls: list,
chapter_path: Union[str, Path],
download_wait: float,
verbose: bool,
verbosity: int,
) -> None:
total_img = len(image_urls)
for image_num, image in enumerate(image_urls, 1):
@ -22,11 +22,11 @@ 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 if verbose logging is not active
if verbose:
print(f"INFO: Downloading image {image_num}/{total_img}")
else:
# show progress bar or progress by image for verbose
if verbosity == 0:
utils.progress_bar(image_num, total_img)
elif verbosity >= 2:
print(f"INFO: Downloading image {image_num}/{total_img}")
counter = 1
while counter <= 3:

View file

@ -5,7 +5,7 @@ from pathlib import Path
import mangadlp.app as app
MDLP_VERSION = "2.1.8"
MDLP_VERSION = "2.1.9"
def check_args(args):
@ -48,7 +48,7 @@ def call_app(args):
args.forcevol,
args.path,
args.wait,
args.verbose,
args.verbosity,
)
mdlp.get_manga()
@ -95,8 +95,10 @@ def get_args():
parser = argparse.ArgumentParser(
description="Script to download mangas from various sites"
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
action = parser.add_mutually_exclusive_group(required=True)
verbosity = parser.add_mutually_exclusive_group(required=False)
action.add_argument(
"-u",
"--url",
"--uuid",
@ -105,14 +107,14 @@ def get_args():
help="URL or UUID of the manga",
action="store",
)
group.add_argument(
action.add_argument(
"--read",
dest="read",
required=False,
help="Path of file with manga links to download. One per line",
action="store",
)
group.add_argument(
action.add_argument(
"-v",
"--version",
dest="version",
@ -176,12 +178,32 @@ def get_args():
default=0.5,
help="Time to wait for each picture to download in seconds(float). Defaults 0.5",
)
parser.add_argument(
verbosity.add_argument(
"--lean",
dest="verbosity",
required=False,
help="Lean logging. Defaults to false",
action="store_const",
const=1,
default=0,
)
verbosity.add_argument(
"--verbose",
dest="verbose",
dest="verbosity",
required=False,
help="Verbose logging. Defaults to false",
action="store_true",
action="store_const",
const=2,
default=0,
)
verbosity.add_argument(
"--debug",
dest="verbosity",
required=False,
help="Lean logging. Defaults to false",
action="store_const",
const=3,
default=0,
)
# parser.print_help()

View file

@ -3,7 +3,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
version = "2.1.8"
version = "2.1.9"
name = "manga-dlp"
description = "A cli manga downloader"
readme = "README.md"
@ -39,7 +39,25 @@ Source = "https://github.com/olofvndrhr/manga-dlp"
mangadlp = "mangadlp.input:main"
manga-dlp = "mangadlp.input:main"
# isort config
[tool.hatch.build]
ignore-vcs = true
[tool.hatch.build.targets.sdist]
include = ["mangadlp"]
exclude = [
"docker",
".tox",
".mypy_cache"
]
[tool.hatch.build.targets.wheel]
include = ["mangadlp"]
exclude = [
"docker",
".tox",
".mypy_cache"
]
[tool.isort]
py_version = 39
skip_gitignore = true
@ -49,7 +67,6 @@ multi_line_output = 3
include_trailing_comma = true
use_parentheses = true
# mypy config
[tool.mypy]
python_version = "3.9"
disallow_untyped_defs = false
@ -62,11 +79,15 @@ show_column_numbers = true
show_error_codes = true
pretty = true
# coverage.py config
[tool.pytest.ini_options]
pythonpath = [
"."
]
[tool.coverage.run]
source = ["mangadlp"]
branch = true
command_line = "-m pytest --verbose --exitfirst"
command_line = "-m pytest --exitfirst"
[tool.coverage.report]
# Regexes for lines to exclude from consideration
@ -86,22 +107,3 @@ exclude_lines = [
"@(abc.)?abstractmethod",
]
ignore_errors = true
[tool.hatch.build]
ignore-vcs = true
[tool.hatch.build.targets.sdist]
include = ["mangadlp"]
exclude = [
"docker",
".tox",
".mypy_cache"
]
[tool.hatch.build.targets.wheel]
include = ["mangadlp"]
exclude = [
"docker",
".tox",
".mypy_cache"
]

View file

@ -1,5 +1,3 @@
from pathlib import Path
import pytest
import mangadlp.app as app
@ -8,7 +6,7 @@ from mangadlp.api.mangadex import Mangadex
def test_check_api_mangadex():
url = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
test = app.MangaDLP(url_uuid=url, list_chapters=True)
test = app.MangaDLP(url_uuid=url, list_chapters=True, download_wait=2)
assert test.api_used == Mangadex
@ -16,5 +14,5 @@ def test_check_api_mangadex():
def test_check_api_none():
url = "https://abc.defghjk/title/abc/def"
with pytest.raises(ValueError) as e:
app.MangaDLP(url_uuid=url, list_chapters=True)
app.MangaDLP(url_uuid=url, list_chapters=True, download_wait=2)
assert e.type == ValueError

View file

@ -56,8 +56,8 @@ def test_chapter_list_full():
file_format="cbz",
forcevol=True,
download_path="tests",
download_wait=0.5,
verbose=True,
download_wait=2,
verbosity=3,
)
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,11 +18,11 @@ 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), 0.5, True)
downloader.download_chapter(urls, str(chapter_path), 2, True)
for file in chapter_path.iterdir():
images.append(file.name)
print(images)
images.sort()
assert images == ["001.png", "002.png", "003.png", "004.png", "005.png"]
# cleanup
shutil.rmtree(chapter_path, ignore_errors=True)
@ -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), 0.5, True)
downloader.download_chapter(images, str(chapter_path), 2, True)
assert e.type == ConnectionError
# cleanup

View file

@ -13,7 +13,7 @@ def test_read_and_url():
chapters = "1"
file_format = "cbz"
download_path = "tests"
command_args = f"-u {url_uuid} --read {link_file} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
command_args = f"-u {url_uuid} --read {link_file} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug"
script_path = "manga-dlp.py"
assert os.system(f"python3 {script_path} {command_args}") != 0
@ -25,7 +25,7 @@ def test_no_read_and_url():
chapters = "1"
file_format = "cbz"
download_path = "tests"
command_args = f"-l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
command_args = f"-l {language} -c {chapters} --path {download_path} --format {file_format} --debug"
script_path = "manga-dlp.py"
assert os.system(f"python3 {script_path} {command_args}") != 0
@ -36,7 +36,7 @@ def test_no_chaps():
chapters = ""
file_format = "cbz"
download_path = "tests"
command_args = f"-u {url_uuid} -l {language} --path {download_path} --format {file_format} --verbose"
command_args = f"-u {url_uuid} -l {language} --path {download_path} --format {file_format} --debug"
script_path = "manga-dlp.py"
assert os.system(f"python3 {script_path} {command_args}") != 0
@ -47,7 +47,7 @@ def test_no_volume():
chapters = "1"
file_format = "cbz"
download_path = "tests"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose --forcevol"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug --forcevol"
script_path = "manga-dlp.py"
assert os.system(f"python3 {script_path} {command_args}") != 0

View file

@ -8,8 +8,8 @@ def test_uuid_link():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
assert test.manga_uuid == "a96676e5-8ae2-425e-b549-7f15dd34a6d8"
@ -18,8 +18,8 @@ def test_uuid_pure():
url_uuid = "a96676e5-8ae2-425e-b549-7f15dd34a6d8"
language = "en"
forcevol = False
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
assert test.manga_uuid == "a96676e5-8ae2-425e-b549-7f15dd34a6d8"
@ -28,10 +28,10 @@ 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
verbose = True
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbose)
Mangadex(url_uuid, language, forcevol, verbosity)
assert e.type == SystemExit
assert e.value.code == 1
@ -40,8 +40,8 @@ def test_title():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
assert test.manga_title == "Komi-san wa Komyushou Desu"
@ -50,8 +50,8 @@ def test_chapter_infos():
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
language = "en"
forcevol = False
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
chapter_infos = test.get_chapter_infos("1")
chapter_uuid = chapter_infos["uuid"]
chapter_name = chapter_infos["name"]
@ -70,10 +70,10 @@ 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
verbose = True
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbose)
Mangadex(url_uuid, language, forcevol, verbosity)
assert e.type == SystemExit
assert e.value.code == 1
@ -86,10 +86,10 @@ 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
verbose = True
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbose)
Mangadex(url_uuid, language, forcevol, verbosity)
assert e.type == SystemExit
assert e.value.code == 1
@ -98,8 +98,8 @@ 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
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = True
test = Mangadex(url_uuid, language, forcevol, 3)
assert test.check_chapter_lang() > 0
@ -108,11 +108,11 @@ 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
verbose = True
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbose)
Mangadex(url_uuid, language, forcevol, verbose).check_chapter_lang()
Mangadex(url_uuid, language, forcevol, verbosity)
Mangadex(url_uuid, language, forcevol, verbosity).check_chapter_lang()
assert e.type == KeyError or e.type == SystemExit
assert e.value.code == 1
@ -121,10 +121,10 @@ 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
verbose = True
verbosity = 3
with pytest.raises(SystemExit) as e:
Mangadex(url_uuid, language, forcevol, verbose)
Mangadex(url_uuid, language, forcevol, verbosity)
assert e.type == SystemExit
assert e.value.code == 1
@ -135,8 +135,8 @@ def test_create_chapter_list():
)
language = "en"
forcevol = False
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test_list = [
"1",
"2",
@ -170,8 +170,8 @@ def test_create_chapter_list_forcevol():
)
language = "en"
forcevol = True
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
test_list = [
"1:1",
"1:2",
@ -203,8 +203,8 @@ 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
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
img_base_url = "https://uploads.mangadex.org"
chapter_hash = "0752bc5db298beff6b932b9151dd8437"
chapter_uuid = "e86ec2c4-c5e4-4710-bfaa-7604f00939c7"
@ -225,7 +225,7 @@ def test_get_chapter_images():
f"{img_base_url}/data/{chapter_hash}/x13-54d9718036b9d79e930e448b592c4a3df9045ed5b8c22ab411b09dadb864756f.jpg",
f"{img_base_url}/data/{chapter_hash}/x14-f6ed71bbb9af2bceab51028b460813c57935c923e1872fb277beb21d54425434.jpg",
]
assert test.get_chapter_images(chapter_num, 0.5) == test_list
assert test.get_chapter_images(chapter_num, 2) == test_list
def test_get_chapter_images_error(monkeypatch):
@ -235,9 +235,9 @@ 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
verbose = True
test = Mangadex(url_uuid, language, forcevol, verbose)
verbosity = 3
test = Mangadex(url_uuid, language, forcevol, verbosity)
chapter_num = "1"
monkeypatch.setattr(requests, "get", fail_url)
assert not test.get_chapter_images(chapter_num, 0.5)
assert not test.get_chapter_images(chapter_num, 2)

View file

@ -1,11 +1,27 @@
import os
import platform
import shutil
import time
from pathlib import Path
import pytest
import mangadlp.app as app
def test_full_api_mangadex():
@pytest.fixture
def wait_10s():
print("sleeping 10 seconds because of api timeouts")
time.sleep(10)
@pytest.fixture
def wait_20s():
print("sleeping 20 seconds because of api timeouts")
time.sleep(20)
def test_full_api_mangadex(wait_20s):
manga_path = Path("tests/Shikimori's Not Just a Cutie")
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
mdlp = app.MangaDLP(
@ -16,8 +32,8 @@ def test_full_api_mangadex():
file_format="cbz",
forcevol=False,
download_path="tests",
download_wait=0.5,
verbose=True,
download_wait=2,
verbosity=3,
)
mdlp.get_manga()
@ -27,7 +43,7 @@ def test_full_api_mangadex():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_cbz():
def test_full_with_input_cbz(wait_20s):
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
language = "en"
chapters = "1"
@ -35,7 +51,7 @@ def test_full_with_input_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} --verbose"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug --wait 2"
script_path = "manga-dlp.py"
os.system(f"python3 {script_path} {command_args}")
@ -45,7 +61,11 @@ def test_full_with_input_cbz():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_pdf():
def test_full_with_input_pdf(wait_20s):
# check if its arm64, if yes skip this step
if platform.machine() != "x86_64":
return True
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
language = "en"
chapters = "1"
@ -53,7 +73,7 @@ def test_full_with_input_pdf():
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.pdf")
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug --wait 2"
script_path = "manga-dlp.py"
os.system(f"python3 {script_path} {command_args}")
@ -63,7 +83,7 @@ def test_full_with_input_pdf():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_folder():
def test_full_with_input_folder(wait_20s):
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
language = "en"
chapters = "1"
@ -71,7 +91,7 @@ def test_full_with_input_folder():
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")
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format '{file_format}' --verbose"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format '{file_format}' --debug --wait 2"
script_path = "manga-dlp.py"
os.system(f"python3 {script_path} {command_args}")
@ -81,7 +101,7 @@ def test_full_with_input_folder():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_skip_cbz():
def test_full_with_input_skip_cbz(wait_10s):
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
language = "en"
chapters = "1"
@ -89,7 +109,7 @@ def test_full_with_input_skip_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} --verbose"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug --wait 2"
script_path = "manga-dlp.py"
manga_path.mkdir(parents=True, exist_ok=True)
chapter_path.touch()
@ -101,7 +121,7 @@ def test_full_with_input_skip_cbz():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_input_skip_folder():
def test_full_with_input_skip_folder(wait_10s):
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
language = "en"
chapters = "1"
@ -109,7 +129,7 @@ def test_full_with_input_skip_folder():
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")
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format '{file_format}' --verbose"
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format '{file_format}' --debug --wait 2"
script_path = "manga-dlp.py"
chapter_path.mkdir(parents=True, exist_ok=True)
@ -126,7 +146,7 @@ def test_full_with_input_skip_folder():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_read_cbz():
def test_full_with_read_cbz(wait_20s):
url_list = Path("tests/test_list2.txt")
language = "en"
chapters = "1"
@ -134,7 +154,7 @@ def test_full_with_read_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"--read {str(url_list)} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
command_args = f"--read {str(url_list)} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug --wait 2"
script_path = "manga-dlp.py"
url_list.write_text(
"https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
@ -148,7 +168,7 @@ def test_full_with_read_cbz():
shutil.rmtree(manga_path, ignore_errors=True)
def test_full_with_read_skip_cbz():
def test_full_with_read_skip_cbz(wait_10s):
url_list = Path("tests/test_list2.txt")
language = "en"
chapters = "1"
@ -156,7 +176,7 @@ def test_full_with_read_skip_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"--read {str(url_list)} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
command_args = f"--read {str(url_list)} -l {language} -c {chapters} --path {download_path} --format {file_format} --debug --wait 2"
script_path = "manga-dlp.py"
manga_path.mkdir(parents=True, exist_ok=True)
chapter_path.touch()

21
tox.ini
View file

@ -4,10 +4,23 @@ isolated_build = True
[testenv]
deps =
pytest
coverage
-rrequirements.txt
-rcontrib/requirements_dev.txt
commands =
pytest -x --basetemp="{envtmpdir}" {posargs}
pytest --exitfirst --basetemp="{envtmpdir}" {posargs}
[testenv:basic]
deps =
-rcontrib/requirements_dev.txt
commands =
pytest --exitfirst --basetemp="{envtmpdir}" {posargs}
[testenv:coverage]
deps =
-rcontrib/requirements_dev.txt
commands =
coverage erase
coverage run
coverage xml -i