-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path__init__.py
More file actions
1365 lines (1094 loc) · 43.2 KB
/
__init__.py
File metadata and controls
1365 lines (1094 loc) · 43.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# API.py
# Import the "deepcopy" module from the "copy" module
from copy import deepcopy
class API():
def __init__(self):
# Import the classes
self.Import_Classes()
# Define the folders of the module
self.folders = self.Define_Folders(object = self).folders
# Define the "Switches" dictionary
self.Define_Switches()
# Define the API dictionary
self.Define_API()
def Import_Classes(self):
import importlib
# ---------- #
# Define the list of modules to be imported
modules = [
"Define_Folders",
"Global_Switches",
"File",
"Date",
"JSON"
]
# Iterate through the list of modules
for module_title in modules:
# Import the module
module = importlib.import_module("." + module_title, "Utility")
# Get the sub-class
sub_class = getattr(module, module_title)
# If the module title is not "Define_Folders"
if module_title != "Define_Folders":
# Run the sub-class to define its variable
sub_class = sub_class()
# Add the sub-class to the current class
setattr(self, module_title, sub_class)
# ---------- #
# Define the "Language" class as the same class inside the "JSON" class
self.Language = self.JSON.Language
# Import some variables from the "Language" class
# Import the "languages" dictionary
self.languages = self.Language.languages
# Import the "language" dictionary
self.language = self.Language.language
# Import the "user" dictionary
self.user = self.Language.user
# Import the "countries" dictionary
self.countries = self.Language.countries
# Import the "separators" dictionary
self.separators = self.Language.separators
def Define_Switches(self):
# Get the "Switches" dictionary from the "Global_Switches" module
self.switches = self.Global_Switches.switches["Global"]
# Update the "Switches" dictionary, adding the "File" dictionary
self.switches.update({
"File": {
"Create": True,
"Edit": True
}
})
def Define_API(self):
# Define and create the "Services.json" file
self.module["Files"]["Services"] = self.module["Folders"]["Module files"]["root"] + "Services.json"
self.File.Create(self.module["Files"]["Services"])
# Create a root API dictionary
self.api = {
"Services": {},
"Service": {}
}
# Add the "Services" dictionary to the API dictionary
self.api["Services"] = self.JSON.To_Python(self.module["Files"]["Services"])
def Call(self, service, request):
# Get the service dictionary
self.api["Service"] = self.api["Services"]["Dictionary"][service]
# Add the "Request" dictionary to the service dictionary
self.api["Service"]["Request"] = request
# Get the method for the service
method = getattr(self, service)
# Run the method of the service and get the updated service dictionary back
service = method(self.api["Service"])
# Return the service dictionary
return service
def YouTube(self, service):
# Import some important modules from the Google modules
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google.auth.exceptions import RefreshError
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
# If the "Credentials" key is a dictionary
# And is not empty
if (
isinstance(service["Credentials"], dict) and
service["Credentials"] != {}
):
# Get the credentials from it
service["Credentials"] = Credentials.from_authorized_user_info(service["Credentials"], service["Scopes"])
# If the "Credentials" key is an empty dictionary
# Or the token is not valid
if (
service["Credentials"] == {} or
service["Credentials"].valid == False
):
# If the "Credentials" key is not an empty dictionary
# And the credentials expired
# And the refresh token is valid
if (
service["Credentials"] and
service["Credentials"].expired and
service["Credentials"].refresh_token
):
# Try to refresh the credentials using the refrest token
try:
service["Credentials"].refresh(Request())
# If the credentials could not be refreshed
except RefreshError:
# Show a five dash space separator
print()
print(self.separators["5"])
print()
# Ask for the user to authorize the app
service["App flow"] = InstalledAppFlow.from_client_config(service["Client secrets"], service["Scopes"])
# Run a local server to get the credentials from the app flow
service["Credentials"] = service["App flow"].run_local_server(port = 0)
# Otherwise, either the credentials are missing, still valid, or cannot be refreshed
else:
# Show a five dash space separator
print()
print(self.separators["5"])
print()
# Ask for the user to authorize the app
service["App flow"] = InstalledAppFlow.from_client_config(service["Client secrets"], service["Scopes"])
# Run a local server to get the credentials from the app flow
service["Credentials"] = service["App flow"].run_local_server(port = 0)
# ---------- #
# Make a copy of the service dictionary
service_copy = deepcopy(service)
# Convert the credentials to JSON
service_copy["Credentials"] = self.JSON.To_Python(service_copy["Credentials"].to_json())
# If the "App flow" key is present in the dictionary
if "App flow" in service_copy:
# Remove the "App flow" key
service_copy.pop("App flow")
# Remove the "Request" key
service_copy.pop("Request")
# Update the service dictionary inside the services "Dictionary"
self.api["Services"]["Dictionary"][service["Name"]] = service_copy
# Save the credentials for the next run by editing the "Services.json" file
# (Passing the "edit" parameter to write into the file even if the file "Edit" switch is False (that means the "Testing" switch is on))
self.JSON.Edit(self.module["Files"]["Services"], self.api["Services"], edit = True)
# Build the API object
service["Object"] = build(service["Name"], service["Version"], developerKey = service["API key"], credentials = service["Credentials"])
# ---------- #
# Define an items map
items_map = {
"Playlist videos": "playlistItems"
}
# Create a shortcut to the "Request" dictionary
request = service["Request"]
# Update the "Item" key to be a dictionary
request["Item"] = {
"Normal": request["Item"],
"Mapped": request["Item"]
}
# Create a shortcut to the request item
item = request["Item"]["Normal"]
# If the item is inside the items map dictionary
if item in items_map:
# Replace the item with the one inside the items map dictionary
request["Item"]["Mapped"] = items_map[item]
# If the "s" letter is not the last character of the mapped item
# And the item is not "Search"
if (
"s" != request["Item"]["Mapped"][-1] and
item != "Search"
):
# Add it
request["Item"]["Mapped"] += "s"
# Create a shortcut to the mapped item
mapped_item = request["Item"]["Mapped"]
# If the "Method" key is not present inside the request dictionary
if "Method" not in request:
# Define it as the mapped "Item" key
request["Method"] = request["Item"]["Mapped"]
# If the submethod is not present inside the request dictionary
if "Submethod" not in request:
# Define it as "List"
request["Submethod"] = "List"
# ---------- #
# If the "Parameters" dictionary is not present inside the request dictionary
if "Parameters" not in request:
# Define the key as the "id" key
key = "id"
# If the request item is "Playlist videos"
if item == "Playlist videos":
# Define the key as "playlistId"
key = "playlistId"
# If the submethod is not "Insert"
if request["Submethod"] != "Insert":
# Define the "Parameters" dictionary as the "ID" key with the request "ID" value
request["Parameters"] = {
key: request["ID"]
}
# If the request item is "Playlist"
# And the submethod is "Insert"
# (This part creates a new playlist with its title and description)
if (
item == "Playlist" and
request["Submethod"] == "Insert"
):
# Define the request "Parameters" dictionary
request["Parameters"] = {
"body": { # Define the body dictionary with its keys
"snippet": { # Define a snippet dictionary for the playlist
"title": request["Title"], # Define the playlist title
"description": request["Description"] # Define the playlist description
},
"status": {
"privacyStatus": "public" # Set the status of the playlist to public
}
}
}
# If the request item is "Playlist videos"
# And the method is "Insert"
if (
item == "Playlist videos" and
request["Submethod"] == "Insert"
):
# Define the request "Parameters" dictionary
request["Parameters"] = {
"body": { # Define the body dictionary with its keys
"snippet": { # Define a snippet dictionary for the playlist
"playlistId": request["ID"], # Define the playlist ID to add a video to it
"resourceId": { # Define the "resourceId" dictionary with the YouTube "video" kind and the video ID
"kind": "youtube#video",
"videoId": request["Video ID"]
}
}
}
}
# ---------- #
# If the "part" key is not inside the request "Parameters" dictionary
if "part" not in request["Parameters"]:
# Define it as a list with only the "snippet" part
request["Parameters"]["part"] = [
"snippet"
]
# If the request item is "Channel"
if item == "Channel":
# Define a list of parts for channels
request["Parameters"]["part"] = [
"id",
"snippet",
"status",
"brandingSettings",
"contentDetails",
"contentOwnerDetails",
"localizations",
"statistics",
"topicDetails"
]
# If the request item is "Playlist"
if item == "Playlist":
# If the submethod is "List"
if request["Submethod"] == "List":
# Define a list of parts to list playlists
request["Parameters"]["part"] = [
"id",
"snippet",
"status",
"contentDetails",
"localizations",
"player"
]
# If the submethod is "Insert"
if request["Submethod"] == "Insert":
# Define a list of parts to insert a video in a playlist
request["Parameters"]["part"] = [
"id",
"snippet",
"status"
]
# If the request item is "Playlist videos"
if item == "Playlist videos":
# Define a list of parts for playlist videos
request["Parameters"]["part"] = [
"id",
"snippet",
"status",
"contentDetails"
]
# If the request item is "Video"
if item == "Video":
# Define a list of parts to list videos
request["Parameters"]["part"] = [
"id",
"snippet",
"status",
"localizations",
"contentDetails"
]
# If the request item is "Comment"
if item == "Comment":
# Define a list of parts to list comments
request["Parameters"]["part"] = [
"id",
"snippet"
]
# ---------- #
# If the "maxResults" key is not inside the request "Parameters" dictionary
if "maxResults" not in request["Parameters"]:
# If the request item is neither "Comment" nor "Playlist"
if item not in ["Comment", "Playlist"]:
# Define the max results as one
request["Parameters"]["maxResults"] = 1
# If the submethod is "Insert"
# And the "maxResults" key is inside the request "Parameters" dictionary, remove the key
if (
request["Submethod"] == "Insert" and
"maxResults" in request["Parameters"]
):
request["Parameters"].pop("maxResults")
# ---------- #
# Get the method object for the method
# For example: "videos", "playlists", "playlistItems", or "comments" (always in lowercase)
request["Method object"] = getattr(service["Object"], request["Method"].lower())
# Get the submethod object
# For example: the "list" submethod of the "videos", "playlists", and "channels" methods (always in lowercase)
request["Submethod object"] = getattr(request["Method object"](), request["Submethod"].lower())
# ---------- #
# If the "Remove private videos" key is not inside the request dictionary, add it as True
if "Remove private videos" not in request:
request["Remove private videos"] = True
# Define an empty results "Dictionary"
request["Dictionary"] = {
"Numbers": {
"Current": 0,
"Total": 0
}
}
# ---------- #
# Define an empty "Response" dictionary with the next page token and the list of items
request["Response"] = {
"nextPageToken": "",
"items": []
}
# Create a shortcut to the "Response" dictionary
response = request["Response"]
# While the "nextPageToken" is inside the "Response" dictionary
while "nextPageToken" in response:
# If the "nextPageToken" key is not empty, define it as the current page token
if response["nextPageToken"] != "":
request["Parameters"]["pageToken"] = response["nextPageToken"]
# Define the "Request" dictionary by calling the submethod object with the keys and values of the "Parameters" dictionary
request["Request"] = request["Submethod object"](**request["Parameters"])
# Execute the request and get the response
request["Response"] = request["Request"].execute()
# Update the shortcut
response = request["Response"]
# ---------- #
# If the "pageInfo" key is inside the response dictionary
# And the "totalResults" key is also inside it
if (
"pageInfo" in response and
"totalResults" in response["pageInfo"]
):
# Create a shortcut to the "pageInfo" dictionary
page_info = response["pageInfo"]
# Define the "Total" number as the "totalResults" key
request["Dictionary"]["Numbers"]["Total"] = page_info["totalResults"]
# Add to the "Current" number
request["Dictionary"]["Numbers"]["Current"] += 1
# Define shortcuts for the current and total numbers
current_number = request["Dictionary"]["Numbers"]["Current"]
total_number = request["Dictionary"]["Numbers"]["Total"]
# If the "Verbose" switch is True
# And the current number and total number are not zero and also not one
if (
self.switches["Verbose"] == True and
(current_number, total_number) != (0, 0) and
(current_number, total_number) != (1, 1)
):
# If the current number is one
if request["Dictionary"]["Numbers"]["Current"] == 1:
# Show a five dash space separator
print()
print(self.separators["10"])
print()
# Show the current number and the total number
print(self.Language.language_texts["number, title()"] + ":")
print(str(current_number) + "/" + str(total_number))
print()
# ---------- #
# Define a default ID as an empty string
id = ""
# If the "items" key is not inside the response dictionary
if "items" not in response:
# If the "id" key is inside the response dictionary
if "id" in response:
id = response["id"]
# Get the snippet dictionary from the response dictionary
snippet = response["snippet"]
# Create a list of items with only the snippet inside it
response["items"] = [
{
"snippet": snippet
}
]
# ---------- #
# Iterate through the list of items inside the response dictionary
for dictionary in response["items"]:
# Get the snippet from the item dictionary
snippet = dictionary["snippet"]
# Define a default branding settings dictionary
branding_settings = {}
# If the "brandingSettings" key is in the item dictionary
if "brandingSettings" in dictionary:
# Update the branding settings dictionary to be the correct one
branding_settings = dictionary["brandingSettings"]
# Get the channel or playlist dictionary if the key exists inside the snippet dictionary
for name in ["Channel", "Playlist"]:
# Define a name key for the name, converting it to lowercase and adding the "Id" text
name_key = name.lower() + "Id"
# If the name key is inside the snippet dictionary
# And the name is not inside the request "Dictionary" dictionary
if (
name_key in snippet and
name not in request["Dictionary"]
):
# Create a shortcut to the snippet ID
snippet_id = snippet[name_key]
# Define a local request dictionary
local_request = {
"Item": name, # "Channel" or "Playlist"
"Parameters": {
"id": snippet_id # Pass the channel or playlist ID
}
}
# Create a new instance of the API class
instance = API()
# Request the channel or playlist dictionary from the YouTube API and get the local response
# (By calling the "Call" method of the new instance of the API class, to not change the variables of the current execution)
local_response = instance.Call("YouTube", local_request)["Request"]
# Define the request dictionary in the name key ("Channel" or "Playlist") as the dictionary inside the snippet ID inside the local response "Dictionary"
request["Dictionary"][name] = local_response["Dictionary"][snippet_id]
# ---------- #
# If the request item is not "Video"
# And the "Video ID" key is inside the request dictionary
# Or the "videoId" key is inside the snippet dictionary
if (
item != "Video" and
(
"Video ID" in request or
"videoId" in snippet
)
):
# If the "Video ID" key is inside the request dictionary
if "Video ID" in request:
# Define the video ID as it
video_id = request["Video ID"]
else:
# Define the "videoId" key as the video ID
video_id = snippet["videoId"]
# Define a local request dictionary
local_request = {
"Item": "Video",
"Parameters": {
"id": video_id # Pass the video ID
}
}
# Create a new instance of the API class
instance = API()
# Request the video dictionary from the YouTube API and get the local response
# (By calling the "Call" method of the new instance of the API class, to not change the variables of the current execution)
local_response = instance.Call("YouTube", local_request)["Request"]
# Define the request dictionary in the "Video" as the dictionary inside the snippet ID inside the local response "Dictionary"
request["Dictionary"]["Video"] = local_response["Dictionary"][video_id]
# ---------- #
# If the "resourceId" is inside the snippet dictionary
if "resourceId" in snippet:
# Define the local ID as the video ID
id = snippet["resourceId"]["videoId"]
# Else, if the ID is inside the item dictionary, use it
elif "id" in dictionary:
# Get the ID from the "id" key
id = dictionary["id"]
# If the ID is a dictionary
# And the "channelId" key is found inside that dictionary
if (
type(id) == dict and
"channelId" in id
):
# Get the correct channel ID
id = id["channelId"]
# Else, if the playlist ID is inside the "Parameters" dictionary, use it
elif "playlistId" in request["Parameters"]:
id = request["Parameters"]["playlistId"]
# Else, get the normal ID from the "Parameters" dictionary
elif "id" in request["Parameters"]:
id = request["Parameters"]["id"]
# Define the local items dictionary as the "Dictionary" dictionary
items = request["Dictionary"]
# Define the link as an empty string by default
link = ""
# If the request item is "Channel"
if item == "Channel":
# Define the link template as the channel one
link_template = service["Link templates"]["Channel"]
# Replace the "{Channel}" text with the channel ID
link = link_template.replace("{Channel}", id)
# If the request item is "Playlist"
if item == "Playlist":
# Define the link template as the playlist one
link_template = service["Link templates"]["Playlist"]
# Replace the "{Playlist}" text with the playlist ID
link = link_template.replace("{Playlist}", id)
# If the request item is "Playlist videos"
if item == "Playlist videos":
# If the "Videos" dictionary is not inside the items dictionary, add it
if "Videos" not in items:
items["Videos"] = {}
# Define the local items dictionary as the "Videos" dictionary
items = items["Videos"]
# If the request item is either "Video" or "Playlist videos"
if item in ["Video", "Playlist videos"]:
# Define the link template as the video one
link_template = service["Link templates"]["Video"]
# Replace the "{Video}" text with the video ID
link = link_template.replace("{Video}", id)
# ---------- #
# Create the default item dictionary with some variables
items[id] = {
"Title": "",
"ID": id,
"Link": link,
"Description": "",
"Channel": {
"Handle": "",
"ID": "",
"Link": "",
"Custom link": ""
},
"Playlist": {
"Title": "",
"ID": "",
"Link": ""
},
"Video": {
"Title": "",
"ID": "",
"Link": ""
}
}
# ---------- #
# If there is a title or description inside the snippet dictionary, add them to the item dictionary
# Else, remove the key from the item dictionary
for name in ["Title", "Description"]:
# Define the name key
name_key = name.lower()
if name_key in snippet:
items[id][name] = snippet[name_key]
else:
items[id].pop(name)
# ---------- #
# If the channel ID is inside the snippet dictionary, add it to the "Channel" dictionary
if "channelId" in snippet:
items[id]["Channel"]["ID"] = snippet["channelId"]
# Else, if the ID is not an empty string
elif id != "":
# Define the "Channel ID" key as the defined ID
items[id]["Channel"]["ID"] = id
# If the request item is "Channel"
# Or the "Channel" dictionary is inside the item dictionary
if (
item == "Channel" or
"Channel" in items[id]
):
# If the request item is "Channel"
if item == "Channel":
# Define the channel link as the local link
items[id]["Channel"]["Link"] = link
# Else if the "channelId" key is inside the snippet dictionary
elif "channelId" in snippet:
# Get the channel ID
channel_id = snippet["channelId"]
# Define the link template as the channel one
link_template = service["Link templates"]["Channel"]
# Replace the "{Channel}" text with the channel ID
link = link_template.replace("{Channel}", channel_id)
# Define the channel link as the local link
items[id]["Channel"]["Link"] = link
# If the channel title is inside the snippet dictionary, add the channel "Title" key to the "Channel" dictionary
if "channelTitle" in snippet:
items[id]["Channel"] = {
"Title": snippet["channelTitle"],
**items[id]["Channel"]
}
# Else if the "Title" key is inside the item dictionary
# And it is not empty
# And the request item is "Channel"
elif (
"Title" in items[id] and
items[id]["Title"] != "" and
item == "Channel"
):
# Define the channel title as the root "Title" key
items[id]["Channel"] = {
"Title": items[id]["Title"],
**items[id]["Channel"]
}
# If the channel ID is not inside the snippet dictionary
if "channelId" not in snippet:
# If the channel ID is empty, remove the "ID" key
if items[id]["Channel"]["ID"] == "":
items[id]["Channel"].pop("ID")
# If the channel link is empty, remove the "Link" key
if items[id]["Channel"]["Link"] == "":
items[id]["Channel"].pop("Link")
# If the custom URL dictionary is inside the snippet dictionary
if "customUrl" in snippet:
# Define the channel "Handle" key as the custom URL
items[id]["Channel"]["Handle"] = snippet["customUrl"]
# Define the custom link as the root YouTube link plus the handle
items[id]["Channel"]["Custom link"] = service["Link"] + items[id]["Channel"]["Handle"]
# Else, remove the "Handle" and "Custom link" keys if they are empty
else:
# If the channel handle is empty, remove the key
if items[id]["Channel"]["Handle"] == "":
items[id]["Channel"].pop("Handle")
# If the custom link is empty, remove the key
if items[id]["Channel"]["Custom link"] == "":
items[id]["Channel"].pop("Custom link")
# ---------- #
# If the request item is "Playlist" or "Playlist videos"
if item in ["Playlist", "Playlist videos"]:
# Define the playlist ID as the root ID
items[id]["Playlist"]["ID"] = items[id]["ID"]
# If the request item is "Playlist videos"
if item == "Playlist videos":
# Update the playlist ID to be the playlist ID inside the snippet dictionary
items[id]["Playlist"]["ID"] = snippet["playlistId"]
# Define the playlist link as the local link
items[id]["Playlist"]["Link"] = link
# If the request item is "Playlist"
if item == "Playlist":
# Define the playlist title as the root "Title" key
items[id]["Playlist"]["Title"] = items[id]["Title"]
# If the request item is not "Playlist" neither "Playlist videos"
if item not in ["Playlist", "Playlist videos"]:
# Remove the "Playlist" dictionary
items[id].pop("Playlist")
# ---------- #
# If the request is either "Playlist videos", "Video", or "Comment"
if item in ["Playlist videos", "Video", "Comment"]:
# If the request item is not "Comment"
if item != "Comment":
# Define the video ID as the root ID
items[id]["Video"]["ID"] = items[id]["ID"]
# Define the video link as the root link
items[id]["Video"]["Link"] = link
# Define the video title as the root "Title" key
items[id]["Video"]["Title"] = items[id]["Title"]
# If the request item is "Playlist videos"
if item == "Playlist videos":
# Add the video position in the playlist
items[id]["Video"]["Position"] = snippet["position"]
# If the request item is "Comment"
# And the "Video ID" key is inside the request dictionary
if (
item == "Comment" and
"Video ID" in request
):
# Define the video ID as the video ID in the request dictionary
items[id]["Video"]["ID"] = request["Video ID"]
# Define the link template as the video one
link_template = service["Link templates"]["Video"]
# Replace the "{Video}" text with the video ID and define a local link
local_link = link_template.replace("{Video}", items[id]["Video"]["ID"])
# Define the video link as the local link
items[id]["Video"]["Link"] = local_link
# If the "Video" key is inside the response dictionary
if "Video" in request["Dictionary"]:
# Define the video title as the "Title" key inside the root "Video" dictionary
items[id]["Video"]["Title"] = request["Dictionary"]["Video"]["Title"]
# If the request is neither "Playlist videos", "Video", nor "Comment"
if item not in ["Playlist videos", "Video", "Comment"]:
# Remove the "Video" dictionary
items[id].pop("Video")
# ---------- #
# Transform the "publishedAt" date into a date dictionary
date = self.Date.From_String(snippet["publishedAt"])
# Add the "Times" dictionary of the [channel/playlist/video/comment]
items[id]["Times"] = {
"Timezone": date["Timezone"]["DateTime"]["Formats"]["HH:MM DD/MM/YYYY"],
"UTC": date["UTC"]["DateTime"]["Formats"]["YYYY-MM-DDTHH:MM:SSZ"]
}
# ---------- #
# If the request item is "Comment"
if item == "Comment":
# If the "Video ID" key is inside the request dictionary
if "Video ID" in request:
# Define the link template as the comment one
link_template = service["Link templates"]["Comment"]
# Replace the "{Video}" text with the video ID inside the request dictionary
link = link_template.replace("{Video}", request["Video ID"])
# Replace the "{Comment}" text with the comment ID
link = link.replace("{Comment}", items[id]["ID"])
# Define the root link as the local comment link
items[id]["Link"] = link
# Create the "Text" dictionary with the orignal and display texts of the comment inside the item dictionary
items[id]["Text"] = {
"Original": snippet["textOriginal"],
"Display": snippet["textDisplay"]
}
# ---------- #
# If the default audio language is inside the snippet dictionary
# Or the default language is inside the same dictionary
if (
"defaultAudioLanguage" in snippet or
"defaultLanguage" in snippet
):
# Define the default language key as "defaultAudioLanguage"
language_key = "defaultAudioLanguage"
# If the "defaultLanguage" key is present, define the language key as it
if "defaultLanguage" in snippet:
language_key = "defaultLanguage"
# Get the language based on the language key
language = snippet[language_key]
# Define the "Language" dictionary inside the item dictionary with the "Small" key
items[id]["Language"] = {
"Small": language
}
# Create a shortcut to the list of full languages
full_languages = self.languages["Full"]
# If the language is not inside the list of full languages
if language not in full_languages:
# Split the language and get the last value from the split list
language = language.split("-")[0]
# If the language is still not inside the list of full languages
if language not in full_languages:
# Define it as the Brazilian Portuguese language
language = "pt"
# Get the full language for the small language
full_language = full_languages[language]
# Define the full language inside the "Language" dictionary
items[id]["Language"]["Full"] = full_language
# ---------- #
# If the country is inside the snippet dictionary
if "country" in snippet:
# Create a shortcut to the country
country = snippet["country"]
# Create the country dictionary
items[id]["Country"] = {
"Code": country,
"Name": self.countries[country]
}
# ---------- #
# If there are thumbnails inside the snippet dictionary
if "thumbnails" in snippet:
# Define a key map to change the keys
key_map = {
"default": "Small",
"standard": "Standard",
"medium": "Medium",
"high": "Large",
"maxres": "Maximum resolution"
}
# Create the "Images" dictionary
items[id]["Images"] = {}
# Iterate through the keys and thumbnails
for key, image in snippet["thumbnails"].items():
# Get the new key
key = key_map[key]
# Create a new image dictionary
new_image = {
"Link": image["url"]
}
# If the "width" key is inside the original image dictionary
if "width" in image:
# Transform the dimension numbers into strings and add them to the new image dictionary
for dimension in ["width", "height"]:
new_image[dimension.capitalize()] = str(image[dimension]) + "px"
# Join the two dimensions to create the "Dimensions" key
new_image["Dimensions"] = str(image["width"]) + "x" + str(image["height"])
# Add the image dictionary to the "Images" dictionary with the new key
items[id]["Images"][key] = new_image
# If the branding settings dictionary is not an empty dictionary
if branding_settings != {}:
# If the "image" key is inside the branding settings dictionary
if "image" in branding_settings:
# Get the banner image and add it to the "Images" dictionary
image = branding_settings["image"]
items[id]["Images"]["Banner"] = {
"Link": image["bannerExternalUrl"]
}
# ---------- #