simplesyntax 0.1.0__py3-none-any.whl
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.
- easytasks/__init__.py +3 -0
- easytasks/core.py +108 -0
- easytasks/downloader.py +179 -0
- easytasks/fallback.py +44 -0
- easytasks/ffmpeg.py +25 -0
- easytasks/image.py +128 -0
- easytasks/media.py +37 -0
- easytasks/metadata.py +30 -0
- easytasks/observer.py +184 -0
- easytasks/parser.py +147 -0
- easytasks/playlist.py +52 -0
- easytasks/utils.py +5 -0
- easytasks/video.py +13 -0
- simplesyntax-0.1.0.dist-info/METADATA +870 -0
- simplesyntax-0.1.0.dist-info/RECORD +17 -0
- simplesyntax-0.1.0.dist-info/WHEEL +5 -0
- simplesyntax-0.1.0.dist-info/top_level.txt +1 -0
easytasks/__init__.py
ADDED
easytasks/core.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
|
|
3
|
+
from .parser import parse_selector, parse_playlist
|
|
4
|
+
from .video import run_video
|
|
5
|
+
from .image import download_image
|
|
6
|
+
from .media import run_media
|
|
7
|
+
from .metadata import get_metadata
|
|
8
|
+
from .playlist import run_playlist
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_reizuki():
|
|
12
|
+
frame = inspect.currentframe()
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
caller = frame.f_back
|
|
16
|
+
|
|
17
|
+
while caller is not None:
|
|
18
|
+
if "ReiZyuki" in caller.f_locals:
|
|
19
|
+
return caller.f_locals["ReiZyuki"]
|
|
20
|
+
|
|
21
|
+
if "ReiZyuki" in caller.f_globals:
|
|
22
|
+
return caller.f_globals["ReiZyuki"]
|
|
23
|
+
|
|
24
|
+
caller = caller.f_back
|
|
25
|
+
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
finally:
|
|
29
|
+
del frame
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def rz(request, url):
|
|
33
|
+
if not isinstance(request, dict):
|
|
34
|
+
raise TypeError("rz() first argument must be a dictionary.")
|
|
35
|
+
|
|
36
|
+
if not isinstance(url, str):
|
|
37
|
+
raise TypeError("rz() url must be a string.")
|
|
38
|
+
|
|
39
|
+
cookie_paths = _get_reizuki()
|
|
40
|
+
|
|
41
|
+
if cookie_paths is not None:
|
|
42
|
+
if not isinstance(cookie_paths, (list, tuple)):
|
|
43
|
+
raise TypeError("ReiZyuki must be a list or tuple.")
|
|
44
|
+
|
|
45
|
+
cookie_paths = [
|
|
46
|
+
path
|
|
47
|
+
for path in cookie_paths
|
|
48
|
+
if isinstance(path, str) and path.strip()
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
results = {}
|
|
52
|
+
|
|
53
|
+
for selector, variable in request.items():
|
|
54
|
+
|
|
55
|
+
if not isinstance(variable, str):
|
|
56
|
+
raise TypeError(
|
|
57
|
+
"Right-side value must be a variable name string."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if selector == "playlist":
|
|
61
|
+
parsed = parse_playlist(variable)
|
|
62
|
+
|
|
63
|
+
results[variable] = run_playlist(
|
|
64
|
+
url,
|
|
65
|
+
parsed["quality"],
|
|
66
|
+
parsed["skip"],
|
|
67
|
+
parsed["count"],
|
|
68
|
+
cookie_paths
|
|
69
|
+
)
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
parsed = parse_selector(selector)
|
|
73
|
+
|
|
74
|
+
if parsed["type"] == "video":
|
|
75
|
+
result = run_video(
|
|
76
|
+
url,
|
|
77
|
+
parsed["quality"],
|
|
78
|
+
cookie_paths
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
elif parsed["type"] == "image":
|
|
82
|
+
result = download_image(
|
|
83
|
+
url,
|
|
84
|
+
cookie_paths
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
elif parsed["type"] == "media":
|
|
88
|
+
result = run_media(
|
|
89
|
+
url,
|
|
90
|
+
parsed["quality"],
|
|
91
|
+
cookie_paths
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
elif parsed["type"] == "metadata":
|
|
95
|
+
result = get_metadata(
|
|
96
|
+
url,
|
|
97
|
+
parsed["name"],
|
|
98
|
+
cookie_paths
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"Unsupported operation type: {parsed[type]}"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
results[variable] = result
|
|
107
|
+
|
|
108
|
+
return results
|
easytasks/downloader.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import yt_dlp
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
DOWNLOAD_DIR = "/storage/emulated/0/Download/ReiDownloader/"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
QUALITY_FORMATS = {
|
|
9
|
+
0: (
|
|
10
|
+
"bestaudio[acodec^=mp4a]/"
|
|
11
|
+
"bestaudio"
|
|
12
|
+
),
|
|
13
|
+
|
|
14
|
+
1: (
|
|
15
|
+
"bestvideo[height<=360][vcodec^=avc1]+"
|
|
16
|
+
"bestaudio[acodec^=mp4a]/"
|
|
17
|
+
"bestvideo[height<=360][vcodec^=avc1]+"
|
|
18
|
+
"bestaudio/"
|
|
19
|
+
"best"
|
|
20
|
+
),
|
|
21
|
+
|
|
22
|
+
2: (
|
|
23
|
+
"bestvideo[height<=480][vcodec^=avc1]+"
|
|
24
|
+
"bestaudio[acodec^=mp4a]/"
|
|
25
|
+
"bestvideo[height<=480][vcodec^=avc1]+"
|
|
26
|
+
"bestaudio/"
|
|
27
|
+
"best"
|
|
28
|
+
),
|
|
29
|
+
|
|
30
|
+
3: (
|
|
31
|
+
"bestvideo[height<=720][vcodec^=avc1]+"
|
|
32
|
+
"bestaudio[acodec^=mp4a]/"
|
|
33
|
+
"bestvideo[height<=720][vcodec^=avc1]+"
|
|
34
|
+
"bestaudio/"
|
|
35
|
+
"best"
|
|
36
|
+
),
|
|
37
|
+
|
|
38
|
+
4: (
|
|
39
|
+
"bestvideo[height<=1080][vcodec^=avc1]+"
|
|
40
|
+
"bestaudio[acodec^=mp4a]/"
|
|
41
|
+
"bestvideo[height<=1080][vcodec^=avc1]+"
|
|
42
|
+
"bestaudio/"
|
|
43
|
+
"best"
|
|
44
|
+
),
|
|
45
|
+
|
|
46
|
+
5: (
|
|
47
|
+
"bestvideo[height<=1440][vcodec^=avc1]+"
|
|
48
|
+
"bestaudio[acodec^=mp4a]/"
|
|
49
|
+
"bestvideo[height<=1440][vcodec^=avc1]+"
|
|
50
|
+
"bestaudio/"
|
|
51
|
+
"best"
|
|
52
|
+
),
|
|
53
|
+
|
|
54
|
+
6: (
|
|
55
|
+
"bestvideo[vcodec^=avc1]+"
|
|
56
|
+
"bestaudio[acodec^=mp4a]/"
|
|
57
|
+
"bestvideo[vcodec^=avc1]+"
|
|
58
|
+
"bestaudio/"
|
|
59
|
+
"best"
|
|
60
|
+
),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ensure_download_dir():
|
|
65
|
+
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
|
66
|
+
return DOWNLOAD_DIR
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _progress_hook(mode):
|
|
70
|
+
def hook(data):
|
|
71
|
+
status = data.get("status")
|
|
72
|
+
|
|
73
|
+
if status == "downloading":
|
|
74
|
+
total = (
|
|
75
|
+
data.get("total_bytes")
|
|
76
|
+
or data.get("total_bytes_estimate")
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
downloaded = data.get(
|
|
80
|
+
"downloaded_bytes",
|
|
81
|
+
0
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if total:
|
|
85
|
+
percent = (
|
|
86
|
+
downloaded * 100 / total
|
|
87
|
+
)
|
|
88
|
+
percent_text = (
|
|
89
|
+
f"{percent:.1f}%"
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
percent_text = "?"
|
|
93
|
+
|
|
94
|
+
speed = data.get("speed")
|
|
95
|
+
|
|
96
|
+
if speed:
|
|
97
|
+
if speed >= 1024 * 1024:
|
|
98
|
+
speed_text = (
|
|
99
|
+
f"{speed / (1024 * 1024):.2f}MiB/s"
|
|
100
|
+
)
|
|
101
|
+
elif speed >= 1024:
|
|
102
|
+
speed_text = (
|
|
103
|
+
f"{speed / 1024:.2f}KiB/s"
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
speed_text = (
|
|
107
|
+
f"{speed:.0f}B/s"
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
speed_text = "0B/s"
|
|
111
|
+
|
|
112
|
+
print(
|
|
113
|
+
f"\r[Download {percent_text}]"
|
|
114
|
+
f"[Speed {speed_text}]"
|
|
115
|
+
f"[Mode {mode}]",
|
|
116
|
+
end="",
|
|
117
|
+
flush=True
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
elif status == "finished":
|
|
121
|
+
print()
|
|
122
|
+
|
|
123
|
+
return hook
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def download(
|
|
127
|
+
url,
|
|
128
|
+
quality,
|
|
129
|
+
cookie_file=None,
|
|
130
|
+
mode="video"
|
|
131
|
+
):
|
|
132
|
+
if quality not in QUALITY_FORMATS:
|
|
133
|
+
raise ValueError(
|
|
134
|
+
f"Invalid quality: {quality}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
ensure_download_dir()
|
|
138
|
+
|
|
139
|
+
options = {
|
|
140
|
+
"format": QUALITY_FORMATS[quality],
|
|
141
|
+
"outtmpl": os.path.join(
|
|
142
|
+
DOWNLOAD_DIR,
|
|
143
|
+
"%(title)s.%(ext)s"
|
|
144
|
+
),
|
|
145
|
+
"merge_output_format": "mp4",
|
|
146
|
+
"noplaylist": True,
|
|
147
|
+
"quiet": True,
|
|
148
|
+
"no_warnings": True,
|
|
149
|
+
"progress_hooks": [
|
|
150
|
+
_progress_hook(mode)
|
|
151
|
+
],
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if cookie_file is not None:
|
|
155
|
+
options["cookiefile"] = cookie_file
|
|
156
|
+
|
|
157
|
+
with yt_dlp.YoutubeDL(options) as ydl:
|
|
158
|
+
return ydl.download([url])
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def extract_info(
|
|
162
|
+
url,
|
|
163
|
+
cookie_file=None
|
|
164
|
+
):
|
|
165
|
+
options = {
|
|
166
|
+
"quiet": True,
|
|
167
|
+
"no_warnings": True,
|
|
168
|
+
"skip_download": True,
|
|
169
|
+
"noplaylist": True,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if cookie_file is not None:
|
|
173
|
+
options["cookiefile"] = cookie_file
|
|
174
|
+
|
|
175
|
+
with yt_dlp.YoutubeDL(options) as ydl:
|
|
176
|
+
return ydl.extract_info(
|
|
177
|
+
url,
|
|
178
|
+
download=False
|
|
179
|
+
)
|
easytasks/fallback.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from .observer import get_cookie_scores, save_success
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def run_with_fallback(
|
|
5
|
+
operation,
|
|
6
|
+
cookie_paths=None,
|
|
7
|
+
url=None
|
|
8
|
+
):
|
|
9
|
+
try:
|
|
10
|
+
return operation(None)
|
|
11
|
+
|
|
12
|
+
except Exception as first_error:
|
|
13
|
+
if not cookie_paths:
|
|
14
|
+
raise first_error
|
|
15
|
+
|
|
16
|
+
if url is not None:
|
|
17
|
+
ranked = get_cookie_scores(
|
|
18
|
+
url,
|
|
19
|
+
cookie_paths
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
ordered_cookies = [
|
|
23
|
+
cookie_path
|
|
24
|
+
for _, cookie_path in ranked
|
|
25
|
+
]
|
|
26
|
+
else:
|
|
27
|
+
ordered_cookies = list(cookie_paths)
|
|
28
|
+
|
|
29
|
+
for cookie_file in ordered_cookies:
|
|
30
|
+
try:
|
|
31
|
+
result = operation(cookie_file)
|
|
32
|
+
|
|
33
|
+
if url is not None:
|
|
34
|
+
save_success(
|
|
35
|
+
url,
|
|
36
|
+
cookie_file
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return result
|
|
40
|
+
|
|
41
|
+
except Exception:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
raise first_error
|
easytasks/ffmpeg.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def merge_video_audio(video_path, audio_path, output_path):
|
|
6
|
+
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
7
|
+
|
|
8
|
+
command = [
|
|
9
|
+
"ffmpeg",
|
|
10
|
+
"-y",
|
|
11
|
+
"-i",
|
|
12
|
+
video_path,
|
|
13
|
+
"-i",
|
|
14
|
+
audio_path,
|
|
15
|
+
"-c",
|
|
16
|
+
"copy",
|
|
17
|
+
output_path,
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
subprocess.run(
|
|
21
|
+
command,
|
|
22
|
+
check=True
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
return output_path
|
easytasks/image.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import io
|
|
3
|
+
import contextlib
|
|
4
|
+
import gallery_dl
|
|
5
|
+
|
|
6
|
+
from .fallback import run_with_fallback
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
DOWNLOAD_DIR = "/storage/emulated/0/Download/ReiDownloader/"
|
|
10
|
+
|
|
11
|
+
IMAGE_EXTENSIONS = {
|
|
12
|
+
".jpg",
|
|
13
|
+
".jpeg",
|
|
14
|
+
".png",
|
|
15
|
+
".webp",
|
|
16
|
+
".gif",
|
|
17
|
+
".bmp",
|
|
18
|
+
".avif",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def ensure_download_dir():
|
|
23
|
+
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
|
24
|
+
return DOWNLOAD_DIR
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _image_files():
|
|
28
|
+
files = []
|
|
29
|
+
|
|
30
|
+
for root, _, names in os.walk(DOWNLOAD_DIR):
|
|
31
|
+
for name in names:
|
|
32
|
+
path = os.path.join(root, name)
|
|
33
|
+
|
|
34
|
+
if os.path.splitext(name)[1].lower() in IMAGE_EXTENSIONS:
|
|
35
|
+
files.append(path)
|
|
36
|
+
|
|
37
|
+
return files
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def download_image_once(url, cookie_file=None):
|
|
41
|
+
ensure_download_dir()
|
|
42
|
+
|
|
43
|
+
before = set(_image_files())
|
|
44
|
+
|
|
45
|
+
gallery_dl.config.clear()
|
|
46
|
+
|
|
47
|
+
gallery_dl.config.set(
|
|
48
|
+
("extractor",),
|
|
49
|
+
"base-directory",
|
|
50
|
+
DOWNLOAD_DIR
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
gallery_dl.config.set(
|
|
54
|
+
("extractor",),
|
|
55
|
+
"metadata",
|
|
56
|
+
False
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
if cookie_file is not None:
|
|
60
|
+
gallery_dl.config.set(
|
|
61
|
+
("extractor",),
|
|
62
|
+
"cookies",
|
|
63
|
+
cookie_file
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
output = io.StringIO()
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
with contextlib.redirect_stdout(output), contextlib.redirect_stderr(output):
|
|
70
|
+
job = gallery_dl.job.DownloadJob(url)
|
|
71
|
+
result = job.run()
|
|
72
|
+
|
|
73
|
+
except Exception as error:
|
|
74
|
+
message = output.getvalue().strip()
|
|
75
|
+
|
|
76
|
+
if message:
|
|
77
|
+
raise RuntimeError(
|
|
78
|
+
f"gallery-dl failed:\n{message}"
|
|
79
|
+
) from error
|
|
80
|
+
|
|
81
|
+
error_text = str(error).strip()
|
|
82
|
+
|
|
83
|
+
if error_text and error_text.lower() != "none":
|
|
84
|
+
raise RuntimeError(
|
|
85
|
+
f"gallery-dl failed: {error_text}"
|
|
86
|
+
) from error
|
|
87
|
+
|
|
88
|
+
raise RuntimeError(
|
|
89
|
+
"gallery-dl failed without an error message."
|
|
90
|
+
) from error
|
|
91
|
+
|
|
92
|
+
after = _image_files()
|
|
93
|
+
|
|
94
|
+
new_files = [
|
|
95
|
+
path
|
|
96
|
+
for path in after
|
|
97
|
+
if path not in before
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
if new_files:
|
|
101
|
+
new_files.sort(
|
|
102
|
+
key=lambda path: os.path.getmtime(path),
|
|
103
|
+
reverse=True
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return new_files[0]
|
|
107
|
+
|
|
108
|
+
message = output.getvalue().strip()
|
|
109
|
+
|
|
110
|
+
if message:
|
|
111
|
+
raise RuntimeError(
|
|
112
|
+
f"gallery-dl failed:\n{message}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
raise RuntimeError(
|
|
116
|
+
"gallery-dl completed without downloading an image."
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def download_image(url, cookie_paths=None):
|
|
121
|
+
return run_with_fallback(
|
|
122
|
+
lambda cookie_file: download_image_once(
|
|
123
|
+
url,
|
|
124
|
+
cookie_file
|
|
125
|
+
),
|
|
126
|
+
cookie_paths,
|
|
127
|
+
url=url
|
|
128
|
+
)
|
easytasks/media.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from .video import run_video
|
|
2
|
+
from .image import download_image
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def run_media(url, quality, cookie_paths=None):
|
|
6
|
+
video_error = None
|
|
7
|
+
|
|
8
|
+
try:
|
|
9
|
+
return run_video(
|
|
10
|
+
url,
|
|
11
|
+
quality,
|
|
12
|
+
cookie_paths
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
except Exception as error:
|
|
16
|
+
video_error = error
|
|
17
|
+
print("[MEDIA] VIDEO ERROR")
|
|
18
|
+
print(f"yt-dlp failed: {error}")
|
|
19
|
+
|
|
20
|
+
image_error = None
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
return download_image(
|
|
24
|
+
url,
|
|
25
|
+
cookie_paths
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
except Exception as error:
|
|
29
|
+
image_error = error
|
|
30
|
+
print("[MEDIA] IMAGE ERROR")
|
|
31
|
+
print(f"gallery-dl failed: {error}")
|
|
32
|
+
|
|
33
|
+
raise RuntimeError(
|
|
34
|
+
"[MEDIA] FAILED\n\n"
|
|
35
|
+
f"VIDEO ERROR:\n{video_error}\n\n"
|
|
36
|
+
f"IMAGE ERROR:\n{image_error}"
|
|
37
|
+
)
|
easytasks/metadata.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from .downloader import extract_info
|
|
2
|
+
from .fallback import run_with_fallback
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def get_metadata(url, name, cookie_paths=None):
|
|
6
|
+
info = run_with_fallback(
|
|
7
|
+
lambda cookie_file: extract_info(
|
|
8
|
+
url,
|
|
9
|
+
cookie_file
|
|
10
|
+
),
|
|
11
|
+
cookie_paths, url=url
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
values = {
|
|
15
|
+
"title": info.get("title"),
|
|
16
|
+
"creator": (
|
|
17
|
+
info.get("creator")
|
|
18
|
+
or info.get("uploader")
|
|
19
|
+
or info.get("artist")
|
|
20
|
+
or info.get("author")
|
|
21
|
+
),
|
|
22
|
+
"url": info.get("webpage_url") or url,
|
|
23
|
+
"views": info.get("view_count"),
|
|
24
|
+
"likes": info.get("like_count"),
|
|
25
|
+
"comments": info.get("comment_count"),
|
|
26
|
+
"duration": info.get("duration"),
|
|
27
|
+
"thumbnail": info.get("thumbnail"),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return values.get(name)
|