QTube 2.2.0__tar.gz → 2.3.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QTube
3
- Version: 2.2.0
3
+ Version: 2.3.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
@@ -98,6 +98,7 @@ Each of these rules is based on putting some kind of constraint on video propert
98
98
  * Language filtering
99
99
  * Caption filtering
100
100
  * Duration filtering
101
+ * Views, likes & comments counts filtering
101
102
  * Livestream filtering
102
103
  * Premiere filtering
103
104
  * Quality filtering
@@ -145,6 +146,11 @@ For more versatile uses, you can also use command line arguments with the [qtube
145
146
  |`lowest_framerate`|Yes|Minimum framerate. Videos with framerates stricly lower than this value will not be added.|Positive integer|
146
147
  |`preferred_dimensions`|Yes|Dimension the videos need to be in.|*2D*, *3D* or both|
147
148
  |`preferred_projections`|Yes|Projection the videos need to be in.|*rectangular*, *360* or both|
149
+ |`views_threshold`|No|Minimum number of times videos have been viewed.|Positive integer|
150
+ |`likes_threshold`|No|Minimum number of times videos have been liked.|Positive integer|
151
+ |`comments_threshold`|No|Minimum number of times videos have been commented on.|Positive integer|
152
+ |`likes_to_views_ratio`|No|Minimum likes to views ratio.|Positive float between 0 & 1|
153
+ |`comments_to_views_ratio`|No|Minimum comments to views ratio.|Positive float between 0 & 1|
148
154
  |`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|
149
155
  |`keep_shorts`|No|Determines whether to add shorts.|boolean|
150
156
  |`keep_duplicates`|No|Determines whether to add videos that are already in the playlist.|boolean|
@@ -193,6 +199,11 @@ The following *user_params.json* file would add every new videos from channels y
193
199
  "lowest_framerate": null,
194
200
  "preferred_dimensions": null,
195
201
  "preferred_projections": null,
202
+ "views_threshold": 0,
203
+ "likes_threshold": 0,
204
+ "comments_threshold": 0,
205
+ "likes_to_views_ratio": 0,
206
+ "comments_to_views_ratio": 0,
196
207
  "run_frequency":"daily",
197
208
  "keep_shorts": true,
198
209
  "keep_duplicates": false,
@@ -230,6 +241,11 @@ The following *user_params.json* file would only add videos with good quality.
230
241
  "lowest_framerate": 60,
231
242
  "preferred_dimensions": ["2D"],
232
243
  "preferred_projections": ["rectangular"],
244
+ "views_threshold": 0,
245
+ "likes_threshold": 0,
246
+ "comments_threshold": 0,
247
+ "likes_to_views_ratio": 0,
248
+ "comments_to_views_ratio": 0,
233
249
  "run_frequency":"daily",
234
250
  "keep_shorts": true,
235
251
  "keep_duplicates": false,
@@ -267,6 +283,11 @@ The following *user_params.json* file would only add the *$1 vs.* MrBeast videos
267
283
  "lowest_framerate": null,
268
284
  "preferred_dimensions": ["2D"],
269
285
  "preferred_projections": ["rectangular"],
286
+ "views_threshold": 0,
287
+ "likes_threshold": 0,
288
+ "comments_threshold": 0,
289
+ "likes_to_views_ratio": 0,
290
+ "comments_to_views_ratio": 0,
270
291
  "run_frequency":"daily",
271
292
  "keep_shorts": false,
272
293
  "keep_duplicates": false,
@@ -410,6 +410,25 @@ def main():
410
410
  # Live status retrieving
411
411
  live_statuses = QTube.utils.youtube.videos.is_live(response=responses)
412
412
 
413
+ # View counts retrieving
414
+ view_counts = QTube.utils.youtube.videos.get_view_counts(response=responses)
415
+
416
+ # Like counts retrieving
417
+ like_counts = QTube.utils.youtube.videos.get_like_counts(response=responses)
418
+
419
+ # Comment counts retrieving
420
+ comment_counts = QTube.utils.youtube.videos.get_comment_counts(response=responses)
421
+
422
+ # Likes/views ratio retrieving
423
+ likes_to_views_ratio = QTube.utils.youtube.videos.get_likes_to_views_ratio(
424
+ like_counts, view_counts
425
+ )
426
+
427
+ # Comments/views ratio retrieving
428
+ comments_to_views_ratio = QTube.utils.youtube.videos.get_likes_to_views_ratio(
429
+ comment_counts, view_counts
430
+ )
431
+
413
432
  # Resolutions retrieving (does not use YT API)
414
433
  lowest_resolution = user_params_dict.get("lowest_resolution")
415
434
  if lowest_resolution is not None:
@@ -469,6 +488,21 @@ def main():
469
488
  # Live statuses
470
489
  vid_info.update({"live status": live_statuses[index]})
471
490
 
491
+ # Views
492
+ vid_info.update({"views": view_counts[index]})
493
+
494
+ # Likes
495
+ vid_info.update({"likes": like_counts[index]})
496
+
497
+ # Comments
498
+ vid_info.update({"comments": comment_counts[index]})
499
+
500
+ # Likes/views
501
+ vid_info.update({"likes_to_views_ratio": likes_to_views_ratio[index]})
502
+
503
+ # Comments/views
504
+ vid_info.update({"comments_to_views_ratio": comments_to_views_ratio[index]})
505
+
472
506
  # Resolutions
473
507
  if lowest_resolution is not None:
474
508
  vid_info.update({"resolutions": resolutions[index]})
@@ -543,6 +577,12 @@ def main():
543
577
  lowest_definition = user_params_dict.get("lowest_definition")
544
578
  preferred_dimensions = user_params_dict.get("preferred_dimensions")
545
579
 
580
+ views_threshold = user_params_dict.get("views_threshold")
581
+ likes_threshold = user_params_dict.get("likes_threshold")
582
+ comments_threshold = user_params_dict.get("comments_threshold")
583
+ likes_to_views_ratio_threshold = user_params_dict.get("likes_to_views_ratio")
584
+ comments_to_views_ratio_threshold = user_params_dict.get("comments_to_views_ratio")
585
+
546
586
  # Duration filtering
547
587
  if min_max_durations is not None:
548
588
  for vid_info in videos.values():
@@ -771,6 +811,43 @@ def main():
771
811
  ):
772
812
  vid_info.update({"to add": False})
773
813
 
814
+ # Views filtering
815
+ if views_threshold > 0:
816
+ for vid_ID, vid_info in videos.items():
817
+ if vid_info["to add"] and vid_info["views"] < views_threshold:
818
+ vid_info.update({"to add": False})
819
+
820
+ # Likes Filtering
821
+ if likes_threshold > 0:
822
+ for vid_ID, vid_info in videos.items():
823
+ if vid_info["to add"] and vid_info["likes"] < likes_threshold:
824
+ vid_info.update({"to add": False})
825
+
826
+ # Comments filtering
827
+ if comments_threshold > 0:
828
+ for vid_ID, vid_info in videos.items():
829
+ if vid_info["to add"] and vid_info["comments"] < comments_threshold:
830
+ vid_info.update({"to add": False})
831
+
832
+ # Likes/views ratio filtering
833
+ if likes_to_views_ratio_threshold > 0:
834
+ for vid_ID, vid_info in videos.items():
835
+ if (
836
+ vid_info["to add"]
837
+ and vid_info["likes_to_views_ratio"] < likes_to_views_ratio_threshold
838
+ ):
839
+ vid_info.update({"to add": False})
840
+
841
+ # Comments/views ratio filtering
842
+ if comments_to_views_ratio_threshold > 0:
843
+ for vid_ID, vid_info in videos.items():
844
+ if (
845
+ vid_info["to add"]
846
+ and vid_info["comments_to_views_ratio"]
847
+ < comments_to_views_ratio_threshold
848
+ ):
849
+ vid_info.update({"to add": False})
850
+
774
851
  ## Selecting correct videos
775
852
  videos_to_add = {
776
853
  vid_ID: vid_info for vid_ID, vid_info in videos.items() if vid_info["to add"]
@@ -334,6 +334,21 @@ def check_user_params(params_dict: dict) -> bool:
334
334
  isinstance(params_dict.get("ignore_premieres"), bool),
335
335
  # Fancy text
336
336
  isinstance(params_dict.get("fancy_mode"), bool),
337
+ # Views
338
+ isinstance(params_dict.get("views_threshold"), int)
339
+ and params_dict.get("views_threshold") >= 0,
340
+ # Likes
341
+ isinstance(params_dict.get("likes_threshold"), int)
342
+ and params_dict.get("likes_threshold") >= 0,
343
+ # Comments
344
+ isinstance(params_dict.get("comments_threshold"), int)
345
+ and params_dict.get("comments_threshold") >= 0,
346
+ # Likes/views ratio
347
+ isinstance(params_dict.get("likes_to_views_ratio"), (int, float))
348
+ and 0 <= params_dict.get("likes_to_views_ratio") <= 1,
349
+ # Comments/views ratio
350
+ isinstance(params_dict.get("comments_to_views_ratio"), (int, float))
351
+ and 0 <= params_dict.get("comments_to_views_ratio") <= 1,
337
352
  ]
338
353
 
339
354
  ok = all(checks)
@@ -7,7 +7,7 @@ from googleapiclient.errors import HttpError
7
7
  from colorama import Fore, Style
8
8
 
9
9
 
10
- def handle_http_errors(verbosity: list[str],fancy, func, *args, **kwargs):
10
+ def handle_http_errors(verbosity: list[str], fancy, func, *args, **kwargs):
11
11
  """Handles http errors when making API queries.
12
12
  If after 5 tries, the function could not be executed, it shuts the program down.
13
13
 
@@ -27,7 +27,11 @@ def handle_http_errors(verbosity: list[str],fancy, func, *args, **kwargs):
27
27
  try:
28
28
  res = func(*args, **kwargs)
29
29
  print2(
30
- f"{func.__name__} successfully executed.",fancy,"success", ["all", "func"], verbosity
30
+ f"{func.__name__} successfully executed.",
31
+ fancy,
32
+ "success",
33
+ ["all", "func"],
34
+ verbosity,
31
35
  )
32
36
  return res # Return the response if no error occurs
33
37
  except HttpError as err:
@@ -100,9 +104,9 @@ def print2(
100
104
  elif fancy_type == "fail":
101
105
  print(fancify_text(message, Fore.RED, Style.BRIGHT, "❌"))
102
106
  elif fancy_type == "warning":
103
- print(fancify_text(message, Fore.YELLOW, Style.NORMAL, "⚠️"))
107
+ print(fancify_text(message, Fore.YELLOW, Style.BRIGHT, "⚠️"))
104
108
  elif fancy_type == "info":
105
- print(fancify_text(message, Fore.WHITE, Style.BRIGHT,"📢"))
109
+ print(fancify_text(message, Fore.WHITE, Style.BRIGHT, "📢"))
106
110
  elif fancy_type == "video":
107
111
  print(fancify_text(message, Fore.BLUE, Style.BRIGHT, "🎞️"))
108
112
  else:
@@ -223,3 +227,21 @@ def remove_multiple_spaces(text):
223
227
  (str): Same text string, but with multiple spaces replaced by a single space.
224
228
  """
225
229
  return re.sub(" +", " ", text)
230
+
231
+
232
+ def divide_lists(list1, list2, percentage: False):
233
+ """Divides two python lists element-wise.
234
+
235
+ Args:
236
+ list1 (lst[int|float]): Dividend list.
237
+ list2 (lst[inf|float]): Divisor list.
238
+ percentage (bool): Determines if the result of the division is expressed as a percentage.
239
+
240
+ Returns:
241
+ res (lst[int|float]): List containing the results of the element-wise division.
242
+ """
243
+ if percentage:
244
+ res = [(a / b) * 100 if b != 0 else None for a, b in zip(list1, list2)]
245
+ else:
246
+ res = [a / b if b != 0 else None for a, b in zip(list1, list2)]
247
+ return res
@@ -217,6 +217,46 @@ def parse_arguments():
217
217
  help="Projection the videos need to be in. Default: None",
218
218
  )
219
219
 
220
+ parser.add_argument(
221
+ "-vt",
222
+ "--views_threshold",
223
+ metavar="",
224
+ type=int,
225
+ help="Minimum number of views. Default: 0",
226
+ )
227
+
228
+ parser.add_argument(
229
+ "-lt",
230
+ "--likes_threshold",
231
+ metavar="",
232
+ type=int,
233
+ help="Minimum number of likes. Default: 0",
234
+ )
235
+
236
+ parser.add_argument(
237
+ "-ct",
238
+ "--comments_threshold",
239
+ metavar="",
240
+ type=int,
241
+ help="Minimum number of comments. Default: 0",
242
+ )
243
+
244
+ parser.add_argument(
245
+ "-lvr",
246
+ "--likes_to_views_ratio",
247
+ metavar="",
248
+ type=float,
249
+ help="Minimum ratio of likes to views. Default: 0",
250
+ )
251
+
252
+ parser.add_argument(
253
+ "-cvr",
254
+ "--comments_to_views_ratio",
255
+ metavar="",
256
+ type=float,
257
+ help="Minimum ratio of comments to views. Default: 0",
258
+ )
259
+
220
260
  parser.add_argument(
221
261
  "-rf",
222
262
  "--run_frequency",
@@ -2,6 +2,8 @@ import isodate
2
2
 
3
3
  from pytube import YouTube
4
4
 
5
+ from QTube.utils import helpers
6
+
5
7
 
6
8
  def make_video_requests(youtube, video_IDs: list[str]) -> dict:
7
9
  """Retrieves information on a list of YT videos.
@@ -16,7 +18,7 @@ def make_video_requests(youtube, video_IDs: list[str]) -> dict:
16
18
  video_IDs_str = ",".join(video_IDs)
17
19
  response = (
18
20
  youtube.videos()
19
- .list(part="snippet,contentDetails", id=video_IDs_str)
21
+ .list(part="snippet,contentDetails,statistics", id=video_IDs_str)
20
22
  .execute(num_retries=5)
21
23
  )
22
24
  return response
@@ -338,6 +340,158 @@ def get_projections(
338
340
  return projections
339
341
 
340
342
 
343
+ def get_view_counts(
344
+ youtube=None,
345
+ response: dict = None,
346
+ video_IDs: list[str] = None,
347
+ use_API: bool = False,
348
+ ) -> list[str]:
349
+ """Retrieves the number of views of a list of YT videos.
350
+
351
+ Args:
352
+ youtube (Resource): YT API resource.
353
+ response (dict[dict]): YT API response from the make_video_request function.
354
+ video_IDs (list[str]): List of video IDs.
355
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
356
+
357
+ Returns:
358
+ views (list[int]): List of YT videos views.
359
+ """
360
+ if use_API:
361
+ video_IDs_str = ",".join(video_IDs)
362
+ response = (
363
+ youtube.videos()
364
+ .list(part="statistics", id=video_IDs_str)
365
+ .execute(num_retries=5)
366
+ )
367
+
368
+ views = [int(vid["statistics"]["viewCount"]) for vid in response["items"]]
369
+
370
+ return views
371
+
372
+
373
+ def get_like_counts(
374
+ youtube=None,
375
+ response: dict = None,
376
+ video_IDs: list[str] = None,
377
+ use_API: bool = False,
378
+ ) -> list[str]:
379
+ """Retrieves the number of likes of a list of YT videos.
380
+
381
+ Args:
382
+ youtube (Resource): YT API resource.
383
+ response (dict[dict]): YT API response from the make_video_request function.
384
+ video_IDs (list[str]): List of video IDs.
385
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
386
+
387
+ Returns:
388
+ likes (list[int]): List of YT videos likes.
389
+ """
390
+ if use_API:
391
+ video_IDs_str = ",".join(video_IDs)
392
+ response = (
393
+ youtube.videos()
394
+ .list(part="statistics", id=video_IDs_str)
395
+ .execute(num_retries=5)
396
+ )
397
+
398
+ likes = [int(vid["statistics"]["likeCount"]) for vid in response["items"]]
399
+
400
+ return likes
401
+
402
+
403
+ def get_comment_counts(
404
+ youtube=None,
405
+ response: dict = None,
406
+ video_IDs: list[str] = None,
407
+ use_API: bool = False,
408
+ ) -> list[str]:
409
+ """Retrieves the number of comments of a list of YT videos.
410
+
411
+ Args:
412
+ youtube (Resource): YT API resource.
413
+ response (dict[dict]): YT API response from the make_video_request function.
414
+ video_IDs (list[str]): List of video IDs.
415
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
416
+
417
+ Returns:
418
+ comment_counts (list[int]): List of YT videos comment counts.
419
+ """
420
+ if use_API:
421
+ video_IDs_str = ",".join(video_IDs)
422
+ response = (
423
+ youtube.videos()
424
+ .list(part="statistics", id=video_IDs_str)
425
+ .execute(num_retries=5)
426
+ )
427
+
428
+ comment_counts = [
429
+ int(vid["statistics"]["commentCount"]) for vid in response["items"]
430
+ ]
431
+
432
+ return comment_counts
433
+
434
+
435
+ def get_likes_to_views_ratio(
436
+ likes,
437
+ views,
438
+ youtube=None,
439
+ response: dict = None,
440
+ video_IDs: list[str] = None,
441
+ use_API: bool = False,
442
+ ) -> list[str]:
443
+ """Retrieves the likes to views ratio of a list of YT videos.
444
+
445
+ Args:
446
+ likes (list[int]): List of the number of likes.
447
+ views (list[int]): List of the number of views.
448
+ youtube (Resource): YT API resource.
449
+ response (dict[dict]): YT API response from the make_video_request function.
450
+ video_IDs (list[str]): List of video IDs.
451
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
452
+
453
+ Returns:
454
+ ratio (list[int|float]): List of YT videos' likes to views ratios.
455
+ """
456
+ if use_API:
457
+ views = get_view_counts(youtube, response, video_IDs, use_API)
458
+ likes = get_like_counts(youtube, response, video_IDs, use_API)
459
+
460
+ ratio = helpers.divide_lists(likes, views, False)
461
+
462
+ return ratio
463
+
464
+
465
+ def get_comments_to_views_ratio(
466
+ likes,
467
+ views,
468
+ youtube=None,
469
+ response: dict = None,
470
+ video_IDs: list[str] = None,
471
+ use_API: bool = False,
472
+ ) -> list[str]:
473
+ """Retrieves the comments to views ratio of a list of YT videos.
474
+
475
+ Args:
476
+ comments (list[int]): List of the number of comments.
477
+ views (list[int]): List of the number of views.
478
+ youtube (Resource): YT API resource.
479
+ response (dict[dict]): YT API response from the make_video_request function.
480
+ video_IDs (list[str]): List of video IDs.
481
+ use_API (bool): Determines if a new API request is made or if the response dictionary is used.
482
+
483
+ Returns:
484
+ views (list[int|float]): List of YT videos' comments to views ratios.
485
+ """
486
+ if use_API:
487
+ views = get_view_counts(youtube, response, video_IDs, use_API)
488
+ comments = get_comment_counts(youtube, response, video_IDs, use_API)
489
+
490
+ ratio = helpers.divide_lists(comments, views, False)
491
+
492
+ return ratio
493
+
494
+
341
495
  def has_captions(
342
496
  youtube=None,
343
497
  response: dict = None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: QTube
3
- Version: 2.2.0
3
+ Version: 2.3.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
@@ -98,6 +98,7 @@ Each of these rules is based on putting some kind of constraint on video propert
98
98
  * Language filtering
99
99
  * Caption filtering
100
100
  * Duration filtering
101
+ * Views, likes & comments counts filtering
101
102
  * Livestream filtering
102
103
  * Premiere filtering
103
104
  * Quality filtering
@@ -145,6 +146,11 @@ For more versatile uses, you can also use command line arguments with the [qtube
145
146
  |`lowest_framerate`|Yes|Minimum framerate. Videos with framerates stricly lower than this value will not be added.|Positive integer|
146
147
  |`preferred_dimensions`|Yes|Dimension the videos need to be in.|*2D*, *3D* or both|
147
148
  |`preferred_projections`|Yes|Projection the videos need to be in.|*rectangular*, *360* or both|
149
+ |`views_threshold`|No|Minimum number of times videos have been viewed.|Positive integer|
150
+ |`likes_threshold`|No|Minimum number of times videos have been liked.|Positive integer|
151
+ |`comments_threshold`|No|Minimum number of times videos have been commented on.|Positive integer|
152
+ |`likes_to_views_ratio`|No|Minimum likes to views ratio.|Positive float between 0 & 1|
153
+ |`comments_to_views_ratio`|No|Minimum comments to views ratio.|Positive float between 0 & 1|
148
154
  |`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|
149
155
  |`keep_shorts`|No|Determines whether to add shorts.|boolean|
150
156
  |`keep_duplicates`|No|Determines whether to add videos that are already in the playlist.|boolean|
@@ -193,6 +199,11 @@ The following *user_params.json* file would add every new videos from channels y
193
199
  "lowest_framerate": null,
194
200
  "preferred_dimensions": null,
195
201
  "preferred_projections": null,
202
+ "views_threshold": 0,
203
+ "likes_threshold": 0,
204
+ "comments_threshold": 0,
205
+ "likes_to_views_ratio": 0,
206
+ "comments_to_views_ratio": 0,
196
207
  "run_frequency":"daily",
197
208
  "keep_shorts": true,
198
209
  "keep_duplicates": false,
@@ -230,6 +241,11 @@ The following *user_params.json* file would only add videos with good quality.
230
241
  "lowest_framerate": 60,
231
242
  "preferred_dimensions": ["2D"],
232
243
  "preferred_projections": ["rectangular"],
244
+ "views_threshold": 0,
245
+ "likes_threshold": 0,
246
+ "comments_threshold": 0,
247
+ "likes_to_views_ratio": 0,
248
+ "comments_to_views_ratio": 0,
233
249
  "run_frequency":"daily",
234
250
  "keep_shorts": true,
235
251
  "keep_duplicates": false,
@@ -267,6 +283,11 @@ The following *user_params.json* file would only add the *$1 vs.* MrBeast videos
267
283
  "lowest_framerate": null,
268
284
  "preferred_dimensions": ["2D"],
269
285
  "preferred_projections": ["rectangular"],
286
+ "views_threshold": 0,
287
+ "likes_threshold": 0,
288
+ "comments_threshold": 0,
289
+ "likes_to_views_ratio": 0,
290
+ "comments_to_views_ratio": 0,
270
291
  "run_frequency":"daily",
271
292
  "keep_shorts": false,
272
293
  "keep_duplicates": false,
@@ -42,6 +42,7 @@ Each of these rules is based on putting some kind of constraint on video propert
42
42
  * Language filtering
43
43
  * Caption filtering
44
44
  * Duration filtering
45
+ * Views, likes & comments counts filtering
45
46
  * Livestream filtering
46
47
  * Premiere filtering
47
48
  * Quality filtering
@@ -89,6 +90,11 @@ For more versatile uses, you can also use command line arguments with the [qtube
89
90
  |`lowest_framerate`|Yes|Minimum framerate. Videos with framerates stricly lower than this value will not be added.|Positive integer|
90
91
  |`preferred_dimensions`|Yes|Dimension the videos need to be in.|*2D*, *3D* or both|
91
92
  |`preferred_projections`|Yes|Projection the videos need to be in.|*rectangular*, *360* or both|
93
+ |`views_threshold`|No|Minimum number of times videos have been viewed.|Positive integer|
94
+ |`likes_threshold`|No|Minimum number of times videos have been liked.|Positive integer|
95
+ |`comments_threshold`|No|Minimum number of times videos have been commented on.|Positive integer|
96
+ |`likes_to_views_ratio`|No|Minimum likes to views ratio.|Positive float between 0 & 1|
97
+ |`comments_to_views_ratio`|No|Minimum comments to views ratio.|Positive float between 0 & 1|
92
98
  |`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|
93
99
  |`keep_shorts`|No|Determines whether to add shorts.|boolean|
94
100
  |`keep_duplicates`|No|Determines whether to add videos that are already in the playlist.|boolean|
@@ -137,6 +143,11 @@ The following *user_params.json* file would add every new videos from channels y
137
143
  "lowest_framerate": null,
138
144
  "preferred_dimensions": null,
139
145
  "preferred_projections": null,
146
+ "views_threshold": 0,
147
+ "likes_threshold": 0,
148
+ "comments_threshold": 0,
149
+ "likes_to_views_ratio": 0,
150
+ "comments_to_views_ratio": 0,
140
151
  "run_frequency":"daily",
141
152
  "keep_shorts": true,
142
153
  "keep_duplicates": false,
@@ -174,6 +185,11 @@ The following *user_params.json* file would only add videos with good quality.
174
185
  "lowest_framerate": 60,
175
186
  "preferred_dimensions": ["2D"],
176
187
  "preferred_projections": ["rectangular"],
188
+ "views_threshold": 0,
189
+ "likes_threshold": 0,
190
+ "comments_threshold": 0,
191
+ "likes_to_views_ratio": 0,
192
+ "comments_to_views_ratio": 0,
177
193
  "run_frequency":"daily",
178
194
  "keep_shorts": true,
179
195
  "keep_duplicates": false,
@@ -211,6 +227,11 @@ The following *user_params.json* file would only add the *$1 vs.* MrBeast videos
211
227
  "lowest_framerate": null,
212
228
  "preferred_dimensions": ["2D"],
213
229
  "preferred_projections": ["rectangular"],
230
+ "views_threshold": 0,
231
+ "likes_threshold": 0,
232
+ "comments_threshold": 0,
233
+ "likes_to_views_ratio": 0,
234
+ "comments_to_views_ratio": 0,
214
235
  "run_frequency":"daily",
215
236
  "keep_shorts": false,
216
237
  "keep_duplicates": false,
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "QTube"
8
- version = "2.2.0"
8
+ version = "2.3.0"
9
9
  requires-python = ">=3.8"
10
10
  authors = [
11
11
  { name = "Killian Lebreton", email = "killian.lebreton35@gmail.com" },
@@ -5,7 +5,7 @@ with open("README.md", "r") as f:
5
5
 
6
6
  setup(
7
7
  name="QTube",
8
- version="2.2.0",
8
+ version="2.3.0",
9
9
  description="Automatically add Youtube videos to a playlist.",
10
10
  long_description=long_description,
11
11
  long_description_content_type="text/markdown",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes