QTube 2.3.0__py3-none-any.whl → 2.5.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"],
@@ -308,6 +308,7 @@ def main():
308
308
  wanted_channels_upload_playlists.update(partial_dict)
309
309
 
310
310
  ## Dictionnary of the latest videos from selected channels
311
+ vid_nb = user_params_dict["video_history_limit"]
311
312
  recent_videos = {}
312
313
  for ch_name, playlist_Id in wanted_channels_upload_playlists.items():
313
314
  latest_partial = QTube.utils.helpers.handle_http_errors(
@@ -316,6 +317,7 @@ def main():
316
317
  QTube.utils.youtube.playlists.get_recent_videos,
317
318
  youtube,
318
319
  playlist_Id,
320
+ vid_nb,
319
321
  )
320
322
 
321
323
  if latest_partial == "ignore":
@@ -383,6 +385,8 @@ def main():
383
385
  vid_dicts = partial["items"]
384
386
  responses["items"].extend(vid_dicts)
385
387
 
388
+ video_IDs_lst = [vid["id"] for vid in responses["items"]]
389
+
386
390
  # Titles retrieving
387
391
  titles = QTube.utils.youtube.videos.get_titles(response=responses)
388
392
 
@@ -390,7 +394,9 @@ def main():
390
394
  durations = QTube.utils.youtube.videos.get_durations(response=responses)
391
395
 
392
396
  # Shorts retrieving
393
- shorts = QTube.utils.youtube.videos.is_short(response=responses)
397
+ shorts = QTube.utils.youtube.videos.is_short(
398
+ response=responses, video_IDs=video_IDs_lst
399
+ )
394
400
 
395
401
  # Languages retrieving
396
402
  languages = QTube.utils.youtube.videos.get_languages(response=responses)
@@ -429,6 +435,14 @@ def main():
429
435
  comment_counts, view_counts
430
436
  )
431
437
 
438
+ # Paid promotions retrieving
439
+ paid_advertising = QTube.utils.youtube.videos.has_paid_advertising(
440
+ response=responses
441
+ )
442
+
443
+ # Made for Kids retrieving
444
+ made_for_kids = QTube.utils.youtube.videos.is_made_for_kids(response=responses)
445
+
432
446
  # Resolutions retrieving (does not use YT API)
433
447
  lowest_resolution = user_params_dict.get("lowest_resolution")
434
448
  if lowest_resolution is not None:
@@ -503,6 +517,12 @@ def main():
503
517
  # Comments/views
504
518
  vid_info.update({"comments_to_views_ratio": comments_to_views_ratio[index]})
505
519
 
520
+ # Paid promotions
521
+ vid_info.update({"has_paid_ad": paid_advertising[index]})
522
+
523
+ # Made for kids
524
+ vid_info.update({"made_for_kids": made_for_kids[index]})
525
+
506
526
  # Resolutions
507
527
  if lowest_resolution is not None:
508
528
  vid_info.update({"resolutions": resolutions[index]})
@@ -661,6 +681,22 @@ def main():
661
681
  if vid_ID in new_vid_IDs:
662
682
  videos[vid_ID].update({"to add": False})
663
683
 
684
+ # Paid advertisement filtering
685
+ if user_params_dict["allow_paid_promotions"] is False:
686
+ for vid_ID, vid_info in videos.items():
687
+ if vid_info["to add"] is False:
688
+ continue
689
+ elif vid_info["has_paid_ad"]:
690
+ vid_info.update({"to add": False})
691
+
692
+ # Made for kids filtering
693
+ if user_params_dict["only_made_for_kids"] is True:
694
+ for vid_ID, vid_info in videos.items():
695
+ if vid_info["to add"] is False:
696
+ continue
697
+ elif not vid_info["made_for_kids"]:
698
+ vid_info.update({"to add": False})
699
+
664
700
  # Language filtering
665
701
  if preferred_languages is not None:
666
702
  preferred_languages.append("unknown")
QTube/utils/checks.py CHANGED
@@ -349,6 +349,11 @@ 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),
354
+ # Vid number
355
+ isinstance(params_dict.get("video_history_limit"), int)
356
+ and 1 <= params_dict.get("video_history_limit") <= 50,
352
357
  ]
353
358
 
354
359
  ok = all(checks)
@@ -419,7 +424,7 @@ def check_version() -> tuple[str]:
419
424
  return version, latest_release
420
425
 
421
426
 
422
- def compare_software_versions(version1, version2):
427
+ def compare_software_versions(version1, version2) -> str:
423
428
  """Compare two software versions, using version2 as the reference against which version1 is compared.
424
429
 
425
430
  Args:
@@ -455,3 +460,18 @@ def compare_software_versions(version1, version2):
455
460
  return "older"
456
461
 
457
462
  return "same"
463
+
464
+
465
+ def check_URL_redirect(url: str, redirect_code: int) -> bool:
466
+ """Checks if the provided URL redirects to another page.
467
+
468
+ Args:
469
+ url (str): URL to check for redirection
470
+ redirect_code (int): Status code to check for (3xx)
471
+
472
+ Returns:
473
+ (bool): True if the URL redirects to another page with the correct status code, False otherwise.
474
+ """
475
+ r = requests.get(url)
476
+
477
+ 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:
@@ -257,6 +257,15 @@ def parse_arguments():
257
257
  help="Minimum ratio of comments to views. Default: 0",
258
258
  )
259
259
 
260
+ parser.add_argument(
261
+ "-vhl",
262
+ "--video_history_limit",
263
+ metavar="",
264
+ type=int,
265
+ default=5,
266
+ help="How far back videos are checked in creators' upload playlist. Default: 5",
267
+ )
268
+
260
269
  parser.add_argument(
261
270
  "-rf",
262
271
  "--run_frequency",
@@ -273,6 +282,20 @@ def parse_arguments():
273
282
  help="Determines whether to add shorts. Default: True",
274
283
  )
275
284
 
285
+ parser.add_argument(
286
+ "-app",
287
+ "--allow_paid_promotions",
288
+ action="store_false",
289
+ help="Allow videos containing paid advertising. Default: True",
290
+ )
291
+
292
+ parser.add_argument(
293
+ "-mfk",
294
+ "--only_made_for_kids",
295
+ action="store_true",
296
+ help="Determines whether to only add videos that are made for kids, based on Youtube and FTC guidelines. Default: False",
297
+ )
298
+
276
299
  parser.add_argument(
277
300
  "-kd",
278
301
  "--keep_duplicates",
@@ -308,7 +331,7 @@ def parse_arguments():
308
331
  return vars(parser.parse_args())
309
332
 
310
333
 
311
- def format_arguments(args_dict):
334
+ def format_arguments(args_dict) -> dict:
312
335
  """Formats the parsed command line arguments (written with the help of AI, regex is witchcraft to me).
313
336
 
314
337
  Args:
@@ -346,7 +369,7 @@ def format_arguments(args_dict):
346
369
  return args_dict
347
370
 
348
371
 
349
- def format_arguments_legacy(args_dict):
372
+ def format_arguments_legacy(args_dict) -> dict:
350
373
  """Formats the parsed command line arguments (legacy function).
351
374
 
352
375
  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():
@@ -1,19 +1,20 @@
1
1
  import datetime as dt
2
2
 
3
3
 
4
- def get_recent_videos(youtube, playlist_ID: str) -> dict:
5
- """Retrieves the last 5 videos of a YT playlist.
4
+ def get_recent_videos(youtube, playlist_ID: str, vid_nb: int) -> dict:
5
+ """Retrieves the last videos added in a YT playlist.
6
6
 
7
7
  Args:
8
8
  youtube (Resource): YT API resource.
9
9
  playlist_ID (str): ID of the playlist.
10
+ vid_nb (int): Number of videos to retrieve.
10
11
 
11
12
  Returns:
12
- recent_vids (dict): Dictionary containing the ID (keys) and upload date (values) of the last 5 videos in the playlist.
13
+ recent_vids (dict): Dictionary containing the ID (keys) and upload date (values) of the last videos added in the playlist.
13
14
  """
14
15
  response = (
15
16
  youtube.playlistItems()
16
- .list(part="contentDetails", playlistId=playlist_ID, maxResults=5)
17
+ .list(part="contentDetails", playlistId=playlist_ID, maxResults=vid_nb)
17
18
  .execute(num_retries=5)
18
19
  )
19
20
 
@@ -66,7 +67,7 @@ def get_playlist_content(youtube, playlist_ID: str) -> list[str]:
66
67
  return videos_IDs
67
68
 
68
69
 
69
- def get_playlists_titles(youtube=None, playlist_IDs: list[str] = None):
70
+ def get_playlists_titles(youtube=None, playlist_IDs: list[str] = None) -> list[str]:
70
71
  """Retrieves the titles of a list of YT playlists.
71
72
 
72
73
  Args:
@@ -88,7 +89,9 @@ def get_playlists_titles(youtube=None, playlist_IDs: list[str] = None):
88
89
  return titles
89
90
 
90
91
 
91
- def get_playlists_video_counts(youtube=None, playlist_IDs: list[str] = None):
92
+ def get_playlists_video_counts(
93
+ youtube=None, playlist_IDs: list[str] = None
94
+ ) -> list[int]:
92
95
  """Retrieves the number of videos of a list of YT playlists.
