Skip to content

Commit

Permalink
Merged develop into main
Browse files Browse the repository at this point in the history
  • Loading branch information
majora2007 committed Jun 6, 2021
2 parents 9c61c26 + 8211f1c commit cdd01ee
Show file tree
Hide file tree
Showing 88 changed files with 7,190 additions and 178 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Set line formatting for scripts

*.sh text eol=lf
36 changes: 36 additions & 0 deletions .github/workflows/nightly-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI to Docker Hub

on:
push:
branches:
- 'develop'

jobs:
docker:
runs-on: ubuntu-latest
steps:

- name: Check Out Repo
uses: actions/checkout@v2

- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1

- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: ./
file: ./Dockerfile
push: true
tags: kizaing/kavita:nightly-amd64

- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
3 changes: 2 additions & 1 deletion API.Tests/Extensions/SeriesExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public void NameInListTest(string[] seriesInput, string[] list, bool expected)
Name = seriesInput[0],
LocalizedName = seriesInput[1],
OriginalName = seriesInput[2],
NormalizedName = seriesInput.Length == 4 ? seriesInput[3] : API.Parser.Parser.Normalize(seriesInput[0])
NormalizedName = seriesInput.Length == 4 ? seriesInput[3] : API.Parser.Parser.Normalize(seriesInput[0]),
Metadata = new SeriesMetadata()
};

Assert.Equal(expected, series.NameInList(list));
Expand Down
23 changes: 22 additions & 1 deletion API.Tests/Helpers/EntityFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public static Series CreateSeries(string name)
SortName = name,
LocalizedName = name,
NormalizedName = API.Parser.Parser.Normalize(name),
Volumes = new List<Volume>()
Volumes = new List<Volume>(),
Metadata = new SeriesMetadata()
};
}

Expand Down Expand Up @@ -53,5 +54,25 @@ public static MangaFile CreateMangaFile(string filename, MangaFormat format, int
Pages = pages
};
}

public static SeriesMetadata CreateSeriesMetadata(ICollection<CollectionTag> collectionTags)
{
return new SeriesMetadata()
{
CollectionTags = collectionTags
};
}

public static CollectionTag CreateCollectionTag(int id, string title, string summary, bool promoted)
{
return new CollectionTag()
{
Id = id,
NormalizedTitle = API.Parser.Parser.Normalize(title).ToUpper(),
Title = title,
Summary = summary,
Promoted = promoted
};
}
}
}
21 changes: 19 additions & 2 deletions API.Tests/Parser/MangaParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,17 @@ public MangaParserTests(ITestOutputHelper testOutputHelper)
[InlineData("Kedouin Makoto - Corpse Party Musume, Chapter 12 [Dametrans][v2]", "0")]
[InlineData("Vagabond_v03", "3")]
[InlineData("Mujaki No Rakune Volume 10.cbz", "10")]
[InlineData("Umineko no Naku Koro ni - Episode 3 - Banquet of the Golden Witch #02.cbz", "3")]
[InlineData("Umineko no Naku Koro ni - Episode 3 - Banquet of the Golden Witch #02.cbz", "0")]
[InlineData("Volume 12 - Janken Boy is Coming!.cbz", "12")]
[InlineData("[dmntsf.net] One Piece - Digital Colored Comics Vol. 20 Ch. 177 - 30 Million vs 81 Million.cbz", "20")]
[InlineData("Gantz.V26.cbz", "26")]
[InlineData("NEEDLESS_Vol.4_-Simeon_6_v2[SugoiSugoi].rar", "4")]
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "1")]
[InlineData("NEEDLESS_Vol.4_-_Simeon_6_v2_[SugoiSugoi].rar", "4")]
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "1")]

