parent
6dd87fd348
commit
ee4322b9cc
@ -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<TSettings> : ImportListBase<TSettings>
|
||||
where TSettings : YoutubeSettingsBase<TSettings>, 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<ImportListItemInfo> Fetch()
|
||||
{
|
||||
IList<YoutubeImportListItemInfo> releases = new List<YoutubeImportListItemInfo>();
|
||||
|
||||
using (var service = new YouTubeService(new BaseClientService.Initializer()
|
||||
{
|
||||
ApiKey = ""
|
||||
}))
|
||||
{
|
||||
releases = Fetch(service);
|
||||
}
|
||||
|
||||
// TODO remap
|
||||
|
||||
return CleanupListItems(releases);
|
||||
}
|
||||
|
||||
public IList<YoutubeImportListItemInfo> MapYoutubeReleases(IList<YoutubeImportListItemInfo> items)
|
||||
{
|
||||
return items;
|
||||
}
|
||||
|
||||
public abstract IList<YoutubeImportListItemInfo> Fetch(YouTubeService service);
|
||||
|
||||
protected override void Test(List<ValidationFailure> failures)
|
||||
{
|
||||
failures.AddIfNotNull(TestConnection());
|
||||
}
|
||||
|
||||
private ValidationFailure TestConnection()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<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)
|
||||
{
|
||||
// TODO playlist
|
||||
var results = new List<YoutubeImportListItemInfo>();
|
||||
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<string, string> query)
|
||||
{
|
||||
Console.Out.WriteLine(action);
|
||||
|
||||
return base.RequestAction(action, query);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
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>();
|
||||
}
|
||||
|
||||
// public override string Scope => "playlist-read-private";
|
||||
|
||||
[FieldDefinition(1, Label = "Playlists", Type = FieldType.Textbox)]
|
||||
public IEnumerable<string> PlaylistIds { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Youtube
|
||||
{
|
||||
public class YoutubeSettingsBaseValidator<TSettings> : AbstractValidator<TSettings>
|
||||
where TSettings : YoutubeSettingsBase<TSettings>
|
||||
{
|
||||
public YoutubeSettingsBaseValidator()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
public class YoutubeSettingsBase<TSettings> : IImportListSettings
|
||||
where TSettings : YoutubeSettingsBase<TSettings>
|
||||
{
|
||||
protected virtual AbstractValidator<TSettings> Validator => new YoutubeSettingsBaseValidator<TSettings>();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue