mirror of
https://github.com/mxpv/podsync.git
synced 2024-05-11 05:55:04 +00:00
Fix page_size limited at 50 on YouTube
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/BrianHicks/finch/duration"
|
||||
"github.com/mxpv/podsync/pkg/feed"
|
||||
"github.com/pkg/errors"
|
||||
@@ -264,67 +265,115 @@ func (yt *YouTubeBuilder) getSize(duration int64, feed *model.Feed) int64 {
|
||||
// See https://developers.google.com/youtube/v3/docs/videos/list#part
|
||||
func (yt *YouTubeBuilder) queryVideoDescriptions(ctx context.Context, playlist map[string]*youtube.PlaylistItemSnippet, feed *model.Feed) error {
|
||||
// Make the list of video ids
|
||||
// and count how many API calls will be required
|
||||
ids := make([]string, 0, len(playlist))
|
||||
count := 0
|
||||
count_expected_api_calls := 1
|
||||
for _, s := range playlist {
|
||||
count++
|
||||
ids = append(ids, s.ResourceId.VideoId)
|
||||
// Increment the counter of expected API calls
|
||||
if count == maxYoutubeResults {
|
||||
count_expected_api_calls++
|
||||
count = 0
|
||||
}
|
||||
}
|
||||
|
||||
req, err := yt.client.Videos.List("id,snippet,contentDetails").Id(strings.Join(ids, ",")).Context(ctx).Do(yt.key)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to query video descriptions")
|
||||
}
|
||||
log.Debugf("Expected to make %d API calls to get the descriptions for %d episode(s).", count_expected_api_calls, len(ids))
|
||||
|
||||
for _, video := range req.Items {
|
||||
var (
|
||||
snippet = video.Snippet
|
||||
videoID = video.Id
|
||||
videoURL = fmt.Sprintf("https://youtube.com/watch?v=%s", video.Id)
|
||||
image = yt.selectThumbnail(snippet.Thumbnails, feed.Quality, videoID)
|
||||
)
|
||||
// Init a list that will contains the aggregated strings of videos IDs (capped at 50 IDs per API Calls)
|
||||
ids_list := make([]string, 0, count_expected_api_calls )
|
||||
|
||||
// Parse date added to playlist / publication date
|
||||
dateStr := ""
|
||||
playlistItem, ok := playlist[video.Id]
|
||||
if ok {
|
||||
dateStr = playlistItem.PublishedAt
|
||||
} else {
|
||||
dateStr = snippet.PublishedAt
|
||||
// Init some vars for the logic of breaking the IDs down into groups of 50
|
||||
total_count_id := 0
|
||||
count_id := 0
|
||||
temp_ids_list := make([]string, 0)
|
||||
|
||||
for _, id := range ids {
|
||||
total_count_id++
|
||||
count_id++
|
||||
|
||||
// If we have not yet reached the limit of the YouTube API,
|
||||
// append this video ID to the temporary list
|
||||
if count_id <= maxYoutubeResults {
|
||||
temp_ids_list = append(temp_ids_list, id)
|
||||
}
|
||||
|
||||
pubDate, err := yt.parseDate(dateStr)
|
||||
// If we have reached the limit of YouTube API,
|
||||
// convert the temporary ID list into a string and
|
||||
// save it into the final ID list
|
||||
if count_id == maxYoutubeResults {
|
||||
count_id = 0
|
||||
ids_list = append(ids_list, strings.Join(temp_ids_list, ","))
|
||||
// Reset the value of the temporary ID list
|
||||
temp_ids_list = nil
|
||||
} else if total_count_id == len(playlist) {
|
||||
// Convert the temporary ID list into a string and append it to the final ID list
|
||||
ids_list = append(ids_list, strings.Join(temp_ids_list, ","))
|
||||
// Reset the value of the temporary ID list
|
||||
temp_ids_list = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Loop in each list of 50 (or less) IDs and query the description
|
||||
for list_number := 0; list_number < len(ids_list); list_number++ {
|
||||
req, err := yt.client.Videos.List("id,snippet,contentDetails").Id(ids_list[list_number]).Context(ctx).Do(yt.key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse video publish date: %s", dateStr)
|
||||
return errors.Wrap(err, "failed to query video descriptions")
|
||||
}
|
||||
|
||||
// Sometimes YouTube retrun empty content defailt, use arbitrary one
|
||||
var seconds int64 = 1
|
||||
if video.ContentDetails != nil {
|
||||
// Parse duration
|
||||
d, err := duration.FromString(video.ContentDetails.Duration)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse duration %s", video.ContentDetails.Duration)
|
||||
for _, video := range req.Items {
|
||||
var (
|
||||
snippet = video.Snippet
|
||||
videoID = video.Id
|
||||
videoURL = fmt.Sprintf("https://youtube.com/watch?v=%s", video.Id)
|
||||
image = yt.selectThumbnail(snippet.Thumbnails, feed.Quality, videoID)
|
||||
)
|
||||
|
||||
// Parse date added to playlist / publication date
|
||||
dateStr := ""
|
||||
playlistItem, ok := playlist[video.Id]
|
||||
if ok {
|
||||
dateStr = playlistItem.PublishedAt
|
||||
} else {
|
||||
dateStr = snippet.PublishedAt
|
||||
}
|
||||
|
||||
seconds = int64(d.ToDuration().Seconds())
|
||||
pubDate, err := yt.parseDate(dateStr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse video publish date: %s", dateStr)
|
||||
}
|
||||
|
||||
// Sometimes YouTube retrun empty content defailt, use arbitrary one
|
||||
var seconds int64 = 1
|
||||
if video.ContentDetails != nil {
|
||||
// Parse duration
|
||||
d, err := duration.FromString(video.ContentDetails.Duration)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse duration %s", video.ContentDetails.Duration)
|
||||
}
|
||||
|
||||
seconds = int64(d.ToDuration().Seconds())
|
||||
}
|
||||
|
||||
var (
|
||||
order = strconv.FormatInt(playlistItem.Position, 10)
|
||||
size = yt.getSize(seconds, feed)
|
||||
)
|
||||
|
||||
feed.Episodes = append(feed.Episodes, &model.Episode{
|
||||
ID: video.Id,
|
||||
Title: snippet.Title,
|
||||
Description: snippet.Description,
|
||||
Thumbnail: image,
|
||||
Duration: seconds,
|
||||
Size: size,
|
||||
VideoURL: videoURL,
|
||||
PubDate: pubDate,
|
||||
Order: order,
|
||||
Status: model.EpisodeNew,
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
order = strconv.FormatInt(playlistItem.Position, 10)
|
||||
size = yt.getSize(seconds, feed)
|
||||
)
|
||||
|
||||
feed.Episodes = append(feed.Episodes, &model.Episode{
|
||||
ID: video.Id,
|
||||
Title: snippet.Title,
|
||||
Description: snippet.Description,
|
||||
Thumbnail: image,
|
||||
Duration: seconds,
|
||||
Size: size,
|
||||
VideoURL: videoURL,
|
||||
PubDate: pubDate,
|
||||
Order: order,
|
||||
Status: model.EpisodeNew,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -157,8 +157,12 @@ func (u *Manager) downloadEpisodes(ctx context.Context, feedConfig *feed.Config)
|
||||
|
||||
// Build the list of files to download
|
||||
if err := u.db.WalkEpisodes(ctx, feedID, func(episode *model.Episode) error {
|
||||
var (
|
||||
logger = log.WithFields(log.Fields{"episode_id": episode.ID})
|
||||
)
|
||||
if episode.Status != model.EpisodeNew && episode.Status != model.EpisodeError {
|
||||
// File already downloaded
|
||||
logger.Infof("skipping due to file already on disk")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user