@@ -186,6 +186,34 @@ def fetch_video_metadata(access_token: str, video_ids: list[str]) -> dict[str, d
186186 return meta
187187
188188
189+ def validate_authorized_channel (access_token : str , target_channel_id : str ) -> None :
190+ params = parse .urlencode ({
191+ "part" : "id,snippet" ,
192+ "mine" : "true" ,
193+ "maxResults" : 50 ,
194+ })
195+ data = api_json (
196+ f"https://www.googleapis.com/youtube/v3/channels?{ params } " ,
197+ headers = {"Authorization" : f"Bearer { access_token } " , "User-Agent" : "analytics-hub-youtube-snapshot" },
198+ )
199+ channels = [
200+ {
201+ "id" : item .get ("id" ),
202+ "title" : (item .get ("snippet" ) or {}).get ("title" ) or item .get ("id" ),
203+ }
204+ for item in data .get ("items" , [])
205+ if item .get ("id" )
206+ ]
207+ if any (ch ["id" ] == target_channel_id for ch in channels ):
208+ return
209+ seen = ", " .join (f"{ ch ['title' ]} ({ ch ['id' ]} )" for ch in channels ) or "no channels"
210+ raise RuntimeError (
211+ "The YouTube refresh token is not authorized for the target channel "
212+ f"{ target_channel_id } . OAuth currently sees: { seen } . Re-authorize in "
213+ "OAuth Playground and choose the Analytics Hub / MSFTAnalyticsHub YouTube channel."
214+ )
215+
216+
189217def normalize_daily (columns : list [str ], rows : list [list ], include_revenue : bool ) -> list [dict ]:
190218 out = []
191219 for row in rows :
@@ -382,6 +410,7 @@ def main() -> int:
382410
383411 token = refresh_access_token (client_id , client_secret , refresh_token )
384412 validate_access_token_scopes (token )
413+ validate_authorized_channel (token , args .channel_id )
385414 daily_cols , daily_rows = rows_from_report (query_report (
386415 token ,
387416 channel_id = args .channel_id ,
0 commit comments