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 ADDED
@@ -0,0 +1,3 @@
1
+ from .core import rz
2
+
3
+ __all__ = ["rz"]
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
@@ -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)