QTube 2.3.0__py3-none-any.whl → 2.4.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.
QTube/scripts/qtube.py CHANGED
@@ -127,7 +127,7 @@ def main():
127
127
  ["internal"],
128
128
  )
129
129
  QTube.utils.helpers.print2(
130
- f"The following verbosity options are enabled: {verb}.\n",
130
+ f"The following verbosity options are enabled: {', '.join(verb)}.\n",
131
131
  fancy,
132
132
  "info",
133
133
  ["internal"],
@@ -383,6 +383,8 @@ def main():
383
383
  vid_dicts = partial["items"]
384
384
  responses["items"].extend(vid_dicts)
385
385
 
386
+ video_IDs_lst = [vid["id"] for vid in responses["items"]]
387
+
386
388
  # Titles retrieving
387
389
  titles = QTube.utils.youtube.videos.get_titles(response=responses)
388
390
 
@@ -390,7 +392,9 @@ def main():
390
392
  durations = QTube.utils.youtube.videos.get_durations(response=responses)
391
393
 
392
394
  # Shorts retrieving
393
- shorts = QTube.utils.youtube.videos.is_short(response=responses)
395
+ shorts = QTube.utils.youtube.videos.is_short(
396
+ response=responses, video_IDs=video_IDs_lst
397
+ )
394
398
 
395
399
  # Languages retrieving
396
400
  languages = QTube.utils.youtube.videos.get_languages(response=responses)
@@ -429,6 +433,14 @@ def main():
429
433
  comment_counts, view_counts
430
434
  )
431
435
 
436
+ # Paid promotions retrieving
437
+ paid_advertising = QTube.utils.youtube.videos.has_paid_advertising(
438
+ response=responses
439
+ )
440
+
441
+ # Made for Kids retrieving
442
+ made_for_kids = QTube.utils.youtube.videos.is_made_for_kids(response=responses)
443
+
432
444
  # Resolutions retrieving (does not use YT API)
433
445
  lowest_resolution = user_params_dict.get("lowest_resolution")
434
446
  if lowest_resolution is not None:
@@ -503,6 +515,12 @@ def main():
503
515
  # Comments/views
504
516
  vid_info.update({"comments_to_views_ratio": comments_to_views_ratio[index]})
505
517
 
518
+ # Paid promotions
519
+ vid_info.update({"has_paid_ad": paid_advertising[index]})
520
+
521
+ # Made for kids
522
+ vid_info.update({"made_for_kids": made_for_kids[index]})
523
+
506
524
  # Resolutions
507
525
  if lowest_resolution is not None:
508
526
  vid_info.update({"resolutions": resolutions[index]})
@@ -661,6 +679,22 @@ def main():
661
679
  if vid_ID in new_vid_IDs:
662
680
  videos[vid_ID].update({"to add": False})
663
681
 
682
+ # Paid advertisement filtering
683
+ if user_params_dict["allow_paid_promotions"] is False:
684
+ for vid_ID, vid_info in videos.items():
685
+ if vid_info["to add"] is False:
686
+ continue
687
+ elif vid_info["has_paid_ad"]:
688
+ vid_info.update({"to add": False})
689
+
690
+ # Made for kids filtering
691
+ if user_params_dict["only_made_for_kids"] is True:
692
+ for vid_ID, vid_info in videos.items():
693
+ if vid_info["to add"] is False:
694
+ continue
695
+ elif not vid_info["made_for_kids"]:
696
+ vid_info.update({"to add": False})
697
+
664
698
  # Language filtering
665
699
  if preferred_languages is not None:
666
700
  preferred_languages.append("unknown")
QTube/utils/checks.py CHANGED
@@ -349,6 +349,8 @@ def check_user_params(params_dict: dict) -> bool:
349
349
  # Comments/views ratio
350
350
  isinstance(params_dict.get("comments_to_views_ratio"), (int, float))
351
351
  and 0 <= params_dict.get("comments_to_views_ratio") <= 1,
352
+ # Paid promotions
353
+ isinstance(params_dict.get("allow_paid_promotions"), bool),
352
354
  ]
353
355
 
354
356
  ok = all(checks)
@@ -419,7 +421,7 @@ def check_version() -> tuple[str]:
419
421
  return version, latest_release
420
422
 
421
423
 
