commit
280a6f7e41
23 changed files with 453 additions and 197 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -10,6 +10,26 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||
- Add support for more sites
|
||||
|
||||
|
||||
## [2.1.0] - 2022-05-16
|
||||
|
||||
### Fixed
|
||||
- Detection of files. Now it will skip them again
|
||||
|
||||
### Added
|
||||
- Ability to save the chapters as pdf (only on amd64/x86)
|
||||
- New output formats: rar, zip
|
||||
- Progress bar to show image download
|
||||
- Interactive input if no command line flags are given
|
||||
- Better KeyboardInterrupt handling
|
||||
- Better error handling
|
||||
- Removed duplicate code
|
||||
|
||||
### Changed
|
||||
- How the variables are used inside the script
|
||||
- Variables have now the same name as in other scripts (mostly)
|
||||
- Better retrying when a task fails
|
||||
|
||||
|
||||
## [2.0.8] - 2022-05-13
|
||||
|
||||
### Changed
|
||||
|
|
35
README.md
35
README.md
|
@ -20,9 +20,9 @@ chapters without any additional setup.
|
|||
The default behaiviour is to pack the images to a [cbz archive](https://en.wikipedia.org/wiki/Comic_book_archive). If
|
||||
you just want the folder with all the pictures use the flag `--nocbz`.
|
||||
|
||||
## *Currently* Supported sites
|
||||
## _Currently_ Supported sites
|
||||
|
||||
* [Mangadex.org](https://mangadex.org/)
|
||||
- [Mangadex.org](https://mangadex.org/)
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -60,19 +60,23 @@ See the docker [README](./docker/README.md)
|
|||
|
||||
## Options
|
||||
|
||||
```txt
|
||||
usage: manga-dlp.py [-h] [-u URL] [-c CHAPTERS] [-p PATH] [-l LANG] [--read READ] [--list] [--nocbz] [--forcevol] [--wait WAIT]
|
||||
[--verbose]
|
||||
> "--format" currently only works with "", "pdf", "zip", "rar" and "cbz". As it just renames the zip file with the new suffix (except pdf). For pdf creation you have to install img2pdf.
|
||||
|
||||
optional arguments:
|
||||
-h, --help Show this help message and exit
|
||||
-u URL/UUID, --url URL/UUID URL or UUID of the manga
|
||||
```txt
|
||||
usage: manga-dlp.py [-h] (-u URL_UUID | --read READ | -v) [-c CHAPTERS] [-p PATH] [-l LANG] [--list] [--format FORMAT] [--forcevol] [--wait WAIT] [--verbose]
|
||||
|
||||
Script to download mangas from various sites
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-u URL_UUID, --url URL_UUID URL or UUID of the manga
|
||||
--read READ Path of file with manga links to download. One per line
|
||||
-v, --version Show version of manga-dlp and exit
|
||||
-c CHAPTERS, --chapters CHAPTERS Chapters to download
|
||||
-p PATH, --path PATH Download path. Defaults to "<script_dir>/downloads"
|
||||
-l LANG, --language LANG Manga language. Defaults to "en" --> english
|
||||
--read READ Path of file with manga links to download. One per line
|
||||
--list List all available chapters. Defaults to false
|
||||
--nocbz Dont pack it to a cbz archive. Defaults to false
|
||||
--format FORMAT Archive format to create. An empty string means dont archive the folder. Defaults to 'cbz'
|
||||
--forcevol Force naming of volumes. For mangas where chapters reset each volume
|
||||
--wait WAIT Time to wait for each picture to download in seconds(float). Defaults 0.5
|
||||
--verbose Verbose logging. Defaults to false
|
||||
|
@ -135,12 +139,9 @@ If you encounter any bugs, also just open a issue with a description of the prob
|
|||
|
||||
## TODO's
|
||||
|
||||
* <del>Make docker container for easy distribution</del>
|
||||
- <del>Make docker container for easy distribution</del>
|
||||
--> [Dockerhub](https://hub.docker.com/repository/docker/olofvndrhr/manga-dlp)
|
||||
* <del>Automate release</del>
|
||||
- <del>Automate release</del>
|
||||
--> Done with woodpecker-ci
|
||||
* Make pypi package
|
||||
* Add more supported sites
|
||||
|
||||
|
||||
|
||||
- Make pypi package
|
||||
- Add more supported sites
|
||||
|
|
|
@ -7,7 +7,7 @@ LABEL build_version="Version:- ${VERSION} Build-date:- ${BUILD_DATE}"
|
|||
LABEL maintainer="Ivan Schaller"
|
||||
|
||||
# manga-dlp version
|
||||
ENV MDLP_VERSION=2.0.8
|
||||
ENV MDLP_VERSION=2.1.0
|
||||
|
||||
# install packages
|
||||
RUN \
|
||||
|
@ -41,5 +41,4 @@ COPY manga-dlp.py \
|
|||
# install requirements
|
||||
RUN pip install -r /app/requirements.txt
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
|
|
|
@ -7,7 +7,7 @@ LABEL build_version="Version:- ${VERSION} Build-date:- ${BUILD_DATE}"
|
|||
LABEL maintainer="Ivan Schaller"
|
||||
|
||||
# manga-dlp version
|
||||
ENV MDLP_VERSION=2.0.8
|
||||
ENV MDLP_VERSION=2.1.0
|
||||
|
||||
# install packages
|
||||
RUN \
|
||||
|
@ -38,8 +38,9 @@ COPY manga-dlp.py \
|
|||
/app/
|
||||
|
||||
|
||||
# install requirements
|
||||
RUN pip install -r /app/requirements.txt
|
||||
# install requirements (without img2pdf)
|
||||
RUN grep -v img2pdf /app/requirements.txt > /app/requirements-arm64.txt
|
||||
RUN pip install -r /app/requirements-arm64.txt
|
||||
|
||||
|
||||
WORKDIR /app
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
## Quick start
|
||||
|
||||
> the pdf creation only works on amd64 images, as it unfortunately is incompatible with arm64.
|
||||
|
||||
```sh
|
||||
# with docker-compose
|
||||
curl -O docker-compose.yml https://raw.githubusercontent.com/olofvndrhr/manga-dlp/master/docker/docker-compose.yml
|
||||
|
|
36
manga-dlp.py
36
manga-dlp.py
|
@ -1,5 +1,37 @@
|
|||
from mangadlp.input import get_input
|
||||
from mangadlp.input import get_args
|
||||
import os
|
||||
import sys
|
||||
|
||||
mangadlp_version = "2.1.0"
|
||||
|
||||
|
||||
def get_input():
|
||||
print(f"Manga-DLP Version {mangadlp_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: "))
|
||||
chapters = str(input("Chapters: "))
|
||||
except KeyboardInterrupt:
|
||||
exit(1)
|
||||
except:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
args = [f"-l {language}", f"-c {chapters}"]
|
||||
if url_uuid:
|
||||
args.append(f"-u {url_uuid}")
|
||||
if readlist:
|
||||
args.append(f"--read {readlist}")
|
||||
|
||||
# start script again with the arguments
|
||||
os.system(f"python3 manga-dlp.py {' '.join(args)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_input()
|
||||
if len(sys.argv) > 1:
|
||||
get_args()
|
||||
else:
|
||||
get_input()
|
||||
|
|
|
@ -28,6 +28,7 @@ class Mangadex:
|
|||
self.manga_data = self.get_manga_data()
|
||||
self.manga_title = self.get_manga_title()
|
||||
self.manga_chapter_data = self.get_chapter_data()
|
||||
self.chapter_list = self.create_chapter_list()
|
||||
|
||||
# make initial request
|
||||
def get_manga_data(self):
|
||||
|
@ -233,19 +234,6 @@ class Mangadex:
|
|||
|
||||
return chapter_list
|
||||
|
||||
# create filename for chapter
|
||||
def get_filename(self, chapter):
|
||||
if self.verbose:
|
||||
print(f"INFO: Creating filename for: {self.manga_uuid}")
|
||||
chapter_info = self.get_chapter_infos(chapter)
|
||||
chapter_name = chapter_info["name"]
|
||||
chapter_num = chapter_info["chapter"]
|
||||
volume_number = chapter_info["volume"]
|
||||
|
||||
return utils.get_filename(
|
||||
chapter_name, volume_number, chapter_num, self.forcevol
|
||||
)
|
||||
|
||||
# create easy to access chapter infos
|
||||
def get_chapter_infos(self, chapter):
|
||||
if self.verbose:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import mangadlp.downloader as downloader
|
||||
|
@ -10,6 +11,7 @@ from mangadlp.api.mangadex import Mangadex
|
|||
|
||||
class AppArguments:
|
||||
def __init__(self, args: dict):
|
||||
# init parameters
|
||||
self.api = args["api"]
|
||||
self.url_uuid = args["url_uuid"]
|
||||
self.language = args["language"]
|
||||
|
@ -18,10 +20,15 @@ class AppArguments:
|
|||
self.list_chapters = args["list_chapters"]
|
||||
self.file_format = args["file_format"]
|
||||
self.forcevol = args["forcevol"]
|
||||
self.download_path = args["chapter_path"]
|
||||
self.download_path = args["download_path"]
|
||||
self.download_wait = args["download_wait"]
|
||||
self.verbose = args["verbose"]
|
||||
|
||||
# additional stuff
|
||||
# set manga format suffix
|
||||
if self.file_format and "." not in self.file_format:
|
||||
self.file_format = f".{self.file_format}"
|
||||
|
||||
|
||||
def main(
|
||||
url_uuid: str = "",
|
||||
|
@ -68,6 +75,10 @@ def main(
|
|||
if url_uuid and readlist:
|
||||
print(f'ERR: You can only use "-u" or "--read". Dont specify both')
|
||||
exit(1)
|
||||
# if forcevol is used, but didn't specify a volume in the chapters selected
|
||||
if forcevol and ":" not in chapters:
|
||||
print(f"ERR: You need to specify the volume if you use --forcevol.")
|
||||
exit(1)
|
||||
|
||||
# create arguments dict for class creation
|
||||
mdlp_args = {
|
||||
|
@ -78,7 +89,7 @@ def main(
|
|||
"list_chapters": list_chapters,
|
||||
"file_format": file_format,
|
||||
"forcevol": forcevol,
|
||||
"chapter_path": download_path,
|
||||
"download_path": download_path,
|
||||
"download_wait": download_wait,
|
||||
"verbose": verbose,
|
||||
}
|
||||
|
@ -93,6 +104,8 @@ def main(
|
|||
continue
|
||||
# add api used to dict
|
||||
mdlp_args["api"] = api_used
|
||||
# add url to dict
|
||||
mdlp_args["url_uuid"] = url
|
||||
# create class
|
||||
args = AppArguments(mdlp_args)
|
||||
# get manga
|
||||
|
@ -155,9 +168,9 @@ def get_manga(args: AppArguments) -> None:
|
|||
# get manga title and uuid
|
||||
manga_uuid = api.manga_uuid
|
||||
manga_title = api.manga_title
|
||||
# crate chapter list
|
||||
manga_chapter_list = api.create_chapter_list()
|
||||
# create skipped chapters list
|
||||
# get chapter list
|
||||
manga_chapter_list = api.chapter_list
|
||||
# create empty skipped chapters list
|
||||
skipped_chapters = []
|
||||
|
||||
# show infos
|
||||
|
@ -202,7 +215,7 @@ def get_manga(args: AppArguments) -> None:
|
|||
# check if the image urls are empty. if yes skip this chapter (for mass downloads)
|
||||
if not chapter_image_urls:
|
||||
print(
|
||||
f"ERR: Skipping Vol. {chapter_infos['volume']} Ch.{chapter_infos['chapter']}"
|
||||
f"ERR: No images: Skipping Vol. {chapter_infos['volume']} Ch.{chapter_infos['chapter']}"
|
||||
)
|
||||
# add to skipped chapters list
|
||||
skipped_chapters.append(
|
||||
|
@ -211,21 +224,20 @@ def get_manga(args: AppArguments) -> None:
|
|||
|
||||
continue
|
||||
|
||||
# get filename for chapter
|
||||
chapter_filename = api.get_filename(chapter)
|
||||
# get filename for chapter (without suffix)
|
||||
chapter_filename = utils.get_filename(
|
||||
chapter_infos["name"], chapter_infos["volume"], chapter, args.forcevol
|
||||
)
|
||||
|
||||
# set download path for chapter
|
||||
# set download path for chapter (image folder)
|
||||
chapter_path = manga_path / chapter_filename
|
||||
# set archive path with file format
|
||||
chapter_archive_path = Path(f"{chapter_path}{args.file_format}")
|
||||
|
||||
# check if chapter already exists.
|
||||
# check for folder if option nocbz is given. if nocbz is not given, the folder will be overwritten
|
||||
|
||||
if utils.check_existence(chapter_path, args.file_format):
|
||||
print(
|
||||
f"INFO: '{chapter_filename}.{args.file_format}' already exists. Skipping\n"
|
||||
if args.file_format
|
||||
else f"'{chapter_filename}' already exists. Skipping\n"
|
||||
)
|
||||
# check if chapter already exists
|
||||
# check for folder if file format is an empty string
|
||||
if chapter_archive_path.exists():
|
||||
print(f"INFO: '{chapter_archive_path}' already exists. Skipping\n")
|
||||
continue
|
||||
|
||||
# create chapter folder (skips it if it already exists)
|
||||
|
@ -234,11 +246,8 @@ def get_manga(args: AppArguments) -> None:
|
|||
# verbose log
|
||||
if args.verbose:
|
||||
print(f"INFO: Chapter UUID: {chapter_infos['uuid']}")
|
||||
print(
|
||||
f"INFO: Filename: '{chapter_filename}.{args.file_format}'\n"
|
||||
if args.file_format
|
||||
else f"INFO: Filename: '{chapter_filename}'\n"
|
||||
)
|
||||
print(f"INFO: Filename: '{chapter_archive_path.name}'\n")
|
||||
print(f"INFO: File path: '{chapter_archive_path}'\n")
|
||||
print(f"INFO: Image URLS:\n{chapter_image_urls}\n")
|
||||
|
||||
# log
|
||||
|
@ -253,7 +262,12 @@ def get_manga(args: AppArguments) -> None:
|
|||
print("ERR: Stopping")
|
||||
exit(1)
|
||||
except:
|
||||
print(f"ERR: Cant download: '{chapter_filename}'. Exiting")
|
||||
print(f"ERR: Cant download: '{chapter_filename}'. Skipping")
|
||||
# add to skipped chapters list
|
||||
skipped_chapters.append(
|
||||
f"{chapter_infos['volume']}:{chapter_infos['chapter']}"
|
||||
) if args.forcevol else skipped_chapters.append(chapter_infos["chapter"])
|
||||
continue
|
||||
|
||||
else:
|
||||
# Done with chapter
|
||||
|
@ -263,10 +277,25 @@ def get_manga(args: AppArguments) -> None:
|
|||
if args.file_format:
|
||||
print(f"INFO: Creating '{args.file_format}' archive")
|
||||
try:
|
||||
utils.make_archive(chapter_path, args.file_format)
|
||||
# check if image folder is existing
|
||||
if not chapter_path.exists():
|
||||
print(f"ERR: Image folder: {chapter_path} does not exist")
|
||||
raise IOError
|
||||
if args.file_format == ".pdf":
|
||||
utils.make_pdf(chapter_path)
|
||||
else:
|
||||
utils.make_archive(chapter_path, args.file_format)
|
||||
except:
|
||||
print("ERR: Could not create '{file_format}' archive")
|
||||
exit(1)
|
||||
print(f"ERR: Archive error. Skipping chapter")
|
||||
skipped_chapters.append(
|
||||
f"{chapter_infos['volume']}:{chapter_infos['chapter']}"
|
||||
) if args.forcevol else skipped_chapters.append(
|
||||
chapter_infos["chapter"]
|
||||
)
|
||||
continue
|
||||
else:
|
||||
# remove image folder
|
||||
shutil.rmtree(chapter_path)
|
||||
|
||||
# done with chapter
|
||||
print("INFO: Done with chapter")
|
||||
|
|
|
@ -1,40 +1,53 @@
|
|||
import shutil
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
import shutil
|
||||
import requests
|
||||
|
||||
import mangadlp.utils as utils
|
||||
|
||||
|
||||
# download images
|
||||
def download_chapter(image_urls, chapter_path, download_wait, verbose):
|
||||
img_num = 1
|
||||
total_img = len(image_urls)
|
||||
for img in image_urls:
|
||||
for img_num, img in enumerate(image_urls, 1):
|
||||
# set image path
|
||||
image_path = Path(f"{chapter_path}/{img_num:03d}")
|
||||
# show progress bar
|
||||
utils.progress_bar(img_num, total_img)
|
||||
utils.progress_bar(img_num, total_img, verbose)
|
||||
counter = 1
|
||||
while counter <= 3:
|
||||
try:
|
||||
r = requests.get(img, stream=True)
|
||||
except KeyboardInterrupt:
|
||||
print("ERR: Stopping")
|
||||
exit(1)
|
||||
except:
|
||||
if counter >= 3:
|
||||
print("ERR: Maybe the MangaDex Servers are down?")
|
||||
raise ConnectionError
|
||||
print(f"ERR: Request for image {img} failed, retrying")
|
||||
sleep(download_wait)
|
||||
counter += 1
|
||||
else:
|
||||
if r.status_code != 200:
|
||||
print(f"ERR: Image {img} could not be downloaded. Retrying")
|
||||
continue
|
||||
break
|
||||
|
||||
# verbose logging
|
||||
if verbose:
|
||||
print(f"INFO: Downloaded image {img_num}")
|
||||
# write image
|
||||
try:
|
||||
# print('Try getting ' + img)
|
||||
r = requests.get(img, stream=True)
|
||||
except KeyboardInterrupt:
|
||||
print("ERR: Stopping")
|
||||
exit(1)
|
||||
except:
|
||||
print(f"ERR: Request for image {img} failed, retrying")
|
||||
sleep(download_wait)
|
||||
req = requests.get(img, stream=True)
|
||||
|
||||
if r.status_code == 200:
|
||||
r.raw.decode_content = True
|
||||
with image_path.open("wb") as file:
|
||||
r.raw.decode_content = True
|
||||
shutil.copyfileobj(r.raw, file)
|
||||
except:
|
||||
print("ERR: Can't write file")
|
||||
raise IOError
|
||||
|
||||
# verbose logging
|
||||
if verbose:
|
||||
print(f"INFO: Downloaded image {img_num}")
|
||||
img_num += 1
|
||||
sleep(download_wait)
|
||||
|
||||
img_num += 1
|
||||
sleep(download_wait)
|
||||
else:
|
||||
print(f"ERR: Image {img} could not be downloaded. Exiting")
|
||||
exit(1)
|
||||
# if every image was downloaded and written successfully
|
||||
return True
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import argparse
|
||||
import mangadlp.app as app
|
||||
|
||||
mangadlp_version = "2.1.0"
|
||||
|
||||
|
||||
def call_app(args):
|
||||
# check if --version was used
|
||||
mangadlp_version = "2.0.8"
|
||||
if args.version:
|
||||
print(f"manga-dlp version: {mangadlp_version}")
|
||||
exit(0)
|
||||
|
@ -23,7 +24,7 @@ def call_app(args):
|
|||
)
|
||||
|
||||
|
||||
def get_input():
|
||||
def get_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Script to download mangas from various sites"
|
||||
)
|
||||
|
@ -123,4 +124,4 @@ def get_input():
|
|||
|
||||
|
||||
if __name__ == "__main__":
|
||||
get_input()
|
||||
get_args()
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
import re
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from zipfile import ZipFile
|
||||
|
||||
|
||||
# create a cbz archive
|
||||
# create an archive of the chapter images
|
||||
def make_archive(chapter_path, file_format):
|
||||
# set manga format suffix
|
||||
if file_format and "." not in file_format:
|
||||
file_format = f".{file_format}"
|
||||
image_folder = Path(chapter_path)
|
||||
zip_path = Path(f"{chapter_path}.zip")
|
||||
if not image_folder.exists():
|
||||
print(f"ERR: Folder: {image_folder} does not exist")
|
||||
return False
|
||||
with ZipFile(f"{image_folder}.zip", "w") as zip_archive:
|
||||
for file in image_folder.iterdir():
|
||||
zip_archive.write(file, file.name)
|
||||
zip_path.rename(zip_path.with_suffix(file_format))
|
||||
shutil.rmtree(image_folder)
|
||||
|
||||
return True
|
||||
try:
|
||||
# create zip
|
||||
with ZipFile(zip_path, "w") as zipfile:
|
||||
for file in chapter_path.iterdir():
|
||||
zipfile.write(file, file.name)
|
||||
# rename zip to file format requested
|
||||
zip_path.rename(zip_path.with_suffix(file_format))
|
||||
except:
|
||||
raise IOError
|
||||
|
||||
|
||||
# check if the file already exists
|
||||
def check_existence(chapter_path, file_format):
|
||||
# set manga format suffix
|
||||
if file_format and "." not in file_format:
|
||||
file_format = f".{file_format}"
|
||||
# check for folder if no format is given (empty string)
|
||||
# if no format is given, the folder will be overwritten if it exists
|
||||
chapter_path = Path(chapter_path).with_suffix(file_format)
|
||||
if chapter_path.exists():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
def make_pdf(chapter_path):
|
||||
try:
|
||||
import img2pdf
|
||||
except:
|
||||
print("Cant import img2pdf. Please install it first")
|
||||
raise ImportError
|
||||
|
||||
pdf_path = Path(f"{chapter_path}.pdf")
|
||||
images = []
|
||||
for file in chapter_path.iterdir():
|
||||
images.append(str(file))
|
||||
try:
|
||||
pdf_path.write_bytes(img2pdf.convert(images))
|
||||
except:
|
||||
print("ERR: Can't create '.pdf' archive")
|
||||
raise IOError
|
||||
|
||||
|
||||
# create a list of chapters
|
||||
|
@ -98,13 +96,14 @@ def get_filename(chapter_name, chapter_vol, chapter_num, forcevol):
|
|||
)
|
||||
|
||||
|
||||
def progress_bar(progress, total):
|
||||
def progress_bar(progress, total, verbose):
|
||||
percent = int(progress / (int(total) / 100))
|
||||
bar_length = 50
|
||||
bar_progress = int(progress / (int(total) / bar_length))
|
||||
bar_texture = "■" * bar_progress
|
||||
whitespace_texture = " " * (bar_length - bar_progress)
|
||||
if progress == total:
|
||||
print(f"\r❙{bar_texture}{whitespace_texture}❙ 100%", end="\n")
|
||||
full_bar = "■" * bar_length
|
||||
print(f"\r❙{full_bar}❙ 100%", end="\n")
|
||||
else:
|
||||
print(f"\r❙{bar_texture}{whitespace_texture}❙ {percent}%", end="\r")
|
||||
|
|
|
@ -33,7 +33,7 @@ function set_ver_docker() {
|
|||
'docker/Dockerfile.amd64'
|
||||
'docker/Dockerfile.arm64'
|
||||
)
|
||||
docker_regex='s,^ARG MDLP_VERSION=.*$,ARG MDLP_VERSION='"${mdlp_version}"',g'
|
||||
docker_regex='s,^ENV MDLP_VERSION=.*$,ENV MDLP_VERSION='"${mdlp_version}"',g'
|
||||
for file in "${docker_files[@]}"; do
|
||||
if ! sed -i "${docker_regex}" "${file}"; then return 1; fi
|
||||
done
|
||||
|
@ -58,6 +58,7 @@ function set_ver_project() {
|
|||
local project_files project_regex
|
||||
project_files=(
|
||||
'mangadlp/input.py'
|
||||
'manga-dlp.py'
|
||||
)
|
||||
project_regex='s/mangadlp_version =.*$/mangadlp_version = \"'"${mdlp_version}"'\"/g'
|
||||
for file in "${project_files[@]}"; do
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
requests>=2.24.0
|
||||
|
||||
img2pdf>=0.4.4
|
2
setup.py
2
setup.py
|
@ -7,7 +7,7 @@ long_description = readme.read_text()
|
|||
|
||||
setuptools.setup(
|
||||
name="manga-dlp",
|
||||
version="2.0.8",
|
||||
version="2.1.0",
|
||||
author="Ivan Schaller",
|
||||
author_email="ivan@schaller.sh",
|
||||
description="A cli manga downloader",
|
||||
|
|
|
@ -1,61 +1,18 @@
|
|||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import mangadlp.utils as utils
|
||||
|
||||
|
||||
def test_existence_true_folder():
|
||||
path = "tests/test_file"
|
||||
file_format = ""
|
||||
test = utils.check_existence(path, file_format)
|
||||
assert test
|
||||
|
||||
|
||||
def test_existence_true_cbz():
|
||||
path = "tests/test_file"
|
||||
file_format = "cbz"
|
||||
test = utils.check_existence(path, file_format)
|
||||
assert test
|
||||
|
||||
|
||||
def test_existence_true_cbz_dot():
|
||||
path = "tests/test_file"
|
||||
file_format = ".cbz"
|
||||
test = utils.check_existence(path, file_format)
|
||||
assert test
|
||||
|
||||
|
||||
def test_existence_false_folder():
|
||||
path = "tests/test_file_nonexistent"
|
||||
file_format = ""
|
||||
test = utils.check_existence(path, file_format)
|
||||
assert not test
|
||||
|
||||
|
||||
def test_existence_false_cbz():
|
||||
path = "tests/test_file_nonexistent"
|
||||
file_format = "cbz"
|
||||
test = utils.check_existence(path, file_format)
|
||||
assert not test
|
||||
|
||||
|
||||
def test_existence_false_cbz_dot():
|
||||
path = "tests/test_file_nonexistent"
|
||||
file_format = ".cbz"
|
||||
test = utils.check_existence(path, file_format)
|
||||
assert not test
|
||||
|
||||
|
||||
def test_archive_true():
|
||||
def test_make_archive_true():
|
||||
img_path = Path("tests/test_dir")
|
||||
img_path_str = "tests/test_dir"
|
||||
archive_path = Path("tests/test_dir.cbz")
|
||||
file_format = ".cbz"
|
||||
img_path.mkdir(parents=True, exist_ok=True)
|
||||
for n in range(5):
|
||||
img_name = img_path / f"page{n}"
|
||||
img_name.with_suffix(".png").touch(exist_ok=True)
|
||||
assert utils.make_archive(img_path_str, file_format)
|
||||
utils.make_archive(img_path, file_format)
|
||||
assert archive_path.exists()
|
||||
# cleanup
|
||||
archive_path.unlink(missing_ok=True)
|
||||
|
@ -63,12 +20,16 @@ def test_archive_true():
|
|||
shutil.rmtree(img_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_archive_false():
|
||||
def test_make_archive_false():
|
||||
archive_path = Path("tests/test_dir2.cbz")
|
||||
img_path_str = "tests/test_dir2"
|
||||
img_path = Path("tests/test_dir2")
|
||||
file_format = "cbz"
|
||||
assert not utils.make_archive(img_path_str, file_format)
|
||||
with pytest.raises(IOError) as e:
|
||||
utils.make_archive(img_path, file_format)
|
||||
assert e.type == IOError
|
||||
assert not archive_path.exists()
|
||||
# cleanup
|
||||
Path("tests/test_dir2.zip").unlink()
|
||||
|
||||
|
||||
def test_chapter_list():
|
||||
|
|
48
tests/test_03_downloader.py
Normal file
48
tests/test_03_downloader.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from pathlib import Path
|
||||
import pytest
|
||||
import requests
|
||||
import mangadlp.downloader as downloader
|
||||
import shutil
|
||||
|
||||
|
||||
def test_downloader():
|
||||
urls = [
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A1-c111d78b798f1dda1879334a3478f7ae4503578e8adf1af0fcc4e14d2a396ad4.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A2-717ec3c83e8e05ed7b505941431a417ebfed6a005f78b89650efd3b088b951ec.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A3-95f1b873d75f7fb820cf293df903ca37264d4af8963f44d154418c529c737547.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A4-defb89c1919b7721d3b09338f175186cabe4e292e4925818a6982581378f1966.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A5-8d852ab3e9ddb070d8ba70bc5c04d78012032975b3a69603cc88a4a8d12652d4.png",
|
||||
]
|
||||
chapter_path = Path("tests/test_folder1")
|
||||
chapter_path.mkdir(parents=True, exist_ok=True)
|
||||
images = []
|
||||
assert downloader.download_chapter(urls, chapter_path, 0.5, True)
|
||||
for file in chapter_path.iterdir():
|
||||
images.append(file.name)
|
||||
|
||||
print(images)
|
||||
assert images == ["001", "002", "003", "004", "005"]
|
||||
# cleanup
|
||||
shutil.rmtree(chapter_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_downloader_fail(monkeypatch):
|
||||
images = [
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A1-c111d78b798f1dda1879334a3478f7ae4503578e8adf1af0fcc4e14d2a396ad4.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A2-717ec3c83e8e05ed7b505941431a417ebfed6a005f78b89650efd3b088b951ec.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A3-95f1b873d75f7fb820cf293df903ca37264d4af8963f44d154418c529c737547.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A4-defb89c1919b7721d3b09338f175186cabe4e292e4925818a6982581378f1966.png",
|
||||
"https://uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A5-8d852ab3e9ddb070d8ba70bc5c04d78012032975b3a69603cc88a4a8d12652d4.png",
|
||||
]
|
||||
fail_url = (
|
||||
"https://_uploads.mangadex.org/data/f1117c5e7aff315bc3429a8791c89d63/A4-defb89c1919b7721d3b09338f175186cabe4e292e4925818a6982581378f1966.png",
|
||||
)
|
||||
chapter_path = Path("tests/test_folder1")
|
||||
chapter_path.mkdir(parents=True, exist_ok=True)
|
||||
monkeypatch.setattr(requests, "get", fail_url)
|
||||
with pytest.raises(ConnectionError) as e:
|
||||
downloader.download_chapter(images, chapter_path, 0.5, True)
|
||||
|
||||
assert e.type == ConnectionError
|
||||
# cleanup
|
||||
shutil.rmtree(chapter_path, ignore_errors=True)
|
51
tests/test_04_input.py
Normal file
51
tests/test_04_input.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from pathlib import Path
|
||||
import pytest
|
||||
import requests
|
||||
import mangadlp.input as input
|
||||
import os
|
||||
|
||||
|
||||
def test_read_and_url():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
link_file = "tests/testfile.txt"
|
||||
language = "en"
|
||||
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"
|
||||
script_path = "manga-dlp.py"
|
||||
assert os.system(f"python3 {script_path} {command_args}") != 0
|
||||
|
||||
|
||||
def test_no_read_and_url():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
link_file = "tests/testfile.txt"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
command_args = f"-l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
|
||||
script_path = "manga-dlp.py"
|
||||
assert os.system(f"python3 {script_path} {command_args}") != 0
|
||||
|
||||
|
||||
def test_no_chaps():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = ""
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
command_args = f"-u {url_uuid} -l {language} --path {download_path} --format {file_format} --verbose"
|
||||
script_path = "manga-dlp.py"
|
||||
assert os.system(f"python3 {script_path} {command_args}") != 0
|
||||
|
||||
|
||||
def test_no_volume():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose --forcevol"
|
||||
script_path = "manga-dlp.py"
|
||||
assert os.system(f"python3 {script_path} {command_args}") != 0
|
|
@ -129,26 +129,6 @@ def test_not_existing_lang():
|
|||
assert e.value.code == 1
|
||||
|
||||
|
||||
def test_filename():
|
||||
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)
|
||||
|
||||
assert test.get_filename("1") == "Ch. 1 - A Normal Person"
|
||||
|
||||
|
||||
def test_filename_forcevol():
|
||||
url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu"
|
||||
language = "en"
|
||||
forcevol = True
|
||||
verbose = True
|
||||
test = Mangadex(url_uuid, language, forcevol, verbose)
|
||||
|
||||
assert test.get_filename("1:4") == "Vol. 1 Ch. 4 - Bad at This"
|
||||
|
||||
|
||||
def test_create_chapter_list():
|
||||
url_uuid = (
|
||||
"https://mangadex.org/title/6fef1f74-a0ad-4f0d-99db-d32a7cd24098/fire-punch"
|
||||
|
|
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||
import mangadlp.app as app
|
||||
|
||||
|
||||
def test_full_mangadex():
|
||||
def test_full_api_mangadex():
|
||||
manga_path = Path("tests/Shikimori's Not Just a Cutie")
|
||||
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
|
||||
app.main(
|
||||
|
@ -27,14 +27,15 @@ def test_full_mangadex():
|
|||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_with_input():
|
||||
def test_full_with_input_cbz():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
manga_path = Path("tests/Shikimori's Not Just a Cutie")
|
||||
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
|
||||
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path}"
|
||||
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
|
||||
script_path = "manga-dlp.py"
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
|
||||
|
@ -44,9 +45,135 @@ def test_full_with_input():
|
|||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_without_input():
|
||||
def test_full_with_input_pdf():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "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"
|
||||
script_path = "manga-dlp.py"
|
||||
assert os.system(f"python3 {script_path}") != 0
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
|
||||
assert manga_path.exists() and manga_path.is_dir()
|
||||
assert chapter_path.exists() and chapter_path.is_file()
|
||||
# cleanup
|
||||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_with_input_folder():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = ""
|
||||
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"
|
||||
script_path = "manga-dlp.py"
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
|
||||
assert manga_path.exists() and manga_path.is_dir()
|
||||
assert chapter_path.exists() and chapter_path.is_dir()
|
||||
# cleanup
|
||||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_with_input_skip_cbz():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
manga_path = Path("tests/Shikimori's Not Just a Cutie")
|
||||
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
|
||||
command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
|
||||
script_path = "manga-dlp.py"
|
||||
manga_path.mkdir(parents=True, exist_ok=True)
|
||||
chapter_path.touch()
|
||||
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
assert chapter_path.is_file()
|
||||
assert chapter_path.stat().st_size == 0
|
||||
# cleanup
|
||||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_with_input_skip_folder():
|
||||
url_uuid = "https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = ""
|
||||
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"
|
||||
script_path = "manga-dlp.py"
|
||||
chapter_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
found_files = []
|
||||
for file in chapter_path.iterdir():
|
||||
found_files.append(file.name)
|
||||
|
||||
assert chapter_path.is_dir()
|
||||
assert found_files == []
|
||||
assert not Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz").exists()
|
||||
assert not Path("tests/Shikimori's Not Just a Cutie/Ch. 1.zip").exists()
|
||||
# cleanup
|
||||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_with_read_cbz():
|
||||
url_list = Path("tests/test_list2.txt")
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
manga_path = Path("tests/Shikimori's Not Just a Cutie")
|
||||
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
|
||||
command_args = f"--read {str(url_list)} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
|
||||
script_path = "manga-dlp.py"
|
||||
url_list.write_text(
|
||||
"https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
)
|
||||
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
|
||||
assert manga_path.exists() and manga_path.is_dir()
|
||||
assert chapter_path.exists() and chapter_path.is_file()
|
||||
# cleanup
|
||||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
def test_full_with_read_skip_cbz():
|
||||
url_list = Path("tests/test_list2.txt")
|
||||
language = "en"
|
||||
chapters = "1"
|
||||
file_format = "cbz"
|
||||
download_path = "tests"
|
||||
manga_path = Path("tests/Shikimori's Not Just a Cutie")
|
||||
chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1.cbz")
|
||||
command_args = f"--read {str(url_list)} -l {language} -c {chapters} --path {download_path} --format {file_format} --verbose"
|
||||
script_path = "manga-dlp.py"
|
||||
manga_path.mkdir(parents=True, exist_ok=True)
|
||||
chapter_path.touch()
|
||||
url_list.write_text(
|
||||
"https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie"
|
||||
)
|
||||
|
||||
os.system(f"python3 {script_path} {command_args}")
|
||||
assert chapter_path.is_file()
|
||||
assert chapter_path.stat().st_size == 0
|
||||
# cleanup
|
||||
shutil.rmtree(manga_path, ignore_errors=True)
|
||||
|
||||
|
||||
# def test_full_without_input():
|
||||
# script_path = "manga-dlp.py"
|
||||
# assert os.system(f"python3 {script_path}") != 0
|
||||
|
||||
|
||||
def test_full_show_version():
|
||||
|
|
1
tests/test_list2.txt
Normal file
1
tests/test_list2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
https://mangadex.org/title/0aea9f43-d4a9-4bf7-bebc-550a512f9b95/shikimori-s-not-just-a-cutie
|
Loading…
Reference in a new issue