diff --git a/.gitignore b/.gitignore index 739edac..0f4df66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ env __pycache__ .coverage -*.egg-info \ No newline at end of file +*.egg-info +sample \ No newline at end of file diff --git a/yoink/cli.py b/yoink/cli.py index 74d1547..5dec171 100644 --- a/yoink/cli.py +++ b/yoink/cli.py @@ -5,17 +5,18 @@ import sys import click from click_default_group import DefaultGroup -from yoink.common import app_root, library_path, config_path +from yoink.config import YoinkConfig, app_root, config_from_file, library_path, config_path from yoink.comic import Comic queue = [] +config = config_from_file('yoink.json') -def download_comic(url, path, series): +def download_comic(url, series): try: - comic = Comic(url, path=path if path else None) + comic = Comic(url) except ValueError: click.echo(f'{url} is not supported or is not a valid URL') return 1 @@ -32,7 +33,7 @@ def download_comic(url, path, series): click.echo('Success') if series and comic.next: - download_comic(comic.next, path, series) + download_comic(comic.next, series) @click.group(cls=DefaultGroup, default='download', default_if_no_args=True) @@ -45,21 +46,21 @@ def yoink(): def init(): click.echo(f'Initializing for {sys.platform}') + click.echo(config) @yoink.command() # @click.option('-c', '--comic', is_flag=True, help='Download a Comic file') # @click.option('-t', '--torrent', is_flag=True, help='Download a Torrent') @click.option('-s', '--series', is_flag=True, help='Download the entire series') -@click.option('-p', '--path', help='Change the download path') @click.argument('url') -def download(url, path, series): +def download(url, series): # Account for whitespace/blank urls if url.strip() == '': click.echo('url cannot be blank') return 1 - download_comic(url, path, series) + download_comic(url, series) diff --git a/yoink/comic.py b/yoink/comic.py index f9b6d45..f19a098 100644 --- a/yoink/comic.py +++ b/yoink/comic.py @@ -1,4 +1,4 @@ -from yoink.common import required_comic_files, skippable_images, library_path +from yoink.config import required_archive_files, skippable_images, library_path, config from yoink.scraper import Scrapable import os @@ -102,13 +102,13 @@ class Comic(Scrapable): def can_remove(self, filename : str) -> bool: - return not filename.endswith(required_comic_files) + return not filename.endswith(config.skippable_images) class ComicArchiver: def __init__(self, comic : Comic, library=None) -> None: self.comic = comic - self.worktree = library if library else os.path.join(library_path, f'comics/{self.comic.title}') + self.worktree = library if library else os.path.join(config.library_path, f'comics/{self.comic.title}') self.queue = [] def add(self, link : str) -> None: @@ -155,7 +155,7 @@ class ComicArchiver: def cleanup_worktree(self): for image in os.listdir(self.worktree): - if not image.endswith(required_comic_files): + if not image.endswith(required_archive_files): os.remove(os.path.join(self.worktree, image)) if __name__ == '__main__': diff --git a/yoink/common.py b/yoink/common.py deleted file mode 100644 index 2e9f506..0000000 --- a/yoink/common.py +++ /dev/null @@ -1,16 +0,0 @@ -from pathlib import Path -# TODO replace os path with pathlib -import os -from enum import Enum, auto - - - -# TODO replace expan user -home_folder = Path.home() -app_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) -config_path = os.path.abspath(os.path.join(os.path.expanduser('~'), '.config/yoink')) -library_path = os.path.abspath(os.path.join(os.path.expanduser('~'), 'yoink/library')) -required_comic_files = ('.cbr', '.cbz', '000.jpg', '001.jpg') -skippable_images = ('logo-1.png', 'logo.png', 'report.png', 'request.png', 'prev.png', 'Next.png', 'Donate.png', '11.png', 'navbar.svg') -supported_sites = ['readallcomics.com', 'tpb.party', 'dragonballsupermanga.net', 'mangadex.tv'] -headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'} diff --git a/yoink/config.py b/yoink/config.py new file mode 100644 index 0000000..b3e7467 --- /dev/null +++ b/yoink/config.py @@ -0,0 +1,87 @@ +from dataclasses import dataclass +import json +from pathlib import Path +# TODO replace os path with pathlib +import os +from enum import Enum, auto + + + +@dataclass +class YoinkConfig: + home_path: str + config_path: str + app_root: str + library_path: str + skippable_images: set + supported_sites: set + headers: dict + plugins: list + + +defaults = { + 'home_path': str(Path.home()), + 'config_path': os.path.abspath(os.path.join(Path.home(), '.config/yoink')), + 'app_root': os.path.abspath(os.path.join(os.path.dirname(__file__), '..')), + 'library_path': os.path.abspath(os.path.join(Path.home(), 'yoink/library')), + 'skippable_images': ('.cbr', '.cbz', '000.jpg', '001.jpg'), + 'supported_sites': [ + { + "name": 'readallcomics.com', + }, + { + "name": 'tpb.party', + }, + { + "name": 'dragonballsupermanga.net', + }, + { + "name": 'mangadex.tv' + } + ], + 'headers': {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'}, + 'plugins': [] +} + + + +def config_from_file(filepath: str) -> YoinkConfig: + if os.path.exists(filepath): + with open(filepath, 'r') as file: + + data = json.load(file) + return YoinkConfig(**data) + else: + # TODO prompt user for prefered file locations and save yoink.json + return config_from_defaults() + + +def config_from_defaults() -> YoinkConfig: + return YoinkConfig(**defaults) + + +home_folder = Path.home() + + +app_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) + + +config_path = os.path.abspath(os.path.join(home_folder, '.config/yoink')) + + +library_path = os.path.abspath(os.path.join(home_folder, 'yoink/library')) + + +required_archive_files = ('.cbr', '.cbz', '000.jpg', '001.jpg') + +skippable_images = ('logo-1.png', 'logo.png', 'report.png', 'request.png', 'prev.png', 'Next.png', 'Donate.png', '11.png', 'navbar.svg') + +_sites = ['readallcomics.com', 'tpb.party', 'dragonballsupermanga.net', 'mangadex.tv'] + +supported_sites = _sites + + +headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'} + + +config = config_from_file('yoink.json') \ No newline at end of file diff --git a/yoink/factory.py b/yoink/factory.py new file mode 100644 index 0000000..fd62c9f --- /dev/null +++ b/yoink/factory.py @@ -0,0 +1,24 @@ +from typing import Callable, Any + +from yoink.scraper import Scrapable + + +downloader_creation_funcs: dict[str, Callable[..., Scrapable]] = {} + + +def register(url: str, creation_function: Callable[..., Scrapable]): + downloader_creation_funcs[url] = creation_function + +def unregister(url: str): + downloader_creation_funcs.pop(url, None) + +def create(arguments: dict[str, Any]) -> Scrapable: + arguments_copy = arguments.copy() + + url = arguments_copy.pop('url') + + try: + creation_func = downloader_creation_funcs[url] + return creation_func(**arguments_copy) + except KeyError: + raise ValueError(f'Unsupported website: {url}') \ No newline at end of file diff --git a/yoink/scraper.py b/yoink/scraper.py index ea91c6f..8057bb6 100644 --- a/yoink/scraper.py +++ b/yoink/scraper.py @@ -4,7 +4,7 @@ from bs4 import BeautifulSoup import os from enum import Enum, auto -from yoink.common import supported_sites, library_path +from yoink.config import supported_sites, library_path diff --git a/yoink/yoink.json b/yoink/yoink.json new file mode 100644 index 0000000..49e1b47 --- /dev/null +++ b/yoink/yoink.json @@ -0,0 +1,62 @@ +{ + "home_path": "", + "config_path": "", + "app_root": "", + "library_path": "/home/makubex/yoink/library", + "headers": { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36" + }, + "skippable_images": ["logo-1.png","logo.png","report.png","request.png","prev.png","Next.png","Donate.png","11.png","navbar.svg"], + "supported_sites": [ + { + "name": "readallcomics", + "url": "http://readallcomics.com", + "search": { + "default": { + "element": "div", + "class": "separator", + "attrs": null + }, + "no-div": { + "element": "img", + "class": null, + "attrs": { + "width": "1000px" + } + }, + "excaliber": { + "element": "img", + "class": null, + "attrs": null + } + } + }, + { + "name": "dragonballsupermanga", + "url": "https://www.dragonballsupermanga.net", + "search": { + "dbsuper": { + "element": "meta", + "class": null, + "attrs": { + "property": "twitter:image" + } + } + } + }, + { + "name": "mangadex", + "url": "https://www.mangadex.tv", + "search": { + "mangadex": { + "element": "img", + "class": null, + "attrs": { + "draggable": "false" + } + } + } + } + ], + "plugins": [] +} \ No newline at end of file