Audible API GET-Library serializes to new DTO

This commit is contained in:
Robert McRackan 2019-10-25 16:02:05 -04:00
parent 27ffad346f
commit 8e4dcb1780
11 changed files with 2078 additions and 69 deletions

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using AudibleApi; using AudibleApi;
using AudibleApi.Authentication; using AudibleApi.Authentication;
using AudibleApi.Authorization; using AudibleApi.Authorization;
using Newtonsoft.Json.Linq; using DTOs;
namespace AudibleApiDomainService namespace AudibleApiDomainService
{ {
@ -87,6 +87,46 @@ namespace AudibleApiDomainService
} }
#endregion #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) //public async Task DownloadBookAsync(string asinToDownload)
//{ //{
// // console example // // console example
@ -107,40 +147,5 @@ namespace AudibleApiDomainService
// File.Delete(finalFile); // 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;
}
} }
} }

View File

@ -48,9 +48,13 @@ namespace CookieMonster
value = Encoding.ASCII.GetString(decodedData); 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) }); 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; return col;
} }

View File

@ -32,5 +32,7 @@ namespace DTOs
public List<(string categoryId, string categoryName)> Categories { get; } = new List<(string categoryId, string categoryName)>(); public List<(string categoryId, string categoryName)> Categories { get; } = new List<(string categoryId, string categoryName)>();
public List<SeriesEntry> Series { get; } = new List<SeriesEntry>(); public List<SeriesEntry> Series { get; } = new List<SeriesEntry>();
public override string ToString() => $"[{ProductId}] {Title}";
} }
} }

View File

@ -4,4 +4,8 @@
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Dinah.Core\Dinah.Core\Dinah.Core.csproj" />
</ItemGroup>
</Project> </Project>

1829
DTOs/LibraryApiV10.cs Normal file

File diff suppressed because it is too large Load Diff

View 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}";
}
}

View 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 });
}
}

View File

@ -15,7 +15,7 @@ namespace LibationWinForm
public partial class Form1 : Form public partial class Form1 : Form
{ {
// initial call here will initiate config loading // initial call here will initiate config loading
private Configuration config = Configuration.Instance; private Configuration config { get; } = Configuration.Instance;
private string backupsCountsLbl_Format; private string backupsCountsLbl_Format;
private string pdfsCountsLbl_Format; private string pdfsCountsLbl_Format;

View File

@ -371,7 +371,7 @@ namespace ScrapingDomainServices
{ {
var series var series
= context.Series.Local.SingleOrDefault(c => c.AudibleSeriesId == seriesId) = 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); series.UpdateName(seriesName);
} }
} }

View File

@ -18,8 +18,8 @@ LocalDb database files live at:
Migrations Migrations
========== ==========
Visual Studio, EF Core 2.0 Visual Studio, EF Core
-------------------------- ----------------------
View > Other Windows > Package Manager Console View > Other Windows > Package Manager Console
Default project: DataLayer Default project: DataLayer
Startup project: DataLayer Startup project: DataLayer

View File

@ -1,14 +1,12 @@
-- begin CONFIG FILES --------------------------------------------------------------------------------------------------------------------- -- 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 multiple files named "appsettings.json" will overwrite each other
libraries should avoid this generic name. ok for applications to use them 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 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\L1
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\audible api\AudibleApi\_Tests\AudibleApi.Tests\bin\Debug\netcoreapp3.0\ComputedTestValues 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(); using var pageRetriever = websiteProcessorControl1.GetPageRetriever();
jsonFilepaths = await DownloadLibrary.DownloadLibraryAsync(pageRetriever).ConfigureAwait(false); 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 note: some json importing may still be relevant. might want to allow custom importing
scraping stuff to remove: scraping stuff to remove:
@ -60,13 +61,20 @@ ADDED PROJECT REFERENCES
3.1 Domain Internal Utilities 3.1 Domain Internal Utilities
InternalUtilities InternalUtilities
DTOs 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 --------------------------------------------------------------------------------------------------------------------- -- end REPLACE SCRAPING WITH API ---------------------------------------------------------------------------------------------------------------------
-- begin LEGACY --------------------------------------------------------------------------------------------------------------------- -- begin ENHANCEMENT, CATEGORIES ---------------------------------------------------------------------------------------------------------------------
create legacy space to hold legacy code add support for multiple categories
-- end ENHANCEMENT, CATEGORIES ---------------------------------------------------------------------------------------------------------------------
scraping code -- begin LEGACY ---------------------------------------------------------------------------------------------------------------------
"legacy inAudible wire-up code" retain legacy functionality in out-of-the-way area. turn on/off in settings?
> scraping code
> "legacy inAudible wire-up code"
-- end LEGACY --------------------------------------------------------------------------------------------------------------------- -- end LEGACY ---------------------------------------------------------------------------------------------------------------------
-- begin CLEAN UP ARCHITECTURE --------------------------------------------------------------------------------------------------------------------- -- 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 --------------------------------------------------------------------------------------------------------------------- -- begin UNIT TESTS ---------------------------------------------------------------------------------------------------------------------
all "UNTESTED" code needs unit tests all "UNTESTED" code needs unit tests
Turn into unit tests or demos 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 TextBoxBaseTextWriter.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\EnumerationExamples.cs EnumerationExamples.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\EnumExt.cs EnumExt.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\_Collections\Generic\IEnumerable[T]Ext.cs IEnumerable[T]Ext.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Dinah.Core\Dinah.Core\UNTESTED\_IO\MultiTextWriter.cs MultiTextWriter.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\AudibleDotComAutomation\UNTESTED\Selenium.Examples.cs Selenium.Examples.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\DataLayer\UNTESTED\_scratch pad\ScratchPad.cs ScratchPad.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\LibationWinForm\UNTESTED\BookLiberation\ProcessorAutomationController.Examples.cs ProcessorAutomationController.Examples.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\Scraping\UNTESTED\Rules\ScraperRules.Examples.cs ScraperRules.Examples.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\Scraping\UNTESTED\Selectors\ByFactory.Example.cs ByFactory.Example.cs
search 'example code' on: search 'example code' on: LibationWinForm\...\Form1.cs
C:\Dropbox\Dinah's folder\coding\_NET\Visual Studio 2019\Libation\LibationWinForm\UNTESTED\Form1.cs EnumerationFlagsExtensions.EXAMPLES()
// examples
-- end UNIT TESTS --------------------------------------------------------------------------------------------------------------------- -- end UNIT TESTS ---------------------------------------------------------------------------------------------------------------------
-- begin DECRYPTING --------------------------------------------------------------------------------------------------------------------- -- begin DECRYPTING ---------------------------------------------------------------------------------------------------------------------