@@ -121,54 +121,64 @@ class _PkgOfWeekVideoFetcher {
121121 final youtube = YouTubeApi (apiClient);
122122
123123 try {
124+ final pageTokensVisited = < String > {};
125+ String nextPageToken = '' ;
126+
124127 final videos = < PkgOfWeekVideo > [];
125- String ? nextPageToken;
126- for (var check = true ; check && videos.length < 50 ;) {
127- final rs = await cache.youtubePlaylistItems ().get (
128+ final videoIds = < String > {};
129+ while (videos.length < 50 ) {
130+ // check visited status
131+ if (pageTokensVisited.contains (nextPageToken)) {
132+ break ;
133+ }
134+ pageTokensVisited.add (nextPageToken);
135+
136+ // get page from cache or from Youtube API
137+ final rs = await cache.youtubePlaylistItems (nextPageToken).get (
128138 () async => await youtube.playlistItems.list (
129139 ['snippet' , 'contentDetails' ],
130140 playlistId: powPlaylistId,
131- pageToken: nextPageToken,
141+ pageToken: nextPageToken.isEmpty ? null : nextPageToken ,
132142 ),
133143 );
134- videos.addAll (rs! .items! .map (
135- (i) {
136- try {
137- final videoId = i.contentDetails? .videoId;
138- if (videoId == null ) {
139- return null ;
140- }
141- final thumbnails = i.snippet? .thumbnails;
142- if (thumbnails == null ) {
143- return null ;
144- }
145- final thumbnail = thumbnails.high ??
146- thumbnails.default_ ??
147- thumbnails.maxres ??
148- thumbnails.standard ??
149- thumbnails.medium;
150- final thumbnailUrl = thumbnail? .url;
151- if (thumbnailUrl == null || thumbnailUrl.isEmpty) {
152- return null ;
153- }
154- return PkgOfWeekVideo (
155- videoId: videoId,
156- title: i.snippet? .title ?? '' ,
157- description:
158- (i.snippet? .description ?? '' ).trim ().split ('\n ' ).first,
159- thumbnailUrl: thumbnailUrl,
160- );
161- } catch (e, st) {
162- // this item will be skipped, the rest of the list may be displayed
163- _logger.pubNoticeShout (
164- 'youtube' , 'Processing Youtube PlaylistItem failed.' , e, st);
144+
145+ // process playlist items
146+ for (final i in rs! .items! ) {
147+ try {
148+ final videoId = i.contentDetails? .videoId;
149+ if (videoId == null || videoIds.contains (videoId)) {
150+ continue ;
151+ }
152+ final thumbnails = i.snippet? .thumbnails;
153+ if (thumbnails == null ) {
154+ continue ;
155+ }
156+ final thumbnail = thumbnails.high ??
157+ thumbnails.default_ ??
158+ thumbnails.maxres ??
159+ thumbnails.standard ??
160+ thumbnails.medium;
161+ final thumbnailUrl = thumbnail? .url;
162+ if (thumbnailUrl == null || thumbnailUrl.isEmpty) {
163+ continue ;
165164 }
166- return null ;
167- },
168- ).nonNulls);
169- // next page
170- nextPageToken = rs.nextPageToken;
171- check = nextPageToken != null && nextPageToken.isNotEmpty;
165+ videoIds.add (videoId);
166+ videos.add (PkgOfWeekVideo (
167+ videoId: videoId,
168+ title: i.snippet? .title ?? '' ,
169+ description:
170+ (i.snippet? .description ?? '' ).trim ().split ('\n ' ).first,
171+ thumbnailUrl: thumbnailUrl,
172+ ));
173+ } catch (e, st) {
174+ // this item will be skipped, the rest of the list may be displayed
175+ _logger.pubNoticeShout (
176+ 'youtube' , 'Processing Youtube PlaylistItem failed.' , e, st);
177+ }
178+ }
179+
180+ // advance to next page token
181+ nextPageToken = rs.nextPageToken ?? '' ;
172182 }
173183 return videos;
174184 } finally {
0 commit comments