Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

Release 0.1.0 #18

Draft
wants to merge 74 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
783b3c8
feat(rabbitark): add __init__.py
SaidBySolo Nov 7, 2020
7321063
feat(youtube): add youtube extractor
SaidBySolo Nov 8, 2020
5ec6942
style(isort): apply code style
restyled-commits Nov 8, 2020
011e9e5
Merge pull request #16 from Saebasol/youtube
SaidBySolo Nov 8, 2020
baa0c5a
docs(readme): add youtube
SaidBySolo Nov 8, 2020
a9aca2f
fix(main): close #12
SaidBySolo Nov 9, 2020
c070174
style(isort): apply code style
restyled-commits Nov 9, 2020
5e444d2
feat(extractor): add extractor
SaidBySolo Nov 9, 2020
fe653c9
docs(readme): add more desc in readme
SaidBySolo Nov 9, 2020
18fe604
docs(readme): edit table
SaidBySolo Nov 14, 2020
6012b7a
typo: typo error fix
wonderlandpark Nov 15, 2020
591b042
Merge pull request #28 from wonderlandpark/patch-1
SaidBySolo Nov 15, 2020
4381b12
fix(typehint): all incorrect typehints
kijk2869 Nov 15, 2020
b410ffe
style(black): apply code style
restyled-commits Nov 15, 2020
09bf4ec
style(isort): apply code style
restyled-commits Nov 15, 2020
355025e
Merge pull request #35 from Saebasol/restyled/pull-34
SaidBySolo Nov 15, 2020
2388f02
style(rabbitark): change to absolute
SaidBySolo Nov 16, 2020
2b3fc0a
style(extractor, downloader): rename class name
SaidBySolo Nov 16, 2020
c9aaec9
feat(default_class): add abstract class
SaidBySolo Nov 16, 2020
2600dc3
fix(youtube): close #22
SaidBySolo Nov 16, 2020
e9e9ca3
style(youtube): close #31
SaidBySolo Nov 16, 2020
a4b33b4
style(utils): change class name
SaidBySolo Nov 16, 2020
2c60ce5
feat(default_class): add arg and type hint
SaidBySolo Nov 16, 2020
537c023
refactor(extractor_dict): remove extractor dict
SaidBySolo Nov 16, 2020
2d6c789
refactor(load_dynamic_module): import module only
SaidBySolo Nov 16, 2020
c04973e
feat(extractor): import all extractor
SaidBySolo Nov 16, 2020
fac4ec7
style(downloader): change type hint
SaidBySolo Nov 16, 2020
9267e7d
fix(rabbitark): close #32
SaidBySolo Nov 16, 2020
0857094
style(black): apply code style
restyled-commits Nov 16, 2020
f0a6613
style(isort): apply code style
restyled-commits Nov 16, 2020
f94b061
Merge pull request #37 from Saebasol/hotfix
SaidBySolo Nov 16, 2020
283a33a
fix(rabbitark): cant found extractor
SaidBySolo Nov 17, 2020
bf9e5a4
fix(extractor): pyinstaller extractor load issue
SaidBySolo Nov 18, 2020
5c11708
style(black): apply code style
restyled-commits Nov 18, 2020
f20125b
style(isort): apply code style
restyled-commits Nov 18, 2020
c24a090
style(default_class): change some import
SaidBySolo Nov 19, 2020
3f386ae
fear(request): related to #29
SaidBySolo Nov 19, 2020
65c081d
fix(request): arg position
SaidBySolo Nov 21, 2020
bbb014d
fix(request): edit arg position
SaidBySolo Nov 21, 2020
7c83fbb
refactor(downloader): now write in start_download func
SaidBySolo Nov 22, 2020
060adfc
feat(rabbitark): add logging
SaidBySolo Nov 22, 2020
d2b9d77
feat(rabbitark): add version info and author
SaidBySolo Nov 22, 2020
2def86f
style(isort): apply code style
restyled-commits Nov 22, 2020
27647dd
Merge pull request #41 from Saebasol/logging
SaidBySolo Nov 22, 2020
4ec2abb
test(test): add test code
SaidBySolo Nov 22, 2020
d12a169
style(isort): apply code style
restyled-commits Nov 22, 2020
ff98e01
chore(build): add codecov and github action
SaidBySolo Dec 9, 2020
780ec0d
fix(youtube): change data_json
SaidBySolo Dec 9, 2020
d05ddb1
chore(build): remove path_to_write_report
SaidBySolo Dec 9, 2020
45e3d1e
Merge pull request #43 from Saebasol/test
SaidBySolo Dec 9, 2020
395e6c0
refactor(request): make reuse session
SaidBySolo Dec 14, 2020
aaf0893
style(black): apply code style
restyled-commits Dec 14, 2020
bc0dd98
fix(rabbitark): cant found Requester
SaidBySolo Dec 17, 2020
8d60940
Create Dependabot config file
dependabot-preview[bot] Dec 21, 2020
eefa14f
chore(deps): bump aiohttp from 3.7.2 to 3.7.3
dependabot-preview[bot] Dec 21, 2020
7f3d6ec
Merge pull request #48 from Saebasol/dependabot/add-v2-config-file
SaidBySolo Dec 22, 2020
85edcb5
Merge pull request #49 from Saebasol/dependabot/pip/aiohttp-3.7.3
SaidBySolo Dec 23, 2020
12aa793
chore(deps): bump aiohttp from 3.7.3 to 3.7.4
dependabot[bot] Feb 26, 2021
e389c3c
Merge pull request #50 from Saebasol/dependabot/pip/aiohttp-3.7.4
SaidBySolo Mar 2, 2021
4a250de
chore(deps): bump aiomultiprocess from 0.8.0 to 0.9.0
dependabot[bot] Mar 2, 2021
8ac4b87
Merge pull request #51 from Saebasol/dependabot/pip/aiomultiprocess-0…
SaidBySolo Mar 3, 2021
a2ef0b1
chore(deps): bump aiohttp from 3.7.4 to 3.7.4.post0
dependabot[bot] Mar 8, 2021
bfa29e9
refactor!(request): rewrite request classes
SaidBySolo Apr 29, 2021
319cf82
refactor!(request): rewrite some utils func
SaidBySolo Apr 29, 2021
8a2c857
feat(typing): add TypeVar
SaidBySolo Apr 29, 2021
65393b7
BREAKING CHANGE: All essential parts have been rewritten.
SaidBySolo May 1, 2021
834fc09
fix(downloader): import dataclass error
SaidBySolo May 1, 2021
6f3584d
style(typing): make return method constant
SaidBySolo May 1, 2021
87b4737
feat(downloader): apply folder
SaidBySolo May 1, 2021
ac7f6c9
Merge pull request #54 from SaidBySolo/feature
SaidBySolo May 1, 2021
a279817
docs(readme): close #56
SaidBySolo May 19, 2021
1fcc968
Merge pull request #52 from Saebasol/dependabot/pip/aiohttp-3.7.4.post0
SaidBySolo May 20, 2021
4a0b73e
chore(deps): bump aiofiles from 0.6.0 to 0.7.0
dependabot[bot] May 20, 2021
62f8206
Merge pull request #55 from Saebasol/dependabot/pip/aiofiles-0.7.0
SaidBySolo May 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
46 changes: 46 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Python application

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest pytest-asyncio pytest-cov
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest and generate coverage report
run: |
pytest --cov=./ --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: ./coverage.xml
files: ./coverage1.xml,./coverage2.xml
directory: ./coverage/reports/
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
verbose: true
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
# Rabbit Ark

