From 88d49acdadfcac48e735120e21eeeee83371b69f Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Wed, 13 Nov 2019 08:37:57 -0500 Subject: [PATCH] Pre-beta: image download throttling --- FileManager/UNTESTED/PictureStorage.cs | 143 +++++++++++------- Libation.sln | 28 ++-- LibationWinForm/LibationWinForm.csproj | 3 +- .../Properties/Resources.Designer.cs | 30 ++++ LibationWinForm/Properties/Resources.resx | 9 ++ .../img-coverart-prod-unavailable_300x300.jpg | Bin 0 -> 6151 bytes .../img-coverart-prod-unavailable_500x500.jpg | Bin 0 -> 11528 bytes .../img-coverart-prod-unavailable_80x80.jpg | Bin 0 -> 1490 bytes .../UNTESTED/BookLiberation/DecryptForm.cs | 6 +- LibationWinForm/UNTESTED/Form1.cs | 9 +- LibationWinForm/UNTESTED/GridEntry.cs | 4 +- .../UNTESTED/WinAudibleImageServer.cs | 28 ++++ .../WindowsDesktopUtilities.csproj | 16 ++ _Demos/inAudibleLite/Form1.cs | 2 +- _Demos/inAudibleLite/inAudibleLite.csproj | 3 +- __TODO.txt | 2 +- 16 files changed, 200 insertions(+), 83 deletions(-) create mode 100644 LibationWinForm/Resources/img-coverart-prod-unavailable_300x300.jpg create mode 100644 LibationWinForm/Resources/img-coverart-prod-unavailable_500x500.jpg create mode 100644 LibationWinForm/Resources/img-coverart-prod-unavailable_80x80.jpg create mode 100644 WindowsDesktopUtilities/UNTESTED/WinAudibleImageServer.cs create mode 100644 WindowsDesktopUtilities/WindowsDesktopUtilities.csproj diff --git a/FileManager/UNTESTED/PictureStorage.cs b/FileManager/UNTESTED/PictureStorage.cs index efdd6bab..34fbc4cb 100644 --- a/FileManager/UNTESTED/PictureStorage.cs +++ b/FileManager/UNTESTED/PictureStorage.cs @@ -2,77 +2,108 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Http; namespace FileManager { - /// - /// Files are small. Entire file is read from disk every time. No volitile storage. Paths are well known - /// + public enum PictureSize { _80x80, _300x300, _500x500 } + public struct PictureDefinition + { + public string PictureId { get; } + public PictureSize Size { get; } + + public PictureDefinition(string pictureId, PictureSize pictureSize) + { + PictureId = pictureId; + Size = pictureSize; + } + } public static class PictureStorage { - public enum PictureSize { _80x80, _300x300, _500x500 } - // not customizable. don't move to config private static string ImagesDirectory { get; } = new DirectoryInfo(Configuration.Instance.LibationFiles).CreateSubdirectory("Images").FullName; - private static string getPath(string pictureId, PictureSize size) - => Path.Combine(ImagesDirectory, $"{pictureId}{size}.jpg"); + private static string getPath(PictureDefinition def) + => Path.Combine(ImagesDirectory, $"{def.PictureId}{def.Size}.jpg"); - public static byte[] GetImage(string pictureId, PictureSize size) - { - var path = getPath(pictureId, size); - if (!FileUtility.FileExists(path)) - DownloadImages(pictureId); + private static System.Timers.Timer timer { get; } + static PictureStorage() + { + timer = new System.Timers.Timer(700) + { + AutoReset = true, + Enabled = true + }; + timer.Elapsed += (_, __) => timerDownload(); + } - return File.ReadAllBytes(path); - } + private static Dictionary cache { get; } = new Dictionary(); + public static (bool isDefault, byte[] bytes) GetPicture(PictureDefinition def) + { + if (!cache.ContainsKey(def)) + cache.Add(def, null); + return (cache[def] == null, cache[def] ?? getDefaultImage(def.Size)); + } - public static void DownloadImages(string pictureId) - { - var path80 = getPath(pictureId, PictureSize._80x80); - var path300 = getPath(pictureId, PictureSize._300x300); - var path500 = getPath(pictureId, PictureSize._500x500); + private static Dictionary defaultImages { get; } = new Dictionary(); + public static void SetDefaultImage(PictureSize pictureSize, byte[] bytes) + => defaultImages[pictureSize] = bytes; + private static byte[] getDefaultImage(PictureSize size) + => defaultImages.ContainsKey(size) + ? defaultImages[size] + : new byte[0]; - int retry = 0; - do - { - try - { - using var webClient = new System.Net.WebClient(); - // download any that don't exist - { - if (!FileUtility.FileExists(path80)) - { - var bytes = webClient.DownloadData( - "https://images-na.ssl-images-amazon.com/images/I/" + pictureId + "._SL80_.jpg"); - File.WriteAllBytes(path80, bytes); - } - } + // necessary to avoid IO errors. ReadAllBytes and WriteAllBytes can conflict in some cases, esp when debugging + private static bool isProcessing; + private static void timerDownload() + { + try + { + if (isProcessing) + return; + isProcessing = true; - { - if (!FileUtility.FileExists(path300)) - { - var bytes = webClient.DownloadData( - "https://images-na.ssl-images-amazon.com/images/I/" + pictureId + "._SL300_.jpg"); - File.WriteAllBytes(path300, bytes); - } - } + PictureDefinition def; + string path; - { - if (!FileUtility.FileExists(path500)) - { - var bytes = webClient.DownloadData( - "https://m.media-amazon.com/images/I/" + pictureId + "._SL500_.jpg"); - File.WriteAllBytes(path500, bytes); - } - } + while (true) + { + def = cache + .Where(kvp => kvp.Value is null) + .Select(kvp => kvp.Key) + // 80x80 should be 1st since it's enum value == 0 + .OrderBy(d => d.PictureId) + .FirstOrDefault(); + // no more null entries. all requsted images are cached + if (string.IsNullOrWhiteSpace(def.PictureId)) + return; - break; + path = getPath(def); + // we found the next one to download + if (!FileUtility.FileExists(path)) + break; + + // file exists. read into cache. try again + // the point is to throttle web calls. therefore only return if we performed a d/l or there are no null cache entries + cache[def] = File.ReadAllBytes(path); } - catch { retry++; } - } - while (retry < 3); - } - } + + var bytes = download(def); + File.WriteAllBytes(path, bytes); + cache[def] = bytes; + } + finally + { + isProcessing = false; + } + } + private static HttpClient imageDownloadClient { get; } = new HttpClient(); + private static byte[] download(PictureDefinition def) + { + var sz = def.Size.ToString().Split('x')[1]; + var bytes = imageDownloadClient.GetByteArrayAsync("ht" + $"tps://images-na.ssl-images-amazon.com/images/I/{def.PictureId}._SL{sz}_.jpg").Result; + return bytes; + } + } } diff --git a/Libation.sln b/Libation.sln index 8e5ddfdc..f3cb2547 100644 --- a/Libation.sln +++ b/Libation.sln @@ -60,10 +60,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "inAudibleLite", "_Demos\inA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.Core", "..\Dinah.Core\Dinah.Core\Dinah.Core.csproj", "{9E951521-2587-4FC6-AD26-FAA9179FB6C4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.Core.Drawing", "..\Dinah.Core\Dinah.Core.Drawing\Dinah.Core.Drawing.csproj", "{2CAAD73E-E2F9-4888-B04A-3F3803DABDAE}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.Core.Windows.Forms", "..\Dinah.Core\Dinah.Core.Windows.Forms\Dinah.Core.Windows.Forms.csproj", "{1306F62D-CDAC-4269-982A-2EED51F0E318}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.EntityFrameworkCore", "..\Dinah.Core\Dinah.EntityFrameworkCore\Dinah.EntityFrameworkCore.csproj", "{1255D9BA-CE6E-42E4-A253-6376540B9661}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LuceneNet303r2", "..\LuceneNet303r2\LuceneNet303r2\LuceneNet303r2.csproj", "{35803735-B669-4090-9681-CC7F7FABDC71}" @@ -80,6 +76,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AudibleApiClientExample", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationServices", "ApplicationServices\ApplicationServices.csproj", "{B95650EA-25F0-449E-BA5D-99126BC5D730}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dinah.Core.WindowsDesktop", "..\Dinah.Core\Dinah.Core.WindowsDesktop\Dinah.Core.WindowsDesktop.csproj", "{059CE32C-9AD6-45E9-A166-790DFFB0B730}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsDesktopUtilities", "WindowsDesktopUtilities\WindowsDesktopUtilities.csproj", "{E7EFD64D-6630-4426-B09C-B6862A92E3FD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -154,14 +154,6 @@ Global {9E951521-2587-4FC6-AD26-FAA9179FB6C4}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E951521-2587-4FC6-AD26-FAA9179FB6C4}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E951521-2587-4FC6-AD26-FAA9179FB6C4}.Release|Any CPU.Build.0 = Release|Any CPU - {2CAAD73E-E2F9-4888-B04A-3F3803DABDAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2CAAD73E-E2F9-4888-B04A-3F3803DABDAE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2CAAD73E-E2F9-4888-B04A-3F3803DABDAE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2CAAD73E-E2F9-4888-B04A-3F3803DABDAE}.Release|Any CPU.Build.0 = Release|Any CPU - {1306F62D-CDAC-4269-982A-2EED51F0E318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1306F62D-CDAC-4269-982A-2EED51F0E318}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1306F62D-CDAC-4269-982A-2EED51F0E318}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1306F62D-CDAC-4269-982A-2EED51F0E318}.Release|Any CPU.Build.0 = Release|Any CPU {1255D9BA-CE6E-42E4-A253-6376540B9661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1255D9BA-CE6E-42E4-A253-6376540B9661}.Debug|Any CPU.Build.0 = Debug|Any CPU {1255D9BA-CE6E-42E4-A253-6376540B9661}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -194,6 +186,14 @@ Global {B95650EA-25F0-449E-BA5D-99126BC5D730}.Debug|Any CPU.Build.0 = Debug|Any CPU {B95650EA-25F0-449E-BA5D-99126BC5D730}.Release|Any CPU.ActiveCfg = Release|Any CPU {B95650EA-25F0-449E-BA5D-99126BC5D730}.Release|Any CPU.Build.0 = Release|Any CPU + {059CE32C-9AD6-45E9-A166-790DFFB0B730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {059CE32C-9AD6-45E9-A166-790DFFB0B730}.Debug|Any CPU.Build.0 = Debug|Any CPU + {059CE32C-9AD6-45E9-A166-790DFFB0B730}.Release|Any CPU.ActiveCfg = Release|Any CPU + {059CE32C-9AD6-45E9-A166-790DFFB0B730}.Release|Any CPU.Build.0 = Release|Any CPU + {E7EFD64D-6630-4426-B09C-B6862A92E3FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7EFD64D-6630-4426-B09C-B6862A92E3FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7EFD64D-6630-4426-B09C-B6862A92E3FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7EFD64D-6630-4426-B09C-B6862A92E3FD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -216,8 +216,6 @@ Global {DF72740C-900A-45DA-A3A6-4DDD68F286F2} = {F61184E7-2426-4A13-ACEF-5689928E2CE2} {74D02251-898E-4CAF-80C7-801820622903} = {F61184E7-2426-4A13-ACEF-5689928E2CE2} {9E951521-2587-4FC6-AD26-FAA9179FB6C4} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1} - {2CAAD73E-E2F9-4888-B04A-3F3803DABDAE} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1} - {1306F62D-CDAC-4269-982A-2EED51F0E318} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1} {1255D9BA-CE6E-42E4-A253-6376540B9661} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1} {35803735-B669-4090-9681-CC7F7FABDC71} = {7FBBB086-0807-4998-85BF-6D1A49C8AD05} {5A7681A5-60D9-480B-9AC7-63E0812A2548} = {38E6C6D9-963A-4C5B-89F4-F2F14885ADFD} @@ -226,6 +224,8 @@ Global {6069D7F6-BEA0-4917-AFD4-4EB680CB0EDD} = {38E6C6D9-963A-4C5B-89F4-F2F14885ADFD} {282EEE16-F569-47E1-992F-C6DB8AEC7AA6} = {F61184E7-2426-4A13-ACEF-5689928E2CE2} {B95650EA-25F0-449E-BA5D-99126BC5D730} = {41CDCC73-9B81-49DD-9570-C54406E852AF} + {059CE32C-9AD6-45E9-A166-790DFFB0B730} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1} + {E7EFD64D-6630-4426-B09C-B6862A92E3FD} = {F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {615E00ED-BAEF-4E8E-A92A-9B82D87942A9} diff --git a/LibationWinForm/LibationWinForm.csproj b/LibationWinForm/LibationWinForm.csproj index c30a32f0..848fb6d5 100644 --- a/LibationWinForm/LibationWinForm.csproj +++ b/LibationWinForm/LibationWinForm.csproj @@ -9,9 +9,8 @@ - - + diff --git a/LibationWinForm/Properties/Resources.Designer.cs b/LibationWinForm/Properties/Resources.Designer.cs index cf98ad7b..6aebe3d1 100644 --- a/LibationWinForm/Properties/Resources.Designer.cs +++ b/LibationWinForm/Properties/Resources.Designer.cs @@ -60,6 +60,36 @@ namespace LibationWinForm.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap default_cover_300x300 { + get { + object obj = ResourceManager.GetObject("default_cover_300x300", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap default_cover_500x500 { + get { + object obj = ResourceManager.GetObject("default_cover_500x500", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap default_cover_80x80 { + get { + object obj = ResourceManager.GetObject("default_cover_80x80", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/LibationWinForm/Properties/Resources.resx b/LibationWinForm/Properties/Resources.resx index 27f448c7..1f866c77 100644 --- a/LibationWinForm/Properties/Resources.resx +++ b/LibationWinForm/Properties/Resources.resx @@ -118,6 +118,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\img-coverart-prod-unavailable_300x300.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\img-coverart-prod-unavailable_500x500.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\img-coverart-prod-unavailable_80x80.jpg;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\edit-tags-25x25.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/LibationWinForm/Resources/img-coverart-prod-unavailable_300x300.jpg b/LibationWinForm/Resources/img-coverart-prod-unavailable_300x300.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9b3d864f6410ebf4ad3d970e0c3c86feb5568773 GIT binary patch literal 6151 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 SO^|=C{o@<_e~>#ctN*X&=+F=V literal 0 HcmV?d00001 diff --git a/LibationWinForm/Resources/img-coverart-prod-unavailable_500x500.jpg b/LibationWinForm/Resources/img-coverart-prod-unavailable_500x500.jpg new file mode 100644 index 0000000000000000000000000000000000000000..164865687ed58b4efeaf37d4dbcca4103d568754 GIT binary patch literal 11528 zcmeHscT|&Gm;M_9QWOGGR7#?P;1v)-5JXHw1VmIsrI#oOf)Tg~NGKsFQlv%X3W`W5 zB1L-dpp*!RAV`-MdQT`JBq5n_XYQRf^Vj^o`Mz1}x0WaGUhl~|Wu0fAefECNepv&o zaX|2jo`D_!0)c=#>>t3I04#OU&W-?Z?HX_b0016<3k(9d*fkJau7JURzIW?@vEaYg zcmEg*0i5g?yDHFRy#GJzyZr!ANHPEv;#k9g4!{8d?LPlez+B+nvWJrs%*DNjn|oJy zc=z)1@bK|)bMx`@@$KEmF5J8V5dM7vyY<~kc8BkdVn6$MxOsMa{J#}e10ci$+5l~U zK}P`&ArM#y#A*Uy?3Hq{HL7AbFdf7#mR;X!m!%` zP9d)SN0hbq2piw!KI$%_@+2mONBUynC()bVNiwJH9zEsd6FYG5koYlKx#Pc`P*ppl zu5ngV=aR0T{$+zJw{Dx5nweWz+TU|>sKYIK62L^{Gh?7&(GqZE^3*@!+ zjZMlHb(^+}3j~1w9oFB-{u?eKHZBhK9&mE+;sSAavjr@~$#q0|&wg!V?z`^7M^&Ei zh+K?GDg4AMeflOz)b7!DJ~0{9iDTqlXn!L6p8DlS2qB1Rwx< z#uAwYbgHs|Iw!gocD)0!nt%}jI*SP`K&ywjo%VOL^TrJ@yc5P)H=@tr{%X$neqrBV z!~SZ->jJ0$9`$R|UpoEAP3pIe8z5bgU-C-_zii-_4g7y*1EO{WgQ{K$m)zXuM$abW zc;;=8ZTOsGfH3;Qrnk;St*=Ya-IyYv_U~5x_`NmP zGEOey1QuW9e;l}ywmxj$|M;}IK@r*^m<3cH6-DF%y8mZZA$lw~r@WtcpK_~ew^U|W z`Wsv_I2X{rIcVOIWg~Nj)b6et$}8z)@tO{5%|}sh(GM-y9u^u!zclU6g?v@KwP!|q z#a9+9yd*z|;AE(FF{K7`G~sXEoLj_iRHw)8%p#N-bKvP!&VVlgC&{*hCnrL6%iW08 zZlgj=5wR=)V^?eT8D9ts^-MSIb#p6qMz>|rwU95`v6H>aYWQq)v0>Mk%Sw92V8NlI ze$OItU&DcR($=Tdy;KvRu*>s{IacC=Oe~Z3uzb-*#9cu-Sl7g495}`GXscebMk)h8 zihir-Mw|64!p_O`x>23nI9WhfYX){qpK%;qoLYGf5k1OZ_fVf`BZXZ|Z)5YL6uYvL z&O=VL;q1YMDwj!hZT_~)MpI7UD`}M_;S_1f@A|6uv#Ub5NovPLK&|N5aieEirV`6Z zu%4}gnalQTFedG)Ftz_3x||~UeYFuDm2K<1_Bh&0n6Ps&z_GS19H4d*kVUf*HG(p+ zOR%B1tk_u#*lED~x2@1=obNh8@A;s`m7+vJJsve0%o^Kd^u$)2`c^V3)0s>_H%3%& z$7{f+Fso_c3oyuc+gvh|a;?7xD6)0l?kj0(7kjR(rL*&Xg*$t8PQJ}Z1Tfb9SBY=v z(hmKs4a1_!<|6%TF^QoR^5XZcdtT-{p%+KEwPOGGZT%ls(M?KvyB%m5z2x7SA2+UmE#dIS!P5({W{ z+fTJ}>e(E*Idb5=-9#jItMwT6%7zeNkXydA4r|sMV?3^L3Lpt(B|R=7~0rYne>rD_|CXBZHXf1MfndPHTJvQL)d0f zr~LYhnoo4@ZkjE{yXjCyvFYNbbW$VdQ%$8vWa;gpj(7^}S(>I^#J&Ligox?Z69OE`u!SDUJ07^} zuNoM7)C`YF&$5DY99{iFk6C+B&!pSSQ;6rC*^@$q$7_7rR^2EHw5NaCLkQ`Uw># zw^H&`v_PbYMi#Jdxolq3^Hh7JS|ei}(c6g#Y@j4VUipjCmC%HiC--dB>1ReNg^}ei z5vz#@SO5XlRbn&%cN`ju4@)z;T~?}yrwX59`#;oN+i`I75(~IHw)Z0UyG&HEdP(64 zJR=m}mycMo8E=VjV)mIbNmYl~o!x0>xyBcua#;F4s_SjH;s*&o6^Fl~-y2gOa&P7t zB6ot-P#A(?zrsnvjdEpG?)kMCeRL|H%_WwH|oeV^w} zC049A2ZWgklVOwqQfJc-A0-9Tq3zHEk1L|B=@d7rpel&0=&SxIe@NewYOL_M==1|i zxZa|L)$33X2`r>1W#>VE^XlMzYpT;rTT9nI)%2+jKD*2Mbmvak@;+R;4+g-@lD3HO zSn|0GHaZ3s!us!u^nMzEv0nX!JCKKc%l;Cibs{{M)YzoFV0o2k9nln1u57L3<+Xw^ z{vqg-U%(V#-0KNYM*A(BV&}GAOt|JADk3Sf74iq=p~sRo0M5^;KZI7lPTmTg?cOx> zswl0H4BC1@aXA0_v0BzHdh*+GN(#xZng70f73xr{-??PCMQj)C)Cz)q{3aJySio}@ zAfOQN8&z-eN}aV&kf+L%b?w9SJVfRSuP9kAN(+V=SK2?~Y#FI1|Ius+TQym%lpXU$ z6%_x^AP8ovQXK2e;yI5sG1VisR}T{QV`hia!=M=e`NxK2ujVSk5xIRb3CaTUI$1z! zQlJJZ!befgX*z21rHV;RKkB!Qqi>KXe~TS_2`u82YP{f1#Lbrd6fO>EDBiW3DVqOVkfZ#`ONgAkNfn3c!f>3OQ z$3CwU+KJOr8G?p?%tdAFS?&siT6@K8H%P^JJn4UFKt_fkB>fG^%7b2tdfE>NNBbZn z4Ty2+Rg9HvARJHbT-Kg|aJoeM;Xlk8z#w-Wglzood&qpH05Sx0Ms6PwP1$^T&=y$0^763Pyj>7G@m+0W=t6q)!&P;~n zX-U)VsrpSf^+hLboz|6GT&xZe(-j<5j4J8P1D)TUBsl5}~m zYUS4hH(=RUYQKki!SP|Afd1b45~HOZNOP^+a@u9T2i_?`%{X%#s?nSBM znMwU0vZYlahJJ6omD?pERZfjRJq}(T3&J0G2tP$Nam&Sec<~C248^ru)ger@CaTA` zI0ee;M5#t(IDJAr@^G&L3-E#ctSnayt_(}MO@oe84tpKqUVw=O^g=~RW5Kyz2VLt+ zWb48Vx!%k8)J)x_%kmtat(a5=&6S%O4_ejq7)UDXsh_LnGJ(ENdbL)+>_A3*W&whm z=WcgPzg)agcn)?$r|dWB*(va$hfwYDeMyyh`owZVtsk+uQE`d|IP|RlQ4|<+a6@qf zSIG!=S7mcKU+gCqFz8`Ljh`O13F%f1nf>+(;lp<7cM%l(C!5I-t*(;2W5N&I2j?lp zwl-IPkGT~5ngwju(o9gV#ruethb7YNGNe>A@~fCq4EgWy1I~EIZiPPCyU*pY3C{JS zZgZ!&)BR89V82){GOH2ms_g5{@nnvFRH{1%m7qpkXbLfp23s*Oh8y+fiq?6F!^n^ss;m!paT= zO04tl_uM^P|$i?5uSAxy4Y^9jZ9rLIcWQxw=MLau;A5c)r#`Mab=>*u8 zRsQ%RNIKvz%~p8}WYEK!>O5m>5z@O6Jp1iA!iR?ixF9HZV>a?nB?FI81;x}Dxj=@} znV4jQsh`Mv##JwogKEk(jG4NjT^xAYa+%MxOPQ!X^fi%+~YTFQP#!g z*Hr1@4-34=ggL*4*?)#)c9vp5@6-*-tiov4ZFLW~H%AAhNe#)6s10mz z-7zwUYjKyCUl?uGP=9!PQuO^8pyl+unkiI!3;O*Y(`1WF1VlPhInM9hcqBR{Z6WST zO6}jl17RGqI#fdpgwsjLD9^DlC^YDH_lg*?&waybxT;{Ov4Up9` zRheT#%OkP1E_|HlCN%OKtntNVgcBOsM1iZ2cJi_^u9u5>Kd)1V#c+28z0kMgN#^Gx zA%`W#r*mgsj>;FK{7?gD(uYnzJ7Cy#s?!Ym?(!A@#j(BYvP~-E{JO$Mr;>SZ{L{>_ zif1gKq%n&Ed!7GA%a5#NNsZ5bTWh;!7y?_Egely>>DmTuckSU9xt01nOWzy+KstKlcDn^SQg_nwEgS!HRAHrQ!|epJ68-x~m75;Q}@7}cx7s#P}lQ7c^{ z5xG@g0hwKrpNH*38(kyW#!5xU-{+6Y!R%LSG0Hk`uys}yG=tqWg<4pHt<6f|^rB`e zqwx_7NNezAWCxJ9+`gFQv16g=_UhkfQjNmh0vWomL$#DCjS9)iIcc_3P~OhV0zO1B z5ZbXzn*9WCGuVTwmp9M6kt~G1Ih)M$zopu5jefYQz8%POaZDH@ij7DXp4Hb`;F&V3 z^M~#fzb{)WVF6-UFX5tI2V-Z9S->?xFIqmFkn2y^1pg+$vF^GHkG}ji9$LuAIUw&a;3Z_Aaf2l6BgOD;&DL zLO$;#IBv@#Hacjqt?Q8fT)@}HM~;0qPwSIc-}t3%`<}`E`R*eVcXqW>e?+bqD~Rfw zONKo4_t@Pp_m6MS+qD^5oIOsMi8fxy(U4{&nkohaqVb;-+#)xgD|i`dxaHoPA{?FN z{sB#tJ-Bmi4Tk$@5L^d6Nt+WB`0_d}~5 zzxhHt*@dv2`LI){ngWDUvkR{KY<@_kOKKs1duB0?=`xCyzX)nta;bH@Cmah%#O99=_P#>B^g!`aq<8Mp?)WAB0a~zCCC|i?wl1TV?I;o&XZKbx z*6=H-oAyicGx$hEX`fvf;oq>D=AE(^H2lURs70F3ileu<=JCZ>j$HI)^fb^)h)XF z`3~YYULV+rL!kZCXXNK`ocb&Y^hU zNYuKSVv1~F)BX*R>0gwJ9bVN5^v)o&a-mkq_#u<-IKMjs@`qvazb(fGVrm=m&!q;Q zQwqmRsjWt+UqV{@h@J3|N|bwOIFDb2dC<%xbRSZs8^r6IY^=@aC^tv1eR`C}E4cYs z>o~1zkDBlOUBVb@O--_|t}yNPd_21>EZ4~Je01 zU#adVzIJ8=h|NEZM&Zlb;11tSb|Oz8K-dY(x3OA0FNTtgR90-ltFB{f#=7qKnHlJs z^r^iyg3K4&BCU0tQNV~yQH6=|rYh(`B`h2vz5#pIcdk@EcPp|o{2MI684He1g@r5)AdnfqEjzN-fOzPG-`JMCu;15zfagMKQsJ4%a;G!JfdU%(mSbMtIxXM zD;w;jq~BL`I4701(=wGpyXPBZ%`XDl;{H4qMT0bd)SBOdn=Irx6uk~kKv+YFNUtxO zpNQ3bjLpJ&ykSi=amYD$<%rAocHuuf;mOh3G_%^>K$>2(mZN2vtEF{fL0N895ZiMJ z=C@Eo7w!NsN33U?kip`x=JThQXtfDn+LT2<`xk#=E@E|F_yt*Mo~*o2yrv@G0bh`4 zw+Ojuo~TkzM8rQRy#Rx#>o}rm9rhE)UM+k(Hw3sHf<^|<|rU6fS9rr z>!ddgGj(ZHeY)lxf;Y`zlAX&nC3N^{GTNEMH9?Gu&r||Hu4glV!oD;j@&vu9{~AdzMk*wBmsR;ci}ZrO-?U#bP)ld~FjGWS4fehN zx$i@C^V*Pa(2IM{`WS5)`xes7zOsOWSkyZ!0@NisbY^bI%E2&*`ATz_7Op@X5YHuV za2NaE-N{fz1dqBTDyZ4qeN^ulTxV{@Oi(AWfGQPCsO2`6Xw=-{LCj01L|M+2I5jo8 zKuviNBS@r2?5cI^S$4=d{;wBJqfmsHT4!(qen>TVCf82>~GGkwY zxa%MALx|q5?mudOzsMgfwpP9}`xlG);M-65;|BL!u;|+TPpd=2<|NoF`<(5BzCS8% zSk!mlfh`r%8?aur1sC!^`16zZ>QixP5zA1@xeByE+VdlF#aYuv{rL~Wfl?xlcfL;b z`&7@S6`M%OEJ_BYY;tGEEbtty$=BHluQ{7eEi!D6_}IRzA=ow0x)yxCv0^%&?LT72 z29GE6JXAQ`oikZp8Y1C`HhbuP>4^|`GB^T4586bEZ1Kp*Ymo|UBuEP zJCxqVbY3CnN8n3rYsq%{PYn>o8;Ojv8)t_a+55`X*$<8e@;CxjYs@D3jR@jR@wC#Z z{{EX@K9CV;I5*$qcN)*>>p`0YQE>J* zgrM=Q<){oQuC0KqLP#G?JTa;y){UoBw_w#4+cxs=^=j$Zr_QZr+KgHa541e_x_Ve> z`MiLy$8cXge4)3c8snY*$zE;xN1SPn}T+5Ri6|E+FFJVCMj-APxLK zz#z!M5Wo$}_MCvI;30I#U-U>$dGXcJ4ZK_{h;?$4{I*b?NeztJkjIxOwa0 zqsLF4K70P+<*SdMK7aZ8?fZ|Pzd-(CWMGDP3+^*Ce+dHp#RQB47Iu)o7@5j}m|2j8 zRnd@5$T5&Tu~1masF6d&Y2w0-2RW6EgFc8R6KQ!#m{`Vr(cu+NC|SQA-3gL@3& zuUiZ}z=X*p$SlZU&+vPF!Wa3e((DWVtjt)za>JJWsoTL9_F7RIjAs0fKLa;Z@t>O3 z#o*g~p)MpxuKCl_MGeA88ko8?7BF>beQ&ohT~V<=%v)CPT=wgojEOSI?^e7z+!h(32_x$L8@`P)V8dP1i<%6{-I5v)6L|HGu0=j~hf zq(A)6AZU`T*JZPI-;vcPo8LJ6(O^EFu~uK~hjqvYwN1C`HYVH6FI!yT$o-ezO`GR`ijf(WBkENIARBjh{J7XzZvqR}l;hQ)9mlu4I=cp)uc>l=W)ZCKL zYyaea924Ai$K>3)v;7Zwju?Cm2<$kYGwV@J^h14_y0hE)HpY0SCfhXUElp@Xz0>~C zy5HscTk=0WS4p2#rE7iUp26Q0drXs;tm{rReK)g6C11#cM?UpdXxpU)jOsou=iK;? z9!Ox^`_cFj@6`KEU;VlM_-sr4s5edY%kA6JwZ{YQPMGX^G2}_&m8roeB&2oJE@-GEF)&V%SZ^vZ+$^V4c z%bYlJepRg558WloEX|WY^Si3MU%14;ry831N-*9AThCRS+{C~AOAgDo{V^5#8F(Y|p_I$Z_EhHP^Cx!16_l5*w_eo1 OVAiJrG!+;Ae-i+ bookInfoLbl.UIThread(() => bookInfoLbl.Text = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}"); public void SetCoverImage(byte[] coverBytes) - => pictureBox1.UIThread(() => pictureBox1.Image = ImageConverter.GetPictureFromBytes(coverBytes)); + => pictureBox1.UIThread(() => pictureBox1.Image = ImageReader.ToImage(coverBytes)); - public void AppendError(Exception ex) => AppendText("ERROR: " + ex.Message); - public void AppendText(string text) => + public static void AppendError(Exception ex) => AppendText("ERROR: " + ex.Message); + public static void AppendText(string text) => // redirected to log textbox Console.WriteLine($"{DateTime.Now} {text}") //logTb.UIThread(() => logTb.AppendText($"{DateTime.Now} {text}{Environment.NewLine}")) diff --git a/LibationWinForm/UNTESTED/Form1.cs b/LibationWinForm/UNTESTED/Form1.cs index ea3487ae..badc9ed1 100644 --- a/LibationWinForm/UNTESTED/Form1.cs +++ b/LibationWinForm/UNTESTED/Form1.cs @@ -6,6 +6,7 @@ using System.Windows.Forms; using DataLayer; using Dinah.Core; using Dinah.Core.Collections.Generic; +using Dinah.Core.Drawing; using Dinah.Core.Windows.Forms; using FileManager; @@ -41,7 +42,13 @@ namespace LibationWinForm // call static ctor. There are bad race conditions if static ctor is first executed when we're running in parallel in setBackupCountsAsync() var foo = FilePathCache.JsonFile; - reloadGrid(); + // load default/missing cover images. this will also initiate the background image downloader + var format = System.Drawing.Imaging.ImageFormat.Jpeg; + PictureStorage.SetDefaultImage(PictureSize._80x80, Properties.Resources.default_cover_80x80.ToBytes(format)); + PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format)); + PictureStorage.SetDefaultImage(PictureSize._500x500, Properties.Resources.default_cover_500x500.ToBytes(format)); + + reloadGrid(); // also applies filter. ONLY call AFTER loading grid loadInitialQuickFilterState(); diff --git a/LibationWinForm/UNTESTED/GridEntry.cs b/LibationWinForm/UNTESTED/GridEntry.cs index 0cdf4de0..9a7a1d58 100644 --- a/LibationWinForm/UNTESTED/GridEntry.cs +++ b/LibationWinForm/UNTESTED/GridEntry.cs @@ -37,9 +37,7 @@ namespace LibationWinForm public bool TryGetFormatted(string key, out string value) => formatReplacements.TryGetValue(key, out value); public Image Cover => - Dinah.Core.Drawing.ImageConverter.GetPictureFromBytes( - FileManager.PictureStorage.GetImage(book.PictureId, FileManager.PictureStorage.PictureSize._80x80) - ); + WindowsDesktopUtilities.WinAudibleImageServer.GetImage(book.PictureId, FileManager.PictureSize._80x80); public string Title { diff --git a/WindowsDesktopUtilities/UNTESTED/WinAudibleImageServer.cs b/WindowsDesktopUtilities/UNTESTED/WinAudibleImageServer.cs new file mode 100644 index 00000000..1b3eeab2 --- /dev/null +++ b/WindowsDesktopUtilities/UNTESTED/WinAudibleImageServer.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using Dinah.Core.Drawing; +using FileManager; + +namespace WindowsDesktopUtilities +{ + public static class WinAudibleImageServer + { + private static Dictionary cache { get; } = new Dictionary(); + + public static Image GetImage(string pictureId, PictureSize size) + { + var def = new PictureDefinition(pictureId, size); + if (!cache.ContainsKey(def)) + { + (var isDefault, var bytes) = PictureStorage.GetPicture(def); + + var image = ImageReader.ToImage(bytes); + if (isDefault) + return image; + cache[def] = image; + } + return cache[def]; + } + } +} diff --git a/WindowsDesktopUtilities/WindowsDesktopUtilities.csproj b/WindowsDesktopUtilities/WindowsDesktopUtilities.csproj new file mode 100644 index 00000000..b0860a9b --- /dev/null +++ b/WindowsDesktopUtilities/WindowsDesktopUtilities.csproj @@ -0,0 +1,16 @@ + + + + Library + netcoreapp3.0 + true + + + + + + + + + + \ No newline at end of file diff --git a/_Demos/inAudibleLite/Form1.cs b/_Demos/inAudibleLite/Form1.cs index 36ba84d4..b2617ba7 100644 --- a/_Demos/inAudibleLite/Form1.cs +++ b/_Demos/inAudibleLite/Form1.cs @@ -49,7 +49,7 @@ namespace inAudibleLite this.txtInputFile.Text = openFileDialog.FileName; converter = await AaxToM4bConverter.CreateAsync(this.txtInputFile.Text, this.decryptKeyTb.Text); - pictureBox1.Image = Dinah.Core.Drawing.ImageConverter.GetPictureFromBytes(converter.coverBytes); + pictureBox1.Image = Dinah.Core.Drawing.ImageReader.ToImage(converter.coverBytes); this.txtOutputFile.Text = converter.outputFileName; diff --git a/_Demos/inAudibleLite/inAudibleLite.csproj b/_Demos/inAudibleLite/inAudibleLite.csproj index 1f4bc76e..7f8e272c 100644 --- a/_Demos/inAudibleLite/inAudibleLite.csproj +++ b/_Demos/inAudibleLite/inAudibleLite.csproj @@ -7,8 +7,7 @@ - - + diff --git a/__TODO.txt b/__TODO.txt index f33a8918..b095ca6c 100644 --- a/__TODO.txt +++ b/__TODO.txt @@ -1,5 +1,5 @@ -- begin BETA --------------------------------------------------------------------------------------------------------------------- -throttle needed for downloading images, pdf.s +throttle needed for downloading pdf.s no mdf,ldf files created in C:\Users\[username]