@@ -15,6 +15,41 @@ import getSinglePlaylistAndReturnVideoData, {
1515 PlaylistType ,
1616} from "./getSinglePlaylistAndReturnVideoData" ;
1717
18+ /**
19+ * Parse an ISO 8601 duration string (e.g. "PT1H2M3S") into total seconds.
20+ */
21+ function parseISO8601Duration ( duration : string ) : number {
22+ const match = duration . match ( / P T (?: ( \d + ) H ) ? (?: ( \d + ) M ) ? (?: ( \d + ) S ) ? / ) ;
23+ if ( ! match ) return 0 ;
24+ const hours = parseInt ( match [ 1 ] || "0" , 10 ) ;
25+ const minutes = parseInt ( match [ 2 ] || "0" , 10 ) ;
26+ const seconds = parseInt ( match [ 3 ] || "0" , 10 ) ;
27+ return hours * 3600 + minutes * 60 + seconds ;
28+ }
29+
30+ /**
31+ * Fetch the duration (in seconds) of a video using the YouTube Videos API.
32+ * Returns 0 if the duration cannot be determined.
33+ */
34+ async function fetchVideoDuration ( videoId : string ) : Promise < number > {
35+ const res = await fetch (
36+ `https://youtube.googleapis.com/youtube/v3/videos?part=contentDetails&id=${ videoId } &key=${ env . youtubeApiKey } ` ,
37+ ) ;
38+
39+ if ( ! res . ok ) {
40+ console . error (
41+ "Error fetching video duration:" ,
42+ res . statusText ,
43+ ) ;
44+ return 0 ;
45+ }
46+
47+ const data = await res . json ( ) ;
48+ if ( ! data . items || data . items . length === 0 ) return 0 ;
49+
50+ return parseISO8601Duration ( data . items [ 0 ] . contentDetails . duration ) ;
51+ }
52+
1853export const updates = new Map <
1954 string ,
2055 {
@@ -99,12 +134,29 @@ export default async function fetchLatestUploads() {
99134 "Requires update?" ,
100135 requiresUpdate ,
101136 ) ;
102- const [ longVideoId , shortVideoId , streamVideoId ] =
103- await Promise . all ( [
104- getSinglePlaylistAndReturnVideoData (
105- channelId ,
106- PlaylistType . Video ,
107- ) ,
137+ // Use duration-based detection to reduce API quota usage
138+ // and avoid UULF which is currently lagging
139+ const durationSeconds = await fetchVideoDuration ( videoId ) ;
140+ const THREE_MINUTES = 180 ;
141+
142+ let contentType : PlaylistType | null = null ;
143+
144+ if ( durationSeconds >= THREE_MINUTES ) {
145+ // Over 3 minutes: cannot be a short, check only if it's a stream
146+ const streamVideoId = await getSinglePlaylistAndReturnVideoData (
147+ channelId ,
148+ PlaylistType . Stream ,
149+ ) ;
150+
151+ if ( videoId === streamVideoId . videoId ) {
152+ contentType = PlaylistType . Stream ;
153+ } else {
154+ // Not a stream and over 3 min; must be a regular video
155+ contentType = PlaylistType . Video ;
156+ }
157+ } else {
158+ // Under 3 minutes: could be a short or a video, check UUSH and UULV
159+ const [ shortVideoId , streamVideoId ] = await Promise . all ( [
108160 getSinglePlaylistAndReturnVideoData (
109161 channelId ,
110162 PlaylistType . Short ,
@@ -115,42 +167,24 @@ export default async function fetchLatestUploads() {
115167 ) ,
116168 ] ) ;
117169
118- if ( ! longVideoId && ! shortVideoId && ! streamVideoId ) {
119- console . error (
120- "No video IDs found for channel in fetchLatestUploads" ,
121- ) ;
122- continue ;
123- }
124-
125- let contentType : PlaylistType | null = null ;
126-
127- if ( videoId == longVideoId . videoId ) {
128- contentType = PlaylistType . Video ;
129- } else if ( videoId == shortVideoId . videoId ) {
130- contentType = PlaylistType . Short ;
131- } else if ( videoId == streamVideoId . videoId ) {
132- contentType = PlaylistType . Stream ;
133- } else {
134- console . error (
135- "Video ID does not match any fetched video IDs for channel" ,
136- channelId ,
137- ) ;
170+ if ( videoId === shortVideoId . videoId ) {
171+ contentType = PlaylistType . Short ;
172+ } else if ( videoId === streamVideoId . videoId ) {
173+ contentType = PlaylistType . Stream ;
174+ } else {
175+ // Not in shorts or streams playlist → regular video
176+ contentType = PlaylistType . Video ;
177+ }
138178 }
139179
140- const videoIdMap = {
141- [ PlaylistType . Video ] : longVideoId ,
142- [ PlaylistType . Short ] : shortVideoId ,
143- [ PlaylistType . Stream ] : streamVideoId ,
144- } ;
145-
146- console . log ( "Determined content type:" , contentType ) ;
180+ console . log ( "Determined content type:" , contentType , `(duration: ${ durationSeconds } s)` ) ;
147181
148182 if ( contentType ) {
149183 console . log (
150184 `Updating ${ contentType } video ID for channel` ,
151185 channelId ,
152186 "to" ,
153- videoIdMap [ contentType as keyof typeof videoIdMap ] ,
187+ videoId ,
154188 ) ;
155189 } else {
156190 console . error (
0 commit comments