422
- def compare_software_versions(version1, version2):
424
+ def compare_software_versions(version1, version2) -> str:
423
425
  """Compare two software versions, using version2 as the reference against which version1 is compared.
424
426
 
425
427
  Args:
@@ -455,3 +457,18 @@ def compare_software_versions(version1, version2):
455
457
  return "older"
456
458
 
457
459
  return "same"
460
+
461
+
462
+ def check_URL_redirect(url: str, redirect_code: int) -> bool:
463
+ """Checks if the provided URL redirects to another page.
464
+
465
+ Args:
466
+ url (str): URL to check for redirection
467
+ redirect_code (int): Status code to check for (3xx)
468
+
469
+ Returns:
470
+ (bool): True if the URL redirects to another page with the correct status code, False otherwise.
471
+ """
472
+ r = requests.get(url)
473
+
474
+ return any([resp.status_code == redirect_code for resp in r.history])
QTube/utils/helpers.py CHANGED
@@ -65,7 +65,7 @@ def handle_http_errors(verbosity: list[str], fancy, func, *args, **kwargs):
65
65
  sys.exit() # Exit the program after 5 retries
66
66
 
67
67
 
68
- def fancify_text(text, color, style, emoji):
68
+ def fancify_text(text, color, style, emoji) -> str:
69
69
  """Modifies the color and content of a string.
70
70
 
71
71
  Args:
@@ -161,7 +161,7 @@ def merge_dicts(list_of_dicts: list) -> dict:
161
161
  return {key: value for d in list_of_dicts for key, value in d.items()}
162
162
 
163
163
 
164
- def strip_emojis(text):
164
+ def strip_emojis(text) -> str:
165
165
  """Strips emojis from a string.
166
166
 
167
167
  Args:
@@ -190,7 +190,7 @@ def strip_emojis(text):
190
190
  return remove_multiple_spaces(clean_text)
191
191
 
192
192
 
193
- def strip_punctuation(text):
193
+ def strip_punctuation(text) -> str:
194
194
  """Strips punctuation from a string (all of these characters: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~).
195
195
 
196
196
  Args:
@@ -204,7 +204,7 @@ def strip_punctuation(text):
204
204
  return remove_multiple_spaces(clean_text)
205
205
 
206
206
 
207
- def make_lowercase(text):
207
+ def make_lowercase(text) -> str:
208
208
  """Converts all uppercase letters to lowercase from a string.
209
209
 
210
210
  Args:
@@ -217,7 +217,7 @@ def make_lowercase(text):
217
217
  return text.lower()
218
218
 
219
219
 
220
- def remove_multiple_spaces(text):
220
+ def remove_multiple_spaces(text) -> str:
221
221
  """Removes multiple spaces in a string and replaces them with a single space.
222
222
 
223
223
  Args:
@@ -229,7 +229,7 @@ def remove_multiple_spaces(text):
229
229
  return re.sub(" +", " ", text)
230
230
 
231
231
 
232
- def divide_lists(list1, list2, percentage: False):
232
+ def divide_lists(list1, list2, percentage: False) -> list[int | float]:
233
233
  """Divides two python lists element-wise.
234
234
 
235
235
  Args:
QTube/utils/parsing.py CHANGED
@@ -3,7 +3,7 @@ import json
3
3
  import re
4
4
 
5
5
 
6
- def parse_arguments():
6
+ def parse_arguments() -> dict:
7
7
  """Parses command line arguments.
8
8
 
9
9
  Args:
@@ -273,6 +273,20 @@ def parse_arguments():
273
273
  help="Determines whether to add shorts. Default: True",
274
274
  )
275
275
 
