From ee4322b9cc540dc4714d267aca64b9348bff34eb Mon Sep 17 00:00:00 2001 From: Tyler Mayoff Date: Thu, 13 Mar 2025 20:56:52 -0400 Subject: [PATCH 1/6] Added YTM Import List --- .../ImportLists/ImportListType.cs | 1 + .../Youtube/YoutubeImportListBase.cs | 67 +++++++++++ .../Youtube/YoutubeImportListItemInfo.cs | 14 +++ .../ImportLists/Youtube/YoutubePlaylist.cs | 113 ++++++++++++++++++ .../Youtube/YoutubePlaylistSettings.cs | 31 +++++ .../Youtube/YoutubeSettingsBase.cs | 48 ++++++++ src/NzbDrone.Core/Lidarr.Core.csproj | 1 + 7 files changed, 275 insertions(+) create mode 100644 src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs create mode 100644 src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListItemInfo.cs create mode 100644 src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs create mode 100644 src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs create mode 100644 src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs diff --git a/src/NzbDrone.Core/ImportLists/ImportListType.cs b/src/NzbDrone.Core/ImportLists/ImportListType.cs index 6dd76ef31..d3be3b655 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListType.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListType.cs @@ -5,6 +5,7 @@ namespace NzbDrone.Core.ImportLists Program, Spotify, LastFm, + Youtube, Other, Advanced } diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs new file mode 100644 index 000000000..b0d46bb2c --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using FluentValidation.Results; +using Google.Apis.Services; +using Google.Apis.YouTube.v3; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.ImportLists.Youtube +{ + public abstract class YoutubeImportListBase : ImportListBase + where TSettings : YoutubeSettingsBase, new() + { + private IHttpClient _httpClient; + + protected YoutubeImportListBase(IImportListStatusService importListStatusService, + IConfigService configService, + IParsingService parsingService, + IHttpClient httpClient, + Logger logger) + : base(importListStatusService, configService, parsingService, logger) + { + _httpClient = httpClient; + } + + public override ImportListType ListType => ImportListType.Youtube; + public override TimeSpan MinRefreshInterval => TimeSpan.FromSeconds(1); + + public override IList Fetch() + { + IList releases = new List(); + + using (var service = new YouTubeService(new BaseClientService.Initializer() + { + ApiKey = "" + })) + { + releases = Fetch(service); + } + + // TODO remap + + return CleanupListItems(releases); + } + + public IList MapYoutubeReleases(IList items) + { + return items; + } + + public abstract IList Fetch(YouTubeService service); + + protected override void Test(List failures) + { + failures.AddIfNotNull(TestConnection()); + } + + private ValidationFailure TestConnection() + { + return null; + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListItemInfo.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListItemInfo.cs new file mode 100644 index 000000000..6c75bc5e6 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListItemInfo.cs @@ -0,0 +1,14 @@ +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.ImportLists.Youtube; + +public class YoutubeImportListItemInfo : ImportListItemInfo +{ + public string ArtistYoutubeId { get; set; } + public string AlbumYoutubeId { get; set; } + + public override string ToString() + { + return string.Format("[{0}] {1}", ArtistYoutubeId, AlbumYoutubeId); + } +} diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs new file mode 100644 index 000000000..476ffc267 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using DryIoc.ImTools; +using Google.Apis.YouTube.v3; +using Google.Apis.YouTube.v3.Data; +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Core.ImportLists.Youtube +{ + public class YoutubePlaylist : YoutubeImportListBase + { + public YoutubePlaylist(IImportListStatusService importListStatusService, + IImportListRepository importListRepository, + IConfigService configService, + IParsingService parsingService, + IHttpClient httpClient, + Logger logger) + : base(importListStatusService, configService, parsingService, httpClient, logger) + { + } + + public override string Name => "Youtube Playlists"; + + public override IList Fetch(YouTubeService service) + { + return Settings.PlaylistIds.SelectMany(x => Fetch(service, x)).ToList(); + } + + public IList Fetch(YouTubeService service, string playlistId) + { + // TODO playlist + var results = new List(); + var req = service.PlaylistItems.List("contentDetails,snippet"); + req.PlaylistId = playlistId; + req.MaxResults = 50; + + while (true) + { + var playlist = req.Execute(); + req.PageToken = playlist.NextPageToken; + + foreach (var song in playlist.Items) + { + var listItem = new YoutubeImportListItemInfo(); + var topicChannel = song.Snippet.VideoOwnerChannelTitle.EndsWith("- Topic"); + if (topicChannel) + { + ParseTopicChannel(song, ref listItem); + } + else + { + // No album name just video + listItem.ReleaseDate = ParseDateTimeOffset(song); + listItem.Artist = song.Snippet.VideoOwnerChannelTitle; + } + + results.Add(listItem); + } + + if (playlist.NextPageToken == null) + { + break; + } + } + + return results; + } + + public void ParseTopicChannel(PlaylistItem playlistItem, ref YoutubeImportListItemInfo listItem) + { + var description = playlistItem.Snippet.Description; + var descArgs = description.Split("\n\n"); + + listItem.Artist = playlistItem.Snippet.VideoOwnerChannelTitle.Contains("- Topic") ? + playlistItem.Snippet.VideoOwnerChannelTitle[.. (playlistItem.Snippet.VideoOwnerChannelTitle.LastIndexOf('-') - 1)] : + playlistItem.Snippet.VideoOwnerChannelTitle; + listItem.Album = descArgs[2]; + + if (descArgs.Any(s => s.StartsWith("Released on:"))) + { + // Custom release date + var release = descArgs.FindFirst(s => s.StartsWith("Released on:")); + var date = release.Substring(release.IndexOf(':') + 1); + listItem.ReleaseDate = DateTime.Parse(date); + } + else + { + listItem.ReleaseDate = ParseDateTimeOffset(playlistItem); + } + } + + private DateTime ParseDateTimeOffset(PlaylistItem playlistItem) + { + return (playlistItem.ContentDetails.VideoPublishedAtDateTimeOffset ?? DateTimeOffset.UnixEpoch).DateTime; + } + + private DateTime ParseYoutubeDate(string date, PlaylistItem song) + { + return DateTime.Now; + } + + public override object RequestAction(string action, IDictionary query) + { + Console.Out.WriteLine(action); + + return base.RequestAction(action, query); + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs new file mode 100644 index 000000000..9e3a4fb51 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using FluentValidation; +using NzbDrone.Core.Annotations; + +namespace NzbDrone.Core.ImportLists.Youtube +{ + public class YoutubePlaylistSettingsValidator : YoutubeSettingsBaseValidator + { + public YoutubePlaylistSettingsValidator() + : base() + { + RuleFor(c => c.PlaylistIds).NotEmpty(); + } + } + + public class YoutubePlaylistSettings : YoutubeSettingsBase + { + protected override AbstractValidator Validator => + new YoutubePlaylistSettingsValidator(); + + public YoutubePlaylistSettings() + { + PlaylistIds = System.Array.Empty(); + } + + // public override string Scope => "playlist-read-private"; + + [FieldDefinition(1, Label = "Playlists", Type = FieldType.Textbox)] + public IEnumerable PlaylistIds { get; set; } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs new file mode 100644 index 000000000..ee8d03d4a --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs @@ -0,0 +1,48 @@ +using System; +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.ImportLists.Youtube +{ + public class YoutubeSettingsBaseValidator : AbstractValidator + where TSettings : YoutubeSettingsBase + { + public YoutubeSettingsBaseValidator() + { + // TODO + } + } + + public class YoutubeSettingsBase : IImportListSettings + where TSettings : YoutubeSettingsBase + { + protected virtual AbstractValidator Validator => new YoutubeSettingsBaseValidator(); + + public YoutubeSettingsBase() + { + BaseUrl = "todo"; + } + + public string BaseUrl { get; set; } + + public virtual string Scope => ""; + + [FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] + public string AccessToken { get; set; } + + [FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] + public string RefreshToken { get; set; } + + [FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] + public DateTime Expires { get; set; } + + // [FieldDefinition(99, Label = "Authenticate with Google", Type = FieldType.OAuth)] + // public string SignIn { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate((TSettings)this)); + } + } +} diff --git a/src/NzbDrone.Core/Lidarr.Core.csproj b/src/NzbDrone.Core/Lidarr.Core.csproj index a56bfb295..bcf4e45b7 100644 --- a/src/NzbDrone.Core/Lidarr.Core.csproj +++ b/src/NzbDrone.Core/Lidarr.Core.csproj @@ -6,6 +6,7 @@ + From 1e39f6cd9a875845ed98c83bab86e960f10886db Mon Sep 17 00:00:00 2001 From: Tyler Mayoff Date: Thu, 15 May 2025 21:30:52 -0400 Subject: [PATCH 2/6] cleanup --- src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs | 2 -- src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs index 476ffc267..6528c2c13 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs @@ -105,8 +105,6 @@ namespace NzbDrone.Core.ImportLists.Youtube public override object RequestAction(string action, IDictionary query) { - Console.Out.WriteLine(action); - return base.RequestAction(action, query); } } diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs index ee8d03d4a..177bb62ca 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs @@ -19,11 +19,6 @@ namespace NzbDrone.Core.ImportLists.Youtube { protected virtual AbstractValidator Validator => new YoutubeSettingsBaseValidator(); - public YoutubeSettingsBase() - { - BaseUrl = "todo"; - } - public string BaseUrl { get; set; } public virtual string Scope => ""; From f8fc85c8ddce9c8139572539837eaecb2f2861d8 Mon Sep 17 00:00:00 2001 From: Tyler Mayoff Date: Thu, 19 Jun 2025 19:42:56 -0400 Subject: [PATCH 3/6] cleanup --- .../ImportLists/Youtube/YoutubeImportListBase.cs | 2 -- src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs | 5 ----- .../ImportLists/Youtube/YoutubeSettingsBase.cs | 7 ------- 3 files changed, 14 deletions(-) diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs index b0d46bb2c..83ba1d2cc 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs @@ -42,8 +42,6 @@ namespace NzbDrone.Core.ImportLists.Youtube releases = Fetch(service); } - // TODO remap - return CleanupListItems(releases); } diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs index 6528c2c13..417f41b05 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs @@ -98,11 +98,6 @@ namespace NzbDrone.Core.ImportLists.Youtube return (playlistItem.ContentDetails.VideoPublishedAtDateTimeOffset ?? DateTimeOffset.UnixEpoch).DateTime; } - private DateTime ParseYoutubeDate(string date, PlaylistItem song) - { - return DateTime.Now; - } - public override object RequestAction(string action, IDictionary query) { return base.RequestAction(action, query); diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs index 177bb62ca..76015da92 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs @@ -8,10 +8,6 @@ namespace NzbDrone.Core.ImportLists.Youtube public class YoutubeSettingsBaseValidator : AbstractValidator where TSettings : YoutubeSettingsBase { - public YoutubeSettingsBaseValidator() - { - // TODO - } } public class YoutubeSettingsBase : IImportListSettings @@ -32,9 +28,6 @@ namespace NzbDrone.Core.ImportLists.Youtube [FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] public DateTime Expires { get; set; } - // [FieldDefinition(99, Label = "Authenticate with Google", Type = FieldType.OAuth)] - // public string SignIn { get; set; } - public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate((TSettings)this)); From 33f7ab8cd12ec145b41399f230150743e4e02816 Mon Sep 17 00:00:00 2001 From: Tyler Mayoff Date: Thu, 19 Jun 2025 21:12:10 -0400 Subject: [PATCH 4/6] cleanup and added api key as a setting, this will probably need documenting how to set this up in the Google Cloud Console --- .../Youtube/YoutubeImportListBase.cs | 4 ++-- .../ImportLists/Youtube/YoutubePlaylist.cs | 20 ++++++------------- .../Youtube/YoutubePlaylistSettings.cs | 3 ++- .../Youtube/YoutubeSettingsBase.cs | 13 ------------ 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs index 83ba1d2cc..01bb42105 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs @@ -13,7 +13,7 @@ using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.ImportLists.Youtube { public abstract class YoutubeImportListBase : ImportListBase - where TSettings : YoutubeSettingsBase, new() + where TSettings : YoutubePlaylistSettings, new() { private IHttpClient _httpClient; @@ -36,7 +36,7 @@ namespace NzbDrone.Core.ImportLists.Youtube using (var service = new YouTubeService(new BaseClientService.Initializer() { - ApiKey = "" + ApiKey = Settings.YoutubeApiKey, })) { releases = Fetch(service); diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs index 417f41b05..1a0027f79 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs @@ -32,15 +32,15 @@ namespace NzbDrone.Core.ImportLists.Youtube public IList Fetch(YouTubeService service, string playlistId) { - // TODO playlist var results = new List(); var req = service.PlaylistItems.List("contentDetails,snippet"); req.PlaylistId = playlistId; req.MaxResults = 50; - - while (true) + var page = 0; + var playlist = req.Execute(); + do { - var playlist = req.Execute(); + page++; req.PageToken = playlist.NextPageToken; foreach (var song in playlist.Items) @@ -61,12 +61,9 @@ namespace NzbDrone.Core.ImportLists.Youtube results.Add(listItem); } - if (playlist.NextPageToken == null) - { - break; - } + playlist = req.Execute(); } - + while (playlist.NextPageToken != null && page < 10); return results; } @@ -97,10 +94,5 @@ namespace NzbDrone.Core.ImportLists.Youtube { return (playlistItem.ContentDetails.VideoPublishedAtDateTimeOffset ?? DateTimeOffset.UnixEpoch).DateTime; } - - public override object RequestAction(string action, IDictionary query) - { - return base.RequestAction(action, query); - } } } diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs index 9e3a4fb51..12c5eab17 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylistSettings.cs @@ -23,7 +23,8 @@ namespace NzbDrone.Core.ImportLists.Youtube PlaylistIds = System.Array.Empty(); } - // public override string Scope => "playlist-read-private"; + [FieldDefinition(1, Label = "Youtube API key", Type = FieldType.Textbox)] + public string YoutubeApiKey { get; set; } [FieldDefinition(1, Label = "Playlists", Type = FieldType.Textbox)] public IEnumerable PlaylistIds { get; set; } diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs index 76015da92..73fd4191b 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs @@ -1,6 +1,4 @@ -using System; using FluentValidation; -using NzbDrone.Core.Annotations; using NzbDrone.Core.Validation; namespace NzbDrone.Core.ImportLists.Youtube @@ -17,17 +15,6 @@ namespace NzbDrone.Core.ImportLists.Youtube public string BaseUrl { get; set; } - public virtual string Scope => ""; - - [FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] - public string AccessToken { get; set; } - - [FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] - public string RefreshToken { get; set; } - - [FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] - public DateTime Expires { get; set; } - public NzbDroneValidationResult Validate() { return new NzbDroneValidationResult(Validator.Validate((TSettings)this)); From 85302bded04c86d3fd77800b2c2296443de56267 Mon Sep 17 00:00:00 2001 From: Tyler Mayoff Date: Thu, 19 Jun 2025 21:40:35 -0400 Subject: [PATCH 5/6] test connection by trying to get 1 result from each provided playlist --- .../Youtube/YoutubeImportListBase.cs | 10 ++++++++- .../ImportLists/Youtube/YoutubePlaylist.cs | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs index 01bb42105..dd97727d5 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs @@ -57,9 +57,17 @@ namespace NzbDrone.Core.ImportLists.Youtube failures.AddIfNotNull(TestConnection()); } + public abstract ValidationFailure TestConnection(YouTubeService service); + private ValidationFailure TestConnection() { - return null; + using (var service = new YouTubeService(new BaseClientService.Initializer() + { + ApiKey = Settings.YoutubeApiKey, + })) + { + return TestConnection(service); + } } } } diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs index 1a0027f79..726550e51 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using DryIoc.ImTools; +using FluentValidation.Results; using Google.Apis.YouTube.v3; using Google.Apis.YouTube.v3.Data; using NLog; @@ -94,5 +95,25 @@ namespace NzbDrone.Core.ImportLists.Youtube { return (playlistItem.ContentDetails.VideoPublishedAtDateTimeOffset ?? DateTimeOffset.UnixEpoch).DateTime; } + + public override ValidationFailure TestConnection(YouTubeService service) + { + foreach (var id in Settings.PlaylistIds) + { + try + { + var req = service.PlaylistItems.List("contentDetails,snippet"); + req.PlaylistId = id; + req.MaxResults = 1; + req.Execute(); + } + catch (Exception e) + { + return new ValidationFailure(string.Empty, e.Message); + } + } + + return null; + } } } From 7a7abf577e9eed914a65757e880ac76234a0da2b Mon Sep 17 00:00:00 2001 From: Tyler Mayoff Date: Thu, 19 Jun 2025 21:45:37 -0400 Subject: [PATCH 6/6] cleanup and throttle API calls --- .../ImportLists/Youtube/YoutubeImportListBase.cs | 8 -------- src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs | 2 ++ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs index dd97727d5..391abc22d 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubeImportListBase.cs @@ -15,8 +15,6 @@ namespace NzbDrone.Core.ImportLists.Youtube public abstract class YoutubeImportListBase : ImportListBase where TSettings : YoutubePlaylistSettings, new() { - private IHttpClient _httpClient; - protected YoutubeImportListBase(IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, @@ -24,7 +22,6 @@ namespace NzbDrone.Core.ImportLists.Youtube Logger logger) : base(importListStatusService, configService, parsingService, logger) { - _httpClient = httpClient; } public override ImportListType ListType => ImportListType.Youtube; @@ -45,11 +42,6 @@ namespace NzbDrone.Core.ImportLists.Youtube return CleanupListItems(releases); } - public IList MapYoutubeReleases(IList items) - { - return items; - } - public abstract IList Fetch(YouTubeService service); protected override void Test(List failures) diff --git a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs index 726550e51..52f88988b 100644 --- a/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs +++ b/src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using DryIoc.ImTools; using FluentValidation.Results; using Google.Apis.YouTube.v3; @@ -62,6 +63,7 @@ namespace NzbDrone.Core.ImportLists.Youtube results.Add(listItem); } + Thread.Sleep(TimeSpan.FromSeconds(1)); playlist = req.Execute(); } while (playlist.NextPageToken != null && page < 10);