This commit is contained in:
Sorrow446 2020-02-09 20:14:48 +00:00 committed by GitHub
parent 892e8f05aa
commit 8296d0168d
5 changed files with 238 additions and 0 deletions

1
api/__init__.py Normal file
View File

@ -0,0 +1 @@
from .api import Client

45
api/api.py Normal file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env python3
#Sorrow446.
import os
import re
from random import randint
import requests
from api.exceptions import IneligibleError
class Client:
def __init__(self, **kwargs):
self.session = requests.Session()
self.session.headers.update({
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0'
})
self.base = 'https://read-api.marvel.com/'
def set_cookies(self, cookies):
self.session.cookies.update(cookies)
def get_id(self, url):
r = self.session.get(url)
regex = r'digital_comic_id : "(([0-9]{5}))"'
return re.search(regex, r.text).group(1)
def make_call(self, epoint, json=None, params=None):
r = self.session.get(self.base+epoint, json=json, params=params)
r.raise_for_status()
return r
def get_comic_meta(self, id):
self.session.headers.update({'Referer': 'https://read.marvel.com/'})
r = self.make_call('issue/v1/digitalcomics/'+id+'?')
return r.json()['data']['results'][0]['issue_meta']
def get_comic(self, id):
params={'rand': randint(10000, 99999)}
r = self.make_call('asset/v1/digitalcomics/'+id+'?', params=params)
j = r.json()['data']['results'][0]
if not j['auth_state']['subscriber']:
raise IneligibleError('Marvel Unlimited subscription required.')
urls = [url['assets']['source'] for url in j['pages']]
return urls

2
api/exceptions.py Normal file
View File

@ -0,0 +1,2 @@
class IneligibleError(Exception):
pass

187
mur.py Normal file
View File

@ -0,0 +1,187 @@
#!/usr/bin/env python3
import os
import re
import sys
import json
import shutil
import zipfile
import argparse
import platform
import api
import img2pdf
from tqdm import tqdm
from requests.exceptions import HTTPError
from api.exceptions import IneligibleError
client = api.Client()
def print_title():
print("""
_____ _____ _____
| | | | __ |
| | | | | | -|
|_|_|_|_____|__|__|
""")
def get_os():
if platform.system() == 'Windows':
return True
return False
def set_con_title():
if get_os():
os.system('title MUR R1 (by Sorrow446)')
else:
sys.stdout.write('\x1b]2;MUR R1 (by Sorrow446)\x07')
def sanitize(fn):
if get_os():
return re.sub(r'[\/:*?"><|]', '_', fn)
else:
return re.sub('/', '_', fn)
def parse_args():
parser = argparse.ArgumentParser(
description='Sorrow446.'
)
parser.add_argument(
'-u', '--url',
help="URL - www.marvel.com/comics/issue/ or read.marvel.com/#/book/.",
nargs='*',
required=True
)
parser.add_argument(
'-f', '--format',
help="Export format.",
choices=['cbz', 'pdf'],
required=True
)
parser.add_argument(
'-m', '--meta',
help="Write comic's metadata to JSON file.",
action='store_true'
)
return parser.parse_args()
def parse_cookies(out_cookies={}):
with open('cookies.txt') as f:
for line in f.readlines():
if not line.startswith('#'):
field = line.strip().split('\t')
out_cookies[field[5]] = field[6]
client.set_cookies(out_cookies)
def exist_check(f):
if os.path.isfile(f):
return True
return False
def dir_setup(tmp_dir, dl_dir):
if os.path.isdir(tmp_dir):
shutil.rmtree(tmp_dir)
if not os.path.isdir(dl_dir):
os.makedirs(dl_dir)
os.makedirs(tmp_dir)
def check_url(url):
regexes=[
r'http[s]://(read).marvel.com/#/book/([0-9]{5}$)',
r'http[s]://(www).marvel.com/comics/issue/([0-9]{5})/.+'
]
for regex in regexes:
match = re.match(regex, url)
if match:
return match.group(1), match.group(2)
def download(urls, tmp_dir, cur=0):
total = len(urls)
for url in urls:
cur += 1
print('Downloading image {} of {}...'.format(cur, total))
r = client.session.get(url, stream=True)
r.raise_for_status()
size = int(r.headers.get('content-length', 0))
abs = os.path.join(tmp_dir, str(cur) + '.jpg')
with open(abs, 'wb') as f:
with tqdm(total=size, unit='B',
unit_scale=True, unit_divisor=1024,
initial=0, miniters=1) as bar:
for chunk in r.iter_content(32*1024):
if chunk:
f.write(chunk)
bar.update(len(chunk))
def make_pdf(abs, images, title):
with open(abs, 'wb') as f:
f.write(img2pdf.convert(images, title=title))
def make_cbz(abs, images):
with zipfile.ZipFile(abs, 'w', zipfile.ZIP_STORED) as f:
for i in images: f.write(i)
def write_meta(meta_abs, meta):
with open(meta_abs, 'w') as f:
json.dump(meta, f, indent=4)
def err(e, cur, tot):
print(e)
if cur == tot:
sys.exit(1)
def main():
cd = os.getcwd()
tmp_dir = os.path.join(cd, 'mur_tmp')
dl_dir = os.path.join(cd, 'MUR downloads')
dir_setup(tmp_dir, dl_dir)
parse_cookies()
args = parse_args()
tot = len(args.url)
cur = 0
for url in args.url:
cur += 1
try:
print("Booklet {} of {}:".format(cur, tot))
try:
type, id = check_url(url)
except TypeError:
err('Invalid URL: '+str(url), cur, tot)
continue
if type == "www":
id = client.get_id(url)
fmt = args.format
meta = client.get_comic_meta(id)
title = meta['title']
title_s = sanitize(title)
print(str(title) + "\n")
abs = os.path.join(dl_dir, '{}.{}'.format(title_s, fmt))
if exist_check(abs):
err('Comic already exists locally.', cur, tot)
continue
try:
download(client.get_comic(id), tmp_dir)
except IneligibleError as e:
print(e)
sys.exit(1)
images = [os.path.join(tmp_dir, i) for i in os.listdir(tmp_dir)]
meta_abs = os.path.join(dl_dir, '{}_meta.json'.format(title_s))
print('Converting to {}...'.format(fmt.upper()))
if fmt == 'pdf':
make_pdf(abs, images, title)
else:
make_cbz(abs, images)
if args.meta:
print("Writing metadata to JSON file...")
write_meta(meta_abs, meta)
for i in images:
os.remove(i)
except HTTPError as e:
err(e, cur, tot)
except Exception as e:
err(e, cur, tot)
if __name__ == '__main__':
print_title()
set_con_title()
main()

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
tqdm
img2pdf
requests