Add ContentType.Parent

Import Series parent when only individual episodes are in library
This commit is contained in:
Michael Bucari-Tovo 2022-06-07 15:27:18 -06:00
parent 30e6deeeaa
commit c48eacd9af
4 changed files with 83 additions and 21 deletions

View File

@ -1,11 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AudibleApi; using AudibleApi;
using AudibleApi.Common; using AudibleApi.Common;
using Dinah.Core; using Dinah.Core;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Polly; using Polly;
using Polly.Retry; using Polly.Retry;
@ -129,7 +132,7 @@ namespace AudibleUtilities
await foreach (var item in Api.GetLibraryItemAsyncEnumerable(libraryOptions)) await foreach (var item in Api.GetLibraryItemAsyncEnumerable(libraryOptions))
{ {
if (item.IsEpisodes && importEpisodes) if ((item.IsEpisodes || item.IsSeriesParent) && importEpisodes)
{ {
//Get child episodes asynchronously and await all at the end //Get child episodes asynchronously and await all at the end
getChildEpisodesTasks.Add(getChildEpisodesAsync(concurrencySemaphore, item)); getChildEpisodesTasks.Add(getChildEpisodesAsync(concurrencySemaphore, item));
@ -173,17 +176,66 @@ namespace AudibleUtilities
{ {
Serilog.Log.Logger.Debug("Beginning episode scan for {parent}", parent); Serilog.Log.Logger.Debug("Beginning episode scan for {parent}", parent);
var children = await getEpisodeChildrenAsync(parent); List<Item> children;
if (!children.Any()) if (parent.IsEpisodes)
{ {
//The parent is the only episode in the podcase series, //The 'parent' is a single episode that was added to the library.
//so the parent is its own child. //Get the episode's parent and add it to the database.
parent.Series = new Series[] { new Series { Asin = parent.Asin, Sequence = RelationshipToProduct.Parent, Title = parent.TitleWithSubtitle } };
children.Add(parent); Serilog.Log.Logger.Debug("Supplied Parent is an episode. Beginning parent scan for {parent}", parent);
return children;
children = new() { parent };
var parentAsins = parent.Relationships
.Where(r => r.RelationshipToProduct == RelationshipToProduct.Parent)
.Select(p => p.Asin);
var seriesParents = await Api.GetCatalogProductsAsync(parentAsins, CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
int numSeriesParents = seriesParents.Count(p => p.IsSeriesParent);
if (numSeriesParents != 1)
{
//There should only ever be 1 top-level parent per episode. If not, log
//and throw so we can figure out what to do about those special cases.
JsonSerializerSettings Settings = new()
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
var ex = new ApplicationException($"Found {numSeriesParents} parents for {parent.Asin}");
Serilog.Log.Logger.Error(ex, $"Episode Product:\r\n{JsonConvert.SerializeObject(parent, Formatting.None, Settings)}");
throw ex;
} }
var realParent = seriesParents.Single(p => p.IsSeriesParent);
realParent.PurchaseDate = parent.PurchaseDate;
Serilog.Log.Logger.Debug("Completed parent scan for {parent}", parent);
parent = realParent;
}
else
{
children = await getEpisodeChildrenAsync(parent);
if (!children.Any())
return new();
}
//A series parent will always have exactly 1 Series
parent.Series = new Series[]
{
new Series
{
Asin = parent.Asin,
Sequence = "-1",
Title = parent.TitleWithSubtitle
}
};
foreach (var child in children) foreach (var child in children)
{ {
// use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime // use parent's 'DateAdded'. DateAdded is just a convenience prop for: PurchaseDate.UtcDateTime
@ -199,17 +251,10 @@ namespace AudibleUtilities
Title = parent.TitleWithSubtitle Title = parent.TitleWithSubtitle
} }
}; };
// overload (read: abuse) IsEpisodes flag
child.Relationships = new Relationship[]
{
new Relationship
{
RelationshipToProduct = RelationshipToProduct.Child,
RelationshipType = RelationshipType.Episode
}
};
} }
children.Add(parent);
Serilog.Log.Logger.Debug("Completed episode scan for {parent}", parent); Serilog.Log.Logger.Debug("Completed episode scan for {parent}", parent);
return children; return children;

View File

@ -16,8 +16,14 @@ namespace DataLayer
} }
} }
// enum will be easier than bool to extend later // enum will be easier than bool to extend later.
public enum ContentType { Unknown = 0, Product = 1, Episode = 2 } public enum ContentType
{
Unknown = 0,
Product = 1,
Episode = 2,
Parent = 4,
}
public class Book public class Book
{ {

View File

@ -75,7 +75,7 @@ namespace DtoImporterService
{ {
var item = importItem.DtoItem; var item = importItem.DtoItem;
var contentType = item.IsEpisodes ? DataLayer.ContentType.Episode : DataLayer.ContentType.Product; var contentType = GetContentType(item);
// absence of authors is very rare, but possible // absence of authors is very rare, but possible
if (!item.Authors?.Any() ?? true) if (!item.Authors?.Any() ?? true)
@ -184,5 +184,15 @@ namespace DtoImporterService
} }
} }
} }
private static DataLayer.ContentType GetContentType(Item item)
{
if (item.IsEpisodes)
return DataLayer.ContentType.Episode;
else if (item.IsSeriesParent)
return DataLayer.ContentType.Parent;
else
return DataLayer.ContentType.Product;
}
} }
} }

View File

@ -29,7 +29,8 @@ namespace FileLiberator
public IEnumerable<LibraryBook> GetValidLibraryBooks(IEnumerable<LibraryBook> library) public IEnumerable<LibraryBook> GetValidLibraryBooks(IEnumerable<LibraryBook> library)
=> library.Where(libraryBook => => library.Where(libraryBook =>
Validate(libraryBook) Validate(libraryBook)
&& (libraryBook.Book.ContentType != ContentType.Episode || LibationFileManager.Configuration.Instance.DownloadEpisodes) && libraryBook.Book.ContentType != ContentType.Parent
&& (libraryBook.Book.ContentType != ContentType.Episode || Configuration.Instance.DownloadEpisodes)
); );
public async Task<StatusHandler> ProcessSingleAsync(LibraryBook libraryBook, bool validate) public async Task<StatusHandler> ProcessSingleAsync(LibraryBook libraryBook, bool validate)