rcdl 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rcdl-2.0.0/.gitignore +228 -0
- rcdl-2.0.0/CHANGELOG.md +68 -0
- rcdl-2.0.0/PKG-INFO +81 -0
- rcdl-2.0.0/README.md +64 -0
- rcdl-2.0.0/pyproject.toml +28 -0
- rcdl-2.0.0/rcdl/__init__.py +5 -0
- rcdl-2.0.0/rcdl/__main__.py +23 -0
- rcdl-2.0.0/rcdl/core/api.py +54 -0
- rcdl-2.0.0/rcdl/core/config.py +115 -0
- rcdl-2.0.0/rcdl/core/db.py +262 -0
- rcdl-2.0.0/rcdl/core/downloader.py +246 -0
- rcdl-2.0.0/rcdl/core/downloader_subprocess.py +29 -0
- rcdl-2.0.0/rcdl/core/file_io.py +14 -0
- rcdl-2.0.0/rcdl/core/models.py +52 -0
- rcdl-2.0.0/rcdl/core/parser.py +208 -0
- rcdl-2.0.0/rcdl/core/processor.py +290 -0
- rcdl-2.0.0/rcdl/interface/cli.py +97 -0
- rcdl-2.0.0/rcdl/interface/progress.py +25 -0
- rcdl-2.0.0/rcdl/scripts/upload_pypi.py +83 -0
- rcdl-2.0.0/rcdl/utils.py +40 -0
rcdl-2.0.0/.gitignore
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
#poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
#pdm.lock
|
|
116
|
+
#pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
#pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
#.idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
|
|
204
|
+
# Ruff stuff:
|
|
205
|
+
.ruff_cache/
|
|
206
|
+
|
|
207
|
+
# PyPI configuration file
|
|
208
|
+
.pypirc
|
|
209
|
+
|
|
210
|
+
# Marimo
|
|
211
|
+
marimo/_static/
|
|
212
|
+
marimo/_lsp/
|
|
213
|
+
__marimo__/
|
|
214
|
+
|
|
215
|
+
# Streamlit
|
|
216
|
+
.streamlit/secrets.toml
|
|
217
|
+
|
|
218
|
+
# editor
|
|
219
|
+
.vscode/
|
|
220
|
+
|
|
221
|
+
# dev files
|
|
222
|
+
output/
|
|
223
|
+
dev/
|
|
224
|
+
|
|
225
|
+
# output file
|
|
226
|
+
*.mp4
|
|
227
|
+
*.mp3
|
|
228
|
+
*.txt
|
rcdl-2.0.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
## [Unreleased]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [2.0.0] - 2025-12-25
|
|
8
|
+
## Added
|
|
9
|
+
- Renamed the project from `cdl` to `rcdl` !
|
|
10
|
+
- new command `refresh` to update video to be downloaded without starting the download
|
|
11
|
+
## Changed
|
|
12
|
+
- default file creation changed (see config.py)
|
|
13
|
+
- progress ETA improved
|
|
14
|
+
- replaced .csv cache with `sqlite3` DB
|
|
15
|
+
- `refresh` functionnality removed from `dlsf`
|
|
16
|
+
## Fixed
|
|
17
|
+
- Fix multiple bugs
|
|
18
|
+
|
|
19
|
+
## [1.6.2] - 2025-12-21
|
|
20
|
+
### Fixed
|
|
21
|
+
- fix empty '' ss duration
|
|
22
|
+
|
|
23
|
+
## [1.6.0] - 2025-11-01
|
|
24
|
+
### Added
|
|
25
|
+
- Discovery command: search tags for posts with tag
|
|
26
|
+
- Tag and Max_page aoption arg
|
|
27
|
+
|
|
28
|
+
## [1.5.1] - 2025-10-21
|
|
29
|
+
### Added
|
|
30
|
+
- Fuse part videos with cdl fuse command. Warning: delete part videos that are succesfully fused.
|
|
31
|
+
- preset selection in settings.json
|
|
32
|
+
- data gatehring of fuse performance in .cache/fuse.csv
|
|
33
|
+
- progress info for fuse command
|
|
34
|
+
### Changed
|
|
35
|
+
- Instead of deleting fused file, rename to .mv_oldfilename.mp4
|
|
36
|
+
|
|
37
|
+
## [1.4.1] - 2025-10-20
|
|
38
|
+
### Added
|
|
39
|
+
- Updated parser to extract .m4v video too
|
|
40
|
+
### Fixed
|
|
41
|
+
- Fixed crash when looking for already downloaded posts and error index in .cache/ is missing
|
|
42
|
+
- Fixed issue when video is not written in cache, still check if video not in paths
|
|
43
|
+
|
|
44
|
+
## [1.4.0] - 2025-10-17
|
|
45
|
+
### Added
|
|
46
|
+
- custom yt-dlp flags in "yt_dlp_args" in settings.json. aria2 call live here
|
|
47
|
+
### Fixed
|
|
48
|
+
- fix bug when initialisation wrote default settings despite file already existing
|
|
49
|
+
|
|
50
|
+
## [1.3.1] - 2025-10-04
|
|
51
|
+
### Added
|
|
52
|
+
- use aria2 as external downloader in yt-dlp
|
|
53
|
+
- add --version flag
|
|
54
|
+
- show log file in terminal with command cdl log
|
|
55
|
+
- added DEBUG flag --debug, no functionnality yet
|
|
56
|
+
- add support for kemono
|
|
57
|
+
### Changed
|
|
58
|
+
- Python version required now is >= 3.12 instead of >= 3.13
|
|
59
|
+
- Progress class to track progress, now also give an estimated time remaining
|
|
60
|
+
- Progress eta is now in HH:MM:SS format
|
|
61
|
+
- change log file location
|
|
62
|
+
### Fixed
|
|
63
|
+
- fix empty title bug
|
|
64
|
+
- fix an issue when multiples URLS where in a post, it only checked if the first post had been downloaded and not all urls before skipping download
|
|
65
|
+
|
|
66
|
+
## [1.0.0] - 2025-10-03
|
|
67
|
+
### Added
|
|
68
|
+
- initial release of the project
|
rcdl-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rcdl
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Coomer/Kemono CLI Downloader
|
|
5
|
+
Keywords: downloader,video,media
|
|
6
|
+
Author: Anonymous
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Dist: click>=8.2
|
|
13
|
+
Requires-Dist: requests>=2.32
|
|
14
|
+
Requires-Dist: pathvalidate==3.3.1
|
|
15
|
+
Requires-Dist: moviepy==2.2.1
|
|
16
|
+
|
|
17
|
+
# RCDL
|
|
18
|
+
|
|
19
|
+
Riton Coomer Download Manager
|
|
20
|
+
`rcdl` is a tool to automatically download the videos of your favorites creators from [coomer.st](https://coomer.st) and [kemono.cr](https://kemono.cr)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
### Dependencies
|
|
25
|
+
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
|
26
|
+
- [aria2](https://github.com/aria2/aria2)
|
|
27
|
+
- [ffmpeg](https://www.ffmpeg.org/download.html) (Only for `fuse` command)
|
|
28
|
+
Recommended install:
|
|
29
|
+
```bash
|
|
30
|
+
pipx install yt-dlp
|
|
31
|
+
sudo apt install aria2 ffmpeg
|
|
32
|
+
```
|
|
33
|
+
### Install RCDL
|
|
34
|
+
It is recommended to use pipx
|
|
35
|
+
```bash
|
|
36
|
+
pipx install rcdl
|
|
37
|
+
```
|
|
38
|
+
or else:
|
|
39
|
+
```bash
|
|
40
|
+
pip install rcdl
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## How to use
|
|
44
|
+
|
|
45
|
+
Run the CLI with:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
rcdl --help
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
By default all files will live in `~/Videos/rcdl/`. Cache, configuration and log file will be in a hidden `rcdl/.cache/` folder.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
rcdl refresh # look creators.json and find all possible videos
|
|
55
|
+
rcdl dlsf # download all found videos
|
|
56
|
+
rcdl discover # WIP
|
|
57
|
+
rcdl fuse # WIP
|
|
58
|
+
rcdl log # debug only; show the log file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Dev
|
|
62
|
+
### Install
|
|
63
|
+
```bash
|
|
64
|
+
git clone https://github.com/ritonun/cdl.git rcdl
|
|
65
|
+
cd rcdl
|
|
66
|
+
python3 -m venv .venv
|
|
67
|
+
source .venv/bin/activate
|
|
68
|
+
pip install -e .
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Deploy
|
|
72
|
+
```bash
|
|
73
|
+
python3 -m pip install --upgrade build
|
|
74
|
+
python3 -m pip install --upgrade twine
|
|
75
|
+
pip install flit packaging requests # necessary to run auto release scripts
|
|
76
|
+
|
|
77
|
+
# Use convenience scripts in rcdl/scripts
|
|
78
|
+
# Create api_key.txt with the pypi api key in the root folder
|
|
79
|
+
python3 rcdl/scripts/upload_pypi.py
|
|
80
|
+
```
|
|
81
|
+
|
rcdl-2.0.0/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# RCDL
|
|
2
|
+
|
|
3
|
+
Riton Coomer Download Manager
|
|
4
|
+
`rcdl` is a tool to automatically download the videos of your favorites creators from [coomer.st](https://coomer.st) and [kemono.cr](https://kemono.cr)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
### Dependencies
|
|
9
|
+
- [yt-dlp](https://github.com/yt-dlp/yt-dlp)
|
|
10
|
+
- [aria2](https://github.com/aria2/aria2)
|
|
11
|
+
- [ffmpeg](https://www.ffmpeg.org/download.html) (Only for `fuse` command)
|
|
12
|
+
Recommended install:
|
|
13
|
+
```bash
|
|
14
|
+
pipx install yt-dlp
|
|
15
|
+
sudo apt install aria2 ffmpeg
|
|
16
|
+
```
|
|
17
|
+
### Install RCDL
|
|
18
|
+
It is recommended to use pipx
|
|
19
|
+
```bash
|
|
20
|
+
pipx install rcdl
|
|
21
|
+
```
|
|
22
|
+
or else:
|
|
23
|
+
```bash
|
|
24
|
+
pip install rcdl
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## How to use
|
|
28
|
+
|
|
29
|
+
Run the CLI with:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
rcdl --help
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
By default all files will live in `~/Videos/rcdl/`. Cache, configuration and log file will be in a hidden `rcdl/.cache/` folder.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
rcdl refresh # look creators.json and find all possible videos
|
|
39
|
+
rcdl dlsf # download all found videos
|
|
40
|
+
rcdl discover # WIP
|
|
41
|
+
rcdl fuse # WIP
|
|
42
|
+
rcdl log # debug only; show the log file
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Dev
|
|
46
|
+
### Install
|
|
47
|
+
```bash
|
|
48
|
+
git clone https://github.com/ritonun/cdl.git rcdl
|
|
49
|
+
cd rcdl
|
|
50
|
+
python3 -m venv .venv
|
|
51
|
+
source .venv/bin/activate
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Deploy
|
|
56
|
+
```bash
|
|
57
|
+
python3 -m pip install --upgrade build
|
|
58
|
+
python3 -m pip install --upgrade twine
|
|
59
|
+
pip install flit packaging requests # necessary to run auto release scripts
|
|
60
|
+
|
|
61
|
+
# Use convenience scripts in rcdl/scripts
|
|
62
|
+
# Create api_key.txt with the pypi api key in the root folder
|
|
63
|
+
python3 rcdl/scripts/upload_pypi.py
|
|
64
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "rcdl"
|
|
3
|
+
version = "2.0.0"
|
|
4
|
+
description = "Coomer/Kemono CLI Downloader"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"click>=8.2",
|
|
8
|
+
"requests>=2.32",
|
|
9
|
+
"pathvalidate==3.3.1",
|
|
10
|
+
"moviepy==2.2.1",
|
|
11
|
+
]
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Anonymous"},
|
|
14
|
+
]
|
|
15
|
+
readme = "README.md"
|
|
16
|
+
keywords = ["downloader", "video", "media"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
rcdl = "rcdl.__main__:cli"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["flit_core<4"]
|
|
28
|
+
build-backend = "flit_core.buildapi"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# __main__.py
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from rcdl.core.config import Config, setup_logging
|
|
6
|
+
|
|
7
|
+
# setup file structure
|
|
8
|
+
Config.ensure_dirs()
|
|
9
|
+
Config.ensure_files()
|
|
10
|
+
|
|
11
|
+
# setup logging
|
|
12
|
+
setup_logging(Config.LOG_FILE, level=0)
|
|
13
|
+
|
|
14
|
+
logging.info("--- INIT ---")
|
|
15
|
+
logging.info("Logger initialized")
|
|
16
|
+
|
|
17
|
+
# init database
|
|
18
|
+
from rcdl.core.db import DB # noqa: E402
|
|
19
|
+
|
|
20
|
+
d = DB()
|
|
21
|
+
d.close()
|
|
22
|
+
|
|
23
|
+
from rcdl.interface.cli import cli # noqa: E402, F401
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# core/api.py
|
|
2
|
+
|
|
3
|
+
from .models import Creator
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class URL:
|
|
7
|
+
DOMAINS_BASE_URL = {
|
|
8
|
+
"coomer": "https://coomer.st/api/v1/",
|
|
9
|
+
"kemono": "https://kemono.cr/api/v1/",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def get_base_url(domain: str) -> str:
|
|
14
|
+
if domain not in URL.DOMAINS_BASE_URL:
|
|
15
|
+
raise KeyError(f"{domain} not in known domains urls")
|
|
16
|
+
return URL.DOMAINS_BASE_URL[domain]
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def get_post_revision(creator: Creator, post_id) -> str:
|
|
20
|
+
return f"{URL.get_base_url(creator.domain)}{creator.service}/user/{creator.creator_id}/post/{post_id}/revisions"
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def get_headers() -> dict:
|
|
24
|
+
return {
|
|
25
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0 Safari/537.36",
|
|
26
|
+
"Accept": "text/css",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def get_url_from_file(domain: str, path_url: str):
|
|
31
|
+
if domain == "coomer":
|
|
32
|
+
return f"https://coomer.st{path_url}"
|
|
33
|
+
elif domain == "kemono":
|
|
34
|
+
return f"https://kemono.cr{path_url}"
|
|
35
|
+
else:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Domain {domain} is not an accepted value/does not exist. Please check your creators.json file"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def add_params(url: str, params: dict):
|
|
42
|
+
url += "?"
|
|
43
|
+
for key in params:
|
|
44
|
+
url += f"{key}={params[key]}&"
|
|
45
|
+
return url[:-1]
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def get_creator_post_wo_param(creator: Creator) -> str:
|
|
49
|
+
return f"{URL.get_base_url(creator.domain)}{creator.service}/user/{creator.creator_id}/posts"
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def get_posts_page_url_wo_param():
|
|
53
|
+
domain = URL.DOMAINS_BASE_URL["coomer"]
|
|
54
|
+
return f"{domain}posts"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# core/config.py
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from .file_io import write_json
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Config:
|
|
12
|
+
# paths
|
|
13
|
+
APP_NAME = "cdl"
|
|
14
|
+
BASE_DIR = Path.home() / "Videos/rcdl"
|
|
15
|
+
|
|
16
|
+
BASE_DIR = Path(os.environ.get("RCDL_BASE_DIR", Path.home() / "Videos/rcdl"))
|
|
17
|
+
|
|
18
|
+
CACHE_DIR = BASE_DIR / ".cache"
|
|
19
|
+
DB_PATH = CACHE_DIR / "cdl.db"
|
|
20
|
+
LOG_FILE = CACHE_DIR / "cdl.log"
|
|
21
|
+
FUSE_CSV_FILE = CACHE_DIR / "cdl_fuse.csv"
|
|
22
|
+
SETTINGS_FILE = CACHE_DIR / "settings.json"
|
|
23
|
+
CREATORS_FILE = CACHE_DIR / "creators.json"
|
|
24
|
+
DISCOVER_DIR = CACHE_DIR / "discover"
|
|
25
|
+
|
|
26
|
+
# defaults
|
|
27
|
+
DEFAULT_SETTINGS = {
|
|
28
|
+
"work_folder": str(BASE_DIR),
|
|
29
|
+
"yt_dlp_args": "--external-downloader aria2c",
|
|
30
|
+
"preset": "veryfast",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# default creators
|
|
34
|
+
DEFAULT_CREATORS = [
|
|
35
|
+
{"creator_id": "boixd", "service": "onlyfans", "domain": "coomer"}
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
DEBUG = False
|
|
39
|
+
DRY_RUN = False
|
|
40
|
+
|
|
41
|
+
# api settings
|
|
42
|
+
POST_PER_PAGE = 50
|
|
43
|
+
DEFAULT_MAX_PAGE = 10
|
|
44
|
+
MAX_FAIL_COUNT = 7
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def ensure_dirs(cls):
|
|
48
|
+
cls.CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
cls.DISCOVER_DIR.mkdir(exist_ok=True)
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def ensure_files(cls):
|
|
53
|
+
files = [
|
|
54
|
+
cls.DB_PATH,
|
|
55
|
+
cls.FUSE_CSV_FILE,
|
|
56
|
+
cls.CREATORS_FILE,
|
|
57
|
+
]
|
|
58
|
+
for file in files:
|
|
59
|
+
if not file.exists():
|
|
60
|
+
file.touch()
|
|
61
|
+
logging.info("Created file %s", file)
|
|
62
|
+
if file == cls.CREATORS_FILE:
|
|
63
|
+
write_json(file, cls.DEFAULT_CREATORS)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def load_settings(cls):
|
|
67
|
+
if not cls.SETTINGS_FILE.exists():
|
|
68
|
+
with open(cls.SETTINGS_FILE, "w") as f:
|
|
69
|
+
json.dump(cls.DEFAULT_SETTINGS, f, indent=4)
|
|
70
|
+
return cls.DEFAULT_SETTINGS
|
|
71
|
+
|
|
72
|
+
with open(cls.SETTINGS_FILE, "r") as f:
|
|
73
|
+
return json.load(f)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def creator_folder(cls, creator_id: str) -> Path:
|
|
77
|
+
folder = cls.BASE_DIR / creator_id
|
|
78
|
+
folder.mkdir(exist_ok=True)
|
|
79
|
+
return folder
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def cache_file(cls, filename: str, ext: str = ".json") -> Path:
|
|
83
|
+
file_name = filename + ext
|
|
84
|
+
file = cls.CACHE_DIR / file_name
|
|
85
|
+
return file
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def set_debug(cls, debug: bool):
|
|
89
|
+
cls.DEBUG = debug
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def set_dry_run(cls, dry_run: bool):
|
|
93
|
+
cls.DRY_RUN = dry_run
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def setup_logging(log_file: Path, level: int = 0):
|
|
97
|
+
logger = logging.getLogger()
|
|
98
|
+
logger.setLevel(level)
|
|
99
|
+
logger.handlers.clear() # avoid double handlers if called multiple times
|
|
100
|
+
|
|
101
|
+
# file handler
|
|
102
|
+
file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a")
|
|
103
|
+
file_handler.setFormatter(
|
|
104
|
+
logging.Formatter(
|
|
105
|
+
"{asctime} - {levelname} - {message}",
|
|
106
|
+
style="{",
|
|
107
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
logger.addHandler(file_handler)
|
|
111
|
+
|
|
112
|
+
# console handler (stdout)
|
|
113
|
+
console_handler = logging.StreamHandler()
|
|
114
|
+
console_handler.setFormatter(logging.Formatter("{levelname}: {message}", style="{"))
|
|
115
|
+
logger.addHandler(console_handler)
|