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