93
96
 
94
97
  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.5.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
@@ -151,8 +153,11 @@ For more versatile uses, you can also use command line arguments with the [qtube
151
153
  |`comments_threshold`|No|Minimum number of times videos have been commented on.|Positive integer|
152
154
  |`likes_to_views_ratio`|No|Minimum likes to views ratio.|Positive float between 0 & 1|
153
155
  |`comments_to_views_ratio`|No|Minimum comments to views ratio.|Positive float between 0 & 1|
156
+ |`video_history_limit`|No|How far back videos are checked in creators' upload playlist.|Positive integer between 1 & 50|
154
157
  |`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
158
  |`keep_shorts`|No|Determines whether to add shorts.|boolean|
159
+ |`allow_paid_promotions`|No|Determines whether to add videos containing paid advertisement.|boolean|
160
+ |`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
161
  |`keep_duplicates`|No|Determines whether to add videos that are already in the playlist.|boolean|
157
162
  |`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
163
  |`override_json`|No|Allow command line arguments to override user_params.json parameters.|boolean|
@@ -204,8 +209,11 @@ The following *user_params.json* file would add every new videos from channels y
204
209
  "comments_threshold": 0,
205
210
  "likes_to_views_ratio": 0,
206
211
  "comments_to_views_ratio": 0,
212
+ "video_history_limit": 5,
207
213
  "run_frequency":"daily",
208
214
  "keep_shorts": true,
215
+ "allow_paid_promotions": true,
216
+ "only_made_for_kids": false,
209
217
  "keep_duplicates": false,
210
218
  "upload_playlist_ID": "your_playlist_ID",
211
219
  "override_json":false,
@@ -246,8 +254,11 @@ The following *user_params.json* file would only add videos with good quality.
246
254
  "comments_threshold": 0,
247
255
  "likes_to_views_ratio": 0,
248
256
  "comments_to_views_ratio": 0,
257
+ "video_history_limit": 5,
249
258
  "run_frequency":"daily",
250
259
  "keep_shorts": true,
260
+ "allow_paid_promotions": true,
261
+ "only_made_for_kids": false,
251
262
  "keep_duplicates": false,
252
263
  "upload_playlist_ID": "your_playlist_ID",
253
264
  "override_json":false,
@@ -288,8 +299,11 @@ The following *user_params.json* file would only add the *$1 vs.* MrBeast videos
288
299
  "comments_threshold": 0,
289
300
  "likes_to_views_ratio": 0,
290
301
  "comments_to_views_ratio": 0,
302
+ "video_history_limit": 5,
291
303
  "run_frequency":"daily",
292
304
  "keep_shorts": false,
305
+ "allow_paid_promotions": true,
306
+ "only_made_for_kids": false,
293
307
  "keep_duplicates": false,
294
308
  "upload_playlist_ID": "your_playlist_ID",
295
309
  "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=x-kzqfB54hzFTT7oolaTTn3Y-Jy9cM7W7L-fIlrj7Ls,35183
4
+ QTube/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ QTube/utils/checks.py,sha256=w6_Mwhh1lpxYEQ6cusKtiOTtvrhDHFjNU5Yx43V3Ee0,13310
6
+ QTube/utils/helpers.py,sha256=fl1_fePwy7ot-KRFB9Ieq7fMFtD8Q4TriK8cW17qPy0,9187
7
+ QTube/utils/parsing.py,sha256=H1uVtevbDF-6JWy42--pDvkBBHzAvxBgujIErQk54AI,11057
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=tjfY-ohrQvk1BznbsdwQUcnX0Wq7rlBK43C-ZWOE4iY,3953
11
+ QTube/utils/youtube/videos.py,sha256=YWhpLQVADr95lp1XMgnkg5z05YOxtBgpEauUz95zMeQ,20607
12
+ QTube-2.5.0.dist-info/LICENSE.txt,sha256=cIZNbD-BZYZPzWYHhtE-iUCasUxQIwWzALL9nZh32pQ,1098
13
+ QTube-2.5.0.dist-info/METADATA,sha256=5onw-FEmuyBiM62nsxA_iTUJCXSP8vc2j2ncI2Tjvkc,18001
14
+ QTube-2.5.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
15
+ QTube-2.5.0.dist-info/entry_points.txt,sha256=Q3SLDyRahuzrNnHC3UOkMH5gM_Um3eCLiLG4vSTPjqE,51
16
+ QTube-2.5.0.dist-info/top_level.txt,sha256=z6oXrT8BiTTZuygjsbfe-NE4rmkHlDK8euAL9m6BT4A,6
17
+ QTube-2.5.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