Update yt_utils.py
Browse fileshttps://chatgpt.com/share/692e08b6-c7fc-8007-9a16-6756e42b3a62
- yt_utils.py +91 -55
yt_utils.py
CHANGED
|
@@ -1,82 +1,118 @@
|
|
| 1 |
-
import requests
|
| 2 |
import os
|
|
|
|
| 3 |
|
| 4 |
YT_API_KEY = os.getenv("YT_API_KEY")
|
| 5 |
|
|
|
|
| 6 |
def get_latest_video_id(channel_id, device_name=None, playlist_id=None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
if device_name is None and playlist_id is None:
|
| 8 |
-
raise
|
| 9 |
|
| 10 |
if device_name is not None and playlist_id is not None:
|
|
|
|
| 11 |
print("Both device_name and playlist_id entered.. device_name will be ignored.")
|
| 12 |
|
|
|
|
|
|
|
|
|
|
| 13 |
if playlist_id is None:
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
'key': YT_API_KEY
|
| 21 |
}
|
| 22 |
-
|
|
|
|
| 23 |
res.raise_for_status()
|
| 24 |
playlists = res.json().get("items", [])
|
| 25 |
|
| 26 |
for p in playlists:
|
| 27 |
-
|
|
|
|
| 28 |
playlist_id = p["id"]
|
| 29 |
break
|
| 30 |
|
| 31 |
if not playlist_id:
|
| 32 |
raise Exception(f"No playlist found matching device name '{device_name}'")
|
| 33 |
|
| 34 |
-
#
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
'part': 'snippet',
|
| 38 |
-
'channelId': channel_id,
|
| 39 |
-
'maxResults': 1,
|
| 40 |
-
'order': 'date', # This sorts by publication date (newest first)
|
| 41 |
-
'type': 'video', # Only return videos
|
| 42 |
-
'key': YT_API_KEY
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
# If we have a playlist_id, we need to get videos from that specific playlist
|
| 46 |
-
# Note: To properly filter by both channel and playlist, we need an additional step
|
| 47 |
if playlist_id:
|
| 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 |
res.raise_for_status()
|
| 77 |
items = res.json().get("items", [])
|
| 78 |
-
|
| 79 |
if not items:
|
| 80 |
return None
|
| 81 |
-
|
| 82 |
-
return items[0]["id"]["videoId"]
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
+
import requests
|
| 3 |
|
| 4 |
YT_API_KEY = os.getenv("YT_API_KEY")
|
| 5 |
|
| 6 |
+
|
| 7 |
def get_latest_video_id(channel_id, device_name=None, playlist_id=None):
|
| 8 |
+
"""
|
| 9 |
+
Return the videoId of the most recently published video from either:
|
| 10 |
+
- a specific playlist (if playlist_id is provided or can be resolved from device_name), or
|
| 11 |
+
- the channel (if only channel_id is provided).
|
| 12 |
+
|
| 13 |
+
This does NOT rely on playlist UI ordering; it walks the whole playlist and
|
| 14 |
+
picks the newest item by snippet.publishedAt.
|
| 15 |
+
"""
|
| 16 |
if device_name is None and playlist_id is None:
|
| 17 |
+
raise ValueError("Must specify either device_name or playlist_id")
|
| 18 |
|
| 19 |
if device_name is not None and playlist_id is not None:
|
| 20 |
+
# Keep existing behavior
|
| 21 |
print("Both device_name and playlist_id entered.. device_name will be ignored.")
|
| 22 |
|
| 23 |
+
# ------------------------------------------------------------------
|
| 24 |
+
# 1) If we only know the device_name, resolve playlist_id from channel
|
| 25 |
+
# ------------------------------------------------------------------
|
| 26 |
if playlist_id is None:
|
| 27 |
+
playlists_url = "https://www.googleapis.com/youtube/v3/playlists"
|
| 28 |
+
playlists_params = {
|
| 29 |
+
"part": "snippet",
|
| 30 |
+
"channelId": channel_id,
|
| 31 |
+
"maxResults": 50,
|
| 32 |
+
"key": YT_API_KEY,
|
|
|
|
| 33 |
}
|
| 34 |
+
|
| 35 |
+
res = requests.get(playlists_url, params=playlists_params)
|
| 36 |
res.raise_for_status()
|
| 37 |
playlists = res.json().get("items", [])
|
| 38 |
|
| 39 |
for p in playlists:
|
| 40 |
+
title = p["snippet"]["title"].lower()
|
| 41 |
+
if device_name.lower() in title:
|
| 42 |
playlist_id = p["id"]
|
| 43 |
break
|
| 44 |
|
| 45 |
if not playlist_id:
|
| 46 |
raise Exception(f"No playlist found matching device name '{device_name}'")
|
| 47 |
|
| 48 |
+
# ------------------------------------------------------------------
|
| 49 |
+
# 2) If we have a playlist_id, walk ALL pages and find newest item
|
| 50 |
+
# ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
if playlist_id:
|
| 52 |
+
playlist_url = "https://www.googleapis.com/youtube/v3/playlistItems"
|
| 53 |
+
latest_video_id = None
|
| 54 |
+
latest_published_at = None
|
| 55 |
+
page_token = None
|
| 56 |
+
|
| 57 |
+
while True:
|
| 58 |
+
playlist_params = {
|
| 59 |
+
"part": "snippet,contentDetails",
|
| 60 |
+
"playlistId": playlist_id,
|
| 61 |
+
"maxResults": 50, # API max
|
| 62 |
+
"key": YT_API_KEY,
|
| 63 |
+
}
|
| 64 |
+
if page_token:
|
| 65 |
+
playlist_params["pageToken"] = page_token
|
| 66 |
+
|
| 67 |
+
playlist_res = requests.get(playlist_url, params=playlist_params)
|
| 68 |
+
playlist_res.raise_for_status()
|
| 69 |
+
data = playlist_res.json()
|
| 70 |
+
|
| 71 |
+
for item in data.get("items", []):
|
| 72 |
+
snippet = item.get("snippet", {})
|
| 73 |
+
published_at = snippet.get("publishedAt")
|
| 74 |
+
if not published_at:
|
| 75 |
+
continue
|
| 76 |
+
|
| 77 |
+
# For playlistItems, videoId lives in contentDetails.videoId
|
| 78 |
+
video_id = item.get("contentDetails", {}).get("videoId")
|
| 79 |
+
if not video_id:
|
| 80 |
+
# Fallback to snippet.resourceId if needed
|
| 81 |
+
video_id = (
|
| 82 |
+
snippet.get("resourceId", {}) or {}
|
| 83 |
+
).get("videoId")
|
| 84 |
+
|
| 85 |
+
if not video_id:
|
| 86 |
+
continue
|
| 87 |
+
|
| 88 |
+
if latest_published_at is None or published_at > latest_published_at:
|
| 89 |
+
latest_published_at = published_at
|
| 90 |
+
latest_video_id = video_id
|
| 91 |
+
|
| 92 |
+
page_token = data.get("nextPageToken")
|
| 93 |
+
if not page_token:
|
| 94 |
+
break
|
| 95 |
+
|
| 96 |
+
return latest_video_id
|
| 97 |
+
|
| 98 |
+
# ------------------------------------------------------------------
|
| 99 |
+
# 3) Fallback: no playlist, just get latest video on the channel
|
| 100 |
+
# ------------------------------------------------------------------
|
| 101 |
+
search_url = "https://www.googleapis.com/youtube/v3/search"
|
| 102 |
+
search_params = {
|
| 103 |
+
"part": "snippet",
|
| 104 |
+
"channelId": channel_id,
|
| 105 |
+
"maxResults": 1,
|
| 106 |
+
"order": "date", # newest first
|
| 107 |
+
"type": "video",
|
| 108 |
+
"key": YT_API_KEY,
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
res = requests.get(search_url, params=search_params)
|
| 112 |
res.raise_for_status()
|
| 113 |
items = res.json().get("items", [])
|
| 114 |
+
|
| 115 |
if not items:
|
| 116 |
return None
|
| 117 |
+
|
| 118 |
+
return items[0]["id"]["videoId"]
|