276
+ parser.add_argument(
277
+ "-app",
278
+ "--allow_paid_promotions",
279
+ action="store_false",
280
+ help="Allow videos containing paid advertising. Default: True",
281
+ )
282
+
283
+ parser.add_argument(
284
+ "-mfk",
285
+ "--only_made_for_kids",
286
+ action="store_true",
287
+ help="Determines whether to only add videos that are made for kids, based on Youtube and FTC guidelines. Default: False",
288
+ )
289
+
276
290
  parser.add_argument(
277
291
  "-kd",
278
292
  "--keep_duplicates",
@@ -308,7 +322,7 @@ def parse_arguments():
308
322
  return vars(parser.parse_args())
309
323
 
310
324
 
311
- def format_arguments(args_dict):
325
+ def format_arguments(args_dict) -> dict:
312
326
  """Formats the parsed command line arguments (written with the help of AI, regex is witchcraft to me).
313
327
 
314
328
  Args:
@@ -346,7 +360,7 @@ def format_arguments(args_dict):
346
360
  return args_dict
347
361
 
348
362
 
349
- def format_arguments_legacy(args_dict):
363
+ def format_arguments_legacy(args_dict) -> dict:
350
364
  """Formats the parsed command line arguments (legacy function).
351
365
 
352
366
  Args:
@@ -36,7 +36,7 @@ def get_subscriptions(youtube, next_page_token=None) -> dict:
36
36
  return channels
37
37
 
38
38
 
39
- def get_channel_info(youtube, handle: str):
39
+ def get_channel_info(youtube, handle: str) -> dict:
40
40
  """Retrieves basic information about a YT channel.
41
41
 
42
42
  Args:
@@ -49,9 +49,7 @@ def get_channel_info(youtube, handle: str):
49
49
  channel = {}
50
50
 
51
51
  response = (
52
- youtube.channels()
53
- .list(part="snippet", forHandle=handle)
54
- .execute(num_retries=5)
52
+ youtube.channels().list(part="snippet", forHandle=handle).execute(num_retries=5)
55
53
  )
56
54
 
57
55
  if "items" in response.keys():
@@ -66,7 +66,7 @@ def get_playlist_content(youtube, playlist_ID: str) -> list[str]:
66
66
  return videos_IDs
67
67
 
68
68
 
69
- def get_playlists_titles(youtube=None, playlist_IDs: list[str] = None):
69
+ def get_playlists_titles(youtube=None, playlist_IDs: list[str] = None) -> list[str]:
70
70
  """Retrieves the titles of a list of YT playlists.
71
71
 
72
72
  Args:
@@ -88,7 +88,9 @@ def get_playlists_titles(youtube=None, playlist_IDs: list[str] = None):
88
88
  return titles
89
89
 
90
90
 
91
- def get_playlists_video_counts(youtube=None, playlist_IDs: list[str] = None):
91
+ def get_playlists_video_counts(
92
+ youtube=None, playlist_IDs: list[str] = None
93
+ ) -> list[int]:
92
94
  """Retrieves the number of videos of a list of YT playlists.
93
95
 
94
96
  Args:
@@ -2,7 +2,7 @@ import isodate
2
2
 
3
3
  from pytube import YouTube
4
4
 
5
- from QTube.utils import helpers
5
+ from QTube.utils import helpers, checks
6
6
 
7
7
 
8
8
  def make_video_requests(youtube, video_IDs: list[str]) -> dict:
@@ -18,7 +18,10 @@ def make_video_requests(youtube, video_IDs: list[str]) -> dict:
18
18
  video_IDs_str = ",".join(video_IDs)
19
19
  response = (
20
20
  youtube.videos()
21
- .list(part="snippet,contentDetails,statistics", id=video_IDs_str)
21
+ .list(
22
+ part="snippet,contentDetails,statistics,paidProductPlacementDetails,status",
23
+ id=video_IDs_str,
24
+ )
22
25
  .execute(num_retries=5)
23
26
  )
24
27
  return response
@@ -345,7 +348,7 @@ def get_view_counts(
345
348
  response: dict = None,
346
349
  video_IDs: list[str] = None,
347
350
  use_API: bool = False,
348
- ) -> list[str]:
351
+ ) -> list[int]:
349
352
  """Retrieves the number of views of a list of YT videos.
350
353
 
351
354
  Args:
@@ -375,7 +378,7 @@ def get_like_counts(
375
378
  response: dict = None,
376
379
  video_IDs: list[str] = None,
377
380
  use_API: bool = False,
378
- ) -> list[str]:
381
+ ) -> list[int]:
379
382
  """Retrieves the number of likes of a list of YT videos.
380
383
 
381
384
  Args:
@@ -405,7 +408,7 @@ def get_comment_counts(
405
408
  response: dict = None,
406
409
  video_IDs: list[str] = None,
407
410
  use_API: bool = False,
408
- ) -> list[str]:
411
+ ) -> list[int]:
409
412
  """Retrieves the number of comments of a list of YT videos.
410
413
 
411
414
  Args:
