simplesoup 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.
simplesoup/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .core import SimpleSoup, rz
2
+
3
+ __all__ = ["SimpleSoup", "rz"]
@@ -0,0 +1,32 @@
1
+ import subprocess
2
+
3
+
4
+ class Converter:
5
+
6
+ @staticmethod
7
+ def convert(input_file, output_file):
8
+
9
+ command = [
10
+ "ffmpeg",
11
+ "-y",
12
+ "-i",
13
+ input_file,
14
+ "-vf",
15
+ "format=nv12",
16
+ "-c:v",
17
+ "h264_mediacodec",
18
+ "-g",
19
+ "120",
20
+ "-c:a",
21
+ "aac",
22
+ "-movflags",
23
+ "+faststart",
24
+ output_file
25
+ ]
26
+
27
+ subprocess.run(
28
+ command,
29
+ check=True
30
+ )
31
+
32
+ return output_file
simplesoup/core.py ADDED
@@ -0,0 +1,85 @@
1
+ from .parser import parse
2
+ from .downloader import Downloader
3
+ from .gallery import Gallery
4
+
5
+
6
+ # ============================================================
7
+ # SimpleSoup Configuration
8
+ # ============================================================
9
+
10
+ # Put your cookies.txt file path here.
11
+ # Example:
12
+ # COOKIES = "/storage/emulated/0/Download/instagram_cookies.txt"
13
+ #
14
+ # Disable cookies:
15
+ # COOKIES = None
16
+
17
+ COOKIES = "/storage/emulated/0/Download/reddit_cookies.txt"
18
+
19
+
20
+ class SimpleSoup:
21
+
22
+ def __init__(self):
23
+ self.downloader = Downloader()
24
+ self.gallery = Gallery()
25
+
26
+ def __call__(
27
+ self,
28
+ expression,
29
+ url=None,
30
+ progress_callback=None
31
+ ):
32
+ return self.run(
33
+ expression,
34
+ url,
35
+ progress_callback
36
+ )
37
+
38
+ def run(
39
+ self,
40
+ expression,
41
+ url=None,
42
+ progress_callback=None
43
+ ):
44
+
45
+ config = parse(expression)
46
+
47
+ if url is not None:
48
+ config["url"] = url
49
+
50
+ if not config["url"]:
51
+ raise ValueError("URL missing")
52
+
53
+ # ========================================================
54
+ # Gallery-dl engine
55
+ # ========================================================
56
+
57
+ for block in config["blocks"]:
58
+
59
+ if block["type"] == "gallery":
60
+
61
+ result = self.gallery.execute(
62
+ config["url"],
63
+ cookies=COOKIES
64
+ )
65
+
66
+ if block["variable"]:
67
+
68
+ return {
69
+ block["variable"]: result
70
+ }
71
+
72
+ return result
73
+
74
+ # ========================================================
75
+ # Normal yt-dlp engine
76
+ # ========================================================
77
+
78
+ return self.downloader.execute(
79
+ config,
80
+ cookies=COOKIES,
81
+ progress_callback=progress_callback
82
+ )
83
+
84
+
85
+ rz = SimpleSoup()
@@ -0,0 +1,555 @@
1
+ import os
2
+ import urllib.request
3
+ import yt_dlp
4
+
5
+ from .parser import STAGES
6
+
7
+
8
+ class Downloader:
9
+
10
+ def __init__(self):
11
+ self.path = "/storage/emulated/0/Download/ReiDownloader"
12
+ os.makedirs(self.path, exist_ok=True)
13
+
14
+ # ============================================================
15
+ # Helpers
16
+ # ============================================================
17
+
18
+ def _format_speed(self, speed):
19
+ if not speed:
20
+ return "0 KB/s"
21
+
22
+ if speed >= 1024 * 1024:
23
+ return f"{speed / (1024 * 1024):.1f} MB/s"
24
+
25
+ return f"{speed / 1024:.0f} KB/s"
26
+
27
+ def _progress_hook(
28
+ self,
29
+ data,
30
+ mode="Video",
31
+ progress_callback=None
32
+ ):
33
+
34
+ status = data.get("status")
35
+
36
+ if status == "downloading":
37
+
38
+ total = (
39
+ data.get("total_bytes")
40
+ or data.get("total_bytes_estimate")
41
+ )
42
+
43
+ downloaded = data.get("downloaded_bytes", 0)
44
+ speed = data.get("speed")
45
+
46
+ if total:
47
+ percent = (downloaded / total) * 100
48
+ else:
49
+ percent = 0
50
+
51
+ network = self._format_speed(speed)
52
+
53
+ print(
54
+ f"\r[Download {percent:.0f}%] "
55
+ f"[Network {network}] "
56
+ f"[Mode {mode}]",
57
+ end="",
58
+ flush=True
59
+ )
60
+
61
+ if progress_callback:
62
+ try:
63
+ progress_callback(
64
+ f"{percent:.0f}%",
65
+ network,
66
+ mode
67
+ )
68
+ except Exception:
69
+ pass
70
+
71
+ elif status == "finished":
72
+
73
+ network = self._format_speed(
74
+ data.get("speed")
75
+ )
76
+
77
+ print(
78
+ f"\r[Download 100%] "
79
+ f"[Network {network}] "
80
+ f"[Mode {mode}]"
81
+ )
82
+
83
+ if progress_callback:
84
+ try:
85
+ progress_callback(
86
+ "100%",
87
+ network,
88
+ mode
89
+ )
90
+ except Exception:
91
+ pass
92
+
93
+ def _get_format(self, quality):
94
+
95
+ if quality == "audio":
96
+
97
+ return (
98
+ "bestaudio[acodec^=mp4a]/"
99
+ "bestaudio"
100
+ )
101
+
102
+ elif quality == "best":
103
+
104
+ return (
105
+ "bestvideo[vcodec^=avc1]+"
106
+ "bestaudio[acodec^=mp4a]/"
107
+ "bestvideo[vcodec^=avc1]+"
108
+ "bestaudio/"
109
+ "best"
110
+ )
111
+
112
+ else:
113
+
114
+ height = quality.replace("p", "")
115
+
116
+ return (
117
+ f"bestvideo[height<={height}]"
118
+ f"[vcodec^=avc1]+"
119
+ f"bestaudio[acodec^=mp4a]/"
120
+ f"bestvideo[height<={height}]"
121
+ f"[vcodec^=avc1]+"
122
+ f"bestaudio/"
123
+ f"best"
124
+ )
125
+
126
+ # ============================================================
127
+ # Main Engine
128
+ # ============================================================
129
+
130
+ def execute(
131
+ self,
132
+ config,
133
+ cookies=None,
134
+ progress_callback=None
135
+ ):
136
+
137
+ url = config["url"]
138
+
139
+ # ---------------------------------
140
+ # Cookie validation
141
+ # ---------------------------------
142
+
143
+ if cookies:
144
+
145
+ if not os.path.isfile(cookies):
146
+ raise FileNotFoundError(
147
+ f"Cookies file not found: {cookies}"
148
+ )
149
+
150
+ # ---------------------------------
151
+ # Find playlist block
152
+ # ---------------------------------
153
+
154
+ playlist = None
155
+
156
+ for block in config["blocks"]:
157
+
158
+ if block["type"] == "playlist":
159
+ playlist = block
160
+ break
161
+
162
+ # ---------------------------------
163
+ # Find video block
164
+ # ---------------------------------
165
+
166
+ video = None
167
+
168
+ for block in config["blocks"]:
169
+
170
+ if block["type"] == "video":
171
+ video = block
172
+ break
173
+
174
+ # =========================================================
175
+ # PLAYLIST ENGINE
176
+ # =========================================================
177
+
178
+ if playlist is not None:
179
+
180
+ quality_stage = playlist["quality"]
181
+ skip = playlist["skip"]
182
+ count = playlist["count"]
183
+
184
+ quality = STAGES[quality_stage]
185
+
186
+ fmt = self._get_format(quality)
187
+
188
+ # -----------------------------------------------------
189
+ # Metadata + playlist title
190
+ # -----------------------------------------------------
191
+
192
+ metadata_options = {
193
+ "quiet": True,
194
+ "no_warnings": True,
195
+ "extract_flat": True,
196
+ "noplaylist": False
197
+ }
198
+
199
+ if cookies:
200
+ metadata_options["cookiefile"] = cookies
201
+
202
+ with yt_dlp.YoutubeDL(
203
+ metadata_options
204
+ ) as ydl:
205
+
206
+ playlist_info = ydl.extract_info(
207
+ url,
208
+ download=False
209
+ )
210
+
211
+ result = {}
212
+
213
+ # -----------------------------------------------------
214
+ # Metadata blocks
215
+ # -----------------------------------------------------
216
+
217
+ for block in config["blocks"]:
218
+
219
+ variable = block["variable"]
220
+
221
+ if not variable:
222
+ continue
223
+
224
+ block_type = block["type"]
225
+
226
+ if block_type == "title":
227
+
228
+ result[variable] = playlist_info.get(
229
+ "title"
230
+ )
231
+
232
+ elif block_type == "creator":
233
+
234
+ result[variable] = (
235
+ playlist_info.get("uploader")
236
+ or playlist_info.get("channel")
237
+ )
238
+
239
+ elif block_type == "url":
240
+
241
+ result[variable] = (
242
+ playlist_info.get("webpage_url")
243
+ or url
244
+ )
245
+
246
+ elif block_type == "views":
247
+
248
+ result[variable] = playlist_info.get(
249
+ "view_count"
250
+ )
251
+
252
+ elif block_type == "likes":
253
+
254
+ result[variable] = playlist_info.get(
255
+ "like_count"
256
+ )
257
+
258
+ elif block_type == "count":
259
+
260
+ result[variable] = playlist_info.get(
261
+ "comment_count"
262
+ )
263
+
264
+ elif block_type == "time":
265
+
266
+ result[variable] = playlist_info.get(
267
+ "duration"
268
+ )
269
+
270
+ elif block_type == "playlist_title":
271
+
272
+ result[variable] = playlist_info.get(
273
+ "title"
274
+ )
275
+
276
+ # -----------------------------------------------------
277
+ # Playlist range
278
+ # -----------------------------------------------------
279
+
280
+ start = skip + 1
281
+ end = skip + count
282
+
283
+ playlist_items = f"{start}-{end}"
284
+
285
+ output = os.path.join(
286
+ self.path,
287
+ "%(title)s.%(ext)s"
288
+ )
289
+
290
+ options = {
291
+ "format": fmt,
292
+ "outtmpl": output,
293
+ "noplaylist": False,
294
+ "playlist_items": playlist_items,
295
+ "merge_output_format": "mp4",
296
+ "quiet": True,
297
+ "no_warnings": True,
298
+ "progress_hooks": [
299
+ lambda data: self._progress_hook(
300
+ data,
301
+ "Playlist",
302
+ progress_callback
303
+ )
304
+ ]
305
+ }
306
+
307
+ if cookies:
308
+ options["cookiefile"] = cookies
309
+
310
+ print(
311
+ f"Playlist download: "
312
+ f"quality={quality}, "
313
+ f"skip={skip}, "
314
+ f"count={count}"
315
+ )
316
+
317
+ # -----------------------------------------------------
318
+ # Download playlist
319
+ # -----------------------------------------------------
320
+
321
+ downloaded_files = []
322
+
323
+ with yt_dlp.YoutubeDL(options) as ydl:
324
+
325
+ final_info = ydl.extract_info(
326
+ url,
327
+ download=True
328
+ )
329
+
330
+ entries = final_info.get(
331
+ "entries",
332
+ []
333
+ )
334
+
335
+ for entry in entries:
336
+
337
+ if not entry:
338
+ continue
339
+
340
+ filepath = ydl.prepare_filename(
341
+ entry
342
+ )
343
+
344
+ if not os.path.exists(filepath):
345
+
346
+ mp4_path = (
347
+ os.path.splitext(filepath)[0]
348
+ + ".mp4"
349
+ )
350
+
351
+ if os.path.exists(mp4_path):
352
+ filepath = mp4_path
353
+
354
+ if os.path.exists(filepath):
355
+
356
+ downloaded_files.append(
357
+ filepath
358
+ )
359
+
360
+ # -----------------------------------------------------
361
+ # Return playlist files
362
+ # -----------------------------------------------------
363
+
364
+ result["videos"] = downloaded_files
365
+
366
+ return result
367
+
368
+ # =========================================================
369
+ # NORMAL SINGLE-VIDEO ENGINE
370
+ # =========================================================
371
+
372
+ metadata_options = {
373
+ "quiet": True,
374
+ "no_warnings": True
375
+ }
376
+
377
+ if cookies:
378
+ metadata_options["cookiefile"] = cookies
379
+
380
+ with yt_dlp.YoutubeDL(
381
+ metadata_options
382
+ ) as ydl:
383
+
384
+ info = ydl.extract_info(
385
+ url,
386
+ download=False
387
+ )
388
+
389
+ result = {}
390
+
391
+ # ---------------------------------
392
+ # Metadata blocks
393
+ # ---------------------------------
394
+
395
+ for block in config["blocks"]:
396
+
397
+ variable = block["variable"]
398
+
399
+ if not variable:
400
+ continue
401
+
402
+ block_type = block["type"]
403
+
404
+ if block_type == "title":
405
+
406
+ result[variable] = info.get(
407
+ "title"
408
+ )
409
+
410
+ elif block_type == "creator":
411
+
412
+ result[variable] = (
413
+ info.get("uploader")
414
+ or info.get("channel")
415
+ )
416
+
417
+ elif block_type == "url":
418
+
419
+ result[variable] = (
420
+ info.get("webpage_url")
421
+ or url
422
+ )
423
+
424
+ elif block_type == "views":
425
+
426
+ result[variable] = info.get(
427
+ "view_count"
428
+ )
429
+
430
+ elif block_type == "likes":
431
+
432
+ result[variable] = info.get(
433
+ "like_count"
434
+ )
435
+
436
+ elif block_type == "count":
437
+
438
+ result[variable] = info.get(
439
+ "comment_count"
440
+ )
441
+
442
+ elif block_type == "time":
443
+
444
+ result[variable] = info.get(
445
+ "duration"
446
+ )
447
+
448
+ elif block_type == "thumbnail":
449
+
450
+ thumbnail_url = info.get(
451
+ "thumbnail"
452
+ )
453
+
454
+ if thumbnail_url:
455
+
456
+ title = (
457
+ info.get("title")
458
+ or "thumbnail"
459
+ )
460
+
461
+ thumbnail_path = os.path.join(
462
+ self.path,
463
+ f"{title}.jpg"
464
+ )
465
+
466
+ urllib.request.urlretrieve(
467
+ thumbnail_url,
468
+ thumbnail_path
469
+ )
470
+
471
+ result[variable] = thumbnail_path
472
+
473
+ else:
474
+
475
+ result[variable] = None
476
+
477
+ elif block_type == "formats":
478
+
479
+ result[variable] = info.get(
480
+ "formats",
481
+ []
482
+ )
483
+
484
+ # ---------------------------------
485
+ # Metadata-only request
486
+ # ---------------------------------
487
+
488
+ if video is None:
489
+ return result
490
+
491
+ # ---------------------------------
492
+ # Quality
493
+ # ---------------------------------
494
+
495
+ stage = video["stage"]
496
+
497
+ quality = STAGES[stage]
498
+
499
+ fmt = self._get_format(quality)
500
+
501
+ # ---------------------------------
502
+ # Download options
503
+ # ---------------------------------
504
+
505
+ output = os.path.join(
506
+ self.path,
507
+ "%(title)s.%(ext)s"
508
+ )
509
+
510
+ options = {
511
+ "format": fmt,
512
+ "outtmpl": output,
513
+ "noplaylist": True,
514
+ "merge_output_format": "mp4",
515
+ "quiet": True,
516
+ "progress_hooks": [
517
+ lambda data: self._progress_hook(
518
+ data,
519
+ "Video",
520
+ progress_callback
521
+ )
522
+ ]
523
+ }
524
+
525
+ if cookies:
526
+ options["cookiefile"] = cookies
527
+
528
+ print(
529
+ f"Downloading: {quality}"
530
+ )
531
+
532
+ with yt_dlp.YoutubeDL(options) as ydl:
533
+
534
+ final_info = ydl.extract_info(
535
+ url,
536
+ download=True
537
+ )
538
+
539
+ filepath = ydl.prepare_filename(
540
+ final_info
541
+ )
542
+
543
+ if not os.path.exists(filepath):
544
+
545
+ mp4_path = (
546
+ os.path.splitext(filepath)[0]
547
+ + ".mp4"
548
+ )
549
+
550
+ if os.path.exists(mp4_path):
551
+ filepath = mp4_path
552
+
553
+ result[video["variable"]] = filepath
554
+
555
+ return result
simplesoup/gallery.py ADDED
@@ -0,0 +1,44 @@
1
+ import os
2
+ import subprocess
3
+
4
+
5
+ class Gallery:
6
+
7
+ def __init__(self):
8
+ self.path = "/storage/emulated/0/Download/ReiDownloader"
9
+ os.makedirs(self.path, exist_ok=True)
10
+
11
+ def execute(self, url, cookies=None):
12
+
13
+ command = [
14
+ "gallery-dl",
15
+ "--directory",
16
+ self.path,
17
+ ]
18
+
19
+ # ---------------------------------
20
+ # Automatic cookies
21
+ # ---------------------------------
22
+
23
+ if cookies:
24
+
25
+ if not os.path.isfile(cookies):
26
+ raise FileNotFoundError(
27
+ f"Cookies file not found: {cookies}"
28
+ )
29
+
30
+ command.extend([
31
+ "--cookies",
32
+ cookies
33
+ ])
34
+
35
+ command.append(url)
36
+
37
+ print("Downloading gallery...")
38
+
39
+ subprocess.run(
40
+ command,
41
+ check=True
42
+ )
43
+
44
+ return self.path
simplesoup/parser.py ADDED
@@ -0,0 +1,218 @@
1
+ STAGES = {
2
+ 0: "audio",
3
+ 2: "360p",
4
+ 3: "480p",
5
+ 4: "720p",
6
+ 5: "1080p",
7
+ 6: "best",
8
+ }
9
+
10
+
11
+ def parse(expression):
12
+ """
13
+ Parse SimpleSoup configuration.
14
+
15
+ Supported format:
16
+
17
+ {
18
+ "video[4]": "video",
19
+ "playlist": "4:10:4",
20
+ "title": "title",
21
+ "creator": "creator",
22
+ "url": "url",
23
+ "views": "views",
24
+ "likes": "likes",
25
+ "count": "count",
26
+ "time": "duration",
27
+ "thumbnail": "thumbnail"
28
+ }
29
+
30
+ Playlist format:
31
+
32
+ "playlist": "quality:skip:count"
33
+
34
+ Example:
35
+
36
+ "playlist": "4:10:4"
37
+
38
+ Means:
39
+
40
+ 4 = 720p
41
+ 10 = skip first 10 videos
42
+ 4 = download next 4 videos
43
+
44
+ Old rz.(...) syntax is also supported.
45
+ """
46
+
47
+ # -----------------------------
48
+ # New dictionary-style syntax
49
+ # -----------------------------
50
+ if isinstance(expression, dict):
51
+
52
+ result = {
53
+ "url": None,
54
+ "blocks": []
55
+ }
56
+
57
+ for key, variable in expression.items():
58
+
59
+ key = str(key).strip()
60
+ variable = str(variable).strip()
61
+
62
+ # video[4]
63
+ if key.startswith("video[") and key.endswith("]"):
64
+
65
+ stage_text = key[6:-1]
66
+
67
+ try:
68
+ stage = int(stage_text)
69
+ except ValueError:
70
+ raise ValueError(
71
+ f"Invalid video stage: {stage_text}"
72
+ )
73
+
74
+ if stage not in STAGES:
75
+ raise ValueError(
76
+ f"Unknown video stage: {stage}"
77
+ )
78
+
79
+ result["blocks"].append({
80
+ "type": "video",
81
+ "stage": stage,
82
+ "variable": variable
83
+ })
84
+
85
+ continue
86
+
87
+ # playlist
88
+ if key == "playlist":
89
+
90
+ parts = variable.split(":")
91
+
92
+ if len(parts) != 3:
93
+ raise ValueError(
94
+ "Playlist syntax: playlist:quality:skip:count"
95
+ )
96
+
97
+ try:
98
+ quality = int(parts[0])
99
+ skip = int(parts[1])
100
+ count = int(parts[2])
101
+ except ValueError:
102
+ raise ValueError(
103
+ "Playlist values must be numbers"
104
+ )
105
+
106
+ if quality not in STAGES:
107
+ raise ValueError(
108
+ f"Unknown playlist quality: {quality}"
109
+ )
110
+
111
+ if skip < 0:
112
+ raise ValueError(
113
+ "Playlist skip cannot be negative"
114
+ )
115
+
116
+ if count <= 0:
117
+ raise ValueError(
118
+ "Playlist count must be greater than 0"
119
+ )
120
+
121
+ result["blocks"].append({
122
+ "type": "playlist",
123
+ "quality": quality,
124
+ "skip": skip,
125
+ "count": count,
126
+ "variable": None
127
+ })
128
+
129
+ continue
130
+
131
+ # normal block
132
+ result["blocks"].append({
133
+ "type": key,
134
+ "variable": variable
135
+ })
136
+
137
+ return result
138
+
139
+ # -----------------------------
140
+ # Old rz.(...) syntax
141
+ # -----------------------------
142
+ expression = expression.strip()
143
+
144
+ if not expression.startswith("rz.(") or not expression.endswith(")"):
145
+ raise ValueError("Invalid SimpleSoup syntax")
146
+
147
+ body = expression[4:-1].strip()
148
+
149
+ parts = [
150
+ p.strip()
151
+ for p in body.split(";")
152
+ if p.strip()
153
+ ]
154
+
155
+ result = {
156
+ "url": None,
157
+ "blocks": []
158
+ }
159
+
160
+ for part in parts:
161
+
162
+ # URL
163
+ if part.startswith("{") and part.endswith("}"):
164
+ result["url"] = part[1:-1].strip()
165
+ continue
166
+
167
+ # video[4]:variable
168
+ if part.startswith("video["):
169
+
170
+ if "]:" not in part:
171
+ raise ValueError(
172
+ "Video syntax: video[stage]:variable"
173
+ )
174
+
175
+ stage_text, variable = part.split("]:", 1)
176
+
177
+ try:
178
+ stage = int(stage_text[6:])
179
+ except ValueError:
180
+ raise ValueError(
181
+ "Invalid video stage"
182
+ )
183
+
184
+ if stage not in STAGES:
185
+ raise ValueError(
186
+ f"Unknown video stage: {stage}"
187
+ )
188
+
189
+ result["blocks"].append({
190
+ "type": "video",
191
+ "stage": stage,
192
+ "variable": variable.strip()
193
+ })
194
+
195
+ continue
196
+
197
+ # name:variable
198
+ if ":" in part:
199
+
200
+ name, variable = part.split(":", 1)
201
+
202
+ result["blocks"].append({
203
+ "type": name.strip(),
204
+ "variable": variable.strip()
205
+ })
206
+
207
+ continue
208
+
209
+ # simple block
210
+ result["blocks"].append({
211
+ "type": part,
212
+ "variable": None
213
+ })
214
+
215
+ if not result["url"]:
216
+ raise ValueError("URL missing")
217
+
218
+ return result
@@ -0,0 +1,315 @@
1
+ Metadata-Version: 2.4
2
+ Name: simplesoup
3
+ Version: 0.1.0
4
+ Summary: Simple CSS-style yt-dlp and FFmpeg wrapper
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: yt-dlp
8
+ Provides-Extra: gallery
9
+ Requires-Dist: gallery-dl; extra == "gallery"
10
+
11
+ # SimpleSoup
12
+
13
+ A simple Python wrapper for yt-dlp and FFmpeg.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install simplesoup
19
+
20
+ For gallery support:
21
+
22
+ pip install "simplesoup[gallery]"
23
+
24
+ Import
25
+
26
+ from simplesoup import rz
27
+
28
+ Basic Syntax
29
+
30
+ result = rz({
31
+ "WHAT_TO_REQUEST": "VARIABLE_NAME"
32
+ }, url)
33
+
34
+ WHAT_TO_REQUEST batata hai kya chahiye, aur VARIABLE_NAME batata hai result mein us value ko kis naam se save karna hai.
35
+
36
+ Example:
37
+
38
+ result = rz({
39
+ "title": "title"
40
+ }, url)
41
+
42
+ print(result["title"])
43
+
44
+ Video Download
45
+
46
+ result = rz({
47
+ "video[4]": "video"
48
+ }, url)
49
+
50
+ print(result["video"])
51
+
52
+ video[4] = 720p.
53
+
54
+ Video Quality
55
+
56
+ video[0] → audio
57
+ video[2] → 360p
58
+ video[3] → 480p
59
+ video[4] → 720p
60
+ video[5] → 1080p
61
+ video[6] → best available
62
+
63
+ Example:
64
+
65
+ result = rz({
66
+ "video[5]": "video"
67
+ }, url)
68
+
69
+ Multiple Values At Once
70
+
71
+ Ek hi request mein video aur metadata dono le sakte ho:
72
+
73
+ result = rz({
74
+ "video[4]": "video",
75
+ "title": "title",
76
+ "creator": "creator",
77
+ "url": "video_url",
78
+ "views": "views",
79
+ "likes": "likes",
80
+ "count": "comments",
81
+ "time": "duration",
82
+ "thumbnail": "thumbnail"
83
+ }, url)
84
+
85
+ Values access karne ke liye:
86
+
87
+ print(result["video"])
88
+ print(result["title"])
89
+ print(result["creator"])
90
+ print(result["video_url"])
91
+ print(result["views"])
92
+ print(result["likes"])
93
+ print(result["comments"])
94
+ print(result["duration"])
95
+ print(result["thumbnail"])
96
+
97
+ Order matter nahi karta.
98
+
99
+ Metadata
100
+
101
+ Available metadata:
102
+
103
+ title → video title
104
+ creator → uploader/channel
105
+ url → video URL
106
+ views → view count
107
+ likes → like count
108
+ count → comment count
109
+ time → duration in seconds
110
+ thumbnail → downloaded thumbnail path
111
+ formats → available yt-dlp formats
112
+
113
+ Metadata Only
114
+
115
+ Agar sirf metadata chahiye aur video download nahi karna:
116
+
117
+ result = rz({
118
+ "title": "title",
119
+ "creator": "creator",
120
+ "views": "views",
121
+ "likes": "likes",
122
+ "count": "comments",
123
+ "time": "duration"
124
+ }, url)
125
+
126
+ video[...] block nahi hone par video download nahi hota.
127
+
128
+ Thumbnail
129
+
130
+ result = rz({
131
+ "thumbnail": "thumbnail"
132
+ }, url)
133
+
134
+ print(result["thumbnail"])
135
+
136
+ Thumbnail actual .jpg file ke roop mein download hota hai.
137
+
138
+ Playlist
139
+
140
+ Playlist syntax:
141
+
142
+ result = rz({
143
+ "playlist": "QUALITY:SKIP:COUNT"
144
+ }, playlist_url)
145
+
146
+ Example:
147
+
148
+ result = rz({
149
+ "playlist": "4:20:3"
150
+ }, playlist_url)
151
+
152
+ Meaning:
153
+
154
+ 4 → 720p
155
+ 20 → first 20 videos skip
156
+ 3 → next 3 videos download
157
+
158
+ So:
159
+
160
+ Video 1-20 → skip
161
+ Video 21 → download
162
+ Video 22 → download
163
+ Video 23 → download
164
+
165
+ Downloaded videos:
166
+
167
+ for video in result["videos"]:
168
+ print(video)
169
+
170
+ Playlist + Metadata
171
+
172
+ result = rz({
173
+ "playlist": "4:20:3",
174
+ "title": "playlist_title"
175
+ }, playlist_url)
176
+
177
+ print(result["playlist_title"])
178
+ print(result["videos"])
179
+
180
+ Gallery
181
+
182
+ Gallery downloading ke liye:
183
+
184
+ result = rz({
185
+ "gallery": "images"
186
+ }, gallery_url)
187
+
188
+ print(result)
189
+
190
+ Gallery support ke liye gallery-dl install hona chahiye:
191
+
192
+ pip install "simplesoup[gallery]"
193
+
194
+ Cookies
195
+
196
+ Cookies simplesoup/core.py mein configure kiye jaate hain:
197
+
198
+ COOKIES = "/storage/emulated/0/Download/cookies.txt"
199
+
200
+ Example:
201
+
202
+ COOKIES = "/storage/emulated/0/Download/reddit_cookies.txt"
203
+
204
+ Cookies disable karne ke liye:
205
+
206
+ COOKIES = None
207
+
208
+ SimpleSoup configured cookie file ko use karta hai.
209
+
210
+ Progress Callback
211
+
212
+ Optional progress callback:
213
+
214
+ def progress(percent, speed, mode):
215
+ print(
216
+ f"[Download {percent}] "
217
+ f"[Network {speed}] "
218
+ f"[Mode {mode}]"
219
+ )
220
+
221
+ result = rz(
222
+ {
223
+ "video[4]": "video"
224
+ },
225
+ url,
226
+ progress_callback=progress
227
+ )
228
+
229
+ Callback ko ye 3 values milti hain:
230
+
231
+ percent → download percentage
232
+ speed → network speed
233
+ mode → Video / Playlist
234
+
235
+ Complete Example
236
+
237
+ from simplesoup import rz
238
+
239
+ url = "VIDEO_URL"
240
+
241
+ result = rz({
242
+ "video[4]": "video",
243
+ "title": "title",
244
+ "creator": "creator",
245
+ "url": "video_url",
246
+ "views": "views",
247
+ "likes": "likes",
248
+ "count": "comments",
249
+ "time": "duration",
250
+ "thumbnail": "thumbnail"
251
+ }, url)
252
+
253
+ print("Video:", result["video"])
254
+ print("Title:", result["title"])
255
+ print("Creator:", result["creator"])
256
+ print("URL:", result["video_url"])
257
+ print("Views:", result["views"])
258
+ print("Likes:", result["likes"])
259
+ print("Comments:", result["comments"])
260
+ print("Duration:", result["duration"])
261
+ print("Thumbnail:", result["thumbnail"])
262
+
263
+ Default Download Location
264
+
265
+ /storage/emulated/0/Download/ReiDownloader/
266
+
267
+ API Syntax Summary
268
+
269
+ Single video:
270
+
271
+ rz({
272
+ "video[QUALITY]": "variable"
273
+ }, url)
274
+
275
+ Metadata:
276
+
277
+ rz({
278
+ "title": "variable",
279
+ "creator": "variable",
280
+ "url": "variable",
281
+ "views": "variable",
282
+ "likes": "variable",
283
+ "count": "variable",
284
+ "time": "variable",
285
+ "thumbnail": "variable"
286
+ }, url)
287
+
288
+ Video + metadata:
289
+
290
+ rz({
291
+ "video[4]": "video",
292
+ "title": "title",
293
+ "creator": "creator",
294
+ "views": "views",
295
+ "likes": "likes",
296
+ "count": "comments",
297
+ "time": "duration",
298
+ "thumbnail": "thumbnail"
299
+ }, url)
300
+
301
+ Playlist:
302
+
303
+ rz({
304
+ "playlist": "QUALITY:SKIP:COUNT"
305
+ }, playlist_url)
306
+
307
+ Gallery:
308
+
309
+ rz({
310
+ "gallery": "variable"
311
+ }, gallery_url)
312
+
313
+ Version
314
+
315
+ SimpleSoup 0.1.0
@@ -0,0 +1,10 @@
1
+ simplesoup/__init__.py,sha256=sqmWkYkrTrw-fNgy-SJU0nLd2Gwnt8WJITEAfucpARE,65
2
+ simplesoup/converter.py,sha256=x8Rhipe1XYvrsVjnxsObKrGAB-lQwiNFohVMP3MQXYQ,566
3
+ simplesoup/core.py,sha256=gjHagBqNHnI2-ur5wBSKFSKtq39PShHSk7mxp_6pWag,1958
4
+ simplesoup/downloader.py,sha256=LECSYyzWnhvymfHH-HzTrzwjhbJZIXCmtoGzSbgfH9Q,14052
5
+ simplesoup/gallery.py,sha256=9S7DT5AD1maEXFye3H5Q9fBbZU-0JjD15amQ39_vgNE,903
6
+ simplesoup/parser.py,sha256=AJzp9Lkjgu7arCk-MPTu_yK6xvF6YgRs8QG8gPwtM_g,5216
7
+ simplesoup-0.1.0.dist-info/METADATA,sha256=deC95gVPhsOnDmJcr0E19l5gjmffaFLBbzphK1qGc84,5283
8
+ simplesoup-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
9
+ simplesoup-0.1.0.dist-info/top_level.txt,sha256=jtEKGzuqamPe-w0c81H0XtiE-9xk27twJdIwN43L_tk,11
10
+ simplesoup-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ simplesoup