mirror of
https://github.com/lidarr/lidarr.git
synced 2025-07-06 04:52:21 -07:00
Merge 7a7abf577e
into 91f06801ca
This commit is contained in:
commit
a179f322cd
7 changed files with 257 additions and 0 deletions
|
@ -5,6 +5,7 @@ namespace NzbDrone.Core.ImportLists
|
||||||
Program,
|
Program,
|
||||||
Spotify,
|
Spotify,
|
||||||
LastFm,
|
LastFm,
|
||||||
|
Youtube,
|
||||||
Other,
|
Other,
|
||||||
Advanced
|
Advanced
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
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<TSettings> : ImportListBase<TSettings>
|
||||||
|
where TSettings : YoutubePlaylistSettings, new()
|
||||||
|
{
|
||||||
|
protected YoutubeImportListBase(IImportListStatusService importListStatusService,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
Logger logger)
|
||||||
|
: base(importListStatusService, configService, parsingService, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ImportListType ListType => ImportListType.Youtube;
|
||||||
|
public override TimeSpan MinRefreshInterval => TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
public override IList<ImportListItemInfo> Fetch()
|
||||||
|
{
|
||||||
|
IList<YoutubeImportListItemInfo> releases = new List<YoutubeImportListItemInfo>();
|
||||||
|
|
||||||
|
using (var service = new YouTubeService(new BaseClientService.Initializer()
|
||||||
|
{
|
||||||
|
ApiKey = Settings.YoutubeApiKey,
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
releases = Fetch(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CleanupListItems(releases);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract IList<YoutubeImportListItemInfo> Fetch(YouTubeService service);
|
||||||
|
|
||||||
|
protected override void Test(List<ValidationFailure> failures)
|
||||||
|
{
|
||||||
|
failures.AddIfNotNull(TestConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ValidationFailure TestConnection(YouTubeService service);
|
||||||
|
|
||||||
|
private ValidationFailure TestConnection()
|
||||||
|
{
|
||||||
|
using (var service = new YouTubeService(new BaseClientService.Initializer()
|
||||||
|
{
|
||||||
|
ApiKey = Settings.YoutubeApiKey,
|
||||||
|
}))
|
||||||
|
{
|
||||||
|
return TestConnection(service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
121
src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs
Normal file
121
src/NzbDrone.Core/ImportLists/Youtube/YoutubePlaylist.cs
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using DryIoc.ImTools;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
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<YoutubePlaylistSettings>
|
||||||
|
{
|
||||||
|
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<YoutubeImportListItemInfo> Fetch(YouTubeService service)
|
||||||
|
{
|
||||||
|
return Settings.PlaylistIds.SelectMany(x => Fetch(service, x)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList<YoutubeImportListItemInfo> Fetch(YouTubeService service, string playlistId)
|
||||||
|
{
|
||||||
|
var results = new List<YoutubeImportListItemInfo>();
|
||||||
|
var req = service.PlaylistItems.List("contentDetails,snippet");
|
||||||
|
req.PlaylistId = playlistId;
|
||||||
|
req.MaxResults = 50;
|
||||||
|
var page = 0;
|
||||||
|
var playlist = req.Execute();
|
||||||
|
do
|
||||||
|
{
|
||||||
|
page++;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
playlist = req.Execute();
|
||||||
|
}
|
||||||
|
while (playlist.NextPageToken != null && page < 10);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ImportLists.Youtube
|
||||||
|
{
|
||||||
|
public class YoutubePlaylistSettingsValidator : YoutubeSettingsBaseValidator<YoutubePlaylistSettings>
|
||||||
|
{
|
||||||
|
public YoutubePlaylistSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.PlaylistIds).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class YoutubePlaylistSettings : YoutubeSettingsBase<YoutubePlaylistSettings>
|
||||||
|
{
|
||||||
|
protected override AbstractValidator<YoutubePlaylistSettings> Validator =>
|
||||||
|
new YoutubePlaylistSettingsValidator();
|
||||||
|
|
||||||
|
public YoutubePlaylistSettings()
|
||||||
|
{
|
||||||
|
PlaylistIds = System.Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Youtube API key", Type = FieldType.Textbox)]
|
||||||
|
public string YoutubeApiKey { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Playlists", Type = FieldType.Textbox)]
|
||||||
|
public IEnumerable<string> PlaylistIds { get; set; }
|
||||||
|
}
|
||||||
|
}
|
23
src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs
Normal file
23
src/NzbDrone.Core/ImportLists/Youtube/YoutubeSettingsBase.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.ImportLists.Youtube
|
||||||
|
{
|
||||||
|
public class YoutubeSettingsBaseValidator<TSettings> : AbstractValidator<TSettings>
|
||||||
|
where TSettings : YoutubeSettingsBase<TSettings>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class YoutubeSettingsBase<TSettings> : IImportListSettings
|
||||||
|
where TSettings : YoutubeSettingsBase<TSettings>
|
||||||
|
{
|
||||||
|
protected virtual AbstractValidator<TSettings> Validator => new YoutubeSettingsBaseValidator<TSettings>();
|
||||||
|
|
||||||
|
public string BaseUrl { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
<PackageReference Include="Dapper" Version="2.0.151" />
|
<PackageReference Include="Dapper" Version="2.0.151" />
|
||||||
<PackageReference Include="Diacritical.Net" Version="1.0.4" />
|
<PackageReference Include="Diacritical.Net" Version="1.0.4" />
|
||||||
<PackageReference Include="Equ" Version="2.3.0" />
|
<PackageReference Include="Equ" Version="2.3.0" />
|
||||||
|
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.69.0.3680" />
|
||||||
<PackageReference Include="MailKit" Version="4.8.0" />
|
<PackageReference Include="MailKit" Version="4.8.0" />
|
||||||
<PackageReference Include="Polly" Version="8.5.2" />
|
<PackageReference Include="Polly" Version="8.5.2" />
|
||||||
<PackageReference Include="System.Text.Json" Version="6.0.10" />
|
<PackageReference Include="System.Text.Json" Version="6.0.10" />
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue