diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmApi.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmApi.cs new file mode 100644 index 000000000..1767b1878 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmApi.cs @@ -0,0 +1,21 @@ + +using System.Collections.Generic; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmArtistList + { + public List Artist { get; set; } + } + + public class LastFmArtistResponse + { + public LastFmArtistList TopArtists { get; set; } + } + + public class LastFmArtist + { + public string Name { get; set; } + public string Mbid { get; set; } + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmParser.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmParser.cs new file mode 100644 index 000000000..333dd71c8 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmParser.cs @@ -0,0 +1,62 @@ +using NzbDrone.Core.ImportLists.Exceptions; +using NzbDrone.Core.Parser.Model; +using System.Collections.Generic; +using System.Net; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Serializer; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmParser : IParseImportListResponse + { + private ImportListResponse _importListResponse; + + public IList ParseResponse(ImportListResponse importListResponse) + { + _importListResponse = importListResponse; + + var items = new List(); + + if (!PreProcess(_importListResponse)) + { + return items; + } + + var jsonResponse = Json.Deserialize(_importListResponse.Content); + + if (jsonResponse == null) + { + return items; + } + + foreach (var item in jsonResponse.TopArtists.Artist) + { + items.AddIfNotNull(new ImportListItemInfo + { + Artist = item.Name, + ArtistMusicBrainzId = item.Mbid + }); + } + + return items; + } + + protected virtual bool PreProcess(ImportListResponse importListResponse) + { + if (importListResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { + throw new ImportListException(importListResponse, "Import List API call resulted in an unexpected StatusCode [{0}]", importListResponse.HttpResponse.StatusCode); + } + + if (importListResponse.HttpResponse.Headers.ContentType != null && importListResponse.HttpResponse.Headers.ContentType.Contains("text/json") && + importListResponse.HttpRequest.Headers.Accept != null && !importListResponse.HttpRequest.Headers.Accept.Contains("text/json")) + { + throw new ImportListException(importListResponse, "Import List responded with html content. Site is likely blocked or unavailable."); + } + + return true; + } + + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmTag.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTag.cs new file mode 100644 index 000000000..58606beca --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTag.cs @@ -0,0 +1,30 @@ +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmTag : HttpImportListBase + { + public override string Name => "Last.fm Tag"; + + public override int PageSize => 1000; + + public LastFmTag(IHttpClient httpClient, IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger) + : base(httpClient, importListStatusService, configService, parsingService, logger) + { + } + + public override IImportListRequestGenerator GetRequestGenerator() + { + return new LastFmTagRequestGenerator { Settings = Settings}; + } + + public override IParseImportListResponse GetParser() + { + return new LastFmParser(); + } + + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmTagRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTagRequestGenerator.cs new file mode 100644 index 000000000..9c7d03ee9 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTagRequestGenerator.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmTagRequestGenerator : IImportListRequestGenerator + { + public LastFmTagSettings Settings { get; set; } + + public int MaxPages { get; set; } + public int PageSize { get; set; } + + public LastFmTagRequestGenerator() + { + MaxPages = 1; + PageSize = 1000; + } + + public virtual ImportListPageableRequestChain GetListItems() + { + var pageableRequests = new ImportListPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests()); + + return pageableRequests; + } + + private IEnumerable GetPagedRequests() + { + yield return new ImportListRequest(string.Format("{0}&tag={1}&limit={2}&api_key={3}&format=json", Settings.BaseUrl.TrimEnd('/'), Settings.TagId, Settings.Count, Settings.ApiKey), HttpAccept.Json); + } + + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmTagSettings.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTagSettings.cs new file mode 100644 index 000000000..b9a4b41bc --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmTagSettings.cs @@ -0,0 +1,41 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmTagSettingsValidator : AbstractValidator + { + public LastFmTagSettingsValidator() + { + RuleFor(c => c.TagId).NotEmpty(); + RuleFor(c => c.Count).LessThanOrEqualTo(1000); + } + } + + public class LastFmTagSettings : IImportListSettings + { + private static readonly LastFmTagSettingsValidator Validator = new LastFmTagSettingsValidator(); + + public LastFmTagSettings() + { + BaseUrl = "http://ws.audioscrobbler.com/2.0/?method=tag.gettopartists"; + ApiKey = "204c76646d6020eee36bbc51a2fcd810"; + Count = 25; + } + + public string BaseUrl { get; set; } + public string ApiKey { get; set; } + + [FieldDefinition(0, Label = "Last.fm Tag", HelpText = "Tag to pull artists from")] + public string TagId { get; set; } + + [FieldDefinition(1, Label = "Count", HelpText = "Number of results to pull from list (Max 1000)", Type = FieldType.Number)] + public int Count { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.cs new file mode 100644 index 000000000..bb9143a9e --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUser.cs @@ -0,0 +1,30 @@ +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmUser : HttpImportListBase + { + public override string Name => "Last.fm User"; + + public override int PageSize => 1000; + + public LastFmUser(IHttpClient httpClient, IImportListStatusService importListStatusService, IConfigService configService, IParsingService parsingService, Logger logger) + : base(httpClient, importListStatusService, configService, parsingService, logger) + { + } + + public override IImportListRequestGenerator GetRequestGenerator() + { + return new LastFmUserRequestGenerator { Settings = Settings}; + } + + public override IParseImportListResponse GetParser() + { + return new LastFmParser(); + } + + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmUserRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUserRequestGenerator.cs new file mode 100644 index 000000000..97d541a22 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUserRequestGenerator.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmUserRequestGenerator : IImportListRequestGenerator + { + public LastFmUserSettings Settings { get; set; } + + public int MaxPages { get; set; } + public int PageSize { get; set; } + + public LastFmUserRequestGenerator() + { + MaxPages = 1; + PageSize = 1000; + } + + public virtual ImportListPageableRequestChain GetListItems() + { + var pageableRequests = new ImportListPageableRequestChain(); + + pageableRequests.Add(GetPagedRequests()); + + return pageableRequests; + } + + private IEnumerable GetPagedRequests() + { + yield return new ImportListRequest(string.Format("{0}&user={1}&limit={2}&api_key={3}&format=json", Settings.BaseUrl.TrimEnd('/'), Settings.UserId, Settings.Count, Settings.ApiKey), HttpAccept.Json); + } + + } +} diff --git a/src/NzbDrone.Core/ImportLists/LastFm/LastFmUserSettings.cs b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUserSettings.cs new file mode 100644 index 000000000..bd4cf9a24 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/LastFm/LastFmUserSettings.cs @@ -0,0 +1,41 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.ImportLists.LastFm +{ + public class LastFmSettingsValidator : AbstractValidator + { + public LastFmSettingsValidator() + { + RuleFor(c => c.UserId).NotEmpty(); + RuleFor(c => c.Count).LessThanOrEqualTo(1000); + } + } + + public class LastFmUserSettings : IImportListSettings + { + private static readonly LastFmSettingsValidator Validator = new LastFmSettingsValidator(); + + public LastFmUserSettings() + { + BaseUrl = "http://ws.audioscrobbler.com/2.0/?method=user.gettopartists"; + ApiKey = "204c76646d6020eee36bbc51a2fcd810"; + Count = 25; + } + + public string BaseUrl { get; set; } + public string ApiKey { get; set; } + + [FieldDefinition(0, Label = "Last.fm UserID", HelpText = "User to pull artists from, blank defaults to global top artists")] + public string UserId { get; set; } + + [FieldDefinition(1, Label = "Count", HelpText = "Number of results to pull from list (Max 1000)", Type = FieldType.Number)] + public int Count { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 4df412642..ebcb20d34 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -556,6 +556,14 @@ + + + + + + + +