@@ -439,7 +442,7 @@ def get_likes_to_views_ratio(
439
442
  response: dict = None,
440
443
  video_IDs: list[str] = None,
441
444
  use_API: bool = False,
442
- ) -> list[str]:
445
+ ) -> list[int | float]:
443
446
  """Retrieves the likes to views ratio of a list of YT videos.
444
447
 
445
448
  Args:
@@ -469,7 +472,7 @@ def get_comments_to_views_ratio(
469
472
  response: dict = None,
470
473
  video_IDs: list[str] = None,
471
474
  use_API: bool = False,
472
- ) -> list[str]:
475
+ ) -> list[int | float]:
473
476
  """Retrieves the comments to views ratio of a list of YT videos.
474
477
 
475
478
  Args:
@@ -526,8 +529,8 @@ def is_short(
526
529
  response: dict = None,
527
530
  video_IDs: list[str] = None,
528
531
  use_API: bool = False,
529
- ) -> list[str]:
530
- """Determines if videos are a short or not by putting a threshold on video duration.
532
+ ) -> list[bool]:
533
+ """Determines if videos are a short or not by putting a threshold on video duration and checking for a redirection at the youtube.com/shorts/*vid_ID* URL.
531
534
 
532
535
  Args:
533
536
  youtube (Resource): YT API resource.
@@ -536,13 +539,21 @@ def is_short(
536
539
  use_API (bool): Determines if a new API request is made or if the response dictionary is used.
537
540
 
538
541
  Returns:
539
- is_short (list[bool]): True if the video is shorter than 65 seconds, False otherwise.
542
+ is_a_short (list[bool]): True if the video is shorter than 181 seconds and there is no URL redirection, False otherwise.
540
543
  """
541
544
  durations = get_durations(youtube, response, video_IDs, use_API=use_API)
542
545
 
543
- is_short = [True if length <= 65.0 else False for length in durations]
546
+ is_short_vid = [
547
+ True if length <= 181 else False for length in durations
548
+ ] # Shorts cannot last over 3 minutes.
544
549
 
545
- return is_short
550
+ is_not_redirected = [
551
+ not checks.check_URL_redirect("https://www.youtube.com/shorts/" + vid_ID, 303)
552
+ for vid_ID in video_IDs
553
+ ] # Shorts do not trigger a redirection.
554
+
555
+ is_a_short = is_short_vid and is_not_redirected
556
+ return is_a_short
546
557
 
547
558
 
548
559
  def is_live(
@@ -550,7 +561,7 @@ def is_live(
550
561
  response: dict = None,
551
562
  video_IDs: list[str] = None,
552
563
  use_API: bool = False,
553
- ):
564
+ ) -> list[str]:
554
565
  """Retrieves the live status of YT videos.
555
566
 
556
567
  Args:
@@ -575,3 +586,64 @@ def is_live(
575
586
  ]
576
587
 
577
588
  return live_statuses
589
+
590
+
591
+ def has_paid_advertising(
592
+ youtube=None,
593
+ response: dict = None,
594
+ video_IDs: list[str] = None,
595
+ use_API: bool = False,
596
+ )-> list[bool]:
597
+ """Determines if a video contains paid advertising.
598
+
599
+ Args:
600
+ youtube (Resource): YT API resource.
601
+ response (dict[dict]): YT API response from the make_video_request function.
602
+ video_IDs (list[str]): List of video IDs.
603
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
604
+
605
+ Returns:
606
+ (list[bool]): True if the video contains paid advertising, False otherwise.
607
+ """
608
+
609
+ if use_API:
610
+ video_IDs_str = ",".join(video_IDs)
611
+ response = (
612
+ youtube.videos()
613
+ .list(part="paidProductPlacementDetails", id=video_IDs_str)
614
+ .execute(num_retries=5)
615
+ )
616
+
617
+ return [
618
+ vid["paidProductPlacementDetails"]["hasPaidProductPlacement"]
619
+ for vid in response["items"]
620
+ ]
621
+
622
+
623
+ def is_made_for_kids(
624
+ youtube=None,
625
+ response: dict = None,
626
+ video_IDs: list[str] = None,
627
+ use_API: bool = False,
628
+ )-> list[bool]:
629
+ """Determines if a video is appropriate for children (based on YT's guidelines).
630
+
631
+ Args:
632
+ youtube (Resource): YT API resource.
633
+ response (dict[dict]): YT API response from the make_video_request function.
634
+ video_IDs (list[str]): List of video IDs.
635
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
636
+
637
+ Returns:
638
+ (list(bool)): True if the video is made for kids, False otherwise.
639
+ """
640
+
641
+ if use_API:
642
+ video_IDs_str = ",".join(video_IDs)
643
+ response = (
644
+ youtube.videos()
645
+ .list(part="status", id=video_IDs_str)
646
+ .execute(num_retries=5)
647
+ )
648
+
649
+ return [vid["status"]["madeForKids"] for vid in response["items"]]
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) [2024] [Killian Lebreton]
3
+ Copyright (c) [2025] [Killian Lebreton]
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QTube
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: Automatically add Youtube videos to a playlist.
5
5
  Home-page: https://github.com/Killian42/QTube
6
6
  Author: Killian Lebreton
@@ -8,7 +8,7 @@ Author-email: Killian Lebreton <killian.lebreton35@gmail.com>
8
8
  Maintainer-email: Killian Lebreton <killian.lebreton35@gmail.com>
9
9
  License: MIT License
10
10
 
11
- Copyright (c) [2024] [Killian Lebreton]
11
+ Copyright (c) [2025] [Killian Lebreton]
12
12
 
13
13
  Permission is hereby granted, free of charge, to any person obtaining a copy
14
14
  of this software and associated documentation files (the "Software"), to deal
@@ -104,6 +104,8 @@ Each of these rules is based on putting some kind of constraint on video propert
104
104
  * Quality filtering
105
105
  * Upload date filtering
106
106
  * Shorts filtering
107
+ * Paid promotion filtering
108
+ * Made for Kids filtering
107
109
  * Duplicate checking
108
110
 
109
111
  ## How to use
@@ -153,6 +155,8 @@ For more versatile uses, you can also use command line arguments with the [qtube
153
155
  |`comments_to_views_ratio`|No|Minimum comments to views ratio.|Positive float between 0 & 1|
154
156
  |`run_frequency`|No|Defines the duration, in days, of the timeframe considered by the software. Can be interpreted as the frequency the program should be run.|*daily*, *weekly*, *monthly* or any positive integer|
155
157
  |`keep_shorts`|No|Determines whether to add shorts.|boolean|
158
+ |`allow_paid_promotions`|No|Determines whether to add videos containing paid advertisement.|boolean|
159
+ |`only_made_for_kids`|No|Determines whether to only add videos that are *Made for Kids* (based on [Youtube and FTC guidelines](https://support.google.com/youtube/answer/9528076)).|boolean|
156
160
  |`keep_duplicates`|No|Determines whether to add videos that are already in the playlist.|boolean|
157
161
  |`upload_playlist_ID`|No|ID of the playlist the videos will be added to. Playlist IDs are found at the end of their URL: `https://www.youtube.com/playlist?list=*playlist_ID*`|Playlist ID|
158
162
  |`override_json`|No|Allow command line arguments to override user_params.json parameters.|boolean|
@@ -206,6 +210,8 @@ The following *user_params.json* file would add every new videos from channels y
206
210
  "comments_to_views_ratio": 0,
207
211
  "run_frequency":"daily",
208
212
  "keep_shorts": true,
213
+ "allow_paid_promotions": true,
214
+ "only_made_for_kids": false,
209
215
  "keep_duplicates": false,
210
216
  "upload_playlist_ID": "your_playlist_ID",
211
217
  "override_json":false,
@@ -248,6 +254,8 @@ The following *user_params.json* file would only add videos with good quality.
248
254
  "comments_to_views_ratio": 0,
249
255
  "run_frequency":"daily",
250
256
  "keep_shorts": true,
257
+ "allow_paid_promotions": true,
258
+ "only_made_for_kids": false,
251
259
  "keep_duplicates": false,
252
260
  "upload_playlist_ID": "your_playlist_ID",
253
261
  "override_json":false,
@@ -290,6 +298,8 @@ The following *user_params.json* file would only add the *$1 vs.* MrBeast videos
290
298
  "comments_to_views_ratio": 0,
291
299
  "run_frequency":"daily",
292
300
  "keep_shorts": false,
301
+ "allow_paid_promotions": true,
302
+ "only_made_for_kids": false,
293
303
  "keep_duplicates": false,
294
304
  "upload_playlist_ID": "your_playlist_ID",
295
305
  "override_json":false,
@@ -0,0 +1,17 @@
1
+ QTube/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ QTube/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ QTube/scripts/qtube.py,sha256=7sr4UnaNtxjUAK_cxW9_fgLzW3jMD-kCkBKJXmXh0_o,35108
4
+ QTube/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ QTube/utils/checks.py,sha256=EV-oV89Ufd02fAzsT68wyVCziSMlnbo1NR1XiNqGa0k,13159
6
+ QTube/utils/helpers.py,sha256=fl1_fePwy7ot-KRFB9Ieq7fMFtD8Q4TriK8cW17qPy0,9187
7
+ QTube/utils/parsing.py,sha256=LQ7onVUyqef9bQOS54YNixOHdJGkfydsazP7ijQQoNA,10789
8
+ QTube/utils/youtube/captions.py,sha256=0jUs8SH4L4d2RTS4QHJ5J2Zd9qe3SOHf9VZW966NuY8,1591
9
+ QTube/utils/youtube/channels.py,sha256=_IITSU3tZJLqrJkdQOLGAwTyrrsZb-WywRq2LyqXVBQ,3331
10
+ QTube/utils/youtube/playlists.py,sha256=kMWeaxGp09J3IY6UWpB7qKY7peTpIBlJkMUDb7CDIpM,3874
11
+ QTube/utils/youtube/videos.py,sha256=YWhpLQVADr95lp1XMgnkg5z05YOxtBgpEauUz95zMeQ,20607
12
+ QTube-2.4.0.dist-info/LICENSE.txt,sha256=cIZNbD-BZYZPzWYHhtE-iUCasUxQIwWzALL9nZh32pQ,1098
13
+ QTube-2.4.0.dist-info/METADATA,sha256=SyyF5njBo11yORDL9_UBmRSGlni982O2ZNgyXiP-G28,17798
14
+ QTube-2.4.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
15
+ QTube-2.4.0.dist-info/entry_points.txt,sha256=Q3SLDyRahuzrNnHC3UOkMH5gM_Um3eCLiLG4vSTPjqE,51
16
+ QTube-2.4.0.dist-info/top_level.txt,sha256=z6oXrT8BiTTZuygjsbfe-NE4rmkHlDK8euAL9m6BT4A,6
17
+ QTube-2.4.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- QTube/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- QTube/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- QTube/scripts/qtube.py,sha256=wzEkhjGgmRFtN9kNBZfqd_6rklX11hwzadh_DAQGZpM,33916
4
- QTube/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- QTube/utils/checks.py,sha256=wq__Xj_NEZa9tjliRTh-kZcHHbll0rqdjBXwIfZso9E,12569
6
- QTube/utils/helpers.py,sha256=cOhy4hauguO5-eJK9pK7NvPOtF5ZWp4zfFipA-DY1QM,9131
7
- QTube/utils/parsing.py,sha256=Q9GnSZSKwX7-AVQDlIoXlQ4xIgQIwMZfOIKXZWi7xSk,10359
8
- QTube/utils/youtube/captions.py,sha256=0jUs8SH4L4d2RTS4QHJ5J2Zd9qe3SOHf9VZW966NuY8,1591
9
- QTube/utils/youtube/channels.py,sha256=8K-5_Ff8zHyKhEM02cdwMEV6HLU14MngxI1v2yJh6iI,3343
10
- QTube/utils/youtube/playlists.py,sha256=Cdohhb2M4bxKiTnZ-R-0xNXPV050nhhI6hepvtDVpIM,3840
11
- QTube/utils/youtube/videos.py,sha256=lHl-MrloJODCbGD38lwrPQmyEE8t-ZvLA0B4gfMSG0c,18159
12
- QTube-2.3.0.dist-info/LICENSE.txt,sha256=cctyZoZUseENxkeq5p_C5oCFYpK0-ECku_sCZmWbL0E,1098
13
- QTube-2.3.0.dist-info/METADATA,sha256=Ftm4t0X_hgyJelTS1v46YehDTPbZglWR5Hp4d3iMIio,17265
14
- QTube-2.3.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
15
- QTube-2.3.0.dist-info/entry_points.txt,sha256=Q3SLDyRahuzrNnHC3UOkMH5gM_Um3eCLiLG4vSTPjqE,51
16
- QTube-2.3.0.dist-info/top_level.txt,sha256=z6oXrT8BiTTZuygjsbfe-NE4rmkHlDK8euAL9m6BT4A,6
17
- QTube-2.3.0.dist-info/RECORD,,
File without changes