Skip to content

Commit

Permalink
Making cli
Browse files Browse the repository at this point in the history
  • Loading branch information
zoreander committed Jan 9, 2017
1 parent 80ad5b9 commit 2684561
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 36 deletions.
Empty file added __init__.py
Empty file.
38 changes: 38 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -*- coding:utf-8 -*-

from configparser import ConfigParser

CONFIG = 'default.ini'

HTML_TEMPLATE = '''
<!doctype html>
<html lang="en">
<head></head>
<body>
<div>{}</div>
</body>
</html>
'''


class SingleConfig:
"""Singleton forcing that there is only one configuration instance used"""
class Config(ConfigParser):

def get_config(self, name):
self.read(name)
return self

instance = None

def __init__(self):
if not SingleConfig.instance:
SingleConfig.instance = SingleConfig.Config()

def __getattr__(self, item):
return getattr(self.instance, item)


def get_config(name):
config = SingleConfig()
return config.get_config(name)
39 changes: 39 additions & 0 deletions ct.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding:utf-8 -*-

import sys
from argparse import ArgumentParser
from .main import ComicThief

OPTIONS = [
(('-s', '--search'), {'help': 'search'}),
(('-d', '--download'), {'help': 'download'}),
(('-e', '--episode'), {'help': 'choose episode', 'type': int}),
(('-o', '--output'), {'help': 'output format html. pdf, cbr'}),
]


def add_arguments(parser):
for option, kwargs in OPTIONS:
parser.add_argument(*option, **kwargs)

if __name__ == '__main__':
"""
ct.py -s nazwa_komiksu <-szuka komiks i gdy znajdzie zwraca epizody
ct.py -d nazwa_komiksu -e nazwa_epizodu -> sciaga dany epizod komiksu
ct.py -d nazwa_komiksu -e -all -> sciaga wszystkie epizody komiksu
"""
ct = ComicThief()
parser = ArgumentParser()
add_arguments(parser)
args = parser.parse_args()
if args.search:
print('searching')
ct.search(args.search)
elif args.download:
print('downloading')
if args.output:
print("outputing")
if args.episode:
print("outputing")

print(sys.argv)
14 changes: 14 additions & 0 deletions default.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[COMICS_LIST]
default: http://www.readcomics.tv/comic-list

[COMICS_LIST_XPATH]
default: //div[@class="serie-box"]/ul/li/a

[COMICS_SUBPAGE_XPATH]
default: //ul[@class="basic-list"]/li/a

[COMICS_IMAGES_XPATH]
default: //div[@class="chapter-container"]/img/@src

[SETTINGS]
img_dir = img
112 changes: 80 additions & 32 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,36 @@
# -*- coding:utf-8 -*-

from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, wait
from pathlib import Path
import os
from urllib.parse import urlparse

import requests
from lxml import html
from PyQt5.QtCore import QPoint
from PyQt5.QtPrintSupport import QPrinter
from PyQt5.QtGui import QTextDocument, QImage, QPdfWriter, QPainter, QPageSize, QPagedPaintDevice
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QImage, QPdfWriter, QPainter, QPagedPaintDevice

from .config import get_config, CONFIG, HTML_TEMPLATE

COMICS_LIST = {'default': 'http://www.readcomics.tv/comic-list'}
COMICS_LIST_XPATH = {'default': '//div[@class="serie-box"]/ul/li/a'}
COMICS_SUBPAGE_XPATH = {'default': '//ul[@class="basic-list"]/li/a'}
COMICS_IMAGES_XPATH = {'default': '//div[@class="chapter-container"]/img/@src'}
IMG_DIR = 'img'
CWD = Path.cwd()

HTML_TEMPLATE = '''
<!doctype html>
<html lang="en">
<head></head>
<body>
<div>{}</div>
</body>
</html>
'''


def name_fits(search_phrase, key):
if key:
return search_phrase.lower() in key.lower()


class Fetcher:
class Base:

def __init__(self):
self.config = get_config(CONFIG)
self.img_dir = self.config['SETTINGS'].get('img_dir', 'img')


class Fetcher(Base):

def fetch_comic_list_page(self, service='default'):
return requests.get(COMICS_LIST[service])
return requests.get(self.config['COMICS_LIST'].get(service))

def fetch_subpage(self, url):
return requests.get(url)
Expand All @@ -50,7 +41,12 @@ def prepare_ordered_filename(self, remote_path, order):

def download_image(self, url, local_path, name):
img = requests.get(url)
download_path = Path(Path.cwd(), local_path, IMG_DIR, name)
download_path = Path(
Path.cwd(),
local_path,
self.img_dir,
name
)
with download_path.open('wb') as f:
f.write(img.content)
return download_path
Expand All @@ -66,22 +62,22 @@ def download_images_list(self, local_path, images_list, threads=2):
wait(futures)


class Extractor:
class Extractor(Base):

def extract_comic_links(self, page, service='default'):
tree = html.fromstring(page.content)
return tree.xpath(COMICS_LIST_XPATH[service])
return tree.xpath(self.config['COMICS_LIST_XPATH'].get(service))

def extract_issues_list(self, page, service='default'):
tree = html.fromstring(page.content)
return tree.xpath(COMICS_SUBPAGE_XPATH[service])
return tree.xpath(self.config['COMICS_SUBPAGE_XPATH'].get(service))

def extract_images_list(self, page, service='default'):
tree = html.fromstring(page.content)
return tree.xpath(COMICS_IMAGES_XPATH[service])
return tree.xpath(self.config['COMICS_IMAGES_XPATH'].get(service))


class Creator:
class Creator(Base):

def make_comics_dict(self, hrefs):
return {item.text: item.attrib.get('href') for item in hrefs}
Expand All @@ -90,14 +86,14 @@ def search_comics_dict(self, search_phrase, comics_dict):
return {key: value for key, value in comics_dict.items() if name_fits(search_phrase, key)}

def make_comic_html(self, local_path):
files = next(os.walk(str(Path(local_path, IMG_DIR))))[2]
images_html = ''.join(['<img src="{}"/>'.format(str(Path(CWD, local_path, IMG_DIR, file)))
files = next(os.walk(str(Path(local_path, self.img_dir))))[2]
images_html = ''.join(['<img src="{}"/>'.format(str(Path(CWD, local_path, self.img_dir, file)))
for file in sorted(files, key=lambda x: int(x.split('.')[0]))])
return HTML_TEMPLATE.format(images_html)

def make_comic_images_paths_list(self, local_path):
files = next(os.walk(str(Path(local_path, IMG_DIR))))[2]
images_paths_list = [str(Path(CWD, local_path, IMG_DIR, file))
files = next(os.walk(str(Path(local_path, self.img_dir))))[2]
images_paths_list = [str(Path(CWD, local_path, self.img_dir, file))
for file in sorted(files, key=lambda x: int(x.split('.')[0]))]
return images_paths_list

Expand All @@ -121,4 +117,56 @@ def make_pdf_from_images_list(self, path_list, local_path, name):
painter.drawImage(image_coords, image.scaledToWidth(1600))
pdf_writer.newPage()

pdf_writer.deleteLater()
pdf_writer.deleteLater()

def make_cbr_from_images_list(self):
#http://www.makeuseof.com/tag/create-cbrcbz-files-distribute-comic-strip-graphic/
pass

def compress_images(self):
#TODO: to chyba cos dla rusta
pass


class CreatorPdf(Creator):
pass


class CreatorHtml(Creator):
pass


class CreatorCbr(Creator):
pass


class ComicThief:

def __init__(self):
self.fetcher = Fetcher()
self.extractor = Extractor()
self.creator = Creator()
self.config = get_config(CONFIG)
self.img_dir = self.config['SETTINGS'].get('img_dir', 'img')

def fetch_comics_dict(self):
page = self.fetcher.fetch_comic_list_page()
return self.creator.make_comics_dict(self.extractor.extract_comic_links(page))

def get_first_result(self, results):
return sorted(results.items())[0][1]

def search(self, keyword):
comics_dict = self.fetch_comics_dict()
results = self.creator.search_comics_dict(keyword, comics_dict)
results_len = len(results)
if results_len == 1:
print('od razu pokazulemy subpage i epizody')
subpage = self.fetcher.fetch_subpage(self.get_first_result(results))
comics_dict = self.creator.make_comics_dict(self.extractor.extract_issues_list(subpage))
print(comics_dict)
elif results > 1:
print('pokazujemy liste do sprecyzowania')
print(results)
else:
print('Found nothing.')
8 changes: 5 additions & 3 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import os
from pathlib import Path
import unittest
from .config import get_config, CONFIG
from .main import (
Fetcher,
Extractor,
Creator,
IMG_DIR
Creator
)


Expand All @@ -17,6 +17,8 @@ def setUp(self):
self.fetcher = Fetcher()
self.extractor = Extractor()
self.creator = Creator()
self.config = get_config(CONFIG)
self.img_dir = self.config['SETTINGS'].get('img_dir', 'img')

def fetch_comics_dict(self):
page = self.fetcher.fetch_comic_list_page()
Expand Down Expand Up @@ -66,7 +68,7 @@ def test_download_images_list(self):
images_list = self.extractor.extract_images_list(subpage)
local_path = './comics/test/'
self.fetcher.download_images_list(local_path, images_list)
completed_files = next(os.walk(str(Path(local_path, IMG_DIR))))[2] #os.listdir
completed_files = next(os.walk(str(Path(local_path, self.img_dir))))[2] #os.listdir
self.assertGreaterEqual(len(completed_files), len(images_list))

def test_assemble_and_create_html_file(self):
Expand Down
4 changes: 3 additions & 1 deletion todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
#TODO 2 - wzorce projektowe (min 1), watki, generatory, dekoratory itp. jedna metaklasa bylaby fajna
#TODO 3 - klasy deklaratywne byc moze, getattr i setattr etc.
#TODO 3 - polaczenei z rust ? :P
#TODO: na teardown usuwac zawartosc katalogu testowego..
#TODO: na teardown usuwac zawartosc katalogu testowego..
#format string z pythona 3.6 :)
#udostepnic ludkom ? :)

0 comments on commit 2684561

Please sign in to comment.