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