[![Code Style](https://img.shields.io/badge/code%20style-black-black)](https://github.com/psf/black)

> **Warning!** Only Python 3.9 or higher can guarantee normal operation.
> Scalable downloader that downloads asynchronously

- [Rabbit Ark](#rabbit-ark)
- [Description](#description)
- [Download](#download)
- [How to use](#how-to-use)
- [Supported Sites](#supported-sites)
- [Script](#script)
- [Special Thanks](#special-thanks)
Expand All @@ -12,14 +17,36 @@

This program is inspired by [YouTube-dl](https://github.com/ytdl-org/youtube-dl/) and [Hitomi Downloader](https://github.com/KurtBestor/Hitomi-Downloader)

The download is processed using the [aiomultiprocess](https://github.com/omnilib/aiomultiprocess) module.
### Download

You can Download in [here](https://github.com/Saebasol/rabbit-ark/releases)

### How to use

First you should specify which extractor to use

```sh
rabbitark pixiv
```

Then provide downloadable information, such as a link, via the --downloadable argument.

```sh

rabbitark pixiv --downloadable pixiv-artwork-url
```

After a while, you can see that the folder is created and the images are down.

For more information, check the -h argument.

## Supported Sites

| Site | URL | Extractor release life cycle |
| :-----------: | ------------------- | ---------------------------- |
| **Hitomi.la** | <https://hitomi.la> | Alpha |
| **Pixiv** | <https://pixiv.net> | Alpha |
| Site | URL | Extractor release life cycle |
| :---------: | --------------------- | ---------------------------- |
| **Hitomi** | <https://hitomi.la> | Alpha |
| **Pixiv** | <https://pixiv.net> | Alpha |
| **Youtube** | <https://youtube.com> | Alpha |

## Script

Expand Down
10 changes: 10 additions & 0 deletions rabbitark/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import sys
from collections import namedtuple

__version__ = "0.1.0"

__author__ = "Ryu JuHeon"

VersionInfo = namedtuple("VersionInfo", "major minor micro releaselevel serial")

version_info = VersionInfo(major=0, minor=1, micro=0, releaselevel="alpha", serial=0)
71 changes: 62 additions & 9 deletions rabbitark/__main__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import argparse
import asyncio
import logging
import platform
import sys
from asyncio.events import get_event_loop

from rabbitark.config import config
from rabbitark.config import Config
from rabbitark.extractor import load
from rabbitark.rabbitark import RabbitArk
from rabbitark.utils.utils import load_cookie_txt
from rabbitark.utils import load_cookie_txt

parser = argparse.ArgumentParser("rabbitark")
logger = logging.getLogger("rabbitark")
logger.setLevel(logging.DEBUG)

# formatter
formatter = logging.Formatter("%(asctime)s - (%(name)s) - [%(levelname)s]: %(message)s")

# console handler
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
logger.addHandler(ch)


config = Config()

parser = argparse.ArgumentParser("rabbitark")

parser.add_argument("extractor", type=str, help="Specifies the extractor to use")
parser.add_argument("extractor", type=str, help="Specifies the extractor name")

parser.add_argument(
"--downloadable",
Expand All @@ -18,10 +36,20 @@

parser.add_argument("--base", type=str, help="Specifies the pre-created folder")

parser.add_argument("--folder", type=str, help="")
parser.add_argument("--folder", type=str, help="Specifies the folder name")

parser.add_argument("--cookies", type=str, help="load cookies.txt")

parser.add_argument("--page", type=int, help="Youtube page limit")

parser.add_argument("--custom_extractor", type=str, help="use custom extractor")

parser.add_argument(
"--verbose", action="store_true", help="print debugging information"
)

parser.add_argument("--report", action="store_true", help="save debugging informaion")

args = parser.parse_args()

if args.base:
Expand All @@ -33,6 +61,31 @@
if args.cookies:
config.COOKIES = load_cookie_txt(args.cookies)

if __name__ == "__main__":
ark = RabbitArk(args.extractor)
asyncio.run(ark.start(args.downloadable))
if args.page:
config.YOUTUBE_PAGE_LIMIT = args.page

if args.custom_extractor:
config.CUSTOM_EXTRACTOR = args.custom_extractor

if args.verbose:
ch.setLevel(logging.DEBUG)

if args.report:
fh = logging.FileHandler("rabbitark.log")
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
logger.addHandler(fh)

logger.debug("system ver: %s %s", platform.python_implementation(), sys.version)
logger.debug("platform: %s", platform.platform())
logger.debug("args: %s", sys.argv[1:])

logger.info("start import extractor")
load()
logger.info("sucessfully import extractor")

logger.debug("start loop")
get_event_loop().run_until_complete(
RabbitArk(args.extractor, config).start(args.downloadable)
)
logger.debug("complete loop")
21 changes: 21 additions & 0 deletions rabbitark/abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from abc import ABC, ABCMeta, abstractmethod
from typing import Any, Optional

from aiohttp.client import ClientSession

from rabbitark.config import Config
from rabbitark.dataclass import DownloadInfo


class BaseRequest(ABC):
@abstractmethod
def __init__(self) -> None:
self.session: Optional[ClientSession] = None


class BaseExtractor(BaseRequest, metaclass=ABCMeta):
@abstractmethod
async def get_download_info(
self, download_source: Any, config: Config
) -> DownloadInfo:
pass
26 changes: 20 additions & 6 deletions rabbitark/config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
class _Config:
__slots__ = ["BASE_DIRECTORY", "FOLDER", "COOKIES"]
from typing import Optional

def __init__(self):

class Config:
__slots__: list[str] = [
"BASE_DIRECTORY",
"FOLDER",
"COOKIES",
"YOUTUBE_PAGE_LIMIT",
"CUSTOM_EXTRACTOR",
"REQUEST_PER_SESSION",
]

def __init__(self) -> None:
self.BASE_DIRECTORY: str = "."
self.FOLDER: str = None
self.COOKIES: str = {}
self.FOLDER: Optional[str] = None
self.COOKIES: dict[str, Optional[str]] = {}
self.CUSTOM_EXTRACTOR: Optional[str] = None

# extractor config
self.YOUTUBE_PAGE_LIMIT: int = 6

config = _Config()
# download config
self.REQUEST_PER_SESSION: int = 10
28 changes: 28 additions & 0 deletions rabbitark/dataclass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Any

from rabbitark.utils import Optional, split


class Image:
def __init__(self, url: str, filename: Optional[str] = None) -> None:
self.url: str = url
self.filename: str = filename or split(url)


class DownloadInfo:
def __init__(
self, image: list[Image], title: Optional[str] = None, **kwargs: Any
) -> None:
self.title = title
self.image = image
self.kwargs = kwargs

def to_download(self, path: str):
return {image.url: path + image.filename for image in self.image}


class Response:
def __init__(self, status: int, message: Optional[Any], body: Any) -> None:
self.status = status
self.message = message
self.body = body
56 changes: 56 additions & 0 deletions rabbitark/downloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from os.path import exists
from typing import Any, Literal, Optional

from aiofiles import open
from aiofiles.os import mkdir
from aiohttp.client import ClientSession

from rabbitark.config import Config
from rabbitark.dataclass import DownloadInfo
from rabbitark.request import SessionPoolRequest


class Downloader(SessionPoolRequest):
def __init__(self, config: Config) -> None:
self.config = config
super().__init__()

async def download(
self,
session: ClientSession,
url: str,
method: Literal["GET"],
_: Any,
**kwargs: Any,
):
filename = kwargs.pop("filename")
response = await session.request(method, url, **kwargs)
async with open(filename[url], "wb") as f:
async for data, _ in response.content.iter_chunks():
await f.write(data)

async def create_folder(self, title: Optional[str] = None) -> str:
default_dir = f"{self.config.BASE_DIRECTORY}/{self.config.FOLDER}/"
if not exists(default_dir):
await mkdir(default_dir)

if title:
if not exists(f"{default_dir}/{title}"):
await mkdir(f"{default_dir}/{title}")

return f"{default_dir}/{title}/"

return default_dir

async def start_download(self, download_info: DownloadInfo):
directory = await self.create_folder(download_info.title)
filename_mapping = download_info.to_download(directory)
url_list = list(filename_mapping.keys())
await self.request_using_session_pool(
self.download,
url_list,
"GET",
request_per_session=self.config.REQUEST_PER_SESSION,
filename=filename_mapping,
**download_info.kwargs,
)
Loading