1
+ from googleapiclient .discovery import build
2
+ from google_auth_oauthlib .flow import InstalledAppFlow
3
+ from google .auth .transport .requests import Request
4
+
5
+ import urllib .parse as p
6
+ import re
7
+ import os
8
+ import pickle
9
+
10
+ SCOPES = ["https://www.googleapis.com/auth/youtube.force-ssl" ]
11
+
12
+ def youtube_authenticate ():
13
+ os .environ ["OAUTHLIB_INSECURE_TRANSPORT" ] = "1"
14
+ api_service_name = "youtube"
15
+ api_version = "v3"
16
+ client_secrets_file = "credentials.json"
17
+ creds = None
18
+ # the file token.pickle stores the user's access and refresh tokens, and is
19
+ # created automatically when the authorization flow completes for the first time
20
+ if os .path .exists ("token.pickle" ):
21
+ with open ("token.pickle" , "rb" ) as token :
22
+ creds = pickle .load (token )
23
+ # if there are no (valid) credentials availablle, let the user log in.
24
+ if not creds or not creds .valid :
25
+ if creds and creds .expired and creds .refresh_token :
26
+ creds .refresh (Request ())
27
+ else :
28
+ flow = InstalledAppFlow .from_client_secrets_file (client_secrets_file , SCOPES )
29
+ creds = flow .run_local_server (port = 0 )
30
+ # save the credentials for the next run
31
+ with open ("token.pickle" , "wb" ) as token :
32
+ pickle .dump (creds , token )
33
+
34
+ return build (api_service_name , api_version , credentials = creds )
35
+
36
+
37
+ def get_channel_details (youtube , ** kwargs ):
38
+ return youtube .channels ().list (
39
+ part = "statistics,snippet,contentDetails" ,
40
+ ** kwargs
41
+ ).execute ()
42
+
43
+
44
+ def search (youtube , ** kwargs ):
45
+ return youtube .search ().list (
46
+ part = "snippet" ,
47
+ ** kwargs
48
+ ).execute ()
49
+
50
+
51
+ def get_video_details (youtube , ** kwargs ):
52
+ return youtube .videos ().list (
53
+ part = "snippet,contentDetails,statistics" ,
54
+ ** kwargs
55
+ ).execute ()
56
+
57
+
58
+ def print_video_infos (video_response ):
59
+ items = video_response .get ("items" )[0 ]
60
+ # get the snippet, statistics & content details from the video response
61
+ snippet = items ["snippet" ]
62
+ statistics = items ["statistics" ]
63
+ content_details = items ["contentDetails" ]
64
+ # get infos from the snippet
65
+ channel_title = snippet ["channelTitle" ]
66
+ title = snippet ["title" ]
67
+ description = snippet ["description" ]
68
+ publish_time = snippet ["publishedAt" ]
69
+ # get stats infos
70
+ comment_count = statistics ["commentCount" ]
71
+ like_count = statistics ["likeCount" ]
72
+ dislike_count = statistics ["dislikeCount" ]
73
+ view_count = statistics ["viewCount" ]
74
+ # get duration from content details
75
+ duration = content_details ["duration" ]
76
+ # duration in the form of something like 'PT5H50M15S'
77
+ # parsing it to be something like '5:50:15'
78
+ parsed_duration = re .search (f"PT(\d+H)?(\d+M)?(\d+S)" , duration ).groups ()
79
+ duration_str = ""
80
+ for d in parsed_duration :
81
+ if d :
82
+ duration_str += f"{ d [:- 1 ]} :"
83
+ duration_str = duration_str .strip (":" )
84
+ print (f"""
85
+ Title: { title }
86
+ Description: { description }
87
+ Channel Title: { channel_title }
88
+ Publish time: { publish_time }
89
+ Duration: { duration_str }
90
+ Number of comments: { comment_count }
91
+ Number of likes: { like_count }
92
+ Number of dislikes: { dislike_count }
93
+ Number of views: { view_count }
94
+ """ )
95
+
96
+
97
+ def parse_channel_url (url ):
98
+ """
99
+ This function takes channel `url` to check whether it includes a
100
+ channel ID, user ID or channel name
101
+ """
102
+ path = p .urlparse (url ).path
103
+ id = path .split ("/" )[- 1 ]
104
+ if "/c/" in path :
105
+ return "c" , id
106
+ elif "/channel/" in path :
107
+ return "channel" , id
108
+ elif "/user/" in path :
109
+ return "user" , id
110
+
111
+
112
+ def get_channel_id_by_url (youtube , url ):
113
+ """
114
+ Returns channel ID of a given `id` and `method`
115
+ - `method` (str): can be 'c', 'channel', 'user'
116
+ - `id` (str): if method is 'c', then `id` is display name
117
+ if method is 'channel', then it's channel id
118
+ if method is 'user', then it's username
119
+ """
120
+ # parse the channel URL
121
+ method , id = parse_channel_url (url )
122
+ if method == "channel" :
123
+ # if it's a channel ID, then just return it
124
+ return id
125
+ elif method == "user" :
126
+ # if it's a user ID, make a request to get the channel ID
127
+ response = get_channel_details (youtube , forUsername = id )
128
+ items = response .get ("items" )
129
+ if items :
130
+ channel_id = items [0 ].get ("id" )
131
+ return channel_id
132
+ elif method == "c" :
133
+ # if it's a channel name, search for the channel using the name
134
+ # may be inaccurate
135
+ response = search (youtube , q = id , maxResults = 1 )
136
+ items = response .get ("items" )
137
+ if items :
138
+ channel_id = items [0 ]["snippet" ]["channelId" ]
139
+ return channel_id
140
+ raise Exception (f"Cannot find ID:{ id } with { method } method" )
141
+
142
+
143
+ def get_video_id_by_url (url ):
144
+ """
145
+ Return the Video ID from the video `url`
146
+ """
147
+ # split URL parts
148
+ parsed_url = p .urlparse (url )
149
+ # get the video ID by parsing the query of the URL
150
+ video_id = p .parse_qs (parsed_url .query ).get ("v" )
151
+ if video_id :
152
+ return video_id [0 ]
153
+ else :
154
+ raise Exception (f"Wasn't able to parse video URL: { url } " )
0 commit comments