Merge pull request 'update to work again' (#4) from dev into master
All checks were successful
continuous-integration/drone/push Build is passing

Reviewed-on: #4
This commit is contained in:
Ivan Schaller 2022-05-09 15:43:11 +02:00
commit e62de962a7
8 changed files with 638 additions and 473 deletions

2
.gitignore vendored
View file

@ -9,3 +9,5 @@ downloads/
__pycache__/ __pycache__/
.pytest_cache/ .pytest_cache/
chaps.txt chaps.txt
venv/

12
CHANGELOG.md Normal file
View file

@ -0,0 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
- Add support for new mangadex api
- Rewrite api section
- Add support for more sites

View file

@ -1,86 +1,105 @@
import mangadlp.main as MangaDLP import mangadlp.main as mangadlp
import argparse import argparse
def main(args): def main(args):
MangaDLP.main(args.url, mangadlp.main(
args.lang, args.url_uuid,
args.chapters, args.lang,
args.read, args.chapters,
args.list, args.read,
args.nocbz, args.list,
args.forcevol, args.nocbz,
args.path, args.forcevol,
args.wait, args.path,
args.verbose) args.wait,
args.verbose,
)
if __name__ == '__main__': if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Script to download mangas from various sites') parser = argparse.ArgumentParser(
parser.add_argument('-u', '--url', description="Script to download mangas from various sites"
dest='url', )
required=False, parser.add_argument(
help='URL of the manga', "-u",
action='store', "--url",
) "--uuid",
parser.add_argument('-c', '--chapters', dest="url_uuid",
dest='chapters', required=False,
required=False, help="URL or UUID of the manga",
help='Chapters to download', action="store",
action='store', )
) parser.add_argument(
parser.add_argument('-p', '--path', "-c",
dest='path', "--chapters",
required=False, dest="chapters",
help='Download path. Defaults to "<script_dir>/downloads"', required=False,
action='store', help="Chapters to download",
default='downloads', action="store",
) )
parser.add_argument('-l', '--language', parser.add_argument(
dest='lang', "-p",
required=False, "--path",
help='Manga language. Defaults to "en" --> english', dest="path",
action='store', required=False,
default='en', help='Download path. Defaults to "<script_dir>/downloads"',
) action="store",
parser.add_argument('--read', default="downloads",
dest='read', )
required=False, parser.add_argument(
help='Path of file with manga links to download. One per line', "-l",
action='store', "--language",
) dest="lang",
parser.add_argument('--list', required=False,
dest='list', help='Manga language. Defaults to "en" --> english',
required=False, action="store",
help='List all available chapters. Defaults to false', default="en",
action='store_true', )
) parser.add_argument(
parser.add_argument('--nocbz', "--read",
dest='nocbz', dest="read",
required=False, required=False,
help='Dont pack it to a cbz archive. Defaults to false', help="Path of file with manga links to download. One per line",
action='store_true', action="store",
) )
parser.add_argument('--forcevol', parser.add_argument(
dest='forcevol', "--list",
required=False, dest="list",
help='Force naming of volumes. For mangas where chapters reset each volume', required=False,
action='store_true', help="List all available chapters. Defaults to false",
) action="store_true",
parser.add_argument('--wait', )
dest='wait', parser.add_argument(
required=False, "--nocbz",
type=float, dest="nocbz",
help='Time to wait for each picture to download in seconds(float). Defaults 0.5', required=False,
) help="Dont pack it to a cbz archive. Defaults to false",
parser.add_argument('--verbose', action="store_true",
dest='verbose', )
required=False, parser.add_argument(
help='Verbose logging. Defaults to false', "--forcevol",
action='store_true', 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,
help="Time to wait for each picture to download in seconds(float). Defaults 0.5",
)
parser.add_argument(
"--verbose",
dest="verbose",
required=False,
help="Verbose logging. Defaults to false",
action="store_true",
)
#parser.print_help() # parser.print_help()
args = parser.parse_args() args = parser.parse_args()
main(args)
main(args)

View file

@ -1,146 +1,242 @@
import requests
import re import re
import mangadlp.utils as MUtils from time import sleep
import requests
import mangadlp.utils as utils
class Mangadex(): class Mangadex:
# api information # api information
api_base_url = 'https://api.mangadex.org' api_base_url = "https://api.mangadex.org"
img_base_url = 'https://uploads.mangadex.org' img_base_url = "https://uploads.mangadex.org"
# get infos to initiate class
def __init__(self, manga_url_uuid, manga_lang, forcevol, verbose):
# static info
self.manga_url_uuid = manga_url_uuid
self.manga_lang = manga_lang
self.forcevol = forcevol
self.verbose = verbose
# get infos to initiate class # api stuff
def __init__(self, manga_url, manga_lang): self.api_content_ratings = "contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic"
self.manga_url = manga_url self.api_language = f"translatedLanguage[]={self.manga_lang}"
self.manga_lang = manga_lang self.api_additions = f"{self.api_language}&{self.api_content_ratings}"
self.manga_uuid = self.get_manga_uuid()
self.manga_title = self.get_manga_title(self.manga_uuid)
self.manga_chapter_data = self.get_manga_chapters(self.manga_uuid)
# infos from functions
self.manga_uuid = self.get_manga_uuid()
self.manga_data = self.get_manga_data()
self.manga_title = self.get_manga_title()
self.manga_chapter_data = self.get_chapter_data()
# get the uuid for the manga # make initial request
def get_manga_uuid(self): def get_manga_data(self):
# isolate id from url if self.verbose:
uuid_regex = re.compile('[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}') print(f"INFO: Getting manga data for: {self.manga_uuid}")
# check for new mangadex id counter = 1
if uuid_regex.search(self.manga_url): while counter < 3:
manga_uuid = uuid_regex.search(self.manga_url)[0] try:
else: manga_data = requests.get(
print('No valid uuid found') f"{self.api_base_url}/manga/{self.manga_uuid}"
exit(1) )
# check if the manga exists except:
try: if counter >= 3:
req = requests.get(f'{self.api_base_url}/manga/{manga_uuid}') print("ERR: Maybe the MangaDex API is down?")
except: exit(1)
print('Error. Maybe the MangaDex API is down?') else:
exit(1) print("ERR: Mangadex API not reachable. Retrying")
else: sleep(2)
# check mangadex status counter += 1
response = req.json()['result'] else:
if not response == 'ok': break
print('Manga not found') # check if manga exists
exit(1) if manga_data.json()["result"] != "ok":
print("ERR: Manga not found")
exit(1)
return manga_uuid return manga_data
# get the uuid for the manga
def get_manga_uuid(self):
# isolate id from url
uuid_regex = re.compile(
"[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"
)
# check for new mangadex id
if not uuid_regex.search(self.manga_url_uuid):
print("ERR: No valid UUID found")
exit(1)
manga_uuid = uuid_regex.search(self.manga_url_uuid)[0]
return manga_uuid
# 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, manga_uuid): def get_manga_title(self):
req = requests.get(f'{self.api_base_url}/manga/{manga_uuid}') if self.verbose:
api_resp = req.json() print(f"INFO: Getting manga title for: {self.manga_uuid}")
try: manga_data = self.manga_data.json()
title = api_resp['data']['attributes']['title'][self.manga_lang] try:
except: title = manga_data["data"]["attributes"]["title"][self.manga_lang]
# search in alt titles except:
try: # search in alt titles
alt_titles = {} try:
for title in api_resp['data']['attributes']['altTitles']: alt_titles = {}
alt_titles.update(title) for title in manga_data["data"]["attributes"]["altTitles"]:
title = alt_titles[self.manga_lang] alt_titles.update(title)
except: # no title on requested language found title = alt_titles[self.manga_lang]
print('Chapter in requested language not found.') except: # no title on requested language found
exit(1) print("ERR: Chapter in requested language not found.")
exit(1)
return utils.fix_name(title)
return MUtils.fix_name(title) # check if chapters are available in requested language
def check_chapter_lang(self):
if self.verbose:
print(
f"INFO: Checking for chapters in specified language for: {self.manga_uuid}"
)
r = requests.get(
f"{self.api_base_url}/manga/{self.manga_uuid}/feed?limit=0&{self.api_additions}"
)
try:
total_chapters = r.json()["total"]
except:
print(
"ERR: Error retrieving the chapters list. Did you specify a valid language code?"
)
return 0
else:
if total_chapters == 0:
print("ERR: No chapters available to download!")
return 0
return total_chapters
# get all chapter data for further parsing # get chapter data like name, uuid etc
def get_manga_chapters(self, manga_uuid): def get_chapter_data(self):
content_ratings = 'contentRating[]=safe&contentRating[]=suggestive&contentRating[]=erotica&contentRating[]=pornographic' if self.verbose:
chap_data_list = [] print(f"INFO: Getting chapter data for: {self.manga_uuid}")
req = requests.get(f'{self.api_base_url}/manga/{manga_uuid}/feed?limit=0&translatedLanguage[]={self.manga_lang}&{content_ratings}') api_sorting = "order[chapter]=asc&order[volume]=asc"
try: # check for chapters in specified lang
total = req.json()['total'] total_chapters = self.check_chapter_lang()
except: if total_chapters == 0:
print('Error retrieving the chapters list. Did you specify a valid language code?') exit(1)
exit(1)
if total == 0:
print('No chapters available to download!')
exit(0)
last_chap = ['', '']
offset = 0
while offset < total: # if more than 500 chapters
req = requests.get(f'{self.api_base_url}/manga/{manga_uuid}/feed?order[chapter]=asc&order[volume]=asc&limit=500&translatedLanguage[]={self.manga_lang}&offset={offset}&{content_ratings}')
for chapter in req.json()['data']:
chap_num = chapter['attributes']['chapter']
chap_vol = chapter['attributes']['volume']
chap_uuid = chapter['id']
chap_hash = chapter['attributes']['hash']
chap_data = chapter['attributes']['data']
chap_name = chapter['attributes']['title']
if not chap_name == None:
chap_name = MUtils.fix_name(chap_name)
# check if the chapter is external (cant download them)
chap_external = chapter['attributes']['externalUrl']
# name chapter "oneshot" if there is no chapter number
if chap_external == None and chap_num == None:
# check for duplicates
if last_chap[0] == chap_vol and last_chap[1] == chap_num:
continue
chap_data_list.append([chap_vol, 'Oneshot', chap_uuid, chap_hash, chap_name, chap_data])
# else add chapter number
elif chap_external == None:
# check for duplicates
if last_chap[0] == chap_vol and last_chap[1] == chap_num:
continue
chap_data_list.append([chap_vol, chap_num, chap_uuid, chap_hash, chap_name, chap_data])
last_chap = [chap_vol, chap_num]
offset += 500
return chap_data_list chapter_data = {}
last_chapter = ["", ""]
offset = 0
while offset < total_chapters: # if more than 500 chapters
r = requests.get(
f"{self.api_base_url}/manga/{self.manga_uuid}/feed?{api_sorting}&limit=500&offset={offset}&{self.api_additions}"
)
for chapter in r.json()["data"]:
# chapter infos from feed
chapter_num = chapter["attributes"]["chapter"]
chapter_vol = chapter["attributes"]["volume"]
chapter_uuid = chapter["id"]
chapter_name = chapter["attributes"]["title"]
chapter_external = chapter["attributes"]["externalUrl"]
# check for chapter title and fix it
if chapter_name is None:
chapter_name = "No Title"
else:
chapter_name = utils.fix_name(chapter_name)
# check if the chapter is external (can't download them)
if chapter_external is not None:
continue
# name chapter "oneshot" if there is no chapter number
if chapter_num is None:
chapter_num = "Oneshot"
def get_chapter_index(self, chapter, forcevol): # check if its duplicate from the last entry
# get index of chapter if last_chapter[0] == chapter_vol and last_chapter[1] == chapter_num:
if forcevol: continue
chapter_index = next(c for c in self.manga_chapter_data if f'{c[0]}:{c[1]}' == chapter)
else:
chapter_index = next(c for c in self.manga_chapter_data if c[1] == chapter)
return chapter_index # export chapter data as a dict
chapter_index = (
chapter_num if not self.forcevol else f"{chapter_vol}:{chapter_num}"
)
chapter_data[chapter_index] = [
chapter_uuid,
chapter_vol,
chapter_num,
chapter_name,
]
# add last chapter to duplicate check
last_chapter = [chapter_vol, chapter_num]
# increase offset for mangas with more than 500 chapters
offset += 500
# create list of chapters return chapter_data
def create_chapter_list(self, chapter_data, forcevol):
chapter_list = []
for chap in chapter_data:
volume_number = chap[0]
chapter_number = chap[1]
if forcevol:
chapter_list.append(f'{volume_number}:{chapter_number}')
else:
chapter_list.append(chapter_number)
return chapter_list # get images for the chapter (mangadex@home)
def get_chapter_images(self, chapter):
if self.verbose:
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]
r = requests.get(f"{athome_url}/{chapter_uuid}")
api_data = r.json()
if api_data["result"] != "ok":
print(f"ERR: No chapter with the id {chapter_uuid} found")
elif api_data["chapter"]["data"] is None:
print(f"ERR: No chapter data found for chapter {chapter_uuid}")
# get list of image urls chapter_hash = api_data["chapter"]["hash"]
def get_img_urls(self, images, chapter_hash): chapter_img_data = api_data["chapter"]["data"]
img_urls = []
for img in images:
img_urls.append(f'{self.img_base_url}/data/{chapter_hash}/{img}')
return img_urls # get list of image urls
image_urls = []
for image in chapter_img_data:
image_urls.append(f"{self.img_base_url}/data/{chapter_hash}/{image}")
return image_urls
# create list of chapters
def create_chapter_list(self):
if self.verbose:
print(f"INFO: Creating chapter list for: {self.manga_uuid}")
chapter_list = []
for chapter in self.manga_chapter_data.items():
chapter_info = self.get_chapter_infos(chapter[0])
chapter_number = chapter_info["chapter"]
volume_number = chapter_info["volume"]
if self.forcevol:
chapter_list.append(f"{volume_number}:{chapter_number}")
else:
chapter_list.append(chapter_number)
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:
print(
f"INFO: Getting chapter infos for: {self.manga_chapter_data[chapter][0]}"
)
chapter_uuid = self.manga_chapter_data[chapter][0]
chapter_vol = self.manga_chapter_data[chapter][1]
chapter_num = self.manga_chapter_data[chapter][2]
chapter_name = self.manga_chapter_data[chapter][3]
return {
"uuid": chapter_uuid,
"volume": chapter_vol,
"chapter": chapter_num,
"name": chapter_name,
}

View file

@ -1,34 +1,33 @@
import shutil import shutil
import requests
from time import sleep
from pathlib import Path from pathlib import Path
from time import sleep
import requests
# download images
def download_chapter(image_urls, chapter_path, md_wait=0.5, md_verbose=False): def download_chapter(image_urls, chapter_path, md_wait=0.5, md_verbose=False):
# download images img_num = 1
img_num = 1 for img in image_urls:
for img in image_urls: # set image path
# set image path image_path = Path(f"{chapter_path}/{img_num:03d}")
image_path = Path(f'{chapter_path}/{img_num:03d}') try:
try: # print('Try getting ' + img)
#print('Try getting ' + img) req = requests.get(img, stream=True)
req = requests.get(img, stream = True) except:
except: print(f"ERR: Request for image {img} failed, retrying")
print(f'Request for image {img} failed, retrying') sleep(md_wait)
sleep(md_wait) req = requests.get(img, stream=True)
req = requests.get(img, stream = True)
if req.status_code == 200: if req.status_code == 200:
req.raw.decode_content = True req.raw.decode_content = True
with image_path.open('wb') as file: with image_path.open("wb") as file:
shutil.copyfileobj(req.raw, file) shutil.copyfileobj(req.raw, file)
# verbose logging # verbose logging
if md_verbose: if md_verbose:
print(f' Downloaded image {img_num}') print(f"INFO: Downloaded image {img_num}")
img_num += 1 img_num += 1
sleep(0.5) sleep(0.5)
else: else:
print('Image {img} could not be downloaded. Exiting') print(f"ERR: Image {img} could not be downloaded. Exiting")
exit(1) exit(1)

View file

@ -1,219 +1,248 @@
from pathlib import Path
import re import re
from mangadlp import api from pathlib import Path
import mangadlp.utils as MUtils
import mangadlp.downloader as MDownloader import mangadlp.downloader as downloader
import mangadlp.utils as utils
# supported api's # supported api's
from mangadlp.api.mangadex import Mangadex from mangadlp.api.mangadex import Mangadex
def main(manga_url='', def main(
manga_language='en', manga_url_uuid="",
manga_chapters=None, manga_language="en",
manga_readlist='', manga_chapters=None,
manga_list_chapters=False, manga_readlist="",
manga_nocbz=False, manga_list_chapters=False,
manga_forcevol=False, manga_nocbz=False,
download_path='downloads', manga_forcevol=False,
download_wait=0.5, download_path="downloads",
log_verbose=False): download_wait=0.5,
'''Download Mangas from supported sites\n log_verbose=False,
):
"""Download Mangas from supported sites\n
Args:\n Args:\n
url (str) -- Manga URL to Download. No defaults\n url (str) -- Manga URL or UUID to Download. No defaults\n
lang (str) -- Language to download chapters in. Defaults to "en" -> english\n lang (str) -- Language to download chapters in. Defaults to "en" -> english\n
chapter (str) -- Chapters to download "all" for every chapter available. Defaults to none\n chapter (str) -- Chapters to download "all" for every chapter available. Defaults to none\n
readlist (str) -- List of chapters to read in. One link per line. No defaults\n readlist (str) -- List of chapters to read in. One link per line. No defaults\n
list_chapters (bool) -- If it should only list all available chapters. Defaults to False\n list_chapters (bool) -- If it should only list all available chapters. Defaults to False\n
nocbz (bool) -- If the downloaded images should not be packed into a .cbz archive. Defaults to false\n nocbz (bool) -- If the downloaded images should not be packed into a .cbz archive. Defaults to false\n
forcevol (bool) -- Force naming of volumes. For mangas where chapters reset each volume. Defaults to false.\n forcevol (bool) -- Force naming of volumes. For mangas where chapters reset each volume. Defaults to false.\n
download_path (str) -- Folder to save mangas to. Defaults to "<script_dir>/downloads"\n download_path (str) -- Folder to save mangas to. Defaults to "<script_dir>/downloads"\n
download_wait (float) -- Time to wait for each picture to download in seconds(float). Defaults 0.5.\n download_wait (float) -- Time to wait for each picture to download in seconds(float). Defaults 0.5.\n
log_verbose (bool) -- If verbose logging is enabled. Defaults to false\n log_verbose (bool) -- If verbose logging is enabled. Defaults to false\n
Returns:\n Returns:\n
nothing\n nothing\n
''' """
# prechecks userinput/options # prechecks userinput/options
if not manga_list_chapters and manga_chapters == None: if not manga_list_chapters and manga_chapters is None:
# no chapters to download were given # no chapters to download were given
print(f'You need to specify one or more chapters to download. To see all chapters use "--list"') print(
exit(1) f'ERR: You need to specify one or more chapters to download. To see all chapters use "--list"'
# no url and no readin list given )
elif not manga_url and not manga_readlist: exit(1)
print(f'You need to specify a manga url with "-u" or a list with "--read"') # no url and no readin list given
exit(1) elif not manga_url_uuid and not manga_readlist:
# url and readin list given print(
elif manga_url and manga_readlist: f'ERR: You need to specify a manga url/uuid with "-u" or a list with "--read"'
print(f'You can only use "-u" or "--read". Dont specify both') )
exit(1) exit(1)
# url and readin list given
elif manga_url_uuid and manga_readlist:
print(f'ERR: You can only use "-u" or "--read". Dont specify both')
exit(1)
# check if readin file was specified # check if readin file was specified
if manga_readlist: if manga_readlist:
# loop trough every chapter in readin file # loop trough every chapter in readin file
for url in readin_list(manga_readlist): for url in readin_list(manga_readlist):
ApiUsed = check_api(url) api_used = check_api(url)
if not ApiUsed: if not api_used:
continue continue
if log_verbose: # get manga
print(f'Api used: {ApiUsed}') get_manga(
# get manga api_used,
get_manga(ApiUsed, url, manga_language, manga_chapters, manga_list_chapters, manga_nocbz, manga_forcevol, download_path, download_wait, log_verbose) url,
else: manga_language,
# single manga manga_chapters,
ApiUsed = check_api(manga_url) manga_list_chapters,
if not ApiUsed: manga_nocbz,
exit(1) manga_forcevol,
if log_verbose: download_path,
print(f'Api used: {ApiUsed}') download_wait,
# get manga log_verbose,
get_manga(ApiUsed, manga_url, manga_language, manga_chapters, manga_list_chapters, manga_nocbz, manga_forcevol, download_path, download_wait, log_verbose) )
else:
# single manga
api_used = check_api(manga_url_uuid)
if not api_used:
exit(1)
# get manga
get_manga(
api_used,
manga_url_uuid,
manga_language,
manga_chapters,
manga_list_chapters,
manga_nocbz,
manga_forcevol,
download_path,
download_wait,
log_verbose,
)
# read in the list of links from a file # read in the list of links from a file
def readin_list(manga_readlist): def readin_list(manga_readlist):
url_file = Path(manga_readlist) url_file = Path(manga_readlist)
url_list = [] url_list = []
with url_file.open('r') as file: with url_file.open("r") as file:
for line in file: for line in file:
url_list.append(line.rstrip()) url_list.append(line.rstrip())
return url_list return url_list
# check the api which needs to be used # check the api which needs to be used
def check_api(manga_url): def check_api(manga_url):
# apis to check # apis to check
api_mangadex = re.compile('mangadex.org') api_mangadex = re.compile("mangadex.org")
api_test = re.compile('test.test') api_mangadex2 = re.compile(
# check url for match "[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"
if api_mangadex.search(manga_url): )
return Mangadex api_test = re.compile("test.test")
# this is only for testing multiple apis # check url for match
elif api_test.search(manga_url): if api_mangadex.search(manga_url) or api_mangadex2.search(manga_url):
pass return Mangadex
# no supported api found # this is only for testing multiple apis
else: elif api_test.search(manga_url):
print(f'No supported api in link found\n{manga_url}') pass
return False # no supported api found
else:
print(f"ERR: No supported api in link/uuid found\n{manga_url}")
return False
# main function to get the chapters # main function to get the chapters
def get_manga(ApiUsed, manga_url, manga_language, manga_chapters, manga_list_chapters, manga_nocbz, manga_forcevol, download_path, download_wait, log_verbose): def get_manga(
# init api api_used,
Api = ApiUsed(manga_url, manga_language) manga_url,
# get manga title and uuid manga_language,
manga_uuid = Api.manga_uuid manga_chapters,
manga_title = Api.manga_title manga_list_chapters,
# get chapter data manga_nocbz,
manga_chapter_data = Api.manga_chapter_data manga_forcevol,
# crate chapter list download_path,
manga_chapter_list = Api.create_chapter_list(manga_chapter_data, manga_forcevol) download_wait,
log_verbose,
# print infos ):
print('\n=========================================') # show api used
print(f'Manga Name: {manga_title}')
print(f'UUID: {manga_uuid}')
print(f'Total chapters: {len(manga_chapter_list)}')
# list chapters if manga_list_chapters is true
if manga_list_chapters:
print(f'Available Chapters:\n{", ".join(manga_chapter_list)}')
print('=========================================\n')
return
# check chapters to download if it not all
chapters_to_download = []
if manga_chapters.lower() == 'all':
chapters_to_download = manga_chapter_list
else:
chapters_to_download = MUtils.get_chapter_list(manga_chapters)
# show chapters to download
print(f'Chapters selected:\n{", ".join(chapters_to_download)}')
print('=========================================\n')
# create manga folder
manga_path = Path(f'{download_path}/{manga_title}')
manga_path.mkdir(parents=True, exist_ok=True)
# main download loop
for chapter in chapters_to_download:
# get index of chapter
chapter_index = Api.get_chapter_index(chapter, manga_forcevol)
# default mapping of chapter data
chapter_vol = chapter_index[0]
chapter_num = chapter_index[1]
chapter_uuid = chapter_index[2]
chapter_hash = chapter_index[3]
chapter_name = chapter_index[4]
chapter_img_data = chapter_index[5]
# create image urls from img data
image_urls = Api.get_img_urls(chapter_img_data, chapter_hash)
# get filename for chapter
chapter_filename = MUtils.get_filename(chapter_name, chapter_vol, chapter_num, manga_forcevol)
# set download path for chapter
chapter_path = manga_path / chapter_filename
# check if chapter already exists.
# check for folder if option nocbz is given. if nocbz is not given, the folder will be overwritten
if MUtils.check_existence(chapter_path, manga_nocbz) and manga_forcevol:
print(f'- Vol {chapter_vol} Chapter {chapter_num} already exists. Skipping\n')
continue
elif MUtils.check_existence(chapter_path, manga_nocbz):
print(f'- Chapter {chapter_num} already exists. Skipping\n')
continue
# create chapter folder (skips it if it already exists)
chapter_path.mkdir(parents=True, exist_ok=True)
# verbose log
if log_verbose: if log_verbose:
print(f'Chapter UUID: {chapter_uuid}') print(f"INFO: API used: {api_used}")
print(f'Filename: {chapter_path}\n' if manga_nocbz else f'Filename: {chapter_path}.cbz\n') # init api
print(f'Image URLS: {image_urls}') Api = api_used(manga_url, manga_language, manga_forcevol, log_verbose)
print(f'DEBUG: Downloading Volume {chapter_vol}') # 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()
# log # show infos
if manga_forcevol: print_divider = "========================================="
print(f'+ Downloading Volume {chapter_vol} Chapter {chapter_num}') print(f"\n{print_divider}")
print(f"INFO: Manga Name: {manga_title}")
print(f"INFO: Manga UUID: {manga_uuid}")
print(f"INFO: Total chapters: {len(manga_chapter_list)}")
# list chapters if manga_list_chapters is true
if manga_list_chapters:
print(f'INFO: Available Chapters:\n{", ".join(manga_chapter_list)}')
print(f"{print_divider}\n")
return
# check chapters to download if not all
if manga_chapters.lower() == "all":
chapters_to_download = manga_chapter_list
else: else:
print(f'+ Downloading Chapter {chapter_num}') chapters_to_download = utils.get_chapter_list(manga_chapters)
# download images # show chapters to download
try: print(f'INFO: Chapters selected:\n{", ".join(chapters_to_download)}')
MDownloader.download_chapter(image_urls, chapter_path, download_wait, log_verbose) print(f"{print_divider}\n")
except:
if manga_forcevol:
print(f'Cant download volume {chapter_vol} chapter {chapter_num}. Exiting')
else:
print(f'Cant download chapter {chapter_num}. Exiting')
exit(1)
else:
# Done with chapter
if manga_forcevol:
print(f'Successfully downloaded volume {chapter_vol} chapter {chapter_num}')
else:
print(f'Successfully downloaded chapter {chapter_num}')
# make cbz of folder # create manga folder
if not manga_nocbz: manga_path = Path(f"{download_path}/{manga_title}")
print('\n+ Creating .cbz archive') manga_path.mkdir(parents=True, exist_ok=True)
try:
MUtils.make_archive(chapter_path)
except:
print('Could not make cbz archive')
exit(1)
# done with chapter # main download loop
print('Done with chapter') for chapter in chapters_to_download:
print('------------------------------\n') # get chapter infos
chapter_infos = Api.get_chapter_infos(chapter)
# done with manga # get image urls for chapter
print('=============================') chapter_image_urls = Api.get_chapter_images(chapter)
print(f'Done with manga: {manga_title}')
print('=============================\n')
# get filename for chapter
chapter_filename = Api.get_filename(chapter)
# set download path for chapter
chapter_path = manga_path / chapter_filename
# 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, manga_nocbz):
print(f"INFO: '{chapter_filename}' already exists. Skipping\n")
continue
# create chapter folder (skips it if it already exists)
chapter_path.mkdir(parents=True, exist_ok=True)
# verbose log
if log_verbose:
print(f"INFO: Chapter UUID: {chapter_infos['uuid']}")
print(
f"INFO: Filename: '{chapter_filename}'\n"
if manga_nocbz
else f"INFO: Filename: '{chapter_filename}.cbz'\n"
)
print(f"INFO: Image URLS: {chapter_image_urls}")
# log
print(f"INFO: Downloading: '{chapter_filename}'")
# download images
try:
downloader.download_chapter(
chapter_image_urls, chapter_path, download_wait, log_verbose
)
except KeyboardInterrupt:
print("ERR: Stopping")
exit(1)
except:
print(f"ERR: Cant download: '{chapter_filename}'. Exiting")
else:
# Done with chapter
print(f"INFO: Successfully downloaded: '{chapter_filename}'")
# make cbz of folder
if not manga_nocbz:
print("INFO: Creating .cbz archive")
try:
utils.make_archive(chapter_path)
except:
print("ERR: Could not make cbz archive")
exit(1)
# done with chapter
print("INFO: Done with chapter")
print("-----------------------------------------\n")
# done with manga
print(f"{print_divider}")
print(f"INFO: Done with manga: {manga_title}")
print(f"{print_divider}\n")

View file

@ -3,71 +3,78 @@ import shutil
from zipfile import ZipFile from zipfile import ZipFile
import re import re
# create a cbz archive
def make_archive(chapter_path): def make_archive(chapter_path):
image_folder = Path(chapter_path) image_folder = Path(chapter_path)
zip_path = Path(f'{chapter_path}.zip') zip_path = Path(f"{chapter_path}.zip")
with ZipFile(f'{image_folder}.zip', 'w') as zip_archive: with ZipFile(f"{image_folder}.zip", "w") as zip_archive:
for file in image_folder.iterdir(): for file in image_folder.iterdir():
zip_archive.write(file, file.name) zip_archive.write(file, file.name)
zip_path.rename(zip_path.with_suffix('.cbz')) zip_path.rename(zip_path.with_suffix(".cbz"))
shutil.rmtree(image_folder) shutil.rmtree(image_folder)
# check if the file already exists
def check_existence(chapter_path, manga_nocbz): def check_existence(chapter_path, manga_nocbz):
# check for folder if option nocbz is given. if nocbz is not given, the folder will be overwritten # check for folder if option nocbz is given. if nocbz is not given, the folder will be overwritten
chapter_path = Path(chapter_path) chapter_path = Path(chapter_path)
cbz_path = chapter_path.parent / f'{chapter_path.name}.cbz' cbz_path = chapter_path.parent / f"{chapter_path.name}.cbz"
if manga_nocbz and chapter_path.exists(): if manga_nocbz and chapter_path.exists():
return True return True
# check for cbz archive # check for cbz archive
elif not manga_nocbz and cbz_path.exists(): elif not manga_nocbz and cbz_path.exists():
return True return True
else:
return False
def get_chapter_list(chapters):
chapter_list = []
for chapter in chapters.split(','):
if '-' in chapter and ':' in chapter:
lower = chapter.split('-')[0].split(':')
upper = chapter.split('-')[1].split(':')
for n in range(int(lower[1]), int(upper[1])+1):
chapter_list.append(str(f'{lower[0]}:{n}'))
elif '-' in chapter:
lower = chapter.split('-')[0]
upper = chapter.split('-')[1]
for n in range(int(lower), int(upper)+1):
chapter_list.append(str(n))
else: else:
chapter_list.append(chapter) return False
return chapter_list
def fix_name(name): # create a list of chapters
# remove illegal characters def get_chapter_list(chapters):
name = re.sub('[/<>:"\\|?*!.]', '', name) chapter_list = []
# remove trailing space for chapter in chapters.split(","):
name = re.sub('[ \t]+$', '', name) if "-" in chapter and ":" in chapter:
lower = chapter.split("-")[0].split(":")
upper = chapter.split("-")[1].split(":")
for n in range(int(lower[1]), int(upper[1]) + 1):
chapter_list.append(str(f"{lower[0]}:{n}"))
elif "-" in chapter:
lower = chapter.split("-")[0]
upper = chapter.split("-")[1]
for n in range(int(lower), int(upper) + 1):
chapter_list.append(str(n))
else:
chapter_list.append(chapter)
return name return chapter_list
# remove illegal characters etc
def fix_name(filename):
# remove illegal characters
filename = re.sub("[\\\/\<\>\:\;'\"\|\?\*\!\@]", ".", filename)
# remove multiple dots
filename = re.sub("([\.]{2,})", ".", filename)
# remove dot(s) at the beginning and end of the filename
filename = re.sub("(^[\.]{1,})|([\.]{1,}$)", "", filename)
# remove trailing and beginning spaces
filename = re.sub("([ \t]+$)|(^[ \t]+)", "", filename)
return filename
# create name for chapter
def get_filename(chapter_name, chapter_vol, chapter_num, manga_forcevol): def get_filename(chapter_name, chapter_vol, chapter_num, manga_forcevol):
# filename for chapter # filename for chapter
if chapter_name == 'Oneshot' or chapter_num == 'Oneshot': if chapter_name == "Oneshot" or chapter_num == "Oneshot":
chapter_filename = 'Oneshot' chapter_filename = "Oneshot"
elif not chapter_name and manga_forcevol: elif not chapter_name and manga_forcevol:
chapter_filename = f'Vol. {chapter_vol} Ch. {chapter_num}' chapter_filename = f"Vol. {chapter_vol} Ch. {chapter_num}"
elif not chapter_name: elif not chapter_name:
chapter_filename = f'Ch. {chapter_num}' chapter_filename = f"Ch. {chapter_num}"
elif manga_forcevol: elif manga_forcevol:
chapter_filename = f'Vol. {chapter_vol} Ch. {chapter_num} - {chapter_name}' chapter_filename = f"Vol. {chapter_vol} Ch. {chapter_num} - {chapter_name}"
else: else:
chapter_filename = f'Ch. {chapter_num} - {chapter_name}' chapter_filename = f"Ch. {chapter_num} - {chapter_name}"
return chapter_filename
return chapter_filename

View file

@ -1,2 +1,3 @@
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
https://mangadex.org/title/bd6d0982-0091-4945-ad70-c028ed3c0917/mushoku-tensei-isekai-ittara-honki-dasu https://mangadex.org/title/bd6d0982-0091-4945-ad70-c028ed3c0917/mushoku-tensei-isekai-ittara-honki-dasu
37f5cce0-8070-4ada-96e5-fa24b1bd4ff9