diff --git a/AudibleApiDomainService/AudibleApiLibationClient.cs b/AudibleApiDomainService/AudibleApiLibationClient.cs index a7ff8a73..93128734 100644 --- a/AudibleApiDomainService/AudibleApiLibationClient.cs +++ b/AudibleApiDomainService/AudibleApiLibationClient.cs @@ -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> GetLibraryItemsAsync() + { + var allItems = new List(); + + 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> GetLibraryItemsAsync() - { - var allJsonResults = new List(); - 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(); - allJsonResults.AddRange(items); - - if (!items.Any()) - break; - - pageNum++; - } - - return allJsonResults; - } } } diff --git a/CookieMonster/UNTESTED/Browsers/Chrome.cs b/CookieMonster/UNTESTED/Browsers/Chrome.cs index 8a77f2c7..fc55e613 100644 --- a/CookieMonster/UNTESTED/Browsers/Chrome.cs +++ b/CookieMonster/UNTESTED/Browsers/Chrome.cs @@ -48,15 +48,19 @@ namespace CookieMonster value = Encoding.ASCII.GetString(decodedData); } - col.Add(new CookieValue { Browser = "chrome", Domain = host_key, Name = name, Value = value, LastAccess = chromeTimeToDateTimeUtc(last_access_utc), Expires = chromeTimeToDateTimeUtc(expires_utc) }); + 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; } - // Chrome uses 1601-01-01 00:00:00 UTC as the epoch (ie the starting point for the millisecond time counter). - // this is the same as "FILETIME" in Win32 except FILETIME uses 100ns ticks instead of ms. - private static DateTime chromeTimeToDateTimeUtc(long time) => DateTime.SpecifyKind(DateTime.FromFileTime(time * 10), DateTimeKind.Utc); - } + // Chrome uses 1601-01-01 00:00:00 UTC as the epoch (ie the starting point for the millisecond time counter). + // this is the same as "FILETIME" in Win32 except FILETIME uses 100ns ticks instead of ms. + private static DateTime chromeTimeToDateTimeUtc(long time) => DateTime.SpecifyKind(DateTime.FromFileTime(time * 10), DateTimeKind.Utc); + } } diff --git a/DTOs/BookDetailDTO.cs b/DTOs/BookDetailDTO.cs index 28a90da5..33026283 100644 --- a/DTOs/BookDetailDTO.cs +++ b/DTOs/BookDetailDTO.cs @@ -32,5 +32,7 @@ namespace DTOs public List<(string categoryId, string categoryName)> Categories { get; } = new List<(string categoryId, string categoryName)>(); public List Series { get; } = new List(); - } + + public override string ToString() => $"[{ProductId}] {Title}"; + } } diff --git a/DTOs/DTOs.csproj b/DTOs/DTOs.csproj index d4c395e8..af0258e8 100644 --- a/DTOs/DTOs.csproj +++ b/DTOs/DTOs.csproj @@ -4,4 +4,8 @@ netstandard2.1 + + + + diff --git a/DTOs/LibraryApiV10.cs b/DTOs/LibraryApiV10.cs new file mode 100644 index 00000000..1b450dc1 --- /dev/null +++ b/DTOs/LibraryApiV10.cs @@ -0,0 +1,1829 @@ +// INSTRUCTIONS FOR GENERATING +// =========================== +// library api with all response_groups +// https://app.quicktype.io/ +// left pane: +// name=LibraryApiV10 +// paste in full output of library api with all response_groups +// right pane: +// namespace: DTOs +// array +// complete +// make all properties optional. this adds unneeded stuff (see next step) but also needed stuff +// remove all instances of ", NullValueHandling = NullValueHandling.Ignore" +// PurchaseDate is not optional. remove "?" from type +// Status is not optional. remove "?" from type +// RuntimeLengthMin type: int? +// add comments to class +// Audible API. GET /1.0/library , GET /1.0/library/{asin} +// rename Author class to Person + +// +// +// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do: +// +// using DTOs; +// +// var libraryApiV10 = LibraryApiV10.FromJson(jsonString); + +namespace DTOs +{ + using System; + using System.Globalization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + /// + /// Audible API. GET /1.0/library , GET /1.0/library/{asin} + /// + public partial class LibraryApiV10 + { + [JsonProperty("items")] + public Item[] Items { get; set; } + + [JsonProperty("response_groups")] + public string[] ResponseGroups { get; set; } + } + + public partial class Item + { + [JsonProperty("asin")] + public string Asin { get; set; } + + [JsonProperty("audible_editors_summary")] + public string AudibleEditorsSummary { get; set; } + + [JsonProperty("authors")] + public Person[] Authors { get; set; } + + [JsonProperty("availability")] + public object Availability { get; set; } + + [JsonProperty("available_codecs")] + public AvailableCodec[] AvailableCodecs { get; set; } + + [JsonProperty("badge_types")] + public object BadgeTypes { get; set; } + + [JsonProperty("buying_options")] + public object BuyingOptions { get; set; } + + [JsonProperty("category_ladders")] + public CategoryLadder[] CategoryLadders { get; set; } + + [JsonProperty("claim_code_url")] + public object ClaimCodeUrl { get; set; } + + [JsonProperty("content_delivery_type")] + public ContentDeliveryType? ContentDeliveryType { get; set; } + + [JsonProperty("content_level")] + public object ContentLevel { get; set; } + + [JsonProperty("content_rating")] + public ContentRating ContentRating { get; set; } + + [JsonProperty("content_type")] + public ContentType? ContentType { get; set; } + + [JsonProperty("copyright")] + public object Copyright { get; set; } + + [JsonProperty("credits_required")] + public object CreditsRequired { get; set; } + + [JsonProperty("customer_reviews")] + public Review[] CustomerReviews { get; set; } + + [JsonProperty("date_first_available")] + public object DateFirstAvailable { get; set; } + + [JsonProperty("distribution_rights_region")] + public object DistributionRightsRegion { get; set; } + + [JsonProperty("editorial_reviews")] + public string[] EditorialReviews { get; set; } + + [JsonProperty("extended_product_description")] + public object ExtendedProductDescription { get; set; } + + [JsonProperty("format_type")] + public FormatType? FormatType { get; set; } + + [JsonProperty("generic_keyword")] + public object GenericKeyword { get; set; } + + [JsonProperty("has_children")] + public bool? HasChildren { get; set; } + + [JsonProperty("image_url")] + public object ImageUrl { get; set; } + + [JsonProperty("invites_remaining")] + public object InvitesRemaining { get; set; } + + [JsonProperty("is_adult_product")] + public bool? IsAdultProduct { get; set; } + + [JsonProperty("is_buyable")] + public object IsBuyable { get; set; } + + [JsonProperty("is_downloaded")] + public bool? IsDownloaded { get; set; } + + [JsonProperty("is_finished")] + public object IsFinished { get; set; } + + [JsonProperty("is_in_wishlist")] + public object IsInWishlist { get; set; } + + [JsonProperty("is_listenable")] + public bool? IsListenable { get; set; } + + [JsonProperty("is_preorderable")] + public object IsPreorderable { get; set; } + + [JsonProperty("is_returnable")] + public bool? IsReturnable { get; set; } + + [JsonProperty("is_searchable")] + public object IsSearchable { get; set; } + + [JsonProperty("is_ws4v_companion_asin_owned")] + public object IsWs4VCompanionAsinOwned { get; set; } + + [JsonProperty("is_ws4v_enabled")] + public object IsWs4VEnabled { get; set; } + + [JsonProperty("isbn")] + public object Isbn { get; set; } + + [JsonProperty("issue_date")] + public DateTimeOffset? IssueDate { get; set; } + + [JsonProperty("language")] + public Language? Language { get; set; } + + [JsonProperty("member_giving_status")] + public object MemberGivingStatus { get; set; } + + [JsonProperty("merchandising_description")] + public object MerchandisingDescription { get; set; } + + [JsonProperty("merchandising_summary")] + public string MerchandisingSummary { get; set; } + + [JsonProperty("narration_accent")] + public object NarrationAccent { get; set; } + + [JsonProperty("narrators")] + public Person[] Narrators { get; set; } + + [JsonProperty("order_id")] + public object OrderId { get; set; } + + [JsonProperty("order_item_id")] + public object OrderItemId { get; set; } + + [JsonProperty("origin_asin")] + public string OriginAsin { get; set; } + + [JsonProperty("origin_id")] + public string OriginId { get; set; } + + [JsonProperty("origin_marketplace")] + public OriginMarketplace? OriginMarketplace { get; set; } + + [JsonProperty("origin_type")] + public OriginType? OriginType { get; set; } + + [JsonProperty("part_number")] + public object PartNumber { get; set; } + + [JsonProperty("pdf_link")] + public Uri PdfLink { get; set; } + + [JsonProperty("pdf_url")] + public Uri PdfUrl { get; set; } + + [JsonProperty("percent_complete")] + public double? PercentComplete { get; set; } + + [JsonProperty("periodical_info")] + public object PeriodicalInfo { get; set; } + + [JsonProperty("plans")] + public Plan[] Plans { get; set; } + + [JsonProperty("platinum_keywords")] + public object PlatinumKeywords { get; set; } + + [JsonProperty("price")] + public Price Price { get; set; } + + [JsonProperty("product_images")] + public ProductImages ProductImages { get; set; } + + [JsonProperty("product_page_url")] + public object ProductPageUrl { get; set; } + + [JsonProperty("product_site_launch_date")] + public object ProductSiteLaunchDate { get; set; } + + [JsonProperty("provided_review")] + public Review ProvidedReview { get; set; } + + [JsonProperty("publication_name")] + public string PublicationName { get; set; } + + [JsonProperty("publisher_name")] + public string PublisherName { get; set; } + + [JsonProperty("publisher_summary")] + public string PublisherSummary { get; set; } + + [JsonProperty("purchase_date")] + public DateTimeOffset PurchaseDate { get; set; } + + [JsonProperty("rating")] + public Rating Rating { get; set; } + + [JsonProperty("read_along_support")] + public object ReadAlongSupport { get; set; } + + [JsonProperty("relationships")] + public Relationship[] Relationships { get; set; } + + [JsonProperty("release_date")] + public DateTimeOffset? ReleaseDate { get; set; } + + [JsonProperty("runtime_length_min")] + public int? RuntimeLengthMin { get; set; } + + [JsonProperty("sample_url")] + public Uri SampleUrl { get; set; } + + [JsonProperty("series")] + public Series[] Series { get; set; } + + [JsonProperty("sku")] + public string Sku { get; set; } + + [JsonProperty("sku_lite")] + public string SkuLite { get; set; } + + [JsonProperty("status")] + public Status Status { get; set; } + + [JsonProperty("subscription_asins")] + public object SubscriptionAsins { get; set; } + + [JsonProperty("subtitle")] + public string Subtitle { get; set; } + + [JsonProperty("thesaurus_subject_keywords")] + public string[] ThesaurusSubjectKeywords { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("ws4v_companion_asin")] + public object Ws4VCompanionAsin { get; set; } + } + + public partial class Person + { + [JsonProperty("asin")] + public string Asin { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + } + + public partial class AvailableCodec + { + [JsonProperty("enhanced_codec")] + public EnhancedCodec? EnhancedCodec { get; set; } + + [JsonProperty("format")] + public AvailableCodecFormat? Format { get; set; } + + [JsonProperty("is_kindle_enhanced")] + public bool? IsKindleEnhanced { get; set; } + + [JsonProperty("name")] + public Name? Name { get; set; } + } + + public partial class CategoryLadder + { + [JsonProperty("date_product_available_in_category")] + public object DateProductAvailableInCategory { get; set; } + + [JsonProperty("ladder")] + public Ladder[] Ladder { get; set; } + + [JsonProperty("root")] + public Root? Root { get; set; } + } + + public partial class Ladder + { + [JsonProperty("category_presentation")] + public object CategoryPresentation { get; set; } + + [JsonProperty("children")] + public object Children { get; set; } + + [JsonProperty("default_offline_storage_days_count")] + public object DefaultOfflineStorageDaysCount { get; set; } + + [JsonProperty("default_offline_storage_item_count")] + public object DefaultOfflineStorageItemCount { get; set; } + + [JsonProperty("description")] + public object Description { get; set; } + + [JsonProperty("header")] + public object Header { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("images")] + public object Images { get; set; } + + [JsonProperty("initial_download_days_count")] + public object InitialDownloadDaysCount { get; set; } + + [JsonProperty("initial_download_item_count")] + public object InitialDownloadItemCount { get; set; } + + [JsonProperty("is_new")] + public object IsNew { get; set; } + + [JsonProperty("localized_name")] + public object LocalizedName { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("products")] + public object Products { get; set; } + + [JsonProperty("promote_upsell")] + public object PromoteUpsell { get; set; } + + [JsonProperty("suppress_download_option")] + public object SuppressDownloadOption { get; set; } + + [JsonProperty("suppress_release_date")] + public object SuppressReleaseDate { get; set; } + } + + public partial class ContentRating + { + [JsonProperty("steaminess")] + [JsonConverter(typeof(ParseStringConverter))] + public long? Steaminess { get; set; } + } + + public partial class Review + { + [JsonProperty("asin")] + public string Asin { get; set; } + + [JsonProperty("author_id")] + public string AuthorId { get; set; } + + [JsonProperty("author_name")] + public string AuthorName { get; set; } + + [JsonProperty("body")] + public string Body { get; set; } + + [JsonProperty("customer_vote")] + public object CustomerVote { get; set; } + + [JsonProperty("format")] + public CustomerReviewFormat? Format { get; set; } + + [JsonProperty("guided_responses")] + public GuidedResponse[] GuidedResponses { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("location")] + public string Location { get; set; } + + [JsonProperty("ratings")] + public Ratings Ratings { get; set; } + + [JsonProperty("review_content_scores")] + public ReviewContentScores ReviewContentScores { get; set; } + + [JsonProperty("submission_date")] + public DateTimeOffset? SubmissionDate { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + } + + public partial class GuidedResponse + { + [JsonProperty("answer")] + public string Answer { get; set; } + + [JsonProperty("id")] + [JsonConverter(typeof(ParseStringConverter))] + public long? Id { get; set; } + + [JsonProperty("question")] + public string Question { get; set; } + + [JsonProperty("question_type")] + public QuestionType? QuestionType { get; set; } + } + + public partial class Ratings + { + [JsonProperty("overall_rating")] + public long? OverallRating { get; set; } + + [JsonProperty("performance_rating")] + public long? PerformanceRating { get; set; } + + [JsonProperty("story_rating")] + public long? StoryRating { get; set; } + } + + public partial class ReviewContentScores + { + [JsonProperty("content_quality")] + public long? ContentQuality { get; set; } + + [JsonProperty("num_helpful_votes")] + public long? NumHelpfulVotes { get; set; } + + [JsonProperty("num_unhelpful_votes")] + public long? NumUnhelpfulVotes { get; set; } + } + + public partial class Plan + { + [JsonProperty("detail_plan_names")] + public object DetailPlanNames { get; set; } + + [JsonProperty("end_date")] + public DateTimeOffset? EndDate { get; set; } + + [JsonProperty("plan_name")] + public PlanName? PlanName { get; set; } + + [JsonProperty("start_date")] + public DateTimeOffset? StartDate { get; set; } + } + + public partial class Price + { + [JsonProperty("credit_price")] + public long? CreditPrice { get; set; } + + [JsonProperty("is_buy_for_free_eligible")] + public object IsBuyForFreeEligible { get; set; } + + [JsonProperty("is_credit_price_eligible")] + public object IsCreditPriceEligible { get; set; } + + [JsonProperty("is_free_eligible")] + public object IsFreeEligible { get; set; } + + [JsonProperty("is_ws4v_upsell_eligible")] + public object IsWs4VUpsellEligible { get; set; } + + [JsonProperty("list_price")] + public ListPriceClass ListPrice { get; set; } + + [JsonProperty("lowest_price")] + public ListPriceClass LowestPrice { get; set; } + + [JsonProperty("ws4v_upsell_price")] + public ListPriceClass Ws4VUpsellPrice { get; set; } + } + + public partial class ListPriceClass + { + [JsonProperty("base")] + public double? Base { get; set; } + + [JsonProperty("currency_code")] + public CurrencyCode? CurrencyCode { get; set; } + + [JsonProperty("merchant_id")] + public MerchantId? MerchantId { get; set; } + + [JsonProperty("type")] + public TypeEnum? Type { get; set; } + } + + public partial class ProductImages + { + [JsonProperty("500")] + public Uri The500 { get; set; } + } + + public partial class Rating + { + [JsonProperty("num_reviews")] + public long? NumReviews { get; set; } + + [JsonProperty("overall_distribution")] + public Distribution OverallDistribution { get; set; } + + [JsonProperty("performance_distribution")] + public Distribution PerformanceDistribution { get; set; } + + [JsonProperty("story_distribution")] + public Distribution StoryDistribution { get; set; } + } + + public partial class Distribution + { + [JsonProperty("average_rating")] + public double? AverageRating { get; set; } + + [JsonProperty("display_average_rating")] + public string DisplayAverageRating { get; set; } + + [JsonProperty("display_stars")] + public double? DisplayStars { get; set; } + + [JsonProperty("num_five_star_ratings")] + public long? NumFiveStarRatings { get; set; } + + [JsonProperty("num_four_star_ratings")] + public long? NumFourStarRatings { get; set; } + + [JsonProperty("num_one_star_ratings")] + public long? NumOneStarRatings { get; set; } + + [JsonProperty("num_ratings")] + public long? NumRatings { get; set; } + + [JsonProperty("num_three_star_ratings")] + public long? NumThreeStarRatings { get; set; } + + [JsonProperty("num_two_star_ratings")] + public long? NumTwoStarRatings { get; set; } + } + + public partial class Relationship + { + [JsonProperty("asin")] + public string Asin { get; set; } + + [JsonProperty("relationship_to_product")] + public RelationshipToProduct? RelationshipToProduct { get; set; } + + [JsonProperty("relationship_type")] + public RelationshipType? RelationshipType { get; set; } + + [JsonProperty("sequence")] + public string Sequence { get; set; } + + [JsonProperty("sku")] + public string Sku { get; set; } + + [JsonProperty("sku_lite")] + public string SkuLite { get; set; } + + [JsonProperty("sort")] + [JsonConverter(typeof(ParseStringConverter))] + public long? Sort { get; set; } + } + + public partial class Series + { + [JsonProperty("asin")] + public string Asin { get; set; } + + [JsonProperty("sequence")] + public string Sequence { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + } + + public enum EnhancedCodec { Aax, Format4, Lc128_44100_Stereo, Lc32_22050_Stereo, Lc64_22050_Stereo, Lc64_44100_Stereo, Mp42232, Mp42264, Mp444128, Mp44464, Piff2232, Piff2264, Piff44128, Piff4464 }; + + public enum AvailableCodecFormat { Enhanced, Format4 }; + + public enum Name { Aax, Aax22_32, Aax22_64, Aax44_128, Aax44_64, Format4, Mp422_32, Mp422_64, Mp444_128, Mp444_64, Piff22_32, Piff22_64, Piff44_128, Piff44_64 }; + + public enum Root { EditorsPicks, ExploreBy, Genres, InstitutionsHpMarketing, RodizioBuckets, RodizioGenres, ShortsPrime }; + + public enum ContentDeliveryType { MultiPartBook, Periodical, SinglePartBook }; + + public enum ContentType { Episode, Lecture, Meditation, Misc, Performance, Product, RadioTvProgram, Show, Speech }; + + public enum CustomerReviewFormat { Freeform, Guided }; + + public enum QuestionType { Genre, Misc, Overall, Performance, Story }; + + public enum FormatType { Abridged, OriginalRecording, Unabridged }; + + public enum Language { English }; + + public enum OriginMarketplace { Af2M0Kc94Rcea }; + + public enum OriginType { AudibleChannels, AudibleComplimentaryOriginal, Purchase }; + + public enum PlanName { AyceRomance, ComplimentaryOriginalMemberBenefit, Radio, Rodizio, SpecialBenefit }; + + public enum CurrencyCode { Usd }; + + public enum MerchantId { A2Zo8Jx97D5Mn9 }; + + public enum TypeEnum { List, Member, Sale, Ws4VUpsell }; + + public enum RelationshipToProduct { Child, Parent }; + + public enum RelationshipType { Component, Episode, MerchantTitleAuthority, Season, Series }; + + public enum Status { Active }; + + public enum ThesaurusSubjectKeyword { AdventurersExplorers, AlternateHistory, Comedians, Contemporary, Dramatizations, EasternReligions, LaConfidential, LiteratureAndFiction, Medicine, Spirituality, StandupComedy, Storytelling, SwordSorcery, Workouts }; + + public partial class LibraryApiV10 + { + public static LibraryApiV10 FromJson(string json) => JsonConvert.DeserializeObject(json, DTOs.Converter.Settings); + } + + public static class Serialize + { + public static string ToJson(this LibraryApiV10 self) => JsonConvert.SerializeObject(self, DTOs.Converter.Settings); + } + + internal static class Converter + { + public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings + { + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + DateParseHandling = DateParseHandling.None, + Converters = + { + EnhancedCodecConverter.Singleton, + AvailableCodecFormatConverter.Singleton, + NameConverter.Singleton, + RootConverter.Singleton, + ContentDeliveryTypeConverter.Singleton, + ContentTypeConverter.Singleton, + CustomerReviewFormatConverter.Singleton, + QuestionTypeConverter.Singleton, + FormatTypeConverter.Singleton, + LanguageConverter.Singleton, + OriginMarketplaceConverter.Singleton, + OriginTypeConverter.Singleton, + PlanNameConverter.Singleton, + CurrencyCodeConverter.Singleton, + MerchantIdConverter.Singleton, + TypeEnumConverter.Singleton, + RelationshipToProductConverter.Singleton, + RelationshipTypeConverter.Singleton, + StatusConverter.Singleton, + ThesaurusSubjectKeywordConverter.Singleton, + new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal } + }, + }; + } + + internal class EnhancedCodecConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(EnhancedCodec) || t == typeof(EnhancedCodec?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "LC_128_44100_stereo": + return EnhancedCodec.Lc128_44100_Stereo; + case "LC_32_22050_stereo": + return EnhancedCodec.Lc32_22050_Stereo; + case "LC_64_22050_stereo": + return EnhancedCodec.Lc64_22050_Stereo; + case "LC_64_44100_stereo": + return EnhancedCodec.Lc64_44100_Stereo; + case "aax": + return EnhancedCodec.Aax; + case "format4": + return EnhancedCodec.Format4; + case "mp42232": + return EnhancedCodec.Mp42232; + case "mp42264": + return EnhancedCodec.Mp42264; + case "mp444128": + return EnhancedCodec.Mp444128; + case "mp44464": + return EnhancedCodec.Mp44464; + case "piff2232": + return EnhancedCodec.Piff2232; + case "piff2264": + return EnhancedCodec.Piff2264; + case "piff44128": + return EnhancedCodec.Piff44128; + case "piff4464": + return EnhancedCodec.Piff4464; + } + throw new Exception("Cannot unmarshal type EnhancedCodec"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (EnhancedCodec)untypedValue; + switch (value) + { + case EnhancedCodec.Lc128_44100_Stereo: + serializer.Serialize(writer, "LC_128_44100_stereo"); + return; + case EnhancedCodec.Lc32_22050_Stereo: + serializer.Serialize(writer, "LC_32_22050_stereo"); + return; + case EnhancedCodec.Lc64_22050_Stereo: + serializer.Serialize(writer, "LC_64_22050_stereo"); + return; + case EnhancedCodec.Lc64_44100_Stereo: + serializer.Serialize(writer, "LC_64_44100_stereo"); + return; + case EnhancedCodec.Aax: + serializer.Serialize(writer, "aax"); + return; + case EnhancedCodec.Format4: + serializer.Serialize(writer, "format4"); + return; + case EnhancedCodec.Mp42232: + serializer.Serialize(writer, "mp42232"); + return; + case EnhancedCodec.Mp42264: + serializer.Serialize(writer, "mp42264"); + return; + case EnhancedCodec.Mp444128: + serializer.Serialize(writer, "mp444128"); + return; + case EnhancedCodec.Mp44464: + serializer.Serialize(writer, "mp44464"); + return; + case EnhancedCodec.Piff2232: + serializer.Serialize(writer, "piff2232"); + return; + case EnhancedCodec.Piff2264: + serializer.Serialize(writer, "piff2264"); + return; + case EnhancedCodec.Piff44128: + serializer.Serialize(writer, "piff44128"); + return; + case EnhancedCodec.Piff4464: + serializer.Serialize(writer, "piff4464"); + return; + } + throw new Exception("Cannot marshal type EnhancedCodec"); + } + + public static readonly EnhancedCodecConverter Singleton = new EnhancedCodecConverter(); + } + + internal class AvailableCodecFormatConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(AvailableCodecFormat) || t == typeof(AvailableCodecFormat?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "Enhanced": + return AvailableCodecFormat.Enhanced; + case "Format4": + return AvailableCodecFormat.Format4; + } + throw new Exception("Cannot unmarshal type AvailableCodecFormat"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (AvailableCodecFormat)untypedValue; + switch (value) + { + case AvailableCodecFormat.Enhanced: + serializer.Serialize(writer, "Enhanced"); + return; + case AvailableCodecFormat.Format4: + serializer.Serialize(writer, "Format4"); + return; + } + throw new Exception("Cannot marshal type AvailableCodecFormat"); + } + + public static readonly AvailableCodecFormatConverter Singleton = new AvailableCodecFormatConverter(); + } + + internal class NameConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(Name) || t == typeof(Name?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "aax": + return Name.Aax; + case "aax_22_32": + return Name.Aax22_32; + case "aax_22_64": + return Name.Aax22_64; + case "aax_44_128": + return Name.Aax44_128; + case "aax_44_64": + return Name.Aax44_64; + case "format4": + return Name.Format4; + case "mp4_22_32": + return Name.Mp422_32; + case "mp4_22_64": + return Name.Mp422_64; + case "mp4_44_128": + return Name.Mp444_128; + case "mp4_44_64": + return Name.Mp444_64; + case "piff_22_32": + return Name.Piff22_32; + case "piff_22_64": + return Name.Piff22_64; + case "piff_44_128": + return Name.Piff44_128; + case "piff_44_64": + return Name.Piff44_64; + } + throw new Exception("Cannot unmarshal type Name"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (Name)untypedValue; + switch (value) + { + case Name.Aax: + serializer.Serialize(writer, "aax"); + return; + case Name.Aax22_32: + serializer.Serialize(writer, "aax_22_32"); + return; + case Name.Aax22_64: + serializer.Serialize(writer, "aax_22_64"); + return; + case Name.Aax44_128: + serializer.Serialize(writer, "aax_44_128"); + return; + case Name.Aax44_64: + serializer.Serialize(writer, "aax_44_64"); + return; + case Name.Format4: + serializer.Serialize(writer, "format4"); + return; + case Name.Mp422_32: + serializer.Serialize(writer, "mp4_22_32"); + return; + case Name.Mp422_64: + serializer.Serialize(writer, "mp4_22_64"); + return; + case Name.Mp444_128: + serializer.Serialize(writer, "mp4_44_128"); + return; + case Name.Mp444_64: + serializer.Serialize(writer, "mp4_44_64"); + return; + case Name.Piff22_32: + serializer.Serialize(writer, "piff_22_32"); + return; + case Name.Piff22_64: + serializer.Serialize(writer, "piff_22_64"); + return; + case Name.Piff44_128: + serializer.Serialize(writer, "piff_44_128"); + return; + case Name.Piff44_64: + serializer.Serialize(writer, "piff_44_64"); + return; + } + throw new Exception("Cannot marshal type Name"); + } + + public static readonly NameConverter Singleton = new NameConverter(); + } + + internal class RootConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(Root) || t == typeof(Root?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "EditorsPicks": + return Root.EditorsPicks; + case "ExploreBy": + return Root.ExploreBy; + case "Genres": + return Root.Genres; + case "InstitutionsHpMarketing": + return Root.InstitutionsHpMarketing; + case "RodizioBuckets": + return Root.RodizioBuckets; + case "RodizioGenres": + return Root.RodizioGenres; + case "ShortsPrime": + return Root.ShortsPrime; + } + throw new Exception("Cannot unmarshal type Root"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (Root)untypedValue; + switch (value) + { + case Root.EditorsPicks: + serializer.Serialize(writer, "EditorsPicks"); + return; + case Root.ExploreBy: + serializer.Serialize(writer, "ExploreBy"); + return; + case Root.Genres: + serializer.Serialize(writer, "Genres"); + return; + case Root.InstitutionsHpMarketing: + serializer.Serialize(writer, "InstitutionsHpMarketing"); + return; + case Root.RodizioBuckets: + serializer.Serialize(writer, "RodizioBuckets"); + return; + case Root.RodizioGenres: + serializer.Serialize(writer, "RodizioGenres"); + return; + case Root.ShortsPrime: + serializer.Serialize(writer, "ShortsPrime"); + return; + } + throw new Exception("Cannot marshal type Root"); + } + + public static readonly RootConverter Singleton = new RootConverter(); + } + + internal class ContentDeliveryTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(ContentDeliveryType) || t == typeof(ContentDeliveryType?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "MultiPartBook": + return ContentDeliveryType.MultiPartBook; + case "Periodical": + return ContentDeliveryType.Periodical; + case "SinglePartBook": + return ContentDeliveryType.SinglePartBook; + } + throw new Exception("Cannot unmarshal type ContentDeliveryType"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (ContentDeliveryType)untypedValue; + switch (value) + { + case ContentDeliveryType.MultiPartBook: + serializer.Serialize(writer, "MultiPartBook"); + return; + case ContentDeliveryType.Periodical: + serializer.Serialize(writer, "Periodical"); + return; + case ContentDeliveryType.SinglePartBook: + serializer.Serialize(writer, "SinglePartBook"); + return; + } + throw new Exception("Cannot marshal type ContentDeliveryType"); + } + + public static readonly ContentDeliveryTypeConverter Singleton = new ContentDeliveryTypeConverter(); + } + + internal class ParseStringConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(long) || t == typeof(long?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + long l; + if (Int64.TryParse(value, out l)) + { + return l; + } + throw new Exception("Cannot unmarshal type long"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (long)untypedValue; + serializer.Serialize(writer, value.ToString()); + return; + } + + public static readonly ParseStringConverter Singleton = new ParseStringConverter(); + } + + internal class ContentTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(ContentType) || t == typeof(ContentType?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "Episode": + return ContentType.Episode; + case "Lecture": + return ContentType.Lecture; + case "Meditation": + return ContentType.Meditation; + case "Misc": + return ContentType.Misc; + case "Performance": + return ContentType.Performance; + case "Product": + return ContentType.Product; + case "Radio/TV Program": + return ContentType.RadioTvProgram; + case "Show": + return ContentType.Show; + case "Speech": + return ContentType.Speech; + } + throw new Exception("Cannot unmarshal type ContentType"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (ContentType)untypedValue; + switch (value) + { + case ContentType.Episode: + serializer.Serialize(writer, "Episode"); + return; + case ContentType.Lecture: + serializer.Serialize(writer, "Lecture"); + return; + case ContentType.Meditation: + serializer.Serialize(writer, "Meditation"); + return; + case ContentType.Misc: + serializer.Serialize(writer, "Misc"); + return; + case ContentType.Performance: + serializer.Serialize(writer, "Performance"); + return; + case ContentType.Product: + serializer.Serialize(writer, "Product"); + return; + case ContentType.RadioTvProgram: + serializer.Serialize(writer, "Radio/TV Program"); + return; + case ContentType.Show: + serializer.Serialize(writer, "Show"); + return; + case ContentType.Speech: + serializer.Serialize(writer, "Speech"); + return; + } + throw new Exception("Cannot marshal type ContentType"); + } + + public static readonly ContentTypeConverter Singleton = new ContentTypeConverter(); + } + + internal class CustomerReviewFormatConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(CustomerReviewFormat) || t == typeof(CustomerReviewFormat?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "Freeform": + return CustomerReviewFormat.Freeform; + case "Guided": + return CustomerReviewFormat.Guided; + } + throw new Exception("Cannot unmarshal type CustomerReviewFormat"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (CustomerReviewFormat)untypedValue; + switch (value) + { + case CustomerReviewFormat.Freeform: + serializer.Serialize(writer, "Freeform"); + return; + case CustomerReviewFormat.Guided: + serializer.Serialize(writer, "Guided"); + return; + } + throw new Exception("Cannot marshal type CustomerReviewFormat"); + } + + public static readonly CustomerReviewFormatConverter Singleton = new CustomerReviewFormatConverter(); + } + + internal class QuestionTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(QuestionType) || t == typeof(QuestionType?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "Genre": + return QuestionType.Genre; + case "Misc": + return QuestionType.Misc; + case "Overall": + return QuestionType.Overall; + case "Performance": + return QuestionType.Performance; + case "Story": + return QuestionType.Story; + } + throw new Exception("Cannot unmarshal type QuestionType"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (QuestionType)untypedValue; + switch (value) + { + case QuestionType.Genre: + serializer.Serialize(writer, "Genre"); + return; + case QuestionType.Misc: + serializer.Serialize(writer, "Misc"); + return; + case QuestionType.Overall: + serializer.Serialize(writer, "Overall"); + return; + case QuestionType.Performance: + serializer.Serialize(writer, "Performance"); + return; + case QuestionType.Story: + serializer.Serialize(writer, "Story"); + return; + } + throw new Exception("Cannot marshal type QuestionType"); + } + + public static readonly QuestionTypeConverter Singleton = new QuestionTypeConverter(); + } + + internal class FormatTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(FormatType) || t == typeof(FormatType?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "abridged": + return FormatType.Abridged; + case "original_recording": + return FormatType.OriginalRecording; + case "unabridged": + return FormatType.Unabridged; + } + throw new Exception("Cannot unmarshal type FormatType"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (FormatType)untypedValue; + switch (value) + { + case FormatType.Abridged: + serializer.Serialize(writer, "abridged"); + return; + case FormatType.OriginalRecording: + serializer.Serialize(writer, "original_recording"); + return; + case FormatType.Unabridged: + serializer.Serialize(writer, "unabridged"); + return; + } + throw new Exception("Cannot marshal type FormatType"); + } + + public static readonly FormatTypeConverter Singleton = new FormatTypeConverter(); + } + + internal class LanguageConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(Language) || t == typeof(Language?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + if (value == "english") + { + return Language.English; + } + throw new Exception("Cannot unmarshal type Language"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (Language)untypedValue; + if (value == Language.English) + { + serializer.Serialize(writer, "english"); + return; + } + throw new Exception("Cannot marshal type Language"); + } + + public static readonly LanguageConverter Singleton = new LanguageConverter(); + } + + internal class OriginMarketplaceConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(OriginMarketplace) || t == typeof(OriginMarketplace?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + if (value == "AF2M0KC94RCEA") + { + return OriginMarketplace.Af2M0Kc94Rcea; + } + throw new Exception("Cannot unmarshal type OriginMarketplace"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (OriginMarketplace)untypedValue; + if (value == OriginMarketplace.Af2M0Kc94Rcea) + { + serializer.Serialize(writer, "AF2M0KC94RCEA"); + return; + } + throw new Exception("Cannot marshal type OriginMarketplace"); + } + + public static readonly OriginMarketplaceConverter Singleton = new OriginMarketplaceConverter(); + } + + internal class OriginTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(OriginType) || t == typeof(OriginType?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "AudibleChannels": + return OriginType.AudibleChannels; + case "AudibleComplimentaryOriginal": + return OriginType.AudibleComplimentaryOriginal; + case "Purchase": + return OriginType.Purchase; + } + throw new Exception("Cannot unmarshal type OriginType"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (OriginType)untypedValue; + switch (value) + { + case OriginType.AudibleChannels: + serializer.Serialize(writer, "AudibleChannels"); + return; + case OriginType.AudibleComplimentaryOriginal: + serializer.Serialize(writer, "AudibleComplimentaryOriginal"); + return; + case OriginType.Purchase: + serializer.Serialize(writer, "Purchase"); + return; + } + throw new Exception("Cannot marshal type OriginType"); + } + + public static readonly OriginTypeConverter Singleton = new OriginTypeConverter(); + } + + internal class PlanNameConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(PlanName) || t == typeof(PlanName?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "AyceRomance": + return PlanName.AyceRomance; + case "ComplimentaryOriginalMemberBenefit": + return PlanName.ComplimentaryOriginalMemberBenefit; + case "Radio": + return PlanName.Radio; + case "Rodizio": + return PlanName.Rodizio; + case "SpecialBenefit": + return PlanName.SpecialBenefit; + } + throw new Exception("Cannot unmarshal type PlanName"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (PlanName)untypedValue; + switch (value) + { + case PlanName.AyceRomance: + serializer.Serialize(writer, "AyceRomance"); + return; + case PlanName.ComplimentaryOriginalMemberBenefit: + serializer.Serialize(writer, "ComplimentaryOriginalMemberBenefit"); + return; + case PlanName.Radio: + serializer.Serialize(writer, "Radio"); + return; + case PlanName.Rodizio: + serializer.Serialize(writer, "Rodizio"); + return; + case PlanName.SpecialBenefit: + serializer.Serialize(writer, "SpecialBenefit"); + return; + } + throw new Exception("Cannot marshal type PlanName"); + } + + public static readonly PlanNameConverter Singleton = new PlanNameConverter(); + } + + internal class CurrencyCodeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(CurrencyCode) || t == typeof(CurrencyCode?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + if (value == "USD") + { + return CurrencyCode.Usd; + } + throw new Exception("Cannot unmarshal type CurrencyCode"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (CurrencyCode)untypedValue; + if (value == CurrencyCode.Usd) + { + serializer.Serialize(writer, "USD"); + return; + } + throw new Exception("Cannot marshal type CurrencyCode"); + } + + public static readonly CurrencyCodeConverter Singleton = new CurrencyCodeConverter(); + } + + internal class MerchantIdConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(MerchantId) || t == typeof(MerchantId?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + if (value == "A2ZO8JX97D5MN9") + { + return MerchantId.A2Zo8Jx97D5Mn9; + } + throw new Exception("Cannot unmarshal type MerchantId"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (MerchantId)untypedValue; + if (value == MerchantId.A2Zo8Jx97D5Mn9) + { + serializer.Serialize(writer, "A2ZO8JX97D5MN9"); + return; + } + throw new Exception("Cannot marshal type MerchantId"); + } + + public static readonly MerchantIdConverter Singleton = new MerchantIdConverter(); + } + + internal class TypeEnumConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(TypeEnum) || t == typeof(TypeEnum?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "list": + return TypeEnum.List; + case "member": + return TypeEnum.Member; + case "sale": + return TypeEnum.Sale; + case "ws4v_upsell": + return TypeEnum.Ws4VUpsell; + } + throw new Exception("Cannot unmarshal type TypeEnum"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (TypeEnum)untypedValue; + switch (value) + { + case TypeEnum.List: + serializer.Serialize(writer, "list"); + return; + case TypeEnum.Member: + serializer.Serialize(writer, "member"); + return; + case TypeEnum.Sale: + serializer.Serialize(writer, "sale"); + return; + case TypeEnum.Ws4VUpsell: + serializer.Serialize(writer, "ws4v_upsell"); + return; + } + throw new Exception("Cannot marshal type TypeEnum"); + } + + public static readonly TypeEnumConverter Singleton = new TypeEnumConverter(); + } + + internal class RelationshipToProductConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(RelationshipToProduct) || t == typeof(RelationshipToProduct?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "child": + return RelationshipToProduct.Child; + case "parent": + return RelationshipToProduct.Parent; + } + throw new Exception("Cannot unmarshal type RelationshipToProduct"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (RelationshipToProduct)untypedValue; + switch (value) + { + case RelationshipToProduct.Child: + serializer.Serialize(writer, "child"); + return; + case RelationshipToProduct.Parent: + serializer.Serialize(writer, "parent"); + return; + } + throw new Exception("Cannot marshal type RelationshipToProduct"); + } + + public static readonly RelationshipToProductConverter Singleton = new RelationshipToProductConverter(); + } + + internal class RelationshipTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(RelationshipType) || t == typeof(RelationshipType?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "component": + return RelationshipType.Component; + case "episode": + return RelationshipType.Episode; + case "merchant_title_authority": + return RelationshipType.MerchantTitleAuthority; + case "season": + return RelationshipType.Season; + case "series": + return RelationshipType.Series; + } + throw new Exception("Cannot unmarshal type RelationshipType"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (RelationshipType)untypedValue; + switch (value) + { + case RelationshipType.Component: + serializer.Serialize(writer, "component"); + return; + case RelationshipType.Episode: + serializer.Serialize(writer, "episode"); + return; + case RelationshipType.MerchantTitleAuthority: + serializer.Serialize(writer, "merchant_title_authority"); + return; + case RelationshipType.Season: + serializer.Serialize(writer, "season"); + return; + case RelationshipType.Series: + serializer.Serialize(writer, "series"); + return; + } + throw new Exception("Cannot marshal type RelationshipType"); + } + + public static readonly RelationshipTypeConverter Singleton = new RelationshipTypeConverter(); + } + + internal class StatusConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(Status); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + if (value == "Active") + { + return Status.Active; + } + throw new Exception("Cannot unmarshal type Status"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (Status)untypedValue; + if (value == Status.Active) + { + serializer.Serialize(writer, "Active"); + return; + } + throw new Exception("Cannot marshal type Status"); + } + + public static readonly StatusConverter Singleton = new StatusConverter(); + } + + internal class ThesaurusSubjectKeywordConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(ThesaurusSubjectKeyword) || t == typeof(ThesaurusSubjectKeyword?); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "adventurers_&_explorers": + return ThesaurusSubjectKeyword.AdventurersExplorers; + case "alternate_history": + return ThesaurusSubjectKeyword.AlternateHistory; + case "comedians": + return ThesaurusSubjectKeyword.Comedians; + case "contemporary": + return ThesaurusSubjectKeyword.Contemporary; + case "dramatizations": + return ThesaurusSubjectKeyword.Dramatizations; + case "eastern_religions": + return ThesaurusSubjectKeyword.EasternReligions; + case "la_confidential": + return ThesaurusSubjectKeyword.LaConfidential; + case "literature-and-fiction": + return ThesaurusSubjectKeyword.LiteratureAndFiction; + case "medicine": + return ThesaurusSubjectKeyword.Medicine; + case "spirituality": + return ThesaurusSubjectKeyword.Spirituality; + case "standup_comedy": + return ThesaurusSubjectKeyword.StandupComedy; + case "storytelling": + return ThesaurusSubjectKeyword.Storytelling; + case "sword_&_sorcery": + return ThesaurusSubjectKeyword.SwordSorcery; + case "workouts": + return ThesaurusSubjectKeyword.Workouts; + } + throw new Exception("Cannot unmarshal type ThesaurusSubjectKeyword"); + } + + public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer) + { + if (untypedValue == null) + { + serializer.Serialize(writer, null); + return; + } + var value = (ThesaurusSubjectKeyword)untypedValue; + switch (value) + { + case ThesaurusSubjectKeyword.AdventurersExplorers: + serializer.Serialize(writer, "adventurers_&_explorers"); + return; + case ThesaurusSubjectKeyword.AlternateHistory: + serializer.Serialize(writer, "alternate_history"); + return; + case ThesaurusSubjectKeyword.Comedians: + serializer.Serialize(writer, "comedians"); + return; + case ThesaurusSubjectKeyword.Contemporary: + serializer.Serialize(writer, "contemporary"); + return; + case ThesaurusSubjectKeyword.Dramatizations: + serializer.Serialize(writer, "dramatizations"); + return; + case ThesaurusSubjectKeyword.EasternReligions: + serializer.Serialize(writer, "eastern_religions"); + return; + case ThesaurusSubjectKeyword.LaConfidential: + serializer.Serialize(writer, "la_confidential"); + return; + case ThesaurusSubjectKeyword.LiteratureAndFiction: + serializer.Serialize(writer, "literature-and-fiction"); + return; + case ThesaurusSubjectKeyword.Medicine: + serializer.Serialize(writer, "medicine"); + return; + case ThesaurusSubjectKeyword.Spirituality: + serializer.Serialize(writer, "spirituality"); + return; + case ThesaurusSubjectKeyword.StandupComedy: + serializer.Serialize(writer, "standup_comedy"); + return; + case ThesaurusSubjectKeyword.Storytelling: + serializer.Serialize(writer, "storytelling"); + return; + case ThesaurusSubjectKeyword.SwordSorcery: + serializer.Serialize(writer, "sword_&_sorcery"); + return; + case ThesaurusSubjectKeyword.Workouts: + serializer.Serialize(writer, "workouts"); + return; + } + throw new Exception("Cannot marshal type ThesaurusSubjectKeyword"); + } + + public static readonly ThesaurusSubjectKeywordConverter Singleton = new ThesaurusSubjectKeywordConverter(); + } +} diff --git a/DTOs/LibraryApiV10.custom.cs b/DTOs/LibraryApiV10.custom.cs new file mode 100644 index 00000000..de302e64 --- /dev/null +++ b/DTOs/LibraryApiV10.custom.cs @@ -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 AuthorsDistinct => Items.GetAuthorsDistinct(); + public IEnumerable 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 AuthorsDistinct => Authors.DistinctBy(a => new { a.Name, a.Asin }); + public IEnumerable 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}"; + } +} diff --git a/DTOs/LibraryApiV10Extensions.cs b/DTOs/LibraryApiV10Extensions.cs new file mode 100644 index 00000000..2f000b88 --- /dev/null +++ b/DTOs/LibraryApiV10Extensions.cs @@ -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 GetAuthorsDistinct(this IEnumerable items) + => items.SelectMany(i => i.Authors).DistinctBy(a => new { a.Name, a.Asin }); + + public static IEnumerable GetNarratorsDistinct(this IEnumerable items) + => items.SelectMany(i => i.Narrators).DistinctBy(a => new { a.Name, a.Asin }); + } +} diff --git a/LibationWinForm/UNTESTED/Form1.cs b/LibationWinForm/UNTESTED/Form1.cs index 132803ad..5c891c9a 100644 --- a/LibationWinForm/UNTESTED/Form1.cs +++ b/LibationWinForm/UNTESTED/Form1.cs @@ -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; diff --git a/ScrapingDomainServices/UNTESTED/DtoImporter.cs b/ScrapingDomainServices/UNTESTED/DtoImporter.cs index 535446a0..0b101eba 100644 --- a/ScrapingDomainServices/UNTESTED/DtoImporter.cs +++ b/ScrapingDomainServices/UNTESTED/DtoImporter.cs @@ -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); } } diff --git a/_DB_NOTES.txt b/_DB_NOTES.txt index 20a3e4f8..5b379574 100644 --- a/_DB_NOTES.txt +++ b/_DB_NOTES.txt @@ -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 diff --git a/__TODO.txt b/__TODO.txt index 553520ab..cfcd323d 100644 --- a/__TODO.txt +++ b/__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 ---------------------------------------------------------------------------------------------------------------------