[InlineData("Sword Art Online Vol 10 - Alicization Running [Yen Press] [LuCaZ] {r2}.epub", "10")]
[InlineData("Noblesse - Episode 406 (52 Pages).7z", "0")]
[InlineData("X-Men v1 #201 (September 2007).cbz", "1")]
public void ParseVolumeTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseVolume(filename));
Expand Down Expand Up @@ -137,6 +139,12 @@ public void ParseVolumeTest(string filename, string expected)
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "Okusama wa Shougakusei")]
[InlineData("VanDread-v01-c001[MD].zip", "VanDread")]
[InlineData("Momo The Blood Taker - Chapter 027 Violent Emotion.cbz", "Momo The Blood Taker")]
[InlineData("Kiss x Sis - Ch.15 - The Angst of a 15 Year Old Boy.cbz", "Kiss x Sis")]
[InlineData("Green Worldz - Chapter 112 Final Chapter (End).cbz", "Green Worldz")]
[InlineData("Noblesse - Episode 406 (52 Pages).7z", "Noblesse")]
[InlineData("X-Men v1 #201 (September 2007).cbz", "X-Men")]
[InlineData("Kodoja #001 (March 2016)", "Kodoja")]
[InlineData("Boku No Kokoro No Yabai Yatsu - Chapter 054 I Prayed At The Shrine (V0).cbz", "Boku No Kokoro No Yabai Yatsu")]
public void ParseSeriesTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseSeries(filename));
Expand Down Expand Up @@ -197,6 +205,13 @@ public void ParseSeriesTest(string filename, string expected)
[InlineData("Kiss x Sis - Ch.00 - Let's Start from 0.cbz", "0")]
[InlineData("[Hidoi]_Amaenaideyo_MS_vol01_chp02.rar", "2")]
[InlineData("Okusama wa Shougakusei c003 (v01) [bokuwaNEET]", "3")]
[InlineData("Kiss x Sis - Ch.15 - The Angst of a 15 Year Old Boy.cbz", "15")]
[InlineData("Tomogui Kyoushitsu - Chapter 006 Game 005 - Fingernails On Right Hand (Part 002).cbz", "6")]
[InlineData("Noblesse - Episode 406 (52 Pages).7z", "406")]
[InlineData("X-Men v1 #201 (September 2007).cbz", "201")]
[InlineData("Kodoja #001 (March 2016)", "1")]
[InlineData("Noblesse - Episode 429 (74 Pages).7z", "429")]
[InlineData("Boku No Kokoro No Yabai Yatsu - Chapter 054 I Prayed At The Shrine (V0).cbz", "54")]
public void ParseChaptersTest(string filename, string expected)
{
Assert.Equal(expected, API.Parser.Parser.ParseChapter(filename));
Expand Down Expand Up @@ -225,6 +240,8 @@ public void ParseEditionTest(string input, string expected)
[InlineData("Corpse Party -The Anthology- Sachikos game of love Hysteric Birthday 2U Extra Chapter", true)]
[InlineData("Ani-Hina Art Collection.cbz", true)]
[InlineData("Gifting The Wonderful World With Blessings! - 3 Side Stories [yuNS][Unknown]", true)]
[InlineData("A Town Where You Live - Bonus Chapter.zip", true)]
[InlineData("Yuki Merry - 4-Komga Anthology", true)]
public void ParseMangaSpecialTest(string input, bool expected)
{
Assert.Equal(expected, !string.IsNullOrEmpty(API.Parser.Parser.ParseMangaSpecial(input)));
Expand Down
2 changes: 2 additions & 0 deletions API.Tests/Parser/ParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ public void IsEpubTest(string input, bool expected)
[InlineData("18-04", 4)]
[InlineData("18-04.5", 4.5)]
[InlineData("40", 40)]
[InlineData("40a-040b", 0)]
[InlineData("40.1_a", 0)]
public void MinimumNumberFromRangeTest(string input, float expected)
{
Assert.Equal(expected, MinimumNumberFromRange(input));
Expand Down
6 changes: 4 additions & 2 deletions API.Tests/Services/ScannerServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,16 @@ public void FindSeriesNotOnDisk_Should_RemoveNothing_Test()
Name = "Cage of Eden",
LocalizedName = "Cage of Eden",
OriginalName = "Cage of Eden",
NormalizedName = API.Parser.Parser.Normalize("Cage of Eden")
NormalizedName = API.Parser.Parser.Normalize("Cage of Eden"),
Metadata = new SeriesMetadata()
});
existingSeries.Add(new Series()
{
Name = "Darker Than Black",
LocalizedName = "Darker Than Black",
OriginalName = "Darker Than Black",
NormalizedName = API.Parser.Parser.Normalize("Darker Than Black")
NormalizedName = API.Parser.Parser.Normalize("Darker Than Black"),
Metadata = new SeriesMetadata()
});


Expand Down
22 changes: 22 additions & 0 deletions API/API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@
<ApplicationIcon>../favicon.ico</ApplicationIcon>
</PropertyGroup>

<!-- Set the Product and Version info for our own projects -->
<PropertyGroup>
<Product>Kavita</Product>
<Company>kareadita.github.io</Company>
<Copyright>Copyright 2020-$([System.DateTime]::Now.ToString('yyyy')) kareadita.github.io (GNU General Public v3)</Copyright>

<!-- Should be replaced by CI -->
<AssemblyVersion>0.4.1</AssemblyVersion>
<AssemblyConfiguration>$(Configuration)-dev</AssemblyConfiguration>

<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>

<Deterministic Condition="$(AssemblyVersion.EndsWith('*'))">False</Deterministic>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />
<PackageReference Include="ExCSS" Version="4.1.0" />
Expand All @@ -33,6 +50,7 @@
<PackageReference Include="NetVips" Version="2.0.0" />
<PackageReference Include="NetVips.Native" Version="8.10.6" />
<PackageReference Include="NReco.Logging.File" Version="1.1.1" />
<PackageReference Include="Sentry.AspNetCore" Version="3.3.4" />
<PackageReference Include="SharpCompress" Version="0.28.1" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="8.20.0.28934">
<PrivateAssets>all</PrivateAssets>
Expand Down Expand Up @@ -65,4 +83,8 @@
<_ContentIncludedByDefault Remove="logs\kavita.json" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Kavita.Common\Kavita.Common.csproj" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions API/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public async Task<ActionResult> UpdatePassword(ResetPasswordDto resetPasswordDto
{
_logger.LogInformation("{UserName} is changing {ResetUser}'s password", User.GetUsername(), resetPasswordDto.UserName);
var user = await _userManager.Users.SingleAsync(x => x.UserName == resetPasswordDto.UserName);
var isAdmin = await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole);

if (resetPasswordDto.UserName != User.GetUsername() && !isAdmin) return Unauthorized("You are not permitted to this operation.");
if (resetPasswordDto.UserName != User.GetUsername() && !User.IsInRole(PolicyConstants.AdminRole))
return Unauthorized("You are not permitted to this operation.");

// Validate Password
foreach (var validator in _userManager.PasswordValidators)
Expand Down
48 changes: 38 additions & 10 deletions API/Controllers/BookController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public BookController(ILogger<BookController> logger, IBookService bookService,
public async Task<ActionResult<string>> GetBookInfo(int chapterId)
{
var chapter = await _unitOfWork.VolumeRepository.GetChapterAsync(chapterId);
var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);
using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);

return book.Title;
}
Expand All @@ -47,6 +47,7 @@ public async Task<ActionResult> GetBookPageResources(int chapterId, [FromQuery]

var bookFile = book.Content.AllFiles[key];
var content = await bookFile.ReadContentAsBytesAsync();

Response.AddCacheHeader(content);
var contentType = BookService.GetContentType(bookFile.ContentType);
return File(content, contentType, $"{chapterId}-{file}");
Expand All @@ -58,7 +59,7 @@ public async Task<ActionResult<ICollection<BookChapterItem>>> GetBookChapters(in
// This will return a list of mappings from ID -> pagenum. ID will be the xhtml key and pagenum will be the reading order
// this is used to rewrite anchors in the book text so that we always load properly in FE
var chapter = await _unitOfWork.VolumeRepository.GetChapterAsync(chapterId);
var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);
using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);
var mappings = await _bookService.CreateKeyToPageMappingAsync(book);

var navItems = await book.GetNavigationAsync();
Expand Down Expand Up @@ -170,11 +171,11 @@ public async Task<ActionResult<string>> GetBookPage(int chapterId, [FromQuery] i
{
var chapter = await _unitOfWork.VolumeRepository.GetChapterAsync(chapterId);

var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);
using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath);
var mappings = await _bookService.CreateKeyToPageMappingAsync(book);

var counter = 0;
var doc = new HtmlDocument();
var doc = new HtmlDocument {OptionFixNestedTags = true};
var baseUrl = Request.Scheme + "://" + Request.Host + Request.PathBase + "/api/";
var apiBase = baseUrl + "book/" + chapterId + "/" + BookApiUrl;
var bookPages = await book.GetReadingOrderAsync();
Expand All @@ -186,14 +187,31 @@ public async Task<ActionResult<string>> GetBookPage(int chapterId, [FromQuery] i
if (contentFileRef.ContentType != EpubContentType.XHTML_1_1) return Ok(content);

doc.LoadHtml(content);
var body = doc.DocumentNode.SelectSingleNode("/html/body");

var body = doc.DocumentNode.SelectSingleNode("//body");

if (body == null)
{
if (doc.ParseErrors.Any())
{
_logger.LogError("{FilePath} has an invalid html file (Page {PageName})", book.FilePath, contentFileRef.FileName);
foreach (var error in doc.ParseErrors)
{
_logger.LogError("Line {LineNumber}, Reason: {Reason}", error.Line, error.Reason);
}

return BadRequest("The file is malformed! Cannot read.");
}
_logger.LogError("{FilePath} has no body tag! Generating one for support. Book may be skewed", book.FilePath);
doc.DocumentNode.SelectSingleNode("/html").AppendChild(HtmlNode.CreateNode("<body></body>"));
body = doc.DocumentNode.SelectSingleNode("/html/body");
}

var inlineStyles = doc.DocumentNode.SelectNodes("//style");
if (inlineStyles != null)
{
foreach (var inlineStyle in inlineStyles)
{
var styleContent = await _bookService.ScopeStyles(inlineStyle.InnerHtml, apiBase);
var styleContent = await _bookService.ScopeStyles(inlineStyle.InnerHtml, apiBase, "", book);
body.PrependChild(HtmlNode.CreateNode($"<style>{styleContent}</style>"));
}
}
Expand All @@ -217,7 +235,8 @@ public async Task<ActionResult<string>> GetBookPage(int chapterId, [FromQuery] i

key = correctedKey;
}
var styleContent = await _bookService.ScopeStyles(await book.Content.Css[key].ReadContentAsync(), apiBase);

var styleContent = await _bookService.ScopeStyles(await book.Content.Css[key].ReadContentAsync(), apiBase, book.Content.Css[key].FileName, book);
body.PrependChild(HtmlNode.CreateNode($"<style>{styleContent}</style>"));
}
}
Expand Down Expand Up @@ -280,10 +299,19 @@ public async Task<ActionResult<string>> GetBookPage(int chapterId, [FromQuery] i
}
}

// Check if any classes on the html node (some r2l books do this) and move them to body tag for scoping
var htmlNode = doc.DocumentNode.SelectSingleNode("//html");
if (htmlNode != null && htmlNode.Attributes.Contains("class"))
{
var bodyClasses = body.Attributes.Contains("class") ? body.Attributes["class"].Value : string.Empty;
var classes = htmlNode.Attributes["class"].Value + " " + bodyClasses;
body.Attributes.Add("class", $"{classes}");
// I actually need the body tag itself for the classes, so i will create a div and put the body stuff there.
return Ok($"<div class=\"{body.Attributes["class"].Value}\">{body.InnerHtml}</div>");
}



return Ok(body.InnerHtml);
return Ok(body.InnerHtml);
}

counter++;
Expand Down
Loading

0 comments on commit cdd01ee

Please sign in to comment.