mirror of
				https://github.com/Kareadita/Kavita.git
				synced 2025-10-30 10:12:28 -04:00 
			
		
		
		
	Manga Redesign (#321)
* Code cleanup, refactored FileRepository into Unit of Work. * Added AutoCloseMenu and ReaderMode user perferences to match UI * Added extra information to ChapterInfo * Build changes * Updated the readme to have open collective information and thanks to sponsors * Fixed an issue with UnitOfWork refactor and how stats was bootsrapped. Replaced stats.kavitareader with a temp url to test out redirection bug.
This commit is contained in:
		
							parent
							
								
									b958342394
								
							
						
					
					
						commit
						be2b78fa5a
					
				| @ -42,6 +42,10 @@ namespace API.Tests.Comparers | |||||||
|             new[] {"3and4.cbz", "The World God Only Knows - Oneshot.cbz", "5.cbz", "1and2.cbz"},  |             new[] {"3and4.cbz", "The World God Only Knows - Oneshot.cbz", "5.cbz", "1and2.cbz"},  | ||||||
|             new[] {"1and2.cbz", "3and4.cbz", "5.cbz", "The World God Only Knows - Oneshot.cbz"} |             new[] {"1and2.cbz", "3and4.cbz", "5.cbz", "The World God Only Knows - Oneshot.cbz"} | ||||||
|         )] |         )] | ||||||
|  |         [InlineData( | ||||||
|  |             new[] {"Solo Leveling - c000 (v01) - p000 [Cover] [dig] [Yen Press] [LuCaZ].jpg", "Solo Leveling - c000 (v01) - p001 [dig] [Yen Press] [LuCaZ].jpg", "Solo Leveling - c000 (v01) - p002 [dig] [Yen Press] [LuCaZ].jpg", "Solo Leveling - c000 (v01) - p003 [dig] [Yen Press] [LuCaZ].jpg"},  | ||||||
|  |             new[] {"Solo Leveling - c000 (v01) - p000 [Cover] [dig] [Yen Press] [LuCaZ].jpg", "Solo Leveling - c000 (v01) - p001 [dig] [Yen Press] [LuCaZ].jpg", "Solo Leveling - c000 (v01) - p002 [dig] [Yen Press] [LuCaZ].jpg", "Solo Leveling - c000 (v01) - p003 [dig] [Yen Press] [LuCaZ].jpg"} | ||||||
|  |         )] | ||||||
|         public void TestNaturalSortComparer(string[] input, string[] expected) |         public void TestNaturalSortComparer(string[] input, string[] expected) | ||||||
|         { |         { | ||||||
|             Array.Sort(input, _nc); |             Array.Sort(input, _nc); | ||||||
|  | |||||||
							
								
								
									
										124
									
								
								API/API.csproj
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								API/API.csproj
									
									
									
									
									
								
							| @ -64,23 +64,147 @@ | |||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <None Remove="Hangfire-log.db" /> |     <None Remove="Hangfire-log.db" /> | ||||||
|     <None Remove="obj\**" /> |     <None Remove="obj\**" /> | ||||||
|  |     <None Remove="wwwroot\**" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Compile Remove="Interfaces\IMetadataService.cs" /> |     <Compile Remove="Interfaces\IMetadataService.cs" /> | ||||||
|     <Compile Remove="obj\**" /> |     <Compile Remove="obj\**" /> | ||||||
|  |     <Compile Remove="wwwroot\**" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <EmbeddedResource Remove="obj\**" /> |     <EmbeddedResource Remove="obj\**" /> | ||||||
|  |     <EmbeddedResource Remove="wwwroot\**" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Content Remove="obj\**" /> |     <Content Remove="obj\**" /> | ||||||
|  |     <Content Remove="wwwroot\**" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <_ContentIncludedByDefault Remove="logs\kavita.json" /> |     <_ContentIncludedByDefault Remove="logs\kavita.json" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\3rdpartylicenses.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\6.d9925ea83359bb4c7278.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\6.d9925ea83359bb4c7278.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\7.860cdd6fd9d758e6c210.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\7.860cdd6fd9d758e6c210.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\8.028f6737a2f0621d40c7.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\8.028f6737a2f0621d40c7.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\EBGarmond\EBGaramond-Italic-VariableFont_wght.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\EBGarmond\EBGaramond-VariableFont_wght.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\EBGarmond\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Black.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-BlackItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-BoldItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraBold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraBoldItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraLight.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ExtraLightItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Italic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Light.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-LightItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Medium.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-MediumItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-SemiBold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-SemiBoldItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-Thin.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\FiraSans-ThinItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Fira_Sans\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Black.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-BlackItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-BoldItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Italic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Light.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-LightItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-Thin.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\Lato-ThinItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Lato\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\LibreBaskerville-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\LibreBaskerville-Italic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\LibreBaskerville-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Baskerville\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\LibreCaslonText-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\LibreCaslonText-Italic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\LibreCaslonText-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Libre_Caslon\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Black.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-BlackItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-BoldItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Italic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Light.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-LightItalic.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\Merriweather-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Merriweather\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\NanumGothic-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\NanumGothic-ExtraBold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\NanumGothic-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Nanum_Gothic\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\Oswald-VariableFont_wght.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\README.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Bold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-ExtraLight.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Light.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Medium.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\Oswald\static\Oswald-SemiBold.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\RocknRoll_One\OFL.txt" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\fonts\RocknRoll_One\RocknRollOne-Regular.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder-min.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2-min.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2.dark-min.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2.dark.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\error-placeholder2.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder-min.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder.dark-min.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder.dark.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\image-placeholder.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\images\preset-light.png" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\assets\themes\dark.scss" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\common.ad975892146299f80adb.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\common.ad975892146299f80adb.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\EBGaramond-VariableFont_wght.2a1da2dbe7a28d63f8cb.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.0fea24969112a781acd2.eot" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.c967a94cfbe2b06627ff.woff2" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.dc2cbadd690e1d4b2c9c.woff" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.e33e2cf6e02cac2ccb77.svg" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-brands-400.ec82f282c7f54b637098.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.06b9d19ced8d17f3d5cb.svg" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.08f9891a6f44d9546678.eot" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.1008b5226941c24f4468.woff2" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.1069ea55beaa01060302.woff" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-regular-400.1495f578452eb676f730.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.10ecefc282f2761808bf.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.371dbce0dd46bd4d2033.svg" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.3a24a60e7f9c6574864a.eot" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.3ceb50e7bcafb577367c.woff2" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\fa-solid-900.46fdbd2d897f8824e63c.woff" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\favicon.ico" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\FiraSans-Regular.1c0bf0728b51cb9f2ddc.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\index.html" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\Lato-Regular.9919edff6283018571ad.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\LibreBaskerville-Regular.a27f99ca45522bb3d56d.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\main.44f5c0973044295d8be0.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\main.44f5c0973044295d8be0.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\Merriweather-Regular.55c73e48e04ec926ebfe.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\NanumGothic-Regular.6c84540de7730f833d6c.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\polyfills.348e08e9d0e910a15938.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\polyfills.348e08e9d0e910a15938.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\RocknRollOne-Regular.c75da4712d1e65ed1f69.ttf" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\runtime.ea545c6916f85411478f.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\runtime.ea545c6916f85411478f.js.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\styles.4bd902bb3037f36f2c64.css" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\styles.4bd902bb3037f36f2c64.css.map" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\vendor.6b2a0912ae80e6fd297f.js" /> | ||||||
|  |     <_ContentIncludedByDefault Remove="wwwroot\vendor.6b2a0912ae80e6fd297f.js.map" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| // Version 2 | // Version 2 | ||||||
| // Taken from: https://www.codeproject.com/Articles/11016/Numeric-String-Sort-in-C | // Taken from: https://www.codeproject.com/Articles/11016/Numeric-String-Sort-in-C | ||||||
| 
 | 
 | ||||||
| using System; | using static System.Char; | ||||||
| 
 | 
 | ||||||
| namespace API.Comparators | namespace API.Comparators | ||||||
| { | { | ||||||
| @ -20,26 +20,26 @@ namespace API.Comparators | |||||||
| 			if (string.IsNullOrEmpty(s2)) return -1; | 			if (string.IsNullOrEmpty(s2)) return -1; | ||||||
| 
 | 
 | ||||||
| 			//WE style, special case | 			//WE style, special case | ||||||
| 			var sp1 = Char.IsLetterOrDigit(s1, 0); | 			var sp1 = IsLetterOrDigit(s1, 0); | ||||||
| 			var sp2 = Char.IsLetterOrDigit(s2, 0); | 			var sp2 = IsLetterOrDigit(s2, 0); | ||||||
| 			if(sp1 && !sp2) return 1; | 			if(sp1 && !sp2) return 1; | ||||||
| 			if(!sp1 && sp2) return -1; | 			if(!sp1 && sp2) return -1; | ||||||
| 
 | 
 | ||||||
| 			int i1 = 0, i2 = 0; //current index | 			int i1 = 0, i2 = 0; //current index | ||||||
| 			while(true) | 			while(true) | ||||||
| 			{ | 			{ | ||||||
| 				var c1 = Char.IsDigit(s1, i1); | 				var c1 = IsDigit(s1, i1); | ||||||
| 				var c2 = Char.IsDigit(s2, i2); | 				var c2 = IsDigit(s2, i2); | ||||||
| 				int r; // temp result | 				int r; // temp result | ||||||
| 				if(!c1 && !c2) | 				if(!c1 && !c2) | ||||||
| 				{ | 				{ | ||||||
| 					bool letter1 = Char.IsLetter(s1, i1); | 					bool letter1 = IsLetter(s1, i1); | ||||||
| 					bool letter2 = Char.IsLetter(s2, i2); | 					bool letter2 = IsLetter(s2, i2); | ||||||
| 					if((letter1 && letter2) || (!letter1 && !letter2)) | 					if((letter1 && letter2) || (!letter1 && !letter2)) | ||||||
| 					{ | 					{ | ||||||
| 						if(letter1 && letter2) | 						if(letter1 && letter2) | ||||||
| 						{ | 						{ | ||||||
| 							r = Char.ToLower(s1[i1]).CompareTo(Char.ToLower(s2[i2])); | 							r = ToLower(s1[i1]).CompareTo(ToLower(s2[i2])); | ||||||
| 						} | 						} | ||||||
| 						else | 						else | ||||||
| 						{ | 						{ | ||||||
| @ -114,8 +114,8 @@ namespace API.Comparators | |||||||
| 		{ | 		{ | ||||||
| 			nzStart = start; | 			nzStart = start; | ||||||
| 			end = start; | 			end = start; | ||||||
| 			bool countZeros = true; | 			var countZeros = true; | ||||||
| 			while(Char.IsDigit(s, end)) | 			while(IsDigit(s, end)) | ||||||
| 			{ | 			{ | ||||||
| 				if(countZeros && s[end].Equals('0')) | 				if(countZeros && s[end].Equals('0')) | ||||||
| 				{ | 				{ | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								API/Configurations/CustomOptions/StatsOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								API/Configurations/CustomOptions/StatsOptions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | using System; | ||||||
|  | 
 | ||||||
|  | namespace API.Configurations.CustomOptions | ||||||
|  | { | ||||||
|  |     public class StatsOptions | ||||||
|  |     { | ||||||
|  |         public string ServerUrl { get; set; } | ||||||
|  |         public string ServerSecret { get; set; } | ||||||
|  |         public string SendDataAt { get; set; } | ||||||
|  | 
 | ||||||
|  |         private const char Separator = ':'; | ||||||
|  | 
 | ||||||
|  |         public short SendDataHour => GetValueFromSendAt(0); | ||||||
|  |         public short SendDataMinute => GetValueFromSendAt(1); | ||||||
|  | 
 | ||||||
|  |         // The expected SendDataAt format is: Hour:Minute. Ex: 19:45 | ||||||
|  |         private short GetValueFromSendAt(int index) | ||||||
|  |         { | ||||||
|  |             var key = $"{nameof(StatsOptions)}:{nameof(SendDataAt)}"; | ||||||
|  | 
 | ||||||
|  |             if (string.IsNullOrEmpty(SendDataAt)) | ||||||
|  |                 throw new InvalidOperationException($"{key} is invalid. Check the app settings file"); | ||||||
|  | 
 | ||||||
|  |             if (short.TryParse(SendDataAt.Split(Separator)[index], out var parsedValue)) | ||||||
|  |                 return parsedValue; | ||||||
|  | 
 | ||||||
|  |             throw new InvalidOperationException($"Could not parse {key}. Check the app settings file"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -10,7 +10,6 @@ using API.Interfaces; | |||||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||||
| using Microsoft.AspNetCore.Identity; | using Microsoft.AspNetCore.Identity; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
| using Microsoft.Extensions.Logging; |  | ||||||
| 
 | 
 | ||||||
| namespace API.Controllers | namespace API.Controllers | ||||||
| { | { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ using System.Linq; | |||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.Comparators; | using API.Comparators; | ||||||
| using API.DTOs; | using API.DTOs; | ||||||
|  | using API.DTOs.Reader; | ||||||
| using API.Entities; | using API.Entities; | ||||||
| using API.Extensions; | using API.Extensions; | ||||||
| using API.Interfaces; | using API.Interfaces; | ||||||
| @ -49,15 +50,27 @@ namespace API.Controllers | |||||||
| 
 | 
 | ||||||
|             return File(content, "image/" + format); |             return File(content, "image/" + format); | ||||||
|         } |         } | ||||||
|          | 
 | ||||||
|         [HttpGet("chapter-path")] |         [HttpGet("chapter-info")] | ||||||
|         public async Task<ActionResult<string>> GetImagePath(int chapterId) |         public async Task<ActionResult<ChapterInfoDto>> GetChapterInfo(int chapterId) | ||||||
|         { |         { | ||||||
|             var chapter = await _cacheService.Ensure(chapterId); |             var chapter = await _cacheService.Ensure(chapterId); | ||||||
|             if (chapter == null) return BadRequest("There was an issue finding image file for reading"); |             if (chapter == null) return BadRequest("Could not find Chapter"); | ||||||
| 
 |             var volume = await _unitOfWork.SeriesRepository.GetVolumeAsync(chapter.VolumeId); | ||||||
|  |             if (volume == null) return BadRequest("Could not find Volume"); | ||||||
|             var (_, mangaFile) = await _cacheService.GetCachedPagePath(chapter, 0); |             var (_, mangaFile) = await _cacheService.GetCachedPagePath(chapter, 0); | ||||||
|             return Ok(mangaFile.FilePath); |             var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(volume.SeriesId); | ||||||
|  | 
 | ||||||
|  |             return Ok(new ChapterInfoDto() | ||||||
|  |             { | ||||||
|  |                 ChapterNumber =  chapter.Range, | ||||||
|  |                 VolumeNumber = volume.Number + string.Empty, | ||||||
|  |                 VolumeId = volume.Id, | ||||||
|  |                 FileName = Path.GetFileName(mangaFile.FilePath), | ||||||
|  |                 SeriesName = series?.Name, | ||||||
|  |                 IsSpecial = chapter.IsSpecial, | ||||||
|  |                 Pages = chapter.Pages, | ||||||
|  |             }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         [HttpGet("get-bookmark")] |         [HttpGet("get-bookmark")] | ||||||
|  | |||||||
| @ -1,10 +1,8 @@ | |||||||
| using System; | using System; | ||||||
| using System.IO; | using System.IO; | ||||||
| using System.IO.Compression; |  | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.Extensions; | using API.Extensions; | ||||||
| using API.Interfaces.Services; | using API.Interfaces.Services; | ||||||
| using API.Services; |  | ||||||
| using Kavita.Common; | using Kavita.Common; | ||||||
| using Microsoft.AspNetCore.Authorization; | using Microsoft.AspNetCore.Authorization; | ||||||
| using Microsoft.AspNetCore.Mvc; | using Microsoft.AspNetCore.Mvc; | ||||||
|  | |||||||
| @ -61,6 +61,8 @@ namespace API.Controllers | |||||||
|             existingPreferences.ReadingDirection = preferencesDto.ReadingDirection; |             existingPreferences.ReadingDirection = preferencesDto.ReadingDirection; | ||||||
|             existingPreferences.ScalingOption = preferencesDto.ScalingOption; |             existingPreferences.ScalingOption = preferencesDto.ScalingOption; | ||||||
|             existingPreferences.PageSplitOption = preferencesDto.PageSplitOption; |             existingPreferences.PageSplitOption = preferencesDto.PageSplitOption; | ||||||
|  |             existingPreferences.AutoCloseMenu = preferencesDto.AutoCloseMenu; | ||||||
|  |             existingPreferences.ReaderMode = preferencesDto.ReaderMode; | ||||||
|             existingPreferences.BookReaderMargin = preferencesDto.BookReaderMargin; |             existingPreferences.BookReaderMargin = preferencesDto.BookReaderMargin; | ||||||
|             existingPreferences.BookReaderLineSpacing = preferencesDto.BookReaderLineSpacing; |             existingPreferences.BookReaderLineSpacing = preferencesDto.BookReaderLineSpacing; | ||||||
|             existingPreferences.BookReaderFontFamily = preferencesDto.BookReaderFontFamily; |             existingPreferences.BookReaderFontFamily = preferencesDto.BookReaderFontFamily; | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| using System.Collections.Generic; | namespace API.DTOs | ||||||
| 
 |  | ||||||
| namespace API.DTOs |  | ||||||
| { | { | ||||||
|     public class CollectionTagDto |     public class CollectionTagDto | ||||||
|     { |     { | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								API/DTOs/Reader/ChapterInfoDto.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								API/DTOs/Reader/ChapterInfoDto.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | namespace API.DTOs.Reader | ||||||
|  | { | ||||||
|  |     public class ChapterInfoDto | ||||||
|  |     { | ||||||
|  |          | ||||||
|  |         public string ChapterNumber { get; set; } | ||||||
|  |         public string VolumeNumber { get; set; } | ||||||
|  |         public int VolumeId { get; set; } | ||||||
|  |         public string SeriesName { get; set; } | ||||||
|  |         public string ChapterTitle { get; set; } = ""; | ||||||
|  |         public int Pages { get; set; } | ||||||
|  |         public string FileName { get; set; } | ||||||
|  |         public bool IsSpecial { get; set; } | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,5 +1,4 @@ | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using API.Entities; |  | ||||||
| 
 | 
 | ||||||
| namespace API.DTOs | namespace API.DTOs | ||||||
| { | { | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ namespace API.DTOs | |||||||
|         public ReadingDirection ReadingDirection { get; set; } |         public ReadingDirection ReadingDirection { get; set; } | ||||||
|         public ScalingOption ScalingOption { get; set; } |         public ScalingOption ScalingOption { get; set; } | ||||||
|         public PageSplitOption PageSplitOption { get; set; } |         public PageSplitOption PageSplitOption { get; set; } | ||||||
|  |         public ReaderMode ReaderMode { get; set; } | ||||||
|  |         public bool AutoCloseMenu { get; set; } | ||||||
|         public bool BookReaderDarkMode { get; set; } = false; |         public bool BookReaderDarkMode { get; set; } = false; | ||||||
|         public int BookReaderMargin { get; set; } |         public int BookReaderMargin { get; set; } | ||||||
|         public int BookReaderLineSpacing { get; set; } |         public int BookReaderLineSpacing { get; set; } | ||||||
|  | |||||||
							
								
								
									
										869
									
								
								API/Data/Migrations/20210622164318_NewUserPreferences.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										869
									
								
								API/Data/Migrations/20210622164318_NewUserPreferences.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,869 @@ | |||||||
|  | // <auto-generated /> | ||||||
|  | using System; | ||||||
|  | using API.Data; | ||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using Microsoft.EntityFrameworkCore.Infrastructure; | ||||||
|  | using Microsoft.EntityFrameworkCore.Migrations; | ||||||
|  | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | ||||||
|  | 
 | ||||||
|  | namespace API.Data.Migrations | ||||||
|  | { | ||||||
|  |     [DbContext(typeof(DataContext))] | ||||||
|  |     [Migration("20210622164318_NewUserPreferences")] | ||||||
|  |     partial class NewUserPreferences | ||||||
|  |     { | ||||||
|  |         protected override void BuildTargetModel(ModelBuilder modelBuilder) | ||||||
|  |         { | ||||||
|  | #pragma warning disable 612, 618 | ||||||
|  |             modelBuilder | ||||||
|  |                 .HasAnnotation("ProductVersion", "5.0.4"); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppRole", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ConcurrencyStamp") | ||||||
|  |                         .IsConcurrencyToken() | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Name") | ||||||
|  |                         .HasMaxLength(256) | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("NormalizedName") | ||||||
|  |                         .HasMaxLength(256) | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("NormalizedName") | ||||||
|  |                         .IsUnique() | ||||||
|  |                         .HasDatabaseName("RoleNameIndex"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetRoles"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUser", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("AccessFailedCount") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ConcurrencyStamp") | ||||||
|  |                         .IsConcurrencyToken() | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("Created") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Email") | ||||||
|  |                         .HasMaxLength(256) | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("EmailConfirmed") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastActive") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("LockoutEnabled") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTimeOffset?>("LockoutEnd") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("NormalizedEmail") | ||||||
|  |                         .HasMaxLength(256) | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("NormalizedUserName") | ||||||
|  |                         .HasMaxLength(256) | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("PasswordHash") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("PhoneNumber") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("PhoneNumberConfirmed") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<uint>("RowVersion") | ||||||
|  |                         .IsConcurrencyToken() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("SecurityStamp") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("TwoFactorEnabled") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("UserName") | ||||||
|  |                         .HasMaxLength(256) | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("NormalizedEmail") | ||||||
|  |                         .HasDatabaseName("EmailIndex"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("NormalizedUserName") | ||||||
|  |                         .IsUnique() | ||||||
|  |                         .HasDatabaseName("UserNameIndex"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetUsers"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserPreferences", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("AppUserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("AutoCloseMenu") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("BookReaderDarkMode") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("BookReaderFontFamily") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("BookReaderFontSize") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("BookReaderLineSpacing") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("BookReaderMargin") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("BookReaderReadingDirection") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("BookReaderTapToPaginate") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("PageSplitOption") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("ReaderMode") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("ReadingDirection") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("ScalingOption") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("SiteDarkMode") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("AppUserId") | ||||||
|  |                         .IsUnique(); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AppUserPreferences"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserProgress", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("AppUserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("BookScrollId") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("ChapterId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("Created") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastModified") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("PagesRead") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("SeriesId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("VolumeId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("AppUserId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AppUserProgresses"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserRating", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("AppUserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Rating") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Review") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("SeriesId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("AppUserId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AppUserRating"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserRole", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("UserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("RoleId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("UserId", "RoleId"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("RoleId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetUserRoles"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Chapter", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<byte[]>("CoverImage") | ||||||
|  |                         .HasColumnType("BLOB"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("Created") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("IsSpecial") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastModified") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Number") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Pages") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Range") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Title") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("VolumeId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("VolumeId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("Chapter"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.CollectionTag", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<byte[]>("CoverImage") | ||||||
|  |                         .HasColumnType("BLOB"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("NormalizedTitle") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<bool>("Promoted") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<uint>("RowVersion") | ||||||
|  |                         .IsConcurrencyToken() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Summary") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Title") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("Id", "Promoted") | ||||||
|  |                         .IsUnique(); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("CollectionTag"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.FolderPath", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastScanned") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("LibraryId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Path") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("LibraryId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("FolderPath"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Library", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("CoverImage") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("Created") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastModified") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Name") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Type") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("Library"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.MangaFile", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("ChapterId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("FilePath") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Format") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastModified") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Pages") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("ChapterId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("MangaFile"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Series", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<byte[]>("CoverImage") | ||||||
|  |                         .HasColumnType("BLOB"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("Created") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastModified") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("LibraryId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("LocalizedName") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Name") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("NormalizedName") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("OriginalName") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Pages") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("SortName") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Summary") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("LibraryId"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("Name", "NormalizedName", "LocalizedName", "LibraryId") | ||||||
|  |                         .IsUnique(); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("Series"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.SeriesMetadata", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<uint>("RowVersion") | ||||||
|  |                         .IsConcurrencyToken() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("SeriesId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("SeriesId") | ||||||
|  |                         .IsUnique(); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("Id", "SeriesId") | ||||||
|  |                         .IsUnique(); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("SeriesMetadata"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.ServerSetting", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Key") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<uint>("RowVersion") | ||||||
|  |                         .IsConcurrencyToken() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Value") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Key"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("ServerSetting"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Volume", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<byte[]>("CoverImage") | ||||||
|  |                         .HasColumnType("BLOB"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("Created") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<DateTime>("LastModified") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Name") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Number") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("Pages") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("SeriesId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("SeriesId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("Volume"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("AppUserLibrary", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("AppUsersId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("LibrariesId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("AppUsersId", "LibrariesId"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("LibrariesId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AppUserLibrary"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("CollectionTagSeriesMetadata", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("CollectionTagsId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("SeriesMetadatasId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("CollectionTagsId", "SeriesMetadatasId"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("SeriesMetadatasId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("CollectionTagSeriesMetadata"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ClaimType") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ClaimValue") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("RoleId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("RoleId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetRoleClaims"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ClaimType") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ClaimValue") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("UserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("Id"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("UserId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetUserClaims"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<string>("LoginProvider") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ProviderKey") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("ProviderDisplayName") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<int>("UserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("LoginProvider", "ProviderKey"); | ||||||
|  | 
 | ||||||
|  |                     b.HasIndex("UserId"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetUserLogins"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<int>("UserId") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("LoginProvider") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Name") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.Property<string>("Value") | ||||||
|  |                         .HasColumnType("TEXT"); | ||||||
|  | 
 | ||||||
|  |                     b.HasKey("UserId", "LoginProvider", "Name"); | ||||||
|  | 
 | ||||||
|  |                     b.ToTable("AspNetUserTokens"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserPreferences", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", "AppUser") | ||||||
|  |                         .WithOne("UserPreferences") | ||||||
|  |                         .HasForeignKey("API.Entities.AppUserPreferences", "AppUserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("AppUser"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserProgress", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", "AppUser") | ||||||
|  |                         .WithMany("Progresses") | ||||||
|  |                         .HasForeignKey("AppUserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("AppUser"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserRating", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", "AppUser") | ||||||
|  |                         .WithMany("Ratings") | ||||||
|  |                         .HasForeignKey("AppUserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("AppUser"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUserRole", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppRole", "Role") | ||||||
|  |                         .WithMany("UserRoles") | ||||||
|  |                         .HasForeignKey("RoleId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.HasOne("API.Entities.AppUser", "User") | ||||||
|  |                         .WithMany("UserRoles") | ||||||
|  |                         .HasForeignKey("UserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Role"); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("User"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Chapter", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.Volume", "Volume") | ||||||
|  |                         .WithMany("Chapters") | ||||||
|  |                         .HasForeignKey("VolumeId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Volume"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.FolderPath", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.Library", "Library") | ||||||
|  |                         .WithMany("Folders") | ||||||
|  |                         .HasForeignKey("LibraryId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Library"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.MangaFile", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.Chapter", "Chapter") | ||||||
|  |                         .WithMany("Files") | ||||||
|  |                         .HasForeignKey("ChapterId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Chapter"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Series", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.Library", "Library") | ||||||
|  |                         .WithMany("Series") | ||||||
|  |                         .HasForeignKey("LibraryId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Library"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.SeriesMetadata", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.Series", "Series") | ||||||
|  |                         .WithOne("Metadata") | ||||||
|  |                         .HasForeignKey("API.Entities.SeriesMetadata", "SeriesId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Series"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Volume", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.Series", "Series") | ||||||
|  |                         .WithMany("Volumes") | ||||||
|  |                         .HasForeignKey("SeriesId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Series"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("AppUserLibrary", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("AppUsersId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.HasOne("API.Entities.Library", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("LibrariesId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("CollectionTagSeriesMetadata", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.CollectionTag", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("CollectionTagsId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  | 
 | ||||||
|  |                     b.HasOne("API.Entities.SeriesMetadata", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("SeriesMetadatasId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppRole", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("RoleId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("UserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("UserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("API.Entities.AppUser", null) | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("UserId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired(); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppRole", b => | ||||||
|  |                 { | ||||||
|  |                     b.Navigation("UserRoles"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.AppUser", b => | ||||||
|  |                 { | ||||||
|  |                     b.Navigation("Progresses"); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Ratings"); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("UserPreferences"); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("UserRoles"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Chapter", b => | ||||||
|  |                 { | ||||||
|  |                     b.Navigation("Files"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Library", b => | ||||||
|  |                 { | ||||||
|  |                     b.Navigation("Folders"); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Series"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Series", b => | ||||||
|  |                 { | ||||||
|  |                     b.Navigation("Metadata"); | ||||||
|  | 
 | ||||||
|  |                     b.Navigation("Volumes"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |             modelBuilder.Entity("API.Entities.Volume", b => | ||||||
|  |                 { | ||||||
|  |                     b.Navigation("Chapters"); | ||||||
|  |                 }); | ||||||
|  | #pragma warning restore 612, 618 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								API/Data/Migrations/20210622164318_NewUserPreferences.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								API/Data/Migrations/20210622164318_NewUserPreferences.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore.Migrations; | ||||||
|  | 
 | ||||||
|  | namespace API.Data.Migrations | ||||||
|  | { | ||||||
|  |     public partial class NewUserPreferences : Migration | ||||||
|  |     { | ||||||
|  |         protected override void Up(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.AddColumn<bool>( | ||||||
|  |                 name: "AutoCloseMenu", | ||||||
|  |                 table: "AppUserPreferences", | ||||||
|  |                 type: "INTEGER", | ||||||
|  |                 nullable: false, | ||||||
|  |                 defaultValue: false); | ||||||
|  | 
 | ||||||
|  |             migrationBuilder.AddColumn<int>( | ||||||
|  |                 name: "ReaderMode", | ||||||
|  |                 table: "AppUserPreferences", | ||||||
|  |                 type: "INTEGER", | ||||||
|  |                 nullable: false, | ||||||
|  |                 defaultValue: 0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         protected override void Down(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.DropColumn( | ||||||
|  |                 name: "AutoCloseMenu", | ||||||
|  |                 table: "AppUserPreferences"); | ||||||
|  | 
 | ||||||
|  |             migrationBuilder.DropColumn( | ||||||
|  |                 name: "ReaderMode", | ||||||
|  |                 table: "AppUserPreferences"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -127,6 +127,9 @@ namespace API.Data.Migrations | |||||||
|                     b.Property<int>("AppUserId") |                     b.Property<int>("AppUserId") | ||||||
|                         .HasColumnType("INTEGER"); |                         .HasColumnType("INTEGER"); | ||||||
| 
 | 
 | ||||||
|  |                     b.Property<bool>("AutoCloseMenu") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|                     b.Property<bool>("BookReaderDarkMode") |                     b.Property<bool>("BookReaderDarkMode") | ||||||
|                         .HasColumnType("INTEGER"); |                         .HasColumnType("INTEGER"); | ||||||
| 
 | 
 | ||||||
| @ -151,6 +154,9 @@ namespace API.Data.Migrations | |||||||
|                     b.Property<int>("PageSplitOption") |                     b.Property<int>("PageSplitOption") | ||||||
|                         .HasColumnType("INTEGER"); |                         .HasColumnType("INTEGER"); | ||||||
| 
 | 
 | ||||||
|  |                     b.Property<int>("ReaderMode") | ||||||
|  |                         .HasColumnType("INTEGER"); | ||||||
|  | 
 | ||||||
|                     b.Property<int>("ReadingDirection") |                     b.Property<int>("ReadingDirection") | ||||||
|                         .HasColumnType("INTEGER"); |                         .HasColumnType("INTEGER"); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -289,7 +289,7 @@ namespace API.Data | |||||||
|         /// </summary> |         /// </summary> | ||||||
|         /// <param name="userId"></param> |         /// <param name="userId"></param> | ||||||
|         /// <param name="libraryId">Library to restrict to, if 0, will apply to all libraries</param> |         /// <param name="libraryId">Library to restrict to, if 0, will apply to all libraries</param> | ||||||
|         /// <param name="limit">How many series to pick.</param> |         /// <param name="userParams">Contains pagination information</param> | ||||||
|         /// <returns></returns> |         /// <returns></returns> | ||||||
|         public async Task<PagedList<SeriesDto>> GetRecentlyAdded(int libraryId, int userId, UserParams userParams) |         public async Task<PagedList<SeriesDto>> GetRecentlyAdded(int libraryId, int userId, UserParams userParams) | ||||||
|         { |         { | ||||||
|  | |||||||
| @ -29,7 +29,8 @@ namespace API.Data | |||||||
| 
 | 
 | ||||||
|         public IAppUserProgressRepository AppUserProgressRepository => new AppUserProgressRepository(_context); |         public IAppUserProgressRepository AppUserProgressRepository => new AppUserProgressRepository(_context); | ||||||
|         public ICollectionTagRepository CollectionTagRepository => new CollectionTagRepository(_context, _mapper); |         public ICollectionTagRepository CollectionTagRepository => new CollectionTagRepository(_context, _mapper); | ||||||
|          |         public IFileRepository FileRepository => new FileRepository(_context); | ||||||
|  | 
 | ||||||
|         public bool Commit() |         public bool Commit() | ||||||
|         { |         { | ||||||
|             return _context.SaveChanges() > 0; |             return _context.SaveChanges() > 0; | ||||||
|  | |||||||
| @ -17,7 +17,18 @@ namespace API.Entities | |||||||
|         /// Manga Reader Option: Which side of a split image should we show first |         /// Manga Reader Option: Which side of a split image should we show first | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public PageSplitOption PageSplitOption { get; set; } = PageSplitOption.SplitRightToLeft; |         public PageSplitOption PageSplitOption { get; set; } = PageSplitOption.SplitRightToLeft; | ||||||
| 
 |         /// <summary> | ||||||
|  |         /// Manga Reader Option: How the manga reader should perform paging or reading of the file | ||||||
|  |         /// <example> | ||||||
|  |         /// Webtoon uses scrolling to page, MANGA_LR uses paging by clicking left/right side of reader, MANGA_UD uses paging | ||||||
|  |         /// by clicking top/bottom sides of reader. | ||||||
|  |         /// </example> | ||||||
|  |         /// </summary> | ||||||
|  |         public ReaderMode ReaderMode { get; set; } | ||||||
|  |         /// <summary> | ||||||
|  |         /// Manga Reader Option: Allow the menu to close after 6 seconds without interaction | ||||||
|  |         /// </summary> | ||||||
|  |         public bool AutoCloseMenu { get; set; } | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Book Reader Option: Should the background color be dark |         /// Book Reader Option: Should the background color be dark | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								API/Entities/Enums/ReaderMode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								API/Entities/Enums/ReaderMode.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | using System.ComponentModel; | ||||||
|  | 
 | ||||||
|  | namespace API.Entities.Enums | ||||||
|  | { | ||||||
|  |     public enum ReaderMode | ||||||
|  |     { | ||||||
|  |         [Description("Left and Right")] | ||||||
|  |         MANGA_LR = 0, | ||||||
|  |         [Description("Up and Down")] | ||||||
|  |         MANGA_UP = 1, | ||||||
|  |         [Description("Webtoon")] | ||||||
|  |         WEBTOON = 2 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -32,7 +32,7 @@ namespace API.Entities | |||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Summary information related to the Series |         /// Summary information related to the Series | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         public string Summary { get; set; } // TODO: Migrate into SeriesMetdata |         public string Summary { get; set; } // TODO: Migrate into SeriesMetdata (with Metadata update) | ||||||
|         public DateTime Created { get; set; } |         public DateTime Created { get; set; } | ||||||
|         public DateTime LastModified { get; set; } |         public DateTime LastModified { get; set; } | ||||||
|         public byte[] CoverImage { get; set; } |         public byte[] CoverImage { get; set; } | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| using System; | using API.Data; | ||||||
| using API.Data; |  | ||||||
| using API.Helpers; | using API.Helpers; | ||||||
| using API.Interfaces; | using API.Interfaces; | ||||||
| using API.Interfaces.Services; | using API.Interfaces.Services; | ||||||
| @ -34,7 +33,6 @@ namespace API.Extensions | |||||||
|             services.AddScoped<IBookService, BookService>(); |             services.AddScoped<IBookService, BookService>(); | ||||||
| 
 | 
 | ||||||
|             services.AddSqLite(config, env); |             services.AddSqLite(config, env); | ||||||
|             services.ConfigRepositories(); |  | ||||||
| 
 | 
 | ||||||
|             services.AddLogging(loggingBuilder => |             services.AddLogging(loggingBuilder => | ||||||
|             { |             { | ||||||
| @ -56,17 +54,5 @@ namespace API.Extensions | |||||||
| 
 | 
 | ||||||
|             return services; |             return services; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         private static IServiceCollection ConfigRepositories(this IServiceCollection services) |  | ||||||
|         { |  | ||||||
|             services.AddScoped<ISettingsRepository, SettingsRepository>(); |  | ||||||
|             services.AddScoped<IFileRepository, FileRepository>(); |  | ||||||
|              |  | ||||||
|             return services; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         public static IServiceCollection AddStartupTask<T>(this IServiceCollection services) |  | ||||||
|             where T : class, IStartupTask |  | ||||||
|             => services.AddTransient<IStartupTask, T>(); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -16,7 +16,7 @@ namespace API.Extensions | |||||||
|         { |         { | ||||||
|             services.AddHttpClient<StatsApiClient>(client => |             services.AddHttpClient<StatsApiClient>(client => | ||||||
|             { |             { | ||||||
|                 client.BaseAddress = new Uri("http://stats.kavitareader.com"); |                 client.BaseAddress = new Uri("https://kavitastats.majora2007.duckdns.org"); | ||||||
|                 client.DefaultRequestHeaders.Add("api-key", "MsnvA2DfQqxSK5jh"); |                 client.DefaultRequestHeaders.Add("api-key", "MsnvA2DfQqxSK5jh"); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ namespace API.Interfaces | |||||||
|         ISettingsRepository SettingsRepository { get; } |         ISettingsRepository SettingsRepository { get; } | ||||||
|         IAppUserProgressRepository AppUserProgressRepository { get; } |         IAppUserProgressRepository AppUserProgressRepository { get; } | ||||||
|         ICollectionTagRepository CollectionTagRepository { get; } |         ICollectionTagRepository CollectionTagRepository { get; } | ||||||
|  |         IFileRepository FileRepository { get; } | ||||||
|         bool Commit(); |         bool Commit(); | ||||||
|         Task<bool> CommitAsync(); |         Task<bool> CommitAsync(); | ||||||
|         bool HasChanges(); |         bool HasChanges(); | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ using System.Collections.Generic; | |||||||
| using System.IO.Compression; | using System.IO.Compression; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.Archive; | using API.Archive; | ||||||
| using API.Entities; |  | ||||||
| 
 | 
 | ||||||
| namespace API.Interfaces.Services | namespace API.Interfaces.Services | ||||||
| { | { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ using System.Threading; | |||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using API.Data; | using API.Data; | ||||||
| using API.Entities; | using API.Entities; | ||||||
|  | using API.Services.HostedServices; | ||||||
| using Kavita.Common; | using Kavita.Common; | ||||||
| using Kavita.Common.EnvironmentInfo; | using Kavita.Common.EnvironmentInfo; | ||||||
| using Microsoft.AspNetCore.Hosting; | using Microsoft.AspNetCore.Hosting; | ||||||
| @ -20,7 +21,7 @@ namespace API | |||||||
| { | { | ||||||
|     public class Program |     public class Program | ||||||
|     { |     { | ||||||
|         private static int HttpPort; |         private static int _httpPort; | ||||||
| 
 | 
 | ||||||
|         protected Program() |         protected Program() | ||||||
|         { |         { | ||||||
| @ -48,7 +49,7 @@ namespace API | |||||||
|             } |             } | ||||||
|              |              | ||||||
|             // Get HttpPort from Config |             // Get HttpPort from Config | ||||||
|             HttpPort = Configuration.GetPort(GetAppSettingFilename()); |             _httpPort = Configuration.GetPort(GetAppSettingFilename()); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             var host = CreateHostBuilder(args).Build(); |             var host = CreateHostBuilder(args).Build(); | ||||||
| @ -64,7 +65,6 @@ namespace API | |||||||
|                 await context.Database.MigrateAsync(); |                 await context.Database.MigrateAsync(); | ||||||
|                 await Seed.SeedRoles(roleManager); |                 await Seed.SeedRoles(roleManager); | ||||||
|                 await Seed.SeedSettings(context); |                 await Seed.SeedSettings(context); | ||||||
|                  |  | ||||||
|             } |             } | ||||||
|             catch (Exception ex) |             catch (Exception ex) | ||||||
|             { |             { | ||||||
| @ -81,7 +81,7 @@ namespace API | |||||||
|                 { |                 { | ||||||
|                     webBuilder.UseKestrel((opts) => |                     webBuilder.UseKestrel((opts) => | ||||||
|                     { |                     { | ||||||
|                         opts.ListenAnyIP(HttpPort, options => |                         opts.ListenAnyIP(_httpPort, options => | ||||||
|                         { |                         { | ||||||
|                             options.Protocols = HttpProtocols.Http1AndHttp2; |                             options.Protocols = HttpProtocols.Http1AndHttp2; | ||||||
|                         }); |                         }); | ||||||
|  | |||||||
| @ -63,10 +63,6 @@ namespace API.Services | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             new DirectoryInfo(extractPath).Flatten(); |             new DirectoryInfo(extractPath).Flatten(); | ||||||
|             // if (fileCount > 1) |  | ||||||
|             // { |  | ||||||
|             //     new DirectoryInfo(extractPath).Flatten(); |  | ||||||
|             // } |  | ||||||
| 
 | 
 | ||||||
|             return chapter; |             return chapter; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -2,20 +2,25 @@ | |||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| using System.Net.Http.Json; | using System.Net.Http.Json; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
|  | 
 | ||||||
|  | using API.Configurations.CustomOptions; | ||||||
| using API.DTOs; | using API.DTOs; | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
|  | using Microsoft.Extensions.Options; | ||||||
| 
 | 
 | ||||||
| namespace API.Services.Clients | namespace API.Services.Clients | ||||||
| { | { | ||||||
|     public class StatsApiClient |     public class StatsApiClient | ||||||
|     { |     { | ||||||
|         private readonly HttpClient _client; |         private readonly HttpClient _client; | ||||||
|  |         private readonly StatsOptions _options; | ||||||
|         private readonly ILogger<StatsApiClient> _logger; |         private readonly ILogger<StatsApiClient> _logger; | ||||||
| 
 | 
 | ||||||
|         public StatsApiClient(HttpClient client, ILogger<StatsApiClient> logger) |         public StatsApiClient(HttpClient client, IOptions<StatsOptions> options, ILogger<StatsApiClient> logger) | ||||||
|         { |         { | ||||||
|             _client = client; |             _client = client; | ||||||
|             _logger = logger; |             _logger = logger; | ||||||
|  |             _options = options.Value ?? throw new ArgumentNullException(nameof(options)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public async Task SendDataToStatsServer(UsageStatisticsDto data) |         public async Task SendDataToStatsServer(UsageStatisticsDto data) | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ | |||||||
|         public string Publisher { get; set; } |         public string Publisher { get; set; } | ||||||
|         public string Genre { get; set; } |         public string Genre { get; set; } | ||||||
|         public int PageCount { get; set; } |         public int PageCount { get; set; } | ||||||
|  |         // ReSharper disable once InconsistentNaming | ||||||
|         public string LanguageISO { get; set; } |         public string LanguageISO { get; set; } | ||||||
|         public string Web { get; set; } |         public string Web { get; set; } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -28,7 +28,7 @@ namespace API.Services.HostedServices | |||||||
|             { |             { | ||||||
|                 await ManageStartupStatsTasks(scope, taskScheduler); |                 await ManageStartupStatsTasks(scope, taskScheduler); | ||||||
|             } |             } | ||||||
|             catch (Exception e) |             catch (Exception) | ||||||
|             { |             { | ||||||
|                 //If stats startup fail the user can keep using the app |                 //If stats startup fail the user can keep using the app | ||||||
|             } |             } | ||||||
| @ -36,9 +36,9 @@ namespace API.Services.HostedServices | |||||||
| 
 | 
 | ||||||
|         private async Task ManageStartupStatsTasks(IServiceScope serviceScope, ITaskScheduler taskScheduler) |         private async Task ManageStartupStatsTasks(IServiceScope serviceScope, ITaskScheduler taskScheduler) | ||||||
|         { |         { | ||||||
|             var settingsRepository = serviceScope.ServiceProvider.GetRequiredService<ISettingsRepository>(); |             var unitOfWork = serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>(); | ||||||
| 
 | 
 | ||||||
|             var settingsDto = await settingsRepository.GetSettingsDtoAsync(); |             var settingsDto = await unitOfWork.SettingsRepository.GetSettingsDtoAsync(); | ||||||
| 
 | 
 | ||||||
|             if (!settingsDto.AllowStatCollection) return; |             if (!settingsDto.AllowStatCollection) return; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -25,15 +25,15 @@ namespace API.Services | |||||||
|         private readonly StatsApiClient _client; |         private readonly StatsApiClient _client; | ||||||
|         private readonly DataContext _dbContext; |         private readonly DataContext _dbContext; | ||||||
|         private readonly ILogger<StatsService> _logger; |         private readonly ILogger<StatsService> _logger; | ||||||
|         private readonly IFileRepository _fileRepository; |         private readonly IUnitOfWork _unitOfWork; | ||||||
| 
 | 
 | ||||||
|         public StatsService(StatsApiClient client, DataContext dbContext, ILogger<StatsService> logger, |         public StatsService(StatsApiClient client, DataContext dbContext, ILogger<StatsService> logger, | ||||||
|             IFileRepository fileRepository) |             IUnitOfWork unitOfWork) | ||||||
|         { |         { | ||||||
|             _client = client; |             _client = client; | ||||||
|             _dbContext = dbContext; |             _dbContext = dbContext; | ||||||
|             _logger = logger; |             _logger = logger; | ||||||
|             _fileRepository = fileRepository; |             _unitOfWork = unitOfWork; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private static string FinalPath => Path.Combine(Directory.GetCurrentDirectory(), TempFilePath, TempFileName); |         private static string FinalPath => Path.Combine(Directory.GetCurrentDirectory(), TempFilePath, TempFileName); | ||||||
| @ -77,9 +77,9 @@ namespace API.Services | |||||||
|                 _logger.LogInformation("Deleting the file from disk"); |                 _logger.LogInformation("Deleting the file from disk"); | ||||||
|                 if (FileExists) File.Delete(FinalPath); |                 if (FileExists) File.Delete(FinalPath); | ||||||
|             } |             } | ||||||
|             catch (Exception e) |             catch (Exception ex) | ||||||
|             { |             { | ||||||
|                 _logger.LogError("Error Finalizing Stats collection flow", e); |                 _logger.LogError(ex, "Error Finalizing Stats collection flow"); | ||||||
|                 throw; |                 throw; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @ -121,7 +121,7 @@ namespace API.Services | |||||||
|                 .Select(x => new LibInfo {Type = x.Key, Count = x.Count()}) |                 .Select(x => new LibInfo {Type = x.Key, Count = x.Count()}) | ||||||
|                 .ToArrayAsync(); |                 .ToArrayAsync(); | ||||||
| 
 | 
 | ||||||
|             var uniqueFileTypes = await _fileRepository.GetFileExtensions(); |             var uniqueFileTypes = await _unitOfWork.FileRepository.GetFileExtensions(); | ||||||
| 
 | 
 | ||||||
|             var usageInfo = new UsageInfoDto |             var usageInfo = new UsageInfoDto | ||||||
|             { |             { | ||||||
|  | |||||||
							
								
								
									
										56
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | # How to Contribute # | ||||||
|  | 
 | ||||||
|  | We're always looking for people to help make Kavita even better, there are a number of ways to contribute. | ||||||
|  | 
 | ||||||
|  | ## Documentation ## | ||||||
|  | Setup guides, FAQ, the more information we have on the [wiki](https://github.com/Kareadita/Kavita/wiki) the better. | ||||||
|  | 
 | ||||||
|  | ## Development ## | ||||||
|  | 
 | ||||||
|  | ### Tools required ### | ||||||
|  | - Visual Studio 2019 or higher (https://www.visualstudio.com/vs/).  The community version is free and works fine. [Download it here](https://www.visualstudio.com/downloads/). | ||||||
|  | - Rider (optional to Visual Studio) (https://www.jetbrains.com/rider/)   | ||||||
|  | - HTML/Javascript editor of choice (VS Code/Sublime Text/Webstorm/Atom/etc) | ||||||
|  | - [Git](https://git-scm.com/downloads) | ||||||
|  | - [NodeJS](https://nodejs.org/en/download/) (Node 14.X.X or higher) | ||||||
|  | - .NET 5.0+  | ||||||
|  | 
 | ||||||
|  | ### Getting started ### | ||||||
|  | 
 | ||||||
|  | 1. Fork Kavita | ||||||
|  | 2. Clone the repository into your development machine. [*info*](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github) | ||||||
|  |     - Kavita as of v0.4.2 requires Kavita-webui to be cloned next to the Kavita. Fork and clone this as well. | ||||||
|  | 3. Install the required Node Packages | ||||||
|  |     - cd kavita-webui | ||||||
|  |     - `npm install` | ||||||
|  |     - `npm install -g @angular/cli` | ||||||
|  | 4. Start webui server `ng serve` | ||||||
|  | 5. Build the project in Visual Studio/Rider, Setting startup project to `API` | ||||||
|  | 6. Debug the project in Visual Studio/Rider | ||||||
|  | 7. Open http://localhost:4200 | ||||||
|  | 8. (Deployment only) Run build.sh and pass the Runtime Identifier for your OS or just build.sh for all supported RIDs. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### Contributing Code ### | ||||||
|  | - If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Kareadita/Kavita/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first) | ||||||
|  | - Rebase from Kavita's develop branch, don't merge | ||||||
|  | - Make meaningful commits, or squash them | ||||||
|  | - Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements | ||||||
|  | - Reach out to us on the discord if you have any questions | ||||||
|  | - Add tests (unit/integration) | ||||||
|  | - Commit with *nix line endings for consistency (We checkout Windows and commit *nix) | ||||||
|  | - One feature/bug fix per pull request to keep things clean and easy to understand | ||||||
|  | - Use 4 spaces instead of tabs, this is the default for VS 2019 and WebStorm (to my knowledge) | ||||||
|  |     - Use 2 spaces for Kavita-webui files | ||||||
|  | 
 | ||||||
|  | ### Pull Requesting ### | ||||||
|  | - Only make pull requests to develop, never master, if you make a PR to master we'll comment on it and close it | ||||||
|  | - You're probably going to get some comments or questions from us, they will be to ensure consistency and maintainability | ||||||
|  | - We'll try to respond to pull requests as soon as possible, if its been a day or two, please reach out to us, we may have missed it | ||||||
|  | - Each PR should come from its own [feature branch](http://martinfowler.com/bliki/FeatureBranch.html) not develop in your fork, it should have a meaningful branch name (what is being added/fixed) | ||||||
|  |     - new-feature (Good) | ||||||
|  |     - fix-bug (Good) | ||||||
|  |     - patch (Bad) | ||||||
|  |     - develop (Bad) | ||||||
|  | 
 | ||||||
|  | If you have any questions about any of this, please let us know. | ||||||
| @ -4,7 +4,7 @@ | |||||||
|         <TargetFramework>net5.0</TargetFramework> |         <TargetFramework>net5.0</TargetFramework> | ||||||
|         <Company>kareadita.github.io</Company> |         <Company>kareadita.github.io</Company> | ||||||
|         <Product>Kavita</Product> |         <Product>Kavita</Product> | ||||||
|         <AssemblyVersion>0.4.1.1</AssemblyVersion> |         <AssemblyVersion>0.4.2.0</AssemblyVersion> | ||||||
|         <NeutralLanguage>en</NeutralLanguage> |         <NeutralLanguage>en</NeutralLanguage> | ||||||
|     </PropertyGroup> |     </PropertyGroup> | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								Logo/dottrace.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Logo/dottrace.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||||
|  | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||||
|  | 	 width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve"> | ||||||
|  | <g> | ||||||
|  | 	<g> | ||||||
|  | 		<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-1.3318" y1="43.7371" x2="67.0419" y2="26.0967"> | ||||||
|  | 			<stop  offset="0.1237" style="stop-color:#7866FF"/> | ||||||
|  | 			<stop  offset="0.5376" style="stop-color:#FE2EB6"/> | ||||||
|  | 			<stop  offset="0.8548" style="stop-color:#FD0486"/> | ||||||
|  | 		</linearGradient> | ||||||
|  | 		<polygon style="fill:url(#SVGID_1_);" points="67.3,16 43.7,0 0,31.1 11.1,70 58.9,60.3 		"/> | ||||||
|  | 		<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="45.9148" y1="38.9098" x2="67.6577" y2="9.0989"> | ||||||
|  | 			<stop  offset="0.1237" style="stop-color:#FF0080"/> | ||||||
|  | 			<stop  offset="0.2587" style="stop-color:#FE0385"/> | ||||||
|  | 			<stop  offset="0.4109" style="stop-color:#FA0C92"/> | ||||||
|  | 			<stop  offset="0.5713" style="stop-color:#F41BA9"/> | ||||||
|  | 			<stop  offset="0.7363" style="stop-color:#EB2FC8"/> | ||||||
|  | 			<stop  offset="0.8656" style="stop-color:#E343E6"/> | ||||||
|  | 		</linearGradient> | ||||||
|  | 		<polygon style="fill:url(#SVGID_2_);" points="67.3,16 43.7,0 38,15.7 38,47.8 70,47.8 		"/> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<rect x="13.4" y="13.4" style="fill:#000000;" width="43.2" height="43.2"/> | ||||||
|  | 		<rect x="17.4" y="48.5" style="fill:#FFFFFF;" width="16.2" height="2.7"/> | ||||||
|  | 		<g> | ||||||
|  | 			<path style="fill:#FFFFFF;" d="M17.4,19.1h6.9c5.6,0,9.5,3.8,9.5,8.9V28c0,5-3.9,8.9-9.5,8.9h-6.9V19.1z M21.4,22.7v10.7h3 | ||||||
|  | 				c3.2,0,5.4-2.2,5.4-5.3V28c0-3.2-2.2-5.4-5.4-5.4H21.4z"/> | ||||||
|  | 			<polygon style="fill:#FFFFFF;" points="40.3,22.7 34.9,22.7 34.9,19.1 49.6,19.1 49.6,22.7 44.2,22.7 44.2,37 40.3,37 			"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										66
									
								
								Logo/jetbrains.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Logo/jetbrains.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||||
|  | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||||
|  | 	 width="120.1px" height="130.2px" viewBox="0 0 120.1 130.2" style="enable-background:new 0 0 120.1 130.2;" xml:space="preserve" | ||||||
|  | 	> | ||||||
|  | <g> | ||||||
|  | 	<linearGradient id="XMLID_2_" gradientUnits="userSpaceOnUse" x1="31.8412" y1="120.5578" x2="110.2402" y2="73.24"> | ||||||
|  | 		<stop  offset="0" style="stop-color:#FCEE39"/> | ||||||
|  | 		<stop  offset="1" style="stop-color:#F37B3D"/> | ||||||
|  | 	</linearGradient> | ||||||
|  | 	<path id="XMLID_3041_" style="fill:url(#XMLID_2_);" d="M118.6,71.8c0.9-0.8,1.4-1.9,1.5-3.2c0.1-2.6-1.8-4.7-4.4-4.9 | ||||||
|  | 		c-1.2-0.1-2.4,0.4-3.3,1.1l0,0l-83.8,45.9c-1.9,0.8-3.6,2.2-4.7,4.1c-2.9,4.8-1.3,11,3.6,13.9c3.4,2,7.5,1.8,10.7-0.2l0,0l0,0 | ||||||
|  | 		c0.2-0.2,0.5-0.3,0.7-0.5l78-54.8C117.3,72.9,118.4,72.1,118.6,71.8L118.6,71.8L118.6,71.8z"/> | ||||||
|  | 	<linearGradient id="XMLID_3_" gradientUnits="userSpaceOnUse" x1="48.3607" y1="6.9083" x2="119.9179" y2="69.5546"> | ||||||
|  | 		<stop  offset="0" style="stop-color:#EF5A6B"/> | ||||||
|  | 		<stop  offset="0.57" style="stop-color:#F26F4E"/> | ||||||
|  | 		<stop  offset="1" style="stop-color:#F37B3D"/> | ||||||
|  | 	</linearGradient> | ||||||
|  | 	<path id="XMLID_3049_" style="fill:url(#XMLID_3_);" d="M118.8,65.1L118.8,65.1L55,2.5C53.6,1,51.6,0,49.3,0 | ||||||
|  | 		c-4.3,0-7.7,3.5-7.7,7.7v0c0,2.1,0.8,3.9,2.1,5.3l0,0l0,0c0.4,0.4,0.8,0.7,1.2,1l67.4,57.7l0,0c0.8,0.7,1.8,1.2,3,1.3 | ||||||
|  | 		c2.6,0.1,4.7-1.8,4.9-4.4C120.2,67.3,119.7,66,118.8,65.1z"/> | ||||||
|  | 	<linearGradient id="XMLID_4_" gradientUnits="userSpaceOnUse" x1="52.9467" y1="63.6407" x2="10.5379" y2="37.1562"> | ||||||
|  | 		<stop  offset="0" style="stop-color:#7C59A4"/> | ||||||
|  | 		<stop  offset="0.3852" style="stop-color:#AF4C92"/> | ||||||
|  | 		<stop  offset="0.7654" style="stop-color:#DC4183"/> | ||||||
|  | 		<stop  offset="0.957" style="stop-color:#ED3D7D"/> | ||||||
|  | 	</linearGradient> | ||||||
|  | 	<path id="XMLID_3042_" style="fill:url(#XMLID_4_);" d="M57.1,59.5C57,59.5,17.7,28.5,16.9,28l0,0l0,0c-0.6-0.3-1.2-0.6-1.8-0.9 | ||||||
|  | 		c-5.8-2.2-12.2,0.8-14.4,6.6c-1.9,5.1,0.2,10.7,4.6,13.4l0,0l0,0C6,47.5,6.6,47.8,7.3,48c0.4,0.2,45.4,18.8,45.4,18.8l0,0 | ||||||
|  | 		c1.8,0.8,3.9,0.3,5.1-1.2C59.3,63.7,59,61,57.1,59.5z"/> | ||||||
|  | 	<linearGradient id="XMLID_5_" gradientUnits="userSpaceOnUse" x1="52.1736" y1="3.7019" x2="10.7706" y2="37.8971"> | ||||||
|  | 		<stop  offset="0" style="stop-color:#EF5A6B"/> | ||||||
|  | 		<stop  offset="0.364" style="stop-color:#EE4E72"/> | ||||||
|  | 		<stop  offset="1" style="stop-color:#ED3D7D"/> | ||||||
|  | 	</linearGradient> | ||||||
|  | 	<path id="XMLID_3057_" style="fill:url(#XMLID_5_);" d="M49.3,0c-1.7,0-3.3,0.6-4.6,1.5L4.9,28.3c-0.1,0.1-0.2,0.1-0.2,0.2l-0.1,0 | ||||||
|  | 		l0,0c-1.7,1.2-3.1,3-3.9,5.1C-1.5,39.4,1.5,45.9,7.3,48c3.6,1.4,7.5,0.7,10.4-1.4l0,0l0,0c0.7-0.5,1.3-1,1.8-1.6l34.6-31.2l0,0 | ||||||
|  | 		c1.8-1.4,3-3.6,3-6.1v0C57.1,3.5,53.6,0,49.3,0z"/> | ||||||
|  | 	<g id="XMLID_3008_"> | ||||||
|  | 		<rect id="XMLID_3033_" x="34.6" y="37.4" style="fill:#000000;" width="51" height="51"/> | ||||||
|  | 		<rect id="XMLID_3032_" x="39" y="78.8" style="fill:#FFFFFF;" width="19.1" height="3.2"/> | ||||||
|  | 		<g id="XMLID_3009_"> | ||||||
|  | 			<path id="XMLID_3030_" style="fill:#FFFFFF;" d="M38.8,50.8l1.5-1.4c0.4,0.5,0.8,0.8,1.3,0.8c0.6,0,0.9-0.4,0.9-1.2l0-5.3l2.3,0 | ||||||
|  | 				l0,5.3c0,1-0.3,1.8-0.8,2.3c-0.5,0.5-1.3,0.8-2.3,0.8C40.2,52.2,39.4,51.6,38.8,50.8z"/> | ||||||
|  | 			<path id="XMLID_3028_" style="fill:#FFFFFF;" d="M45.3,43.8l6.7,0v1.9l-4.4,0V47l4,0l0,1.8l-4,0l0,1.3l4.5,0l0,2l-6.7,0 | ||||||
|  | 				L45.3,43.8z"/> | ||||||
|  | 			<path id="XMLID_3026_" style="fill:#FFFFFF;" d="M55,45.8l-2.5,0l0-2l7.3,0l0,2l-2.5,0l0,6.3l-2.3,0L55,45.8z"/> | ||||||
|  | 			<path id="XMLID_3022_" style="fill:#FFFFFF;" d="M39,54l4.3,0c1,0,1.8,0.3,2.3,0.7c0.3,0.3,0.5,0.8,0.5,1.4v0 | ||||||
|  | 				c0,1-0.5,1.5-1.3,1.9c1,0.3,1.6,0.9,1.6,2v0c0,1.4-1.2,2.3-3.1,2.3l-4.3,0L39,54z M43.8,56.6c0-0.5-0.4-0.7-1-0.7l-1.5,0l0,1.5 | ||||||
|  | 				l1.4,0C43.4,57.3,43.8,57.1,43.8,56.6L43.8,56.6z M43,59l-1.8,0l0,1.5H43c0.7,0,1.1-0.3,1.1-0.8v0C44.1,59.2,43.7,59,43,59z"/> | ||||||
|  | 			<path id="XMLID_3019_" style="fill:#FFFFFF;" d="M46.8,54l3.9,0c1.3,0,2.1,0.3,2.7,0.9c0.5,0.5,0.7,1.1,0.7,1.9v0 | ||||||
|  | 				c0,1.3-0.7,2.1-1.7,2.6l2,2.9l-2.6,0l-1.7-2.5h-1l0,2.5l-2.3,0L46.8,54z M50.6,58c0.8,0,1.2-0.4,1.2-1v0c0-0.7-0.5-1-1.2-1 | ||||||
|  | 				l-1.5,0v2H50.6z"/> | ||||||
|  | 			<path id="XMLID_3016_" style="fill:#FFFFFF;" d="M56.8,54l2.2,0l3.5,8.4l-2.5,0l-0.6-1.5l-3.2,0l-0.6,1.5l-2.4,0L56.8,54z | ||||||
|  | 				 M58.8,59l-0.9-2.3L57,59L58.8,59z"/> | ||||||
|  | 			<path id="XMLID_3014_" style="fill:#FFFFFF;" d="M62.8,54l2.3,0l0,8.3l-2.3,0L62.8,54z"/> | ||||||
|  | 			<path id="XMLID_3012_" style="fill:#FFFFFF;" d="M65.7,54l2.1,0l3.4,4.4l0-4.4l2.3,0l0,8.3l-2,0L68,57.8l0,4.6l-2.3,0L65.7,54z" | ||||||
|  | 				/> | ||||||
|  | 			<path id="XMLID_3010_" style="fill:#FFFFFF;" d="M73.7,61.1l1.3-1.5c0.8,0.7,1.7,1,2.7,1c0.6,0,1-0.2,1-0.6v0 | ||||||
|  | 				c0-0.4-0.3-0.5-1.4-0.8c-1.8-0.4-3.1-0.9-3.1-2.6v0c0-1.5,1.2-2.7,3.2-2.7c1.4,0,2.5,0.4,3.4,1.1l-1.2,1.6 | ||||||
|  | 				c-0.8-0.5-1.6-0.8-2.3-0.8c-0.6,0-0.8,0.2-0.8,0.5v0c0,0.4,0.3,0.5,1.4,0.8c1.9,0.4,3.1,1,3.1,2.6v0c0,1.7-1.3,2.7-3.4,2.7 | ||||||
|  | 				C76.1,62.5,74.7,62,73.7,61.1z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 4.8 KiB | 
							
								
								
									
										124
									
								
								Logo/kavita.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								Logo/kavita.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,124 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||||
|  | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> | ||||||
|  | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||||
|  | 	 viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve"> | ||||||
|  | <g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#4AC694" d="M32,0c17.7,0,32,14.3,32,32S49.7,64,32,64S0,49.7,0,32S14.3,0,32,0z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#424C72" d="M52,17H12c-0.6,0-1,0.4-1,1v30c0,0.6,0.4,1,1,1h14.3c1,0,1.9,0.4,2.4,1.2c0.7,1.1,1.9,1.8,3.3,1.8 | ||||||
|  | 				s2.6-0.7,3.3-1.8c0.5-0.8,1.5-1.2,2.4-1.2H52c0.6,0,1-0.4,1-1V18C53,17.4,52.6,17,52,17z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M14,28v18h16c1.1,0,2,0.9,2,2c0-1.1,0.9-2,2-2h16V28H14z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#FFFFFF" d="M35,13c-1.7,0-3,1.3-3,3c0-1.7-1.3-3-3-3H14v31h16c1.1,0,2,0.9,2,2c0-1.1,0.9-2,2-2h16V13H35z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<rect x="18" y="16" fill="#57D1F7" width="4" height="7"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,26.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,26.5,29,26.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,23.5h-4.4c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5H29c0.3,0,0.5,0.2,0.5,0.5S29.3,23.5,29,23.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,20.5h-4.4c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5H29c0.3,0,0.5,0.2,0.5,0.5S29.3,20.5,29,20.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,17.5h-4.4c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5H29c0.3,0,0.5,0.2,0.5,0.5S29.3,17.5,29,17.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,29.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,29.5,29,29.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,32.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,32.5,29,32.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,35.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,35.5,29,35.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,38.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,38.5,29,38.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M29,41.5H18c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S29.3,41.5,29,41.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,26.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,26.5,46,26.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,29.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,29.5,46,29.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,20.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,20.5,46,20.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,17.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,17.5,46,17.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,23.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,23.5,46,23.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,32.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,32.5,46,32.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,35.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,35.5,46,35.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,38.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,38.5,46,38.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<path fill="#E4E7EF" d="M46,41.5H35c-0.3,0-0.5-0.2-0.5-0.5s0.2-0.5,0.5-0.5h11c0.3,0,0.5,0.2,0.5,0.5S46.3,41.5,46,41.5z"/> | ||||||
|  | 		</g> | ||||||
|  | 	</g> | ||||||
|  | </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										50
									
								
								Logo/resharper.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								Logo/resharper.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||||
|  | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||||
|  | 	 width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve"> | ||||||
|  | <g> | ||||||
|  | 	<g> | ||||||
|  | 		<g> | ||||||
|  | 			<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="22.9451" y1="75.7869" x2="74.7868" y2="20.6415"> | ||||||
|  | 				<stop  offset="1.612903e-002" style="stop-color:#B35BA3"/> | ||||||
|  | 				<stop  offset="0.4044" style="stop-color:#C41E57"/> | ||||||
|  | 				<stop  offset="0.4677" style="stop-color:#C41E57"/> | ||||||
|  | 				<stop  offset="0.6505" style="stop-color:#EB8523"/> | ||||||
|  | 				<stop  offset="0.9516" style="stop-color:#FEBD11"/> | ||||||
|  | 			</linearGradient> | ||||||
|  | 			<polygon style="fill:url(#SVGID_1_);" points="49.8,15.2 36,36.7 58.4,70 70,23.1 			"/> | ||||||
|  | 			<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="17.7187" y1="73.2922" x2="69.5556" y2="18.1519"> | ||||||
|  | 				<stop  offset="1.612903e-002" style="stop-color:#B35BA3"/> | ||||||
|  | 				<stop  offset="0.4044" style="stop-color:#C41E57"/> | ||||||
|  | 				<stop  offset="0.4677" style="stop-color:#C41E57"/> | ||||||
|  | 				<stop  offset="0.7043" style="stop-color:#EB8523"/> | ||||||
|  | 			</linearGradient> | ||||||
|  | 			<polygon style="fill:url(#SVGID_2_);" points="51.1,15.7 49,0 18.8,33.6 27.6,42.3 20.8,70 58.4,70 			"/> | ||||||
|  | 		</g> | ||||||
|  | 		<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="1.8281" y1="53.4275" x2="48.8245" y2="9.2255"> | ||||||
|  | 			<stop  offset="1.612903e-002" style="stop-color:#B35BA3"/> | ||||||
|  | 			<stop  offset="0.6613" style="stop-color:#C41E57"/> | ||||||
|  | 		</linearGradient> | ||||||
|  | 		<polygon style="fill:url(#SVGID_3_);" points="49,0 11.6,0 0,47.1 55.6,47.1 		"/> | ||||||
|  | 		<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="49.8935" y1="-11.5569" x2="48.8588" y2="24.0352"> | ||||||
|  | 			<stop  offset="0.5" style="stop-color:#C41E57"/> | ||||||
|  | 			<stop  offset="0.6668" style="stop-color:#D13F48"/> | ||||||
|  | 			<stop  offset="0.7952" style="stop-color:#D94F39"/> | ||||||
|  | 			<stop  offset="0.8656" style="stop-color:#DD5433"/> | ||||||
|  | 		</linearGradient> | ||||||
|  | 		<polygon style="fill:url(#SVGID_4_);" points="55.3,47.1 51.1,15.7 49,0 41.7,23 		"/> | ||||||
|  | 	</g> | ||||||
|  | 	<g> | ||||||
|  | 		 | ||||||
|  | 			<rect x="13.4" y="13.5" transform="matrix(-1 2.577289e-003 -2.577289e-003 -1 70.0288 70.081)" style="fill:#000000;" width="43.2" height="43.2"/> | ||||||
|  | 		 | ||||||
|  | 			<rect x="17.6" y="48.6" transform="matrix(1 -2.577289e-003 2.577289e-003 1 -0.1287 6.634109e-002)" style="fill:#FFFFFF;" width="16.2" height="2.7"/> | ||||||
|  | 		<path style="fill:#FFFFFF;" d="M17.4,19.1l8.2,0c2.3,0,4,0.6,5.2,1.8c1,1,1.5,2.4,1.5,4.1l0,0.1c0,1.5-0.3,2.6-1.1,3.5 | ||||||
|  | 			c-0.7,0.9-1.6,1.6-2.8,2l4.4,6.4l-4.6,0l-3.7-5.5l-3.3,0l0,5.5l-3.9,0L17.4,19.1z M25.3,27.8c1,0,1.7-0.2,2.2-0.7 | ||||||
|  | 			c0.5-0.5,0.8-1.1,0.8-1.8l0-0.1c0-0.9-0.3-1.5-0.8-1.9c-0.5-0.4-1.3-0.6-2.3-0.6l-3.9,0l0,5.1L25.3,27.8z"/> | ||||||
|  | 		<path style="fill:#FFFFFF;" d="M36,33.2l-1.9,0l0-3.3l2.5,0l0.6-3.8l-2.3,0l0-3.3l2.8,0l0.6-3.7l3.4,0l-0.6,3.7l3.7,0l0.6-3.7 | ||||||
|  | 			l3.4,0l-0.6,3.7l1.9,0l0,3.3l-2.5,0L47,29.9l2.3,0l0,3.3l-2.8,0L45.8,37l-3.4,0l0.7-3.8l-3.7,0L38.7,37l-3.4,0L36,33.2z | ||||||
|  | 			 M43.7,29.9l0.6-3.8l-3.7,0L40,29.9L43.7,29.9z"/> | ||||||
|  | 	</g> | ||||||
|  | </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 3.1 KiB | 
							
								
								
									
										42
									
								
								Logo/rider.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Logo/rider.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  --> | ||||||
|  | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | ||||||
|  | 	 width="70px" height="70px" viewBox="0 0 70 70" style="enable-background:new 0 0 70 70;" xml:space="preserve"> | ||||||
|  |   <defs> | ||||||
|  |     <linearGradient id="linear-gradient" x1="70.22612" y1="27.79912" x2="-5.13024" y2="63.12242" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse"> | ||||||
|  |       <stop offset="0" stop-color="#c90f5e"/> | ||||||
|  |       <stop offset="0.22111" stop-color="#c90f5e"/> | ||||||
|  |       <stop offset="0.2356" stop-color="#c90f5e"/> | ||||||
|  |       <stop offset="0.35559" stop-color="#ca135c"/> | ||||||
|  |       <stop offset="0.46633" stop-color="#ce1e57"/> | ||||||
|  |       <stop offset="0.5735" stop-color="#d4314e"/> | ||||||
|  |       <stop offset="0.67844" stop-color="#dc4b41"/> | ||||||
|  |       <stop offset="0.78179" stop-color="#e66d31"/> | ||||||
|  |       <stop offset="0.88253" stop-color="#f3961d"/> | ||||||
|  |       <stop offset="0.94241" stop-color="#fcb20f"/> | ||||||
|  |     </linearGradient> | ||||||
|  |     <linearGradient id="linear-gradient-2" x1="24.65904" y1="61.99608" x2="46.04762" y2="2.93445" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse"> | ||||||
|  |       <stop offset="0.04188" stop-color="#077cfb"/> | ||||||
|  |       <stop offset="0.44503" stop-color="#c90f5e"/> | ||||||
|  |       <stop offset="0.95812" stop-color="#077cfb"/> | ||||||
|  |     </linearGradient> | ||||||
|  |     <linearGradient id="linear-gradient-3" x1="17.39552" y1="63.34592" x2="33.19389" y2="7.20092" gradientTransform="matrix(1, 0, 0, -1, 0, 71.27997)" gradientUnits="userSpaceOnUse"> | ||||||
|  |       <stop offset="0.27749" stop-color="#c90f5e"/> | ||||||
|  |       <stop offset="0.97382" stop-color="#fcb20f"/> | ||||||
|  |     </linearGradient> | ||||||
|  |   </defs> | ||||||
|  |   <title>rider</title> | ||||||
|  |   <g> | ||||||
|  |     <polygon points="70 27.237 63.391 23.75 20.926 0 3.827 17.921 21.619 41.068 60.537 44.397 70 27.237" fill="url(#linear-gradient)"/> | ||||||
|  |     <polygon points="50.423 16.132 44.271 1.107 27.643 17.471 11.768 50.194 49.411 70 70 57.98 50.423 16.132" fill="url(#linear-gradient-2)"/> | ||||||
|  |     <polygon points="20.926 0 0 14.095 7.779 62.172 27.848 69.889 53.78 48.823 20.926 0" fill="url(#linear-gradient-3)"/> | ||||||
|  |   </g> | ||||||
|  |   <g> | ||||||
|  |     <rect x="13.30219" y="13.19311" width="43.61371" height="43.61371"/> | ||||||
|  |     <g> | ||||||
|  |       <path d="M17.22741,18.86293h8.39564a7.38416,7.38416,0,0,1,5.34268,1.85358,5.86989,5.86989,0,0,1,1.52648,4.1433h0A5.74339,5.74339,0,0,1,28.567,30.5296l4.47041,6.54206H28.34891L24.42368,31.1838h-3.162v5.88785H17.22741V18.86293h0ZM25.296,27.69471c1.96262,0,3.053-1.09034,3.053-2.61682h0c0-1.74455-1.19938-2.61682-3.162-2.61682H21.15265v5.23365H25.296Z" fill="#fff"/> | ||||||
|  |       <path d="M36.09034,18.86293H43.2866c5.77882,0,9.70405,3.92523,9.70405,9.15888h0c0,5.12461-3.92523,9.15888-9.70405,9.15888H36.09034V18.86293Zm4.03427,3.59813V33.47352h3.162a5.23727,5.23727,0,0,0,5.56075-5.45171h0a5.26493,5.26493,0,0,0-5.56075-5.56075h-3.162Z" fill="#fff"/> | ||||||
|  |     </g> | ||||||
|  |     <rect x="17.22741" y="48.62925" width="16.35514" height="2.72586" fill="#fff"/> | ||||||
|  |   </g> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 3.0 KiB | 
							
								
								
									
										1
									
								
								Logo/sentry.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Logo/sentry.svg
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | <svg class="css-15xgryy e10nushx5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 222 66" width="400" height="119"><path d="M29,2.26a4.67,4.67,0,0,0-8,0L14.42,13.53A32.21,32.21,0,0,1,32.17,40.19H27.55A27.68,27.68,0,0,0,12.09,17.47L6,28a15.92,15.92,0,0,1,9.23,12.17H4.62A.76.76,0,0,1,4,39.06l2.94-5a10.74,10.74,0,0,0-3.36-1.9l-2.91,5a4.54,4.54,0,0,0,1.69,6.24A4.66,4.66,0,0,0,4.62,44H19.15a19.4,19.4,0,0,0-8-17.31l2.31-4A23.87,23.87,0,0,1,23.76,44H36.07a35.88,35.88,0,0,0-16.41-31.8l4.67-8a.77.77,0,0,1,1.05-.27c.53.29,20.29,34.77,20.66,35.17a.76.76,0,0,1-.68,1.13H40.6q.09,1.91,0,3.81h4.78A4.59,4.59,0,0,0,50,39.43a4.49,4.49,0,0,0-.62-2.28Z M124.32,28.28,109.56,9.22h-3.68V34.77h3.73V15.19l15.18,19.58h3.26V9.22h-3.73ZM87.15,23.54h13.23V20.22H87.14V12.53h14.93V9.21H83.34V34.77h18.92V31.45H87.14ZM71.59,20.3h0C66.44,19.06,65,18.08,65,15.7c0-2.14,1.89-3.59,4.71-3.59a12.06,12.06,0,0,1,7.07,2.55l2-2.83a14.1,14.1,0,0,0-9-3c-5.06,0-8.59,3-8.59,7.27,0,4.6,3,6.19,8.46,7.52C74.51,24.74,76,25.78,76,28.11s-2,3.77-5.09,3.77a12.34,12.34,0,0,1-8.3-3.26l-2.25,2.69a15.94,15.94,0,0,0,10.42,3.85c5.48,0,9-2.95,9-7.51C79.75,23.79,77.47,21.72,71.59,20.3ZM195.7,9.22l-7.69,12-7.64-12h-4.46L186,24.67V34.78h3.84V24.55L200,9.22Zm-64.63,3.46h8.37v22.1h3.84V12.68h8.37V9.22H131.08ZM169.41,24.8c3.86-1.07,6-3.77,6-7.63,0-4.91-3.59-8-9.38-8H154.67V34.76h3.8V25.58h6.45l6.48,9.2h4.44l-7-9.82Zm-10.95-2.5V12.6h7.17c3.74,0,5.88,1.77,5.88,4.84s-2.29,4.86-5.84,4.86Z" transform="translate(11, 11)" fill="#ffffff"></path></svg> | ||||||
| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										79
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								README.md
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| # Kavita | # [<img src="/Logo/kavita.svg" width="32" alt="">]() Kavita | ||||||
| <div align="center"> | <div align="center"> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| @ -9,44 +9,41 @@ your reading collection with your friends and family! | |||||||
| 
 | 
 | ||||||
| [](https://github.com/Kareadita/Kavita/releases) | [](https://github.com/Kareadita/Kavita/releases) | ||||||
| [](https://github.com/Kareadita/Kavita/blob/master/LICENSE) | [](https://github.com/Kareadita/Kavita/blob/master/LICENSE) | ||||||
| [](https://discord.gg/eczRp9eeem) |  | ||||||
| [](https://github.com/Kareadita/Kavita/releases) | [](https://github.com/Kareadita/Kavita/releases) | ||||||
| [](https://hub.docker.com/r/kizaing/kavita/) | [](https://hub.docker.com/r/kizaing/kavita/) | ||||||
| [](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) |  | ||||||
| [](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) | [](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) | ||||||
| [](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) | [](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) | ||||||
| [](https://paypal.me/majora2007?locale.x=en_US) | [](https://paypal.me/majora2007?locale.x=en_US) | ||||||
|  | [](#backers) | ||||||
|  | [](#sponsors) | ||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| ## Goals: | ## Goals | ||||||
| - [x] Serve up Manga/Webtoons/Comics (cbr, cbz, zip/rar, 7zip, raw images) and Books (epub, mobi, azw, djvu, pdf) | - [x] Serve up Manga/Webtoons/Comics (cbr, cbz, zip/rar, 7zip, raw images) and Books (epub, mobi, azw, djvu, pdf) | ||||||
| - [x] First class responsive readers that work great on any device | - [x] First class responsive readers that work great on any device (phone, tablet, desktop) | ||||||
| - [x] Provide a dark theme for web app | - [x] Provide a dark theme for web app | ||||||
| - [ ] Provide hooks into metadata providers to fetch metadata for Comics, Manga, and Books | - [ ] Provide hooks into metadata providers to fetch metadata for Comics, Manga, and Books | ||||||
| - [ ] Metadata should allow for collections, want to read integration from 3rd party services, genres. | - [ ] Metadata should allow for collections, want to read integration from 3rd party services, genres. | ||||||
| - [x] Ability to manage users, access, and ratings | - [x] Ability to manage users, access, and ratings | ||||||
| - [ ] Ability to sync ratings and reviews to external services | - [ ] Ability to sync ratings and reviews to external services | ||||||
| - [x] Fully Accessible  | - [x] Fully Accessible with active accessibility audits | ||||||
|  | - [x] Dedicated webtoon reader | ||||||
| - [ ] And so much [more...](https://github.com/Kareadita/Kavita/projects) | - [ ] And so much [more...](https://github.com/Kareadita/Kavita/projects) | ||||||
| 
 | 
 | ||||||
|  | ## Support | ||||||
|  | [](https://www.reddit.com/r/KavitaManga/) | ||||||
|  | [](https://discord.gg/eczRp9eeem) | ||||||
|  | [](https://github.com/Kareadita/Kavita/issues) | ||||||
| 
 | 
 | ||||||
| # How to contribute | ## Setup | ||||||
| - Ensure you've cloned Kavita-webui. You should have Projects/Kavita and Projects/Kavita-webui | ### Non-Docker | ||||||
| - In Kavita-webui, run ng serve. This will start the webserver on localhost:4200 |  | ||||||
| - Run API project in Kavita, this will start the backend on localhost:5000 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| ## Deploy local build |  | ||||||
| - Run build.sh and pass the Runtime Identifier for your OS or just build.sh for all supported RIDs. |  | ||||||
| 
 |  | ||||||
| ## How to install |  | ||||||
| - Unzip the archive for your target OS | - Unzip the archive for your target OS | ||||||
| - Place in a directory that is writable. If on windows, do not place in Program Files | - Place in a directory that is writable. If on windows, do not place in Program Files | ||||||
| - Linux users must ensure the directory & kavita.db is writable by Kavita (might require starting server once)  | - Linux users must ensure the directory & kavita.db is writable by Kavita (might require starting server once)  | ||||||
| - Run Kavita | - Run Kavita | ||||||
| - If you are updating, do not copy appsettings.json from the new version over. It will override your TokenKey and you will have to reauthenticate on your devices. | - If you are updating, do not copy appsettings.json from the new version over. It will override your TokenKey and you will have to reauthenticate on your devices. | ||||||
| 
 | 
 | ||||||
| ## Docker | ### Docker | ||||||
| Running your Kavita server in docker is super easy! Barely an inconvenience. You can run it with this command:  | Running your Kavita server in docker is super easy! Barely an inconvenience. You can run it with this command:  | ||||||
| 
 | 
 | ||||||
| ``` | ``` | ||||||
| @ -72,17 +69,51 @@ services: | |||||||
|         restart: unless-stopped |         restart: unless-stopped | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| **Note: Kavita is under heavy development and is being updated all the time, so the tag for current builds is :nightly. The :latest tag will be the latest stable release. There is also the :alpine tag if you want a smaller image, but it is only available for x64 systems.** | **Note: Kavita is under heavy development and is being updated all the time, so the tag for current builds is `:nightly`. The `:latest` tag will be the latest stable release. There is also the `:alpine` tag if you want a smaller image, but it is only available for x64 systems.** | ||||||
| 
 | 
 | ||||||
| ## Got an Idea? | ## Feature Requests | ||||||
| Got a great idea? Throw it up on the FeatHub or vote on another persons. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects) first for a list of planned features. | Got a great idea? Throw it up on the FeatHub or vote on another idea. Please check the [Project Board](https://github.com/Kareadita/Kavita/projects) first for a list of planned features. | ||||||
| 
 | 
 | ||||||
| [](https://feathub.com/Kareadita/Kavita) | [](https://feathub.com/Kareadita/Kavita) | ||||||
| 
 | 
 | ||||||
| ## Want to help? | 
 | ||||||
| I am looking for developers with a passion for building the next Plex for Reading. Developers with C#/ASP.NET, Angular 11 please reach out on [Discord](https://discord.gg/eczRp9eeem).   | ## Contributors | ||||||
|  | 
 | ||||||
|  | This project exists thanks to all the people who contribute. [Contribute](CONTRIBUTING.md). | ||||||
|  | <a href="https://github.com/Kareadita/Kavita/graphs/contributors"><img src="https://opencollective.com/kavita/contributors.svg?width=890&button=false" /></a> | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| ## Donate | ## Donate | ||||||
| If you like Kavita, have gotten good use out of it or feel like you want to say thanks with a few bucks, feel free to donate. Money will  | If you like Kavita, have gotten good use out of it or feel like you want to say thanks with a few bucks, feel free to donate. Money will go towards  | ||||||
| likely go towards beer or hosting. | expenses related to Kavita. You can back us through OpenCollective. | ||||||
|  | 
 | ||||||
| [](https://paypal.me/majora2007?locale.x=en_US) | [](https://paypal.me/majora2007?locale.x=en_US) | ||||||
|  | 
 | ||||||
|  | ## Backers | ||||||
|  | 
 | ||||||
|  | Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/Kavita#backer) | ||||||
|  | 
 | ||||||
|  | <img src="https://opencollective.com/Kavita/backers.svg?width=890"></a> | ||||||
|  | 
 | ||||||
|  | ## Sponsors | ||||||
|  | 
 | ||||||
|  | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor](https://opencollective.com/Kavita#sponsor) | ||||||
|  | 
 | ||||||
|  | <img src="https://opencollective.com/Kavita/sponsors.svg?width=890"></a> | ||||||
|  | 
 | ||||||
|  | ## Mega Sponsors | ||||||
|  | <img src="https://opencollective.com/Kavita/tiers/mega-sponsor.svg?width=890"></a> | ||||||
|  | 
 | ||||||
|  | ## JetBrains | ||||||
|  | Thank you to [<img src="/Logo/jetbrains.svg" alt="" width="32"> JetBrains](http://www.jetbrains.com/) for providing us with free licenses to their great tools. | ||||||
|  | 
 | ||||||
|  | * [<img src="/Logo/rider.svg" alt="" width="32"> Rider](http://www.jetbrains.com/rider/) | ||||||
|  | * [<img src="/Logo/dottrace.svg" alt="" width="32"> dotTrace](http://www.jetbrains.com/dottrace/) | ||||||
|  | 
 | ||||||
|  | ## Sentry | ||||||
|  | Thank you to [<img src="/Logo/sentry.svg" alt="" width="32"> Sentry](https://sentry.io/welcome/) for providing us with free license to their software. | ||||||
|  | 
 | ||||||
|  | ### License | ||||||
|  | 
 | ||||||
|  | * [GNU GPL v3](http://www.gnu.org/licenses/gpl.html) | ||||||
|  | * Copyright 2010-2021 | ||||||
							
								
								
									
										9
									
								
								build.sh
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								build.sh
									
									
									
									
									
								
							| @ -48,9 +48,15 @@ Build() | |||||||
| BuildUI() | BuildUI() | ||||||
| { | { | ||||||
|     ProgressStart 'Building UI' |     ProgressStart 'Building UI' | ||||||
|  |     echo 'Removing old wwwroot' | ||||||
|  |     rm -rf API/wwwroot/* | ||||||
|     cd ../Kavita-webui/ || exit |     cd ../Kavita-webui/ || exit | ||||||
|  |     echo 'Installing web dependencies' | ||||||
|     npm install |     npm install | ||||||
|  |     echo 'Building UI' | ||||||
|     npm run prod |     npm run prod | ||||||
|  |     echo 'Copying back to Kavita wwwroot' | ||||||
|  |     cp -r dist/* ../Kavita/API/wwwroot | ||||||
|     cd ../Kavita/ || exit |     cd ../Kavita/ || exit | ||||||
|     ProgressEnd 'Building UI' |     ProgressEnd 'Building UI' | ||||||
| } | } | ||||||
| @ -68,6 +74,9 @@ Package() | |||||||
|     cd API |     cd API | ||||||
|     echo dotnet publish -c Release --self-contained --runtime $runtime -o "$lOutputFolder" --framework $framework |     echo dotnet publish -c Release --self-contained --runtime $runtime -o "$lOutputFolder" --framework $framework | ||||||
|     dotnet publish -c Release --self-contained --runtime $runtime -o "$lOutputFolder" --framework $framework |     dotnet publish -c Release --self-contained --runtime $runtime -o "$lOutputFolder" --framework $framework | ||||||
|  |      | ||||||
|  |     echo "Recopying wwwroot due to bug" | ||||||
|  |     cp -r ./wwwroot/* $lOutputFolder/wwwroot | ||||||
| 
 | 
 | ||||||
|     echo "Copying Install information" |     echo "Copying Install information" | ||||||
|     cp ../INSTALL.txt "$lOutputFolder"/README.txt |     cp ../INSTALL.txt "$lOutputFolder"/README.txt | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user