Audible API GET-Library serializes to new DTO
This commit is contained in:
parent
27ffad346f
commit
8e4dcb1780
@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
using AudibleApi;
|
||||
using AudibleApi.Authentication;
|
||||
using AudibleApi.Authorization;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using DTOs;
|
||||
|
||||
namespace AudibleApiDomainService
|
||||
{
|
||||
@ -87,6 +87,46 @@ namespace AudibleApiDomainService
|
||||
}
|
||||
#endregion
|
||||
|
||||
public async Task ImportLibraryAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var items = await GetLibraryItemsAsync();
|
||||
//var (total, newEntries) = await ScrapingDomainServices.Indexer.IndexLibraryAsync(items);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// catch here for easier debugging
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<Item>> GetLibraryItemsAsync()
|
||||
{
|
||||
var allItems = new List<Item>();
|
||||
|
||||
for (var i = 1; ; i++)
|
||||
{
|
||||
var page = await _api.GetLibraryAsync(new LibraryOptions
|
||||
{
|
||||
NumberOfResultPerPage = 1000,
|
||||
PageNumber = i,
|
||||
PurchasedAfter = new DateTime(2000, 1, 1),
|
||||
ResponseGroups = LibraryOptions.ResponseGroupOptions.ALL_OPTIONS
|
||||
});
|
||||
|
||||
// important! use this convert method
|
||||
var libResult = LibraryApiV10.FromJson(page.ToString());
|
||||
|
||||
if (!libResult.Items.Any())
|
||||
break;
|
||||
|
||||
allItems.AddRange(libResult.Items);
|
||||
}
|
||||
|
||||
return allItems;
|
||||
}
|
||||
|
||||
//public async Task DownloadBookAsync(string asinToDownload)
|
||||
//{
|
||||
// // console example
|
||||
@ -107,40 +147,5 @@ namespace AudibleApiDomainService
|
||||
|
||||
// File.Delete(finalFile);
|
||||
//}
|
||||
|
||||
public async Task ImportLibraryAsync()
|
||||
{
|
||||
// json = api.GetLibrary
|
||||
var jObjects = await GetLibraryItemsAsync();
|
||||
// json => DTOs
|
||||
// indexer.update(DTOs)
|
||||
}
|
||||
|
||||
private async Task<List<JObject>> GetLibraryItemsAsync()
|
||||
{
|
||||
var allJsonResults = new List<JObject>();
|
||||
var pageNum = 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var page = await _api.GetLibraryAsync(new LibraryOptions
|
||||
{
|
||||
NumberOfResultPerPage = 1000,
|
||||
PageNumber = pageNum
|
||||
});
|
||||
|
||||
var debugStr = page.ToString();
|
||||
|
||||
var items = page["items"].Cast<JObject>();
|
||||
allJsonResults.AddRange(items);
|
||||
|
||||
if (!items.Any())
|
||||
break;
|
||||
|
||||
pageNum++;
|
||||
}
|
||||
|
||||
return allJsonResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,9 +48,13 @@ namespace CookieMonster
|
||||
value = Encoding.ASCII.GetString(decodedData);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// if something goes wrong in this step (eg: a cookie has an invalid filetime), then just skip this cookie
|
||||
col.Add(new CookieValue { Browser = "chrome", Domain = host_key, Name = name, Value = value, LastAccess = chromeTimeToDateTimeUtc(last_access_utc), Expires = chromeTimeToDateTimeUtc(expires_utc) });
|
||||
}
|
||||
|
||||
catch { }
|
||||
}
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
@ -32,5 +32,7 @@ namespace DTOs
|
||||
public List<(string categoryId, string categoryName)> Categories { get; } = new List<(string categoryId, string categoryName)>();
|
||||
|
||||
public List<SeriesEntry> Series { get; } = new List<SeriesEntry>();
|
||||
|
||||
public override string ToString() => $"[{ProductId}] {Title}";
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,4 +4,8 @@
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Dinah.Core\Dinah.Core\Dinah.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
1829
DTOs/LibraryApiV10.cs
Normal file
1829
DTOs/LibraryApiV10.cs
Normal file
File diff suppressed because it is too large
Load Diff
140
DTOs/LibraryApiV10.custom.cs
Normal file
140
DTOs/LibraryApiV10.custom.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core.Collections.Generic;
|
||||
|
||||
namespace DTOs
|
||||
{
|
||||
public partial class LibraryApiV10
|
||||
{
|
||||
public IEnumerable<Person> AuthorsDistinct => Items.GetAuthorsDistinct();
|
||||
public IEnumerable<Person> NarratorsDistinct => Items.GetNarratorsDistinct();
|
||||
|
||||
public override string ToString() => $"{Items.Length} {nameof(Items)}, {ResponseGroups.Length} {nameof(ResponseGroups)}";
|
||||
}
|
||||
public partial class Item
|
||||
{
|
||||
public string ProductId => Asin;
|
||||
public int LengthInMinutes => RuntimeLengthMin ?? 0;
|
||||
public string Description => PublisherSummary;
|
||||
public bool Episodes
|
||||
=> Relationships
|
||||
?.Where(r => r.RelationshipToProduct == RelationshipToProduct.Child && r.RelationshipType == RelationshipType.Episode)
|
||||
.Any()
|
||||
?? false;
|
||||
public string PictureId => ProductImages?.PictureId;
|
||||
public string SupplementUrls => PdfUrl.AbsoluteUri; // item.PdfUrl == item.PdfLink
|
||||
public DateTime DateAdded => PurchaseDate.UtcDateTime;
|
||||
|
||||
public float Product_OverallStars => Convert.ToSingle(Rating?.OverallDistribution.DisplayStars ?? 0);
|
||||
public float Product_PerformanceStars => Convert.ToSingle(Rating?.PerformanceDistribution.DisplayStars ?? 0);
|
||||
public float Product_StoryStars => Convert.ToSingle(Rating?.StoryDistribution.DisplayStars ?? 0);
|
||||
|
||||
public int MyUserRating_Overall => Convert.ToInt32(ProvidedReview?.Ratings.OverallRating ?? 0L);
|
||||
public int MyUserRating_Performance => Convert.ToInt32(ProvidedReview?.Ratings.PerformanceRating ?? 0L);
|
||||
public int MyUserRating_Story => Convert.ToInt32(ProvidedReview?.Ratings.StoryRating ?? 0L);
|
||||
|
||||
public bool IsAbridged
|
||||
=> FormatType.HasValue
|
||||
? FormatType == DTOs.FormatType.Abridged
|
||||
: false;
|
||||
public DateTime? DatePublished => IssueDate?.UtcDateTime; // item.IssueDate == item.ReleaseDate
|
||||
public string Publisher => PublisherName;
|
||||
|
||||
// future: need support for multiple categories
|
||||
public Ladder[] Categories => CategoryLadders?.FirstOrDefault()?.Ladder ?? new Ladder[0];
|
||||
|
||||
// LibraryDTO.DownloadBookLink will be handled differently. see api.DownloadAaxWorkaroundAsync(asin)
|
||||
|
||||
public IEnumerable<Person> AuthorsDistinct => Authors.DistinctBy(a => new { a.Name, a.Asin });
|
||||
public IEnumerable<Person> NarratorsDistinct => Narrators.DistinctBy(a => new { a.Name, a.Asin });
|
||||
|
||||
public override string ToString() => $"[{ProductId}] {Title}";
|
||||
}
|
||||
public partial class Person
|
||||
{
|
||||
public override string ToString() => $"{Name}";
|
||||
}
|
||||
public partial class AvailableCodec
|
||||
{
|
||||
public override string ToString() => $"{Name} {Format} {EnhancedCodec}";
|
||||
}
|
||||
public partial class CategoryLadder
|
||||
{
|
||||
public override string ToString() => Ladder.Select(l => l.CategoryName).Aggregate((a, b) => $"{a} | {b}");
|
||||
}
|
||||
public partial class Ladder
|
||||
{
|
||||
public string CategoryId => Id;
|
||||
public string CategoryName => Name;
|
||||
|
||||
public override string ToString() => $"[{CategoryId}] {CategoryName}";
|
||||
}
|
||||
public partial class ContentRating
|
||||
{
|
||||
public override string ToString() => $"{Steaminess}";
|
||||
}
|
||||
public partial class Review
|
||||
{
|
||||
public override string ToString() => $"{this.Title}";
|
||||
}
|
||||
public partial class GuidedResponse
|
||||
{
|
||||
//public override string ToString() =>
|
||||
}
|
||||
public partial class Ratings
|
||||
{
|
||||
public override string ToString() => $"{OverallRating:0.0}|{PerformanceRating:0.0}|{StoryRating:0.0}";
|
||||
}
|
||||
public partial class ReviewContentScores
|
||||
{
|
||||
public override string ToString() => $"Helpful={NumHelpfulVotes}, Unhelpful={NumUnhelpfulVotes}";
|
||||
}
|
||||
public partial class Plan
|
||||
{
|
||||
public override string ToString() => $"{PlanName}";
|
||||
}
|
||||
public partial class Price
|
||||
{
|
||||
public override string ToString() => $"List={ListPrice}, Lowest={LowestPrice}";
|
||||
}
|
||||
public partial class ListPriceClass
|
||||
{
|
||||
public override string ToString() => $"{Base}";
|
||||
}
|
||||
public partial class ProductImages
|
||||
{
|
||||
public string PictureId
|
||||
=> The500
|
||||
.AbsoluteUri // https://m.media-amazon.com/images/I/51T1NWIkR4L._SL500_.jpg?foo=bar
|
||||
?.Split('/').Last() // 51T1NWIkR4L._SL500_.jpg?foo=bar
|
||||
?.Split('.').First() // 51T1NWIkR4L
|
||||
;
|
||||
|
||||
public override string ToString() => $"{The500}";
|
||||
}
|
||||
public partial class Rating
|
||||
{
|
||||
public override string ToString() => $"{OverallDistribution}|{PerformanceDistribution}|{StoryDistribution}";
|
||||
}
|
||||
public partial class Distribution
|
||||
{
|
||||
public override string ToString() => $"{DisplayStars:0.0}";
|
||||
}
|
||||
public partial class Relationship
|
||||
{
|
||||
public override string ToString() => $"{RelationshipToProduct} {RelationshipType}";
|
||||
}
|
||||
public partial class Series
|
||||
{
|
||||
public string SeriesName => Title;
|
||||
public string SeriesId => Asin;
|
||||
public float Index
|
||||
=> string.IsNullOrEmpty(Sequence)
|
||||
? 0
|
||||
// eg: a book containing volumes 5,6,7,8 has sequence "5-8"
|
||||
: float.Parse(Sequence.Split('-').First());
|
||||
|
||||
public override string ToString() => $"[{SeriesId}] {SeriesName}";
|
||||
}
|
||||
}
|
||||
16
DTOs/LibraryApiV10Extensions.cs
Normal file
16
DTOs/LibraryApiV10Extensions.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dinah.Core.Collections.Generic;
|
||||
|
||||
namespace DTOs
|
||||
{
|
||||
public static class LibraryApiV10Extensions
|
||||
{
|
||||
public static IEnumerable<Person> GetAuthorsDistinct(this IEnumerable<Item> items)
|
||||
=> items.SelectMany(i => i.Authors).DistinctBy(a => new { a.Name, a.Asin });
|
||||
|
||||
public static IEnumerable<Person> GetNarratorsDistinct(this IEnumerable<Item> items)
|
||||
=> items.SelectMany(i => i.Narrators).DistinctBy(a => new { a.Name, a.Asin });
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,7 @@ namespace LibationWinForm
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
// initial call here will initiate config loading
|
||||
private Configuration config = Configuration.Instance;
|
||||
private Configuration config { get; } = Configuration.Instance;
|
||||
|
||||
private string backupsCountsLbl_Format;
|
||||
private string pdfsCountsLbl_Format;
|
||||
|
||||
@ -371,7 +371,7 @@ namespace ScrapingDomainServices
|
||||
{
|
||||
var series
|
||||
= context.Series.Local.SingleOrDefault(c => c.AudibleSeriesId == seriesId)
|
||||
?? context.Series.Add(new Series(new AudibleSeriesId(seriesId))).Entity;
|
||||
?? context.Series.Add(new DataLayer.Series(new AudibleSeriesId(seriesId))).Entity;
|
||||
series.UpdateName(seriesName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,8 +18,8 @@ LocalDb database files live at:
|
||||
Migrations
|
||||
==========
|
||||
|
||||
Visual Studio, EF Core 2.0
|
||||
--------------------------
|
||||
Visual Studio, EF Core
|
||||
----------------------
|
||||
View > Other Windows > Package Manager Console
|
||||
Default project: DataLayer
|
||||
Startup project: DataLayer
|
||||
|
||||
53
__TODO.txt
53
__TODO.txt
@ -1,14 +1,12 @@
|
||||
-- begin CONFIG FILES ---------------------------------------------------------------------------------------------------------------------
|
||||
try saving back into config
|
||||
.\appsettings.json should only be a pointer to the real settings file location: LibationSettings.json
|
||||
replace complex config saving throughout with new way in my ConsoleDependencyInjection solution
|
||||
all settings should be strongly typed
|
||||
re-create my shortcuts and bak
|
||||
|
||||
multiple files named "appsettings.json" will overwrite each other
|
||||
|
||||
libraries should avoid this generic name. ok for applications to use them
|
||||
|
||||
LibationSettings.json => appsettings.json
|
||||
also, move these settings into a strong type
|
||||
re-create my shortcuts and bak
|
||||
|
||||
Audible API
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\audible api\AudibleApi\_Tests\AudibleApi.Tests\bin\Debug\netcoreapp3.0\L1
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\audible api\AudibleApi\_Tests\AudibleApi.Tests\bin\Debug\netcoreapp3.0\ComputedTestValues
|
||||
@ -31,6 +29,9 @@ replace all scraping with audible api
|
||||
using var pageRetriever = websiteProcessorControl1.GetPageRetriever();
|
||||
jsonFilepaths = await DownloadLibrary.DownloadLibraryAsync(pageRetriever).ConfigureAwait(false);
|
||||
|
||||
move old DTOs back into scraping so it's easier to move them all to new legacy area
|
||||
library to return strongly typed LibraryApiV10
|
||||
|
||||
note: some json importing may still be relevant. might want to allow custom importing
|
||||
|
||||
scraping stuff to remove:
|
||||
@ -60,13 +61,20 @@ ADDED PROJECT REFERENCES
|
||||
3.1 Domain Internal Utilities
|
||||
InternalUtilities
|
||||
DTOs
|
||||
|
||||
Audible API. GET /1.0/library , GET /1.0/library/{asin}
|
||||
TONS of expensive conversion: GetLibraryAsync > string > JObject > string > LibraryApiV10
|
||||
-- end REPLACE SCRAPING WITH API ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- begin LEGACY ---------------------------------------------------------------------------------------------------------------------
|
||||
create legacy space to hold legacy code
|
||||
-- begin ENHANCEMENT, CATEGORIES ---------------------------------------------------------------------------------------------------------------------
|
||||
add support for multiple categories
|
||||
-- end ENHANCEMENT, CATEGORIES ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
scraping code
|
||||
"legacy inAudible wire-up code"
|
||||
-- begin LEGACY ---------------------------------------------------------------------------------------------------------------------
|
||||
retain legacy functionality in out-of-the-way area. turn on/off in settings?
|
||||
|
||||
> scraping code
|
||||
> "legacy inAudible wire-up code"
|
||||
-- end LEGACY ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- begin CLEAN UP ARCHITECTURE ---------------------------------------------------------------------------------------------------------------------
|
||||
@ -76,18 +84,19 @@ my ui sucks. it's also tightly coupled with biz logic. can't replace ui until bi
|
||||
-- begin UNIT TESTS ---------------------------------------------------------------------------------------------------------------------
|
||||
all "UNTESTED" code needs unit tests
|
||||
Turn into unit tests or demos
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core.Windows.Forms\UNTESTED\TextBoxBaseTextWriter.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\EnumerationExamples.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\EnumExt.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\_Collections\Generic\IEnumerable[T]Ext.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\_IO\MultiTextWriter.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\AudibleDotComAutomation\UNTESTED\Selenium.Examples.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\DataLayer\UNTESTED\_scratch pad\ScratchPad.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\LibationWinForm\UNTESTED\BookLiberation\ProcessorAutomationController.Examples.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\Scraping\UNTESTED\Rules\ScraperRules.Examples.cs
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\Scraping\UNTESTED\Selectors\ByFactory.Example.cs
|
||||
search 'example code' on:
|
||||
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\LibationWinForm\UNTESTED\Form1.cs
|
||||
TextBoxBaseTextWriter.cs
|
||||
EnumerationExamples.cs
|
||||
EnumExt.cs
|
||||
IEnumerable[T]Ext.cs
|
||||
MultiTextWriter.cs
|
||||
Selenium.Examples.cs
|
||||
ScratchPad.cs
|
||||
ProcessorAutomationController.Examples.cs
|
||||
ScraperRules.Examples.cs
|
||||
ByFactory.Example.cs
|
||||
search 'example code' on: LibationWinForm\...\Form1.cs
|
||||
EnumerationFlagsExtensions.EXAMPLES()
|
||||
// examples
|
||||
-- end UNIT TESTS ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- begin DECRYPTING ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user