From b7f4e7a593e2890195e35f1bd09f05a67e6676e0 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Fri, 1 Mar 2024 21:33:07 +0900 Subject: [PATCH 01/45] =?UTF-8?q?#1-1=20=E3=83=95=E3=82=A9=E3=83=AB?= =?UTF-8?q?=E3=83=80=E5=88=86=E3=81=91=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services}/IScrapingService.cs | 4 ++- Epub/KoeBook.Epub/{ => Models}/Audio.cs | 5 ++-- Epub/KoeBook.Epub/{ => Models}/Chapter.cs | 2 +- Epub/KoeBook.Epub/{ => Models}/CssClass.cs | 2 +- Epub/KoeBook.Epub/{ => Models}/Element.cs | 2 +- .../KoeBook.Epub/{ => Models}/EpubDocument.cs | 30 ++++++++----------- Epub/KoeBook.Epub/{ => Models}/Paragraph.cs | 2 +- Epub/KoeBook.Epub/{ => Models}/Picture.cs | 2 +- Epub/KoeBook.Epub/{ => Models}/Section.cs | 6 ++-- Epub/KoeBook.Epub/ScrapingHelper.cs | 1 + .../{ => Services}/ScrapingAozora.cs | 5 ++-- .../{ => Services}/ScrapingNarou.cs | 7 +++-- .../Services/IEpubDocumentStoreService.cs | 2 +- KoeBook.Core/Models/ScriptLine.cs | 2 +- .../AnalyzerService _CoverFileBytes.cs | 2 +- KoeBook.Core/Services/AnalyzerService.cs | 4 +-- .../Services/EpubDocumentStoreService.cs | 2 +- KoeBook.Core/Services/EpubGenerateService.cs | 1 + KoeBook/App.xaml.cs | 3 +- .../Services/CoreMocks/AnalyzerServiceMock.cs | 1 + 20 files changed, 44 insertions(+), 41 deletions(-) rename Epub/KoeBook.Epub/{Service => Contracts/Services}/IScrapingService.cs (70%) rename Epub/KoeBook.Epub/{ => Models}/Audio.cs (89%) rename Epub/KoeBook.Epub/{ => Models}/Chapter.cs (75%) rename Epub/KoeBook.Epub/{ => Models}/CssClass.cs (60%) rename Epub/KoeBook.Epub/{ => Models}/Element.cs (69%) rename Epub/KoeBook.Epub/{ => Models}/EpubDocument.cs (93%) rename Epub/KoeBook.Epub/{ => Models}/Paragraph.cs (78%) rename Epub/KoeBook.Epub/{ => Models}/Picture.cs (75%) rename Epub/KoeBook.Epub/{ => Models}/Section.cs (95%) rename Epub/KoeBook.Epub/{ => Services}/ScrapingAozora.cs (99%) rename Epub/KoeBook.Epub/{ => Services}/ScrapingNarou.cs (98%) diff --git a/Epub/KoeBook.Epub/Service/IScrapingService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs similarity index 70% rename from Epub/KoeBook.Epub/Service/IScrapingService.cs rename to Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs index c78791d..700be5a 100644 --- a/Epub/KoeBook.Epub/Service/IScrapingService.cs +++ b/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs @@ -1,4 +1,6 @@ -namespace KoeBook.Epub.Service; +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Contracts.Services; public interface IScrapingService { diff --git a/Epub/KoeBook.Epub/Audio.cs b/Epub/KoeBook.Epub/Models/Audio.cs similarity index 89% rename from Epub/KoeBook.Epub/Audio.cs rename to Epub/KoeBook.Epub/Models/Audio.cs index 474cdff..1afda6d 100644 --- a/Epub/KoeBook.Epub/Audio.cs +++ b/Epub/KoeBook.Epub/Models/Audio.cs @@ -1,7 +1,6 @@ -using System; -using NAudio.Wave; +using NAudio.Wave; -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public sealed class Audio { diff --git a/Epub/KoeBook.Epub/Chapter.cs b/Epub/KoeBook.Epub/Models/Chapter.cs similarity index 75% rename from Epub/KoeBook.Epub/Chapter.cs rename to Epub/KoeBook.Epub/Models/Chapter.cs index b43e1b7..8c2e72b 100644 --- a/Epub/KoeBook.Epub/Chapter.cs +++ b/Epub/KoeBook.Epub/Models/Chapter.cs @@ -1,4 +1,4 @@ -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public class Chapter { diff --git a/Epub/KoeBook.Epub/CssClass.cs b/Epub/KoeBook.Epub/Models/CssClass.cs similarity index 60% rename from Epub/KoeBook.Epub/CssClass.cs rename to Epub/KoeBook.Epub/Models/CssClass.cs index ed4d69e..564fbe3 100644 --- a/Epub/KoeBook.Epub/CssClass.cs +++ b/Epub/KoeBook.Epub/Models/CssClass.cs @@ -1,3 +1,3 @@ -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public record CssClass(string Name, string Text); diff --git a/Epub/KoeBook.Epub/Element.cs b/Epub/KoeBook.Epub/Models/Element.cs similarity index 69% rename from Epub/KoeBook.Epub/Element.cs rename to Epub/KoeBook.Epub/Models/Element.cs index 1efdf41..e3da24e 100644 --- a/Epub/KoeBook.Epub/Element.cs +++ b/Epub/KoeBook.Epub/Models/Element.cs @@ -1,4 +1,4 @@ -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public abstract class Element { diff --git a/Epub/KoeBook.Epub/EpubDocument.cs b/Epub/KoeBook.Epub/Models/EpubDocument.cs similarity index 93% rename from Epub/KoeBook.Epub/EpubDocument.cs rename to Epub/KoeBook.Epub/Models/EpubDocument.cs index 00525f4..0f723ed 100644 --- a/Epub/KoeBook.Epub/EpubDocument.cs +++ b/Epub/KoeBook.Epub/Models/EpubDocument.cs @@ -1,12 +1,8 @@ -using System.ComponentModel; -using System.Drawing; -using System.Globalization; +using System.Globalization; using System.IO.Compression; using System.Text; -using System.Xml; -using NAudio.Mixer; -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public class EpubDocument(string title, string author, string coverFilePath, Guid id) { @@ -57,7 +53,7 @@ public string CreateNavXhtml() """); if (Chapters.Count == 1 && Chapters[0].Title == null) { - for (int i = 0; i < Chapters[0].Sections.Count; i++) + for (var i = 0; i < Chapters[0].Sections.Count; i++) { builder.AppendLine($"""
  • @@ -68,14 +64,14 @@ public string CreateNavXhtml() } else { - for (int i = 0; i < Chapters.Count; i++) + for (var i = 0; i < Chapters.Count; i++) { builder.AppendLine($"""
  • {Chapters[i].Title}
      """); - for (int j = 0; j < Chapters[i].Sections.Count; j++) + for (var j = 0; j < Chapters[i].Sections.Count; j++) { builder.AppendLine($"""
    1. @@ -126,9 +122,9 @@ public string CreateOpf() """); var totalTime = TimeSpan.Zero; - for (int i = 0; i < Chapters.Count; i++) + for (var i = 0; i < Chapters.Count; i++) { - for (int j = 0; j < Chapters[i].Sections.Count; j++) + for (var j = 0; j < Chapters[i].Sections.Count; j++) { var time = Chapters[i].Sections[j].GetTotalTime(); totalTime += time; @@ -146,9 +142,9 @@ public string CreateOpf() """); - for (int i = 0; i < Chapters.Count; i++) + for (var i = 0; i < Chapters.Count; i++) { - for (int j = 0; j < Chapters[i].Sections.Count; j++) + for (var j = 0; j < Chapters[i].Sections.Count; j++) { builder.AppendLine($""" @@ -174,9 +170,9 @@ public string CreateOpf() """); - for (int i = 0; i < Chapters.Count; i++) + for (var i = 0; i < Chapters.Count; i++) { - for (int j = 0; j < Chapters[i].Sections.Count; j++) + for (var j = 0; j < Chapters[i].Sections.Count; j++) { builder.AppendLine($""" @@ -239,9 +235,9 @@ public async Task TryCreateEpubAsync(string tmpDirectory, string name, Can await opfStream.FlushAsync(ct).ConfigureAwait(false); } - for (int i = 0; i < Chapters.Count; i++) + for (var i = 0; i < Chapters.Count; i++) { - for (int j = 0; j < Chapters[i].Sections.Count; j++) + for (var j = 0; j < Chapters[i].Sections.Count; j++) { var sectionXhtmlEntry = archive.CreateEntry($"OEBPS/{Chapters[i].Sections[j].Id}.xhtml"); using (var sectionXhtmlStream = new StreamWriter(sectionXhtmlEntry.Open())) diff --git a/Epub/KoeBook.Epub/Paragraph.cs b/Epub/KoeBook.Epub/Models/Paragraph.cs similarity index 78% rename from Epub/KoeBook.Epub/Paragraph.cs rename to Epub/KoeBook.Epub/Models/Paragraph.cs index ccbe614..ea1956c 100644 --- a/Epub/KoeBook.Epub/Paragraph.cs +++ b/Epub/KoeBook.Epub/Models/Paragraph.cs @@ -1,4 +1,4 @@ -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public sealed class Paragraph : Element { diff --git a/Epub/KoeBook.Epub/Picture.cs b/Epub/KoeBook.Epub/Models/Picture.cs similarity index 75% rename from Epub/KoeBook.Epub/Picture.cs rename to Epub/KoeBook.Epub/Models/Picture.cs index 80628d7..e85fa3d 100644 --- a/Epub/KoeBook.Epub/Picture.cs +++ b/Epub/KoeBook.Epub/Models/Picture.cs @@ -1,4 +1,4 @@ -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public class Picture(string path) : Element { diff --git a/Epub/KoeBook.Epub/Section.cs b/Epub/KoeBook.Epub/Models/Section.cs similarity index 95% rename from Epub/KoeBook.Epub/Section.cs rename to Epub/KoeBook.Epub/Models/Section.cs index 96d62e1..81da7ec 100644 --- a/Epub/KoeBook.Epub/Section.cs +++ b/Epub/KoeBook.Epub/Models/Section.cs @@ -1,6 +1,6 @@ using System.Text; -namespace KoeBook.Epub; +namespace KoeBook.Epub.Models; public sealed class Section(string title) { @@ -20,7 +20,7 @@ public string CreateSectionXhtml() """); - for (int i = 0; i < Elements.Count; i++) + for (var i = 0; i < Elements.Count; i++) { if (Elements[i] is Paragraph para) { @@ -55,7 +55,7 @@ public string CreateSectionSmil() """); - for (int i = 0; i < Elements.Count; i++) + for (var i = 0; i < Elements.Count; i++) { if (Elements[i] is Paragraph para && para.Audio != null) { diff --git a/Epub/KoeBook.Epub/ScrapingHelper.cs b/Epub/KoeBook.Epub/ScrapingHelper.cs index 3bb8c59..a34c780 100644 --- a/Epub/KoeBook.Epub/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/ScrapingHelper.cs @@ -1,5 +1,6 @@ using System.Net; using System.Reflection.Metadata; +using KoeBook.Epub.Models; namespace KoeBook.Epub; diff --git a/Epub/KoeBook.Epub/ScrapingAozora.cs b/Epub/KoeBook.Epub/Services/ScrapingAozora.cs similarity index 99% rename from Epub/KoeBook.Epub/ScrapingAozora.cs rename to Epub/KoeBook.Epub/Services/ScrapingAozora.cs index ce6406f..22db5f1 100644 --- a/Epub/KoeBook.Epub/ScrapingAozora.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozora.cs @@ -2,12 +2,13 @@ using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Io; -using KoeBook.Epub.Service; +using KoeBook.Epub.Contracts.Services; +using KoeBook.Epub.Models; using System.IO; using static KoeBook.Epub.ScrapingHelper; -namespace KoeBook.Epub +namespace KoeBook.Epub.Services { public partial class ScrapingAozora : IScrapingService { diff --git a/Epub/KoeBook.Epub/ScrapingNarou.cs b/Epub/KoeBook.Epub/Services/ScrapingNarou.cs similarity index 98% rename from Epub/KoeBook.Epub/ScrapingNarou.cs rename to Epub/KoeBook.Epub/Services/ScrapingNarou.cs index 27ae164..8dc7663 100644 --- a/Epub/KoeBook.Epub/ScrapingNarou.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNarou.cs @@ -2,12 +2,13 @@ using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Io; -using KoeBook.Epub.Service; +using KoeBook.Epub.Contracts.Services; +using KoeBook.Epub.Models; using System.IO; using System.Net.Http.Json; using static KoeBook.Epub.ScrapingHelper; -namespace KoeBook.Epub +namespace KoeBook.Epub.Services { public partial class ScrapingNarouService : IScrapingService { @@ -87,7 +88,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, for (int i = 1; i <= allNum; i++) { Console.WriteLine(i); - await Task.Delay(500); + await Task.Delay(500, ct); var pageUrl = System.IO.Path.Combine(url, i.ToString()); var load = await ReadPageAsync(pageUrl, isRensai, imageDirectory, ct).ConfigureAwait(false); SectionWithChapterTitleList.Add(load); diff --git a/KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs b/KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs index b1822a3..8ba00b9 100644 --- a/KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs +++ b/KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs @@ -1,4 +1,4 @@ -using KoeBook.Epub; +using KoeBook.Epub.Models; namespace KoeBook.Core.Contracts.Services; diff --git a/KoeBook.Core/Models/ScriptLine.cs b/KoeBook.Core/Models/ScriptLine.cs index 072d14b..7f7cbec 100644 --- a/KoeBook.Core/Models/ScriptLine.cs +++ b/KoeBook.Core/Models/ScriptLine.cs @@ -1,4 +1,4 @@ -using KoeBook.Epub; +using KoeBook.Epub.Models; namespace KoeBook.Core.Models; diff --git a/KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs b/KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs index 4edc65d..97c30e8 100644 --- a/KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs +++ b/KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs @@ -3,7 +3,7 @@ using KoeBook.Core.Contracts.Services; using KoeBook.Core.Models; using KoeBook.Epub; -using KoeBook.Epub.Service; +using KoeBook.Epub.Services; namespace KoeBook.Core.Services; diff --git a/KoeBook.Core/Services/AnalyzerService.cs b/KoeBook.Core/Services/AnalyzerService.cs index 3491aff..91b098b 100644 --- a/KoeBook.Core/Services/AnalyzerService.cs +++ b/KoeBook.Core/Services/AnalyzerService.cs @@ -2,8 +2,8 @@ using System.Text.RegularExpressions; using KoeBook.Core.Contracts.Services; using KoeBook.Core.Models; -using KoeBook.Epub; -using KoeBook.Epub.Service; +using KoeBook.Epub.Contracts.Services; +using KoeBook.Epub.Models; namespace KoeBook.Core.Services; diff --git a/KoeBook.Core/Services/EpubDocumentStoreService.cs b/KoeBook.Core/Services/EpubDocumentStoreService.cs index 021798f..4c51415 100644 --- a/KoeBook.Core/Services/EpubDocumentStoreService.cs +++ b/KoeBook.Core/Services/EpubDocumentStoreService.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; using KoeBook.Core.Contracts.Services; -using KoeBook.Epub; +using KoeBook.Epub.Models; namespace KoeBook.Core.Services; diff --git a/KoeBook.Core/Services/EpubGenerateService.cs b/KoeBook.Core/Services/EpubGenerateService.cs index 26026b1..6052d8c 100644 --- a/KoeBook.Core/Services/EpubGenerateService.cs +++ b/KoeBook.Core/Services/EpubGenerateService.cs @@ -1,6 +1,7 @@ using KoeBook.Core.Contracts.Services; using KoeBook.Core.Models; using KoeBook.Epub; +using KoeBook.Epub.Models; namespace KoeBook.Core.Services; diff --git a/KoeBook/App.xaml.cs b/KoeBook/App.xaml.cs index c828253..ecc14a2 100644 --- a/KoeBook/App.xaml.cs +++ b/KoeBook/App.xaml.cs @@ -7,7 +7,8 @@ using KoeBook.Core.Services; using KoeBook.Core.Services.Mocks; using KoeBook.Epub; -using KoeBook.Epub.Service; +using KoeBook.Epub.Contracts.Services; +using KoeBook.Epub.Services; using KoeBook.Models; using KoeBook.Notifications; using KoeBook.Services; diff --git a/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs b/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs index 51ed97a..6e6432f 100644 --- a/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs +++ b/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs @@ -2,6 +2,7 @@ using KoeBook.Core.Helpers; using KoeBook.Core.Models; using KoeBook.Epub; +using KoeBook.Epub.Models; using static KoeBook.Core.Helpers.IDisplayStateChangeEx; namespace KoeBook.Services.CoreMocks; From 4577f45e244ab3f9a35b776a65a613bef308ad0f Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Fri, 1 Mar 2024 21:46:12 +0900 Subject: [PATCH 02/45] =?UTF-8?q?#1-1=20=E3=82=A4=E3=83=B3=E3=83=87?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E5=89=8A=E6=B8=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/ScrapingAozora.cs | 133 +++++----- Epub/KoeBook.Epub/Services/ScrapingNarou.cs | 249 ++++++++----------- 2 files changed, 160 insertions(+), 222 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozora.cs b/Epub/KoeBook.Epub/Services/ScrapingAozora.cs index 22db5f1..a4d6f9e 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozora.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozora.cs @@ -4,7 +4,6 @@ using AngleSharp.Io; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -using System.IO; using static KoeBook.Epub.ScrapingHelper; @@ -25,18 +24,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); // title の取得 - var bookTitle = doc.QuerySelector(".title"); - if (bookTitle is null) - { - throw new EpubDocumentException($"Failed to get title properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); - } + var bookTitle = doc.QuerySelector(".title") + ?? throw new EpubDocumentException($"Failed to get title properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); // auther の取得 - var bookAuther = doc.QuerySelector(".author"); - if (bookAuther is null) - { - throw new EpubDocumentException($"Failed to get auther properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); - } + var bookAuther = doc.QuerySelector(".author") + ?? throw new EpubDocumentException($"Failed to get auther properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); // EpubDocument の生成 var document = new EpubDocument(TextReplace(bookTitle.InnerHtml), TextReplace(bookAuther.InnerHtml), coverFilePath, id) @@ -113,84 +106,76 @@ public async Task ScrapingAsync(string url, string coverFilePath, var midashi = element.QuerySelector(".midashi_anchor"); if (midashi != null) { - if (midashi.Id != null) + if (midashi.Id == null) + throw new EpubDocumentException("Unecpected structure of HTML File: div tag with class=\"midashi_anchor\", but id=\"midashi___\" exist"); + + if (!int.TryParse(midashi.Id.Replace("midashi", ""), out var midashiId)) + throw new EpubDocumentException($"Unexpected id of Anchor tag was found: id = {midashi.Id}"); + + if (contentsIds.Contains(midashiId)) { - if (int.TryParse(midashi.Id.Replace("midashi", ""), out var midashiId)) + var contentsId = contentsIds.IndexOf(midashiId); + switch (contentsIds[contentsId] - contentsIds[contentsId - 1]) { - if (contentsIds.Contains(midashiId)) - { - var contentsId = contentsIds.IndexOf(midashiId); - switch (contentsIds[contentsId] - contentsIds[contentsId - 1]) + case 100: + if (chapterNum >= 0 && sectionNum >= 0) { - case 100: - if (chapterNum >= 0 && sectionNum >= 0) - { - document.Chapters[chapterNum].Sections[sectionNum].Elements.RemoveAt(document.Chapters[chapterNum].Sections[sectionNum].Elements.Count - 1); - } - chapterNum++; - sectionNum = -1; - break; - case 10: - if (chapterNum == -1) - { - chapterNum++; - sectionNum = -1; - } - if (chapterNum >= 0 && sectionNum >= 0) - { - document.Chapters[chapterNum].Sections[sectionNum].Elements.RemoveAt(document.Chapters[chapterNum].Sections[sectionNum].Elements.Count - 1); - } - sectionNum++; - break; - default: - break; + document.Chapters[chapterNum].Sections[sectionNum].Elements.RemoveAt(document.Chapters[chapterNum].Sections[sectionNum].Elements.Count - 1); } - } - else //小見出し、行中小見出しの処理 - { + chapterNum++; + sectionNum = -1; + break; + case 10: if (chapterNum == -1) { - if (chapterExist) - { - document.Chapters.Insert(0, new Chapter()); - } chapterNum++; sectionNum = -1; } - if (sectionNum == -1) - { - if (sectionExist) - { - checkChapter(document); - document.Chapters[^1].Sections.Insert(0, new Section("___")); - } - sectionNum++; - } - checkParagraph(document, chapterNum, sectionNum); - if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) + if (chapterNum >= 0 && sectionNum >= 0) { - paragraph.Text += TextProcess(midashi); - document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); - - foreach (var splitText in SplitBrace(TextProcess(midashi))) - { - if (document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph1) - { - paragraph1.Text += splitText; - } - document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); - } + document.Chapters[chapterNum].Sections[sectionNum].Elements.RemoveAt(document.Chapters[chapterNum].Sections[sectionNum].Elements.Count - 1); } + sectionNum++; + break; + default: + break; + } + } + else //小見出し、行中小見出しの処理 + { + if (chapterNum == -1) + { + if (chapterExist) + { + document.Chapters.Insert(0, new Chapter()); } + chapterNum++; + sectionNum = -1; } - else + if (sectionNum == -1) { - throw new EpubDocumentException($"Unexpected id of Anchor tag was found: id = {midashi.Id}"); + if (sectionExist) + { + checkChapter(document); + document.Chapters[^1].Sections.Insert(0, new Section("___")); + } + sectionNum++; + } + checkParagraph(document, chapterNum, sectionNum); + if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) + { + paragraph.Text += TextProcess(midashi); + document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); + + foreach (var splitText in SplitBrace(TextProcess(midashi))) + { + if (document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph1) + { + paragraph1.Text += splitText; + } + document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); + } } - } - else - { - throw new EpubDocumentException("Unecpected structure of HTML File: div tag with class=\"midashi_anchor\", but id=\"midashi___\" exist"); } } else diff --git a/Epub/KoeBook.Epub/Services/ScrapingNarou.cs b/Epub/KoeBook.Epub/Services/ScrapingNarou.cs index 8dc7663..a003f50 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNarou.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNarou.cs @@ -1,11 +1,10 @@ -using AngleSharp; +using System.Net.Http.Json; +using AngleSharp; using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Io; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -using System.IO; -using System.Net.Http.Json; using static KoeBook.Epub.ScrapingHelper; namespace KoeBook.Epub.Services @@ -26,19 +25,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); // title の取得 - var bookTitle = doc.QuerySelector(".novel_title"); - if (bookTitle is null) - { - throw new EpubDocumentException($"Failed to get title properly.\nUrl may be not collect"); - } + var bookTitle = doc.QuerySelector(".novel_title") + ?? throw new EpubDocumentException($"Failed to get title properly.\nUrl may be not collect"); // auther の取得 - var bookAuther = doc.QuerySelector(".novel_writername a"); - if (bookAuther is null) - { - throw new EpubDocumentException($"Failed to get auther properly.\nUrl may be not collect"); - } - + var bookAuther = doc.QuerySelector(".novel_writername a") + ?? throw new EpubDocumentException($"Failed to get auther properly.\nUrl may be not collect"); bool isRensai = true; int allNum = 0; @@ -50,35 +42,27 @@ public async Task ScrapingAsync(string url, string coverFilePath, var client = _httpCliantFactory.CreateClient(); var result = await client.SendAsync(message, ct).ConfigureAwait(false); var test = await result.Content.ReadAsStringAsync(ct).ConfigureAwait(false); - if (result.IsSuccessStatusCode) + if (!result.IsSuccessStatusCode) + throw new EpubDocumentException("Url may be not Correct"); + + var content = await result.Content.ReadFromJsonAsync(ct).ConfigureAwait(false); + if (content != null) { - var content = await result.Content.ReadFromJsonAsync(ct).ConfigureAwait(false); - if (content != null) + if (content[1].noveltype == null) + throw new EpubDocumentException("faild to get data by Narou API"); + + if (content[1].noveltype == 2) { - if (content[1].noveltype != null) - { - if (content[1].noveltype == 2) - { - isRensai = false; - } - } - else - { - throw new EpubDocumentException("faild to get data by Narou API"); - } - if (content[1].general_all_no != null) - { - allNum = (int)content[1].general_all_no!; - } - if (allNum == 0) - { - throw new EpubDocumentException("faild to get data by Narou API"); - } + isRensai = false; } - } - else - { - throw new EpubDocumentException("Url may be not Correct"); + + if (content[1].general_all_no != null) + { + allNum = (int)content[1].general_all_no!; + } + + if (allNum == 0) + throw new EpubDocumentException("faild to get data by Narou API"); } var document = new EpubDocument(bookTitle.InnerHtml, bookAuther.InnerHtml, coverFilePath, id); @@ -89,40 +73,36 @@ public async Task ScrapingAsync(string url, string coverFilePath, { Console.WriteLine(i); await Task.Delay(500, ct); - var pageUrl = System.IO.Path.Combine(url, i.ToString()); + var pageUrl = Path.Combine(url, i.ToString()); var load = await ReadPageAsync(pageUrl, isRensai, imageDirectory, ct).ConfigureAwait(false); SectionWithChapterTitleList.Add(load); } string? chapterTitle = null; foreach (var sectionWithChapterTitle in SectionWithChapterTitleList) { - if (sectionWithChapterTitle != null) + if (sectionWithChapterTitle == null) + throw new EpubDocumentException("failed to get page"); + + if (sectionWithChapterTitle.title != null) { - if (sectionWithChapterTitle.title != null) + if (sectionWithChapterTitle.title != chapterTitle) { - if (sectionWithChapterTitle.title != chapterTitle) - { - chapterTitle = sectionWithChapterTitle.title; - document.Chapters.Add(new Chapter() { Title = chapterTitle }); - document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); - } - else - { - document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); - } + chapterTitle = sectionWithChapterTitle.title; + document.Chapters.Add(new Chapter() { Title = chapterTitle }); + document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); } else { - if (document.Chapters.Count == 0) - { - document.Chapters.Add(new Chapter()); - } document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); } } else { - throw new EpubDocumentException("failed to get page"); + if (document.Chapters.Count == 0) + { + document.Chapters.Add(new Chapter()); + } + document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); } } } @@ -170,121 +150,94 @@ private async Task ReadPageAsync(string url, bool isRen string sectionTitle = ""; if (sectionTitleElement == null) - { throw new EpubDocumentException("Can not find title of page"); - } - else - { - sectionTitle = sectionTitleElement.InnerHtml; - } + sectionTitle = sectionTitleElement.InnerHtml; var section = new Section(sectionTitleElement.InnerHtml); - var main_text = doc.QuerySelector("#novel_honbun"); - if (main_text != null) + var main_text = doc.QuerySelector("#novel_honbun") + ?? throw new EpubDocumentException("There is no honbun."); + + foreach (var item in main_text.Children) { - foreach (var item in main_text.Children) + if (item is not IHtmlParagraphElement) + throw new EpubDocumentException("Unexpected structure"); + + if (item.ChildElementCount == 0) { - if (item is IHtmlParagraphElement) + if (!string.IsNullOrWhiteSpace(item.InnerHtml)) { - if (item.ChildElementCount == 0) + foreach (var split in SplitBrace(item.InnerHtml)) { - if (!string.IsNullOrWhiteSpace(item.InnerHtml)) - { - foreach (var split in SplitBrace(item.InnerHtml)) - { - section.Elements.Add(new Paragraph() { Text = split }); - } - } + section.Elements.Add(new Paragraph() { Text = split }); } - else if (item.ChildElementCount == 1) + } + } + else if (item.ChildElementCount == 1) + { + if (item.Children[0] is IHtmlAnchorElement aElement) + { + if (aElement.ChildElementCount != 1) + throw new EpubDocumentException("Unexpected structure"); + + if (aElement.Children[0] is IHtmlImageElement img) { - if (item.Children[0] is IHtmlAnchorElement aElement) - { - if (aElement.ChildElementCount == 1) - { - if (aElement.Children[0] is IHtmlImageElement img) - { - if (img.Source != null) - { - // 画像のダウンロード - var loader = context.GetService(); - if (loader != null) - { - var downloading = loader.FetchAsync(new DocumentRequest(new Url(img.Source))); - ct.Register(() => downloading.Cancel()); - var response = await downloading.Task.ConfigureAwait(false); - using var ms = new MemoryStream(); - await response.Content.CopyToAsync(ms, ct).ConfigureAwait(false); - var filePass = System.IO.Path.Combine(imageDirectory, FileUrlToFileName().Replace(response.Address.Href, "$1")); - File.WriteAllBytes(filePass, ms.ToArray()); - section.Elements.Add(new Picture(filePass)); - } - } - else - { - throw new EpubDocumentException("Unexpected structure"); - } - } - } - else - { - throw new EpubDocumentException("Unexpected structure"); - } - } - else if (item.Children[0].TagName == "RUBY") - { - if (!string.IsNullOrWhiteSpace(item.InnerHtml)) - { - foreach (var split in SplitBrace(item.InnerHtml)) - { - section.Elements.Add(new Paragraph() { Text = split }); - } - } - } - else if (item.Children[0] is not IHtmlBreakRowElement) - { + if (img.Source == null) throw new EpubDocumentException("Unexpected structure"); + + // 画像のダウンロード + var loader = context.GetService(); + if (loader != null) + { + var downloading = loader.FetchAsync(new DocumentRequest(new Url(img.Source))); + ct.Register(() => downloading.Cancel()); + var response = await downloading.Task.ConfigureAwait(false); + using var ms = new MemoryStream(); + await response.Content.CopyToAsync(ms, ct).ConfigureAwait(false); + var filePass = Path.Combine(imageDirectory, FileUrlToFileName().Replace(response.Address.Href, "$1")); + File.WriteAllBytes(filePass, ms.ToArray()); + section.Elements.Add(new Picture(filePass)); } } - else + } + else if (item.Children[0].TagName == "RUBY") + { + if (!string.IsNullOrWhiteSpace(item.InnerHtml)) { - bool isAllRuby = true; - foreach (var tags in item.Children) + foreach (var split in SplitBrace(item.InnerHtml)) { - if (tags.TagName != "RUBY") - { - isAllRuby = false; - } - } - if (isAllRuby) - { - if (!string.IsNullOrWhiteSpace(item.InnerHtml)) - { - foreach (var split in SplitBrace(item.InnerHtml)) - { - section.Elements.Add(new Paragraph() { Text = split }); - } - } - } - else - { - throw new EpubDocumentException("Unexpected structure"); + section.Elements.Add(new Paragraph() { Text = split }); } } } - else + else if (item.Children[0] is not IHtmlBreakRowElement) + throw new EpubDocumentException("Unexpected structure"); + } + else + { + bool isAllRuby = true; + foreach (var tags in item.Children) { + if (tags.TagName != "RUBY") + { + isAllRuby = false; + } + } + + if (!isAllRuby) throw new EpubDocumentException("Unexpected structure"); + + if (!string.IsNullOrWhiteSpace(item.InnerHtml)) + { + foreach (var split in SplitBrace(item.InnerHtml)) + { + section.Elements.Add(new Paragraph() { Text = split }); + } } } } - else - { - throw new EpubDocumentException("There is no honbun."); - } return new SectionWithChapterTitle(chapterTitle, section); } From 7a3046786f59a3c4d584e217a650919f0304c76d Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Fri, 1 Mar 2024 23:00:07 +0900 Subject: [PATCH 03/45] =?UTF-8?q?#1-2=20ScraperSelector=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/IFileExtensionService.cs | 6 ++ .../Services/IScraperSelectorService.cs | 11 +++ .../Services/IScrapingAozoraService.cs | 8 ++ .../Services/IScrapingNaroService.cs | 9 ++ .../Contracts/Services/IScrapingService.cs | 8 -- Epub/KoeBook.Epub/ScrapingHelper.cs | 90 ------------------- .../Services/FileExtensionService.cs | 19 ++++ .../Services/ScraperSelectorService.cs | 19 ++++ ...pingAozora.cs => ScrapingAozoraService.cs} | 51 ++++++----- ...crapingNarou.cs => ScrapingNaroService.cs} | 13 +-- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 77 ++++++++++++++++ KoeBook.Core/Services/AnalyzerService.cs | 4 +- KoeBook/App.xaml.cs | 2 +- 13 files changed, 181 insertions(+), 136 deletions(-) create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IFileExtensionService.cs create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScraperSelectorService.cs create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs delete mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs delete mode 100644 Epub/KoeBook.Epub/ScrapingHelper.cs create mode 100644 Epub/KoeBook.Epub/Services/FileExtensionService.cs create mode 100644 Epub/KoeBook.Epub/Services/ScraperSelectorService.cs rename Epub/KoeBook.Epub/Services/{ScrapingAozora.cs => ScrapingAozoraService.cs} (94%) rename Epub/KoeBook.Epub/Services/{ScrapingNarou.cs => ScrapingNaroService.cs} (95%) create mode 100644 Epub/KoeBook.Epub/Utility/ScrapingHelper.cs diff --git a/Epub/KoeBook.Epub/Contracts/Services/IFileExtensionService.cs b/Epub/KoeBook.Epub/Contracts/Services/IFileExtensionService.cs new file mode 100644 index 0000000..a498d94 --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IFileExtensionService.cs @@ -0,0 +1,6 @@ +namespace KoeBook.Epub.Contracts.Services; + +public interface IFileExtensionService +{ + public string GetImagesMediaType(string fileName); +} diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScraperSelectorService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScraperSelectorService.cs new file mode 100644 index 0000000..4dca9a9 --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IScraperSelectorService.cs @@ -0,0 +1,11 @@ +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Contracts.Services; + +/// +/// スクレイピングを行い、EpubDocumentを作成します。 +/// +public interface IScraperSelectorService +{ + public ValueTask ScrapingAsync(string url, string coverFillePath, string tempDirectory, Guid id, CancellationToken ct); +} diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs new file mode 100644 index 0000000..86756e5 --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs @@ -0,0 +1,8 @@ +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Contracts.Services; + +public interface IScrapingAozoraService +{ + public ValueTask ScrapingAsync(string url, string coverFillePath, string imageDirectory, Guid id, CancellationToken ct); +} diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs new file mode 100644 index 0000000..f9259aa --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs @@ -0,0 +1,9 @@ +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Contracts.Services; + +public interface IScrapingNaroService +{ + public ValueTask ScrapingAsync(string url, string coverFillePath, string imageDirectory, Guid id, CancellationToken ct); +} + diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs deleted file mode 100644 index 700be5a..0000000 --- a/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using KoeBook.Epub.Models; - -namespace KoeBook.Epub.Contracts.Services; - -public interface IScrapingService -{ - public Task ScrapingAsync(string url, string coverFillePath, string imageDirectory, Guid id, CancellationToken ct); -} diff --git a/Epub/KoeBook.Epub/ScrapingHelper.cs b/Epub/KoeBook.Epub/ScrapingHelper.cs deleted file mode 100644 index a34c780..0000000 --- a/Epub/KoeBook.Epub/ScrapingHelper.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Net; -using System.Reflection.Metadata; -using KoeBook.Epub.Models; - -namespace KoeBook.Epub; - -public static class ScrapingHelper -{ - internal static void checkChapter(EpubDocument document) - { - if (document.Chapters.Count == 0) - { - document.Chapters.Add(new Chapter() { Title = null }); - } - return; - } - - internal static void checkSection(EpubDocument document, int ChapterNum) - { - - checkChapter(document); - - if (document.Chapters[ChapterNum].Sections.Count == 0) - { - if (document.Chapters[ChapterNum].Title != null) - { - document.Chapters[ChapterNum].Sections.Add(new Section(document.Chapters[ChapterNum].Title!)); - } - else - { - document.Chapters[ChapterNum].Sections.Add(new Section(document.Title)); - } - - } - return; - } - - internal static void checkParagraph(EpubDocument document, int chapterNum, int sectionNum) - { - checkSection(document, chapterNum); - if (document.Chapters[chapterNum].Sections[sectionNum].Elements.Count == 0) - { - document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); - } - return; - } - - public static List SplitBrace(string text) - { - if (text.Length == 1 && text != "「" && text != "」") - { - return new List() { text }; - } - var result = new List(); - int bracket = 0; - var brackets = new List(); - foreach (char c in text) - { - if (c == '「') bracket++; - if (c == '」') bracket--; - brackets.Add(bracket); - } - var mn = Math.Min(0, brackets.Min()); - int startIdx = 0; - for (int i = 0; i < brackets.Count; i++) - { - brackets[i] -= mn; - if (text[i] == '「' && brackets[i] == 1 && i != 0) - { - result.Add(text[startIdx..i]); - startIdx = i; - } - if (text[i] == '」' && brackets[i] == 0 && i != 0) - { - result.Add(text[startIdx..(i + 1)]); - startIdx = i + 1; - } - } - if (startIdx != text.Length - 1) - { - result.Add(text[startIdx..]); - } - if (result[^1] == "") - { - result.RemoveAt(result.Count - 1); - } - - return result; - } -} diff --git a/Epub/KoeBook.Epub/Services/FileExtensionService.cs b/Epub/KoeBook.Epub/Services/FileExtensionService.cs new file mode 100644 index 0000000..c5037a6 --- /dev/null +++ b/Epub/KoeBook.Epub/Services/FileExtensionService.cs @@ -0,0 +1,19 @@ +using KoeBook.Epub.Contracts.Services; + +namespace KoeBook.Epub.Services; + +public class FileExtensionService : IFileExtensionService +{ + public string GetImagesMediaType(string fileName) + { + return Path.GetExtension(fileName) switch + { + ".gif" => "image/gif", + ".jpg" or ".jpeg" => "image/jpeg", + ".png" => "image/png", + ".svg" => "image/svg+xml", + ".webp" => "image/webp", + _ => string.Empty, + }; + } +} diff --git a/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs b/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs new file mode 100644 index 0000000..4cfb2ee --- /dev/null +++ b/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs @@ -0,0 +1,19 @@ +using KoeBook.Epub.Contracts.Services; +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Services; + +public class ScraperSelectorService(IScrapingAozoraService scrapingAozoraService, IScrapingNaroService scrapingNaroService) : IScraperSelectorService +{ + public ValueTask ScrapingAsync(string url, string coverFillePath, string tempDirectory, Guid id, CancellationToken ct) + { + var uri = new Uri(url); + + return uri.Host switch + { + "www.aozora.gr.jp" => scrapingAozoraService.ScrapingAsync(url, coverFillePath, tempDirectory, id, ct), + "ncode.syosetu.com" => scrapingNaroService.ScrapingAsync(url, coverFillePath, tempDirectory, id, ct), + _ => throw new ArgumentException("有効なドメインではありません。"), + }; + } +} diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozora.cs b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs similarity index 94% rename from Epub/KoeBook.Epub/Services/ScrapingAozora.cs rename to Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs index a4d6f9e..16f9446 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozora.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs @@ -4,21 +4,20 @@ using AngleSharp.Io; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -using static KoeBook.Epub.ScrapingHelper; +using static KoeBook.Epub.Utility.ScrapingHelper; namespace KoeBook.Epub.Services { - public partial class ScrapingAozora : IScrapingService + public partial class ScrapingAozoraService : IScrapingAozoraService { - private int chapterNum; - private int sectionNum; - private bool chapterExist = false; - private bool sectionExist = false; - - - public async Task ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) + public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { + var chapterNum = 0; + var sectionNum = 0; + var chapterExist = false; + var sectionExist = false; + var config = Configuration.Default.WithDefaultLoader(); using var context = BrowsingContext.New(config); var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); @@ -61,7 +60,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, } if ((MidashiId - previousMidashiId) == 10) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Add(new Section(TextProcess(midashi))); sectionExist = true; } @@ -97,7 +96,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (previous == true) { - checkSection(document, chapterNum); + EnsureSection(document, chapterNum); document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); } } @@ -156,12 +155,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (sectionExist) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { paragraph.Text += TextProcess(midashi); @@ -183,7 +182,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, if (element.ClassName == "caption") { // https://www.aozora.gr.jp/annotation/graphics.html#:~:text=%3Cdiv%20class%3D%22caption%22%3E を処理するための部分 - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { var split = SplitBrace(TextProcess(element)); @@ -216,12 +215,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (sectionExist) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { foreach (var splitText in SplitBrace(TextProcess(element))) @@ -253,7 +252,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (sectionExist) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; @@ -274,7 +273,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, await response.Content.CopyToAsync(ms, ct).ConfigureAwait(false); var filePass = System.IO.Path.Combine(imageDirectory, FileUrlToFileName().Replace(img.Source, "$1")); File.WriteAllBytes(filePass, ms.ToArray()); - checkSection(document, chapterNum); + EnsureSection(document, chapterNum); if (document.Chapters[chapterNum].Sections[sectionNum].Elements.Count > 1) { document.Chapters[chapterNum].Sections[sectionNum].Elements.Insert(document.Chapters[chapterNum].Sections[sectionNum].Elements.Count - 1, new Picture(filePass)); @@ -283,7 +282,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, } if (img.AlternativeText != null) { - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { paragraph.Text += TextReplace(img.AlternativeText); @@ -332,7 +331,7 @@ public async Task ScrapingAsync(string url, string coverFilePath, case "[#ページの左右中央]": break; default: - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { foreach (var splitText in SplitBrace(TextProcess(element))) @@ -362,12 +361,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (sectionExist) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { var split = SplitBrace(TextProcess(element)); @@ -402,12 +401,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (sectionExist) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { paragraph.Text += TextProcess(element); @@ -450,12 +449,12 @@ public async Task ScrapingAsync(string url, string coverFilePath, { if (sectionExist) { - checkChapter(document); + EnsureChapter(document); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - checkParagraph(document, chapterNum, sectionNum); + EnsureParagraph(document, chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { var split = SplitBrace(TextReplace(nextNode.Text())); diff --git a/Epub/KoeBook.Epub/Services/ScrapingNarou.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs similarity index 95% rename from Epub/KoeBook.Epub/Services/ScrapingNarou.cs rename to Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index a003f50..7d6ce7a 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNarou.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -5,20 +5,15 @@ using AngleSharp.Io; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -using static KoeBook.Epub.ScrapingHelper; +using static KoeBook.Epub.Utility.ScrapingHelper; namespace KoeBook.Epub.Services { - public partial class ScrapingNarouService : IScrapingService + public partial class ScrapingNaroService(IHttpClientFactory httpClientFactory) : IScrapingNaroService { - public ScrapingNarouService(IHttpClientFactory httpClientFactory) - { - _httpCliantFactory = httpClientFactory; - } - - private readonly IHttpClientFactory _httpCliantFactory; + private readonly IHttpClientFactory _httpCliantFactory = httpClientFactory; - public async Task ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) + public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); using var context = BrowsingContext.New(config); diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs new file mode 100644 index 0000000..3af91cd --- /dev/null +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -0,0 +1,77 @@ +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Utility; + +public static class ScrapingHelper +{ + internal static void EnsureChapter(EpubDocument document) + { + if (document.Chapters.Count == 0) + document.Chapters.Add(new Chapter() { Title = null }); + } + + internal static void EnsureSection(EpubDocument document, int chapterIndex) + { + EnsureChapter(document); + + if (document.Chapters[chapterIndex].Sections.Count == 0) + { + if (document.Chapters[chapterIndex].Title != null) + document.Chapters[chapterIndex].Sections.Add(new Section(document.Chapters[chapterIndex].Title!)); + else + document.Chapters[chapterIndex].Sections.Add(new Section(document.Title)); + } + } + + internal static void EnsureParagraph(EpubDocument document, int chapterIndex, int sectionIndex) + { + EnsureSection(document, chapterIndex); + + if (document.Chapters[chapterIndex].Sections[sectionIndex].Elements.Count == 0) + document.Chapters[chapterIndex].Sections[sectionIndex].Elements.Add(new Paragraph()); + } + + public static List SplitBrace(string text) + { + if (text.Length == 1 && text != "「" && text != "」") + return [text]; + + var bracket = 0; + var brackets = new int[text.Length]; + for (var i = 0; i < text.Length; i++) + { + var c = text[i]; + if (c == '「') bracket++; + else if (c == '」') bracket--; + brackets[i] = bracket; + } + + var result = new List(); + var mn = Math.Min(0, brackets.Min()); + var startIdx = 0; + for (var i = 0; i < brackets.Length; i++) + { + brackets[i] -= mn; + if (text[i] == '「' && brackets[i] == 1 && i != 0) + { + result.Add(text[startIdx..i]); + startIdx = i; + } + if (text[i] == '」' && brackets[i] == 0 && i != 0) + { + result.Add(text[startIdx..(i + 1)]); + startIdx = i + 1; + } + } + if (startIdx != text.Length - 1) + { + result.Add(text[startIdx..]); + } + if (result[^1] == "") + { + result.RemoveAt(result.Count - 1); + } + + return result; + } +} diff --git a/KoeBook.Core/Services/AnalyzerService.cs b/KoeBook.Core/Services/AnalyzerService.cs index 91b098b..59b72a6 100644 --- a/KoeBook.Core/Services/AnalyzerService.cs +++ b/KoeBook.Core/Services/AnalyzerService.cs @@ -7,9 +7,9 @@ namespace KoeBook.Core.Services; -public partial class AnalyzerService(IScrapingService scrapingService, IEpubDocumentStoreService epubDocumentStoreService, ILlmAnalyzerService llmAnalyzerService) : IAnalyzerService +public partial class AnalyzerService(IScraperSelectorService scrapingService, IEpubDocumentStoreService epubDocumentStoreService, ILlmAnalyzerService llmAnalyzerService) : IAnalyzerService { - private readonly IScrapingService _scrapingService = scrapingService; + private readonly IScraperSelectorService _scrapingService = scrapingService; private readonly IEpubDocumentStoreService _epubDocumentStoreService = epubDocumentStoreService; private readonly ILlmAnalyzerService _llmAnalyzerService = llmAnalyzerService; private Dictionary _rubyReplacements = new Dictionary(); diff --git a/KoeBook/App.xaml.cs b/KoeBook/App.xaml.cs index ecc14a2..84011fe 100644 --- a/KoeBook/App.xaml.cs +++ b/KoeBook/App.xaml.cs @@ -99,7 +99,7 @@ public App() services.AddSingleton(); services.AddSingleton(); // TODO: 切り替えサービスを作成 - services.AddSingleton(); + services.AddSingleton(); // Views and ViewModels services.AddTransient(); From 7a0fb10ce2ef022d9f731b0c09d9a325f9f0496a Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 2 Mar 2024 00:00:17 +0900 Subject: [PATCH 04/45] =?UTF-8?q?#1-2=20Ensure=E2=97=AF=E2=97=AF=E3=82=92?= =?UTF-8?q?=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Models/EpubDocument.cs | 27 +++++++++++++++ .../Services/ScrapingAozoraService.cs | 34 +++++++++---------- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 31 +---------------- 3 files changed, 45 insertions(+), 47 deletions(-) diff --git a/Epub/KoeBook.Epub/Models/EpubDocument.cs b/Epub/KoeBook.Epub/Models/EpubDocument.cs index 0f723ed..583d0ce 100644 --- a/Epub/KoeBook.Epub/Models/EpubDocument.cs +++ b/Epub/KoeBook.Epub/Models/EpubDocument.cs @@ -37,6 +37,33 @@ public class EpubDocument(string title, string author, string coverFilePath, Gui ]; public List Chapters { get; set; } = []; + internal void EnsureChapter() + { + if (Chapters.Count == 0) + Chapters.Add(new Chapter() { Title = null }); + } + + internal void EnsureSection(int chapterIndex) + { + EnsureChapter(); + + if (Chapters[chapterIndex].Sections.Count == 0) + { + if (Chapters[chapterIndex].Title != null) + Chapters[chapterIndex].Sections.Add(new Section(Chapters[chapterIndex].Title!)); + else + Chapters[chapterIndex].Sections.Add(new Section(Title)); + } + } + + internal void EnsureParagraph(int chapterIndex, int sectionIndex) + { + EnsureSection(chapterIndex); + + if (Chapters[chapterIndex].Sections[sectionIndex].Elements.Count == 0) + Chapters[chapterIndex].Sections[sectionIndex].Elements.Add(new Paragraph()); + } + public string CreateNavXhtml() { var builder = new StringBuilder($""" diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs index 16f9446..0915687 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs @@ -60,7 +60,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } if ((MidashiId - previousMidashiId) == 10) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Add(new Section(TextProcess(midashi))); sectionExist = true; } @@ -96,7 +96,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (previous == true) { - EnsureSection(document, chapterNum); + document.EnsureSection(chapterNum); document.Chapters[chapterNum].Sections[sectionNum].Elements.Add(new Paragraph()); } } @@ -155,12 +155,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (sectionExist) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { paragraph.Text += TextProcess(midashi); @@ -182,7 +182,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP if (element.ClassName == "caption") { // https://www.aozora.gr.jp/annotation/graphics.html#:~:text=%3Cdiv%20class%3D%22caption%22%3E を処理するための部分 - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { var split = SplitBrace(TextProcess(element)); @@ -215,12 +215,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (sectionExist) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { foreach (var splitText in SplitBrace(TextProcess(element))) @@ -252,7 +252,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (sectionExist) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; @@ -273,7 +273,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP await response.Content.CopyToAsync(ms, ct).ConfigureAwait(false); var filePass = System.IO.Path.Combine(imageDirectory, FileUrlToFileName().Replace(img.Source, "$1")); File.WriteAllBytes(filePass, ms.ToArray()); - EnsureSection(document, chapterNum); + document.EnsureSection(chapterNum); if (document.Chapters[chapterNum].Sections[sectionNum].Elements.Count > 1) { document.Chapters[chapterNum].Sections[sectionNum].Elements.Insert(document.Chapters[chapterNum].Sections[sectionNum].Elements.Count - 1, new Picture(filePass)); @@ -282,7 +282,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } if (img.AlternativeText != null) { - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { paragraph.Text += TextReplace(img.AlternativeText); @@ -331,7 +331,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP case "[#ページの左右中央]": break; default: - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { foreach (var splitText in SplitBrace(TextProcess(element))) @@ -361,12 +361,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (sectionExist) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { var split = SplitBrace(TextProcess(element)); @@ -401,12 +401,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (sectionExist) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { paragraph.Text += TextProcess(element); @@ -449,12 +449,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP { if (sectionExist) { - EnsureChapter(document); + document.EnsureChapter(); document.Chapters[^1].Sections.Insert(0, new Section("___")); } sectionNum++; } - EnsureParagraph(document, chapterNum, sectionNum); + document.EnsureParagraph(chapterNum, sectionNum); if ((document.Chapters[chapterNum].Sections[sectionNum].Elements[^1] is Paragraph paragraph)) { var split = SplitBrace(TextReplace(nextNode.Text())); diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs index 3af91cd..ba8ff41 100644 --- a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -1,36 +1,7 @@ -using KoeBook.Epub.Models; - -namespace KoeBook.Epub.Utility; +namespace KoeBook.Epub.Utility; public static class ScrapingHelper { - internal static void EnsureChapter(EpubDocument document) - { - if (document.Chapters.Count == 0) - document.Chapters.Add(new Chapter() { Title = null }); - } - - internal static void EnsureSection(EpubDocument document, int chapterIndex) - { - EnsureChapter(document); - - if (document.Chapters[chapterIndex].Sections.Count == 0) - { - if (document.Chapters[chapterIndex].Title != null) - document.Chapters[chapterIndex].Sections.Add(new Section(document.Chapters[chapterIndex].Title!)); - else - document.Chapters[chapterIndex].Sections.Add(new Section(document.Title)); - } - } - - internal static void EnsureParagraph(EpubDocument document, int chapterIndex, int sectionIndex) - { - EnsureSection(document, chapterIndex); - - if (document.Chapters[chapterIndex].Sections[sectionIndex].Elements.Count == 0) - document.Chapters[chapterIndex].Sections[sectionIndex].Elements.Add(new Paragraph()); - } - public static List SplitBrace(string text) { if (text.Length == 1 && text != "「" && text != "」") From 73200703870769657c1a750291954a3cb9728f95 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 2 Mar 2024 16:15:41 +0900 Subject: [PATCH 05/45] =?UTF-8?q?#1=20=E3=82=BB=E3=83=AC=E3=82=AF=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/IScrapingAozoraService.cs | 8 -------- .../Services/IScrapingNaroService.cs | 9 --------- .../Contracts/Services/IScrapingService.cs | 8 ++++++++ .../Services/ScraperSelectorService.cs | 20 +++++++++++-------- .../Services/ScrapingAozoraService.cs | 7 ++++++- .../Services/ScrapingNaroService.cs | 7 ++++++- KoeBook/App.xaml.cs | 6 ++++-- 7 files changed, 36 insertions(+), 29 deletions(-) delete mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs delete mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs deleted file mode 100644 index 86756e5..0000000 --- a/Epub/KoeBook.Epub/Contracts/Services/IScrapingAozoraService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using KoeBook.Epub.Models; - -namespace KoeBook.Epub.Contracts.Services; - -public interface IScrapingAozoraService -{ - public ValueTask ScrapingAsync(string url, string coverFillePath, string imageDirectory, Guid id, CancellationToken ct); -} diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs deleted file mode 100644 index f9259aa..0000000 --- a/Epub/KoeBook.Epub/Contracts/Services/IScrapingNaroService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using KoeBook.Epub.Models; - -namespace KoeBook.Epub.Contracts.Services; - -public interface IScrapingNaroService -{ - public ValueTask ScrapingAsync(string url, string coverFillePath, string imageDirectory, Guid id, CancellationToken ct); -} - diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs new file mode 100644 index 0000000..e0818f3 --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IScrapingService.cs @@ -0,0 +1,8 @@ +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Contracts.Services; + +public interface IScrapingService : IScraperSelectorService +{ + public bool IsMatchSite(Uri url); +} diff --git a/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs b/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs index 4cfb2ee..51a68f3 100644 --- a/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs +++ b/Epub/KoeBook.Epub/Services/ScraperSelectorService.cs @@ -1,19 +1,23 @@ -using KoeBook.Epub.Contracts.Services; +using System.Collections.Immutable; +using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; namespace KoeBook.Epub.Services; -public class ScraperSelectorService(IScrapingAozoraService scrapingAozoraService, IScrapingNaroService scrapingNaroService) : IScraperSelectorService +public class ScraperSelectorService(IEnumerable scrapingServices) : IScraperSelectorService { - public ValueTask ScrapingAsync(string url, string coverFillePath, string tempDirectory, Guid id, CancellationToken ct) + private readonly ImmutableArray _scrapingServices = scrapingServices.ToImmutableArray(); + + public async ValueTask ScrapingAsync(string url, string coverFillePath, string tempDirectory, Guid id, CancellationToken ct) { var uri = new Uri(url); - return uri.Host switch + foreach (var service in _scrapingServices) { - "www.aozora.gr.jp" => scrapingAozoraService.ScrapingAsync(url, coverFillePath, tempDirectory, id, ct), - "ncode.syosetu.com" => scrapingNaroService.ScrapingAsync(url, coverFillePath, tempDirectory, id, ct), - _ => throw new ArgumentException("有効なドメインではありません。"), - }; + if (service.IsMatchSite(uri)) + return await service.ScrapingAsync(url, coverFillePath, tempDirectory, id, ct); + } + + throw new ArgumentException("対応するURLではありません"); } } diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs index 0915687..203bcaa 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs @@ -9,8 +9,13 @@ namespace KoeBook.Epub.Services { - public partial class ScrapingAozoraService : IScrapingAozoraService + public partial class ScrapingAozoraService : IScrapingService { + public bool IsMatchSite(Uri uri) + { + return uri.Host == "www.aozora.gr.jp"; + } + public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { var chapterNum = 0; diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index 7d6ce7a..a1e00ec 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -9,10 +9,15 @@ namespace KoeBook.Epub.Services { - public partial class ScrapingNaroService(IHttpClientFactory httpClientFactory) : IScrapingNaroService + public partial class ScrapingNaroService(IHttpClientFactory httpClientFactory) : IScrapingService { private readonly IHttpClientFactory _httpCliantFactory = httpClientFactory; + public bool IsMatchSite(Uri uri) + { + return uri.Host == "ncode.syosetu.com"; + } + public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); diff --git a/KoeBook/App.xaml.cs b/KoeBook/App.xaml.cs index 84011fe..ddde655 100644 --- a/KoeBook/App.xaml.cs +++ b/KoeBook/App.xaml.cs @@ -98,8 +98,10 @@ public App() services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - // TODO: 切り替えサービスを作成 - services.AddSingleton(); + + services.AddSingleton() + .AddSingleton() + .AddSingleton(); // Views and ViewModels services.AddTransient(); From ad94645a5004ceae3c9a2396c1c551f681984b9e Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 16:35:09 +0900 Subject: [PATCH 06/45] =?UTF-8?q?SplitBrace=E3=81=AE=E5=85=88=E9=A0=AD(?= =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=81=8C1=E6=96=87=E5=AD=97=E3=81=AE?= =?UTF-8?q?=E6=99=82)=E3=81=AE=E6=9D=A1=E4=BB=B6=E5=BC=8F=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=80=82=20SplitBrace=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 2 +- KoeBook.Test/UnitTest1.cs | 27 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs index ba8ff41..9838850 100644 --- a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -4,7 +4,7 @@ public static class ScrapingHelper { public static List SplitBrace(string text) { - if (text.Length == 1 && text != "「" && text != "」") + if (text.Length == 1 && (text == "「" || text == "」")) return [text]; var bracket = 0; diff --git a/KoeBook.Test/UnitTest1.cs b/KoeBook.Test/UnitTest1.cs index 217ca16..8342f41 100644 --- a/KoeBook.Test/UnitTest1.cs +++ b/KoeBook.Test/UnitTest1.cs @@ -1,10 +1,31 @@ -namespace KoeBook.Test; +using KoeBook.Epub.Utility; + +namespace KoeBook.Test; public class UnitTest1 { - [Fact] - public void Test1() + public static IEnumerable Data() + { + yield return new object[] { "「", new List { "「" } }; + yield return new object[] { "」", new List { "」" } }; + yield return new object[] { "abc「abc」abc" , new List { "abc", "「abc」", "abc" } }; + yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; + yield return new object[] { "「abc」abc", new List { "「abc」", "abc", } }; + yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; + yield return new object[] { "「abc」", new List { "「abc」", } }; + yield return new object[] { "abc「abc", new List { "abc", "「abc" } }; + yield return new object[] { "abc「", new List { "abc", "「" } }; + yield return new object[] { "「abc", new List { "「abc" } }; + yield return new object[] { "abc」abc", new List { "abc」", "abc" } }; + yield return new object[] { "abc」", new List { "abc」"} }; + yield return new object[] { "」abc", new List { "」", "abc" } }; + } + + [Theory] + [MemberData(nameof(Data))] + public void SplitBraceTest(string text, List expected) { + Assert.Equal(expected, ScrapingHelper.SplitBrace(text)); } } From dba4de34461d86c4b20665dbd9f9cf45ebd73998 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 16:38:11 +0900 Subject: [PATCH 07/45] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E3=83=83=E3=83=88=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/UnitTest1.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KoeBook.Test/UnitTest1.cs b/KoeBook.Test/UnitTest1.cs index 8342f41..1a4ef89 100644 --- a/KoeBook.Test/UnitTest1.cs +++ b/KoeBook.Test/UnitTest1.cs @@ -8,7 +8,7 @@ public static IEnumerable Data() { yield return new object[] { "「", new List { "「" } }; yield return new object[] { "」", new List { "」" } }; - yield return new object[] { "abc「abc」abc" , new List { "abc", "「abc」", "abc" } }; + yield return new object[] { "abc「abc」abc", new List { "abc", "「abc」", "abc" } }; yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; yield return new object[] { "「abc」abc", new List { "「abc」", "abc", } }; yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; @@ -17,7 +17,7 @@ public static IEnumerable Data() yield return new object[] { "abc「", new List { "abc", "「" } }; yield return new object[] { "「abc", new List { "「abc" } }; yield return new object[] { "abc」abc", new List { "abc」", "abc" } }; - yield return new object[] { "abc」", new List { "abc」"} }; + yield return new object[] { "abc」", new List { "abc」" } }; yield return new object[] { "」abc", new List { "」", "abc" } }; } From ac4a234695b0b492ae709c027d7bd2f548fa682b Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 17:13:45 +0900 Subject: [PATCH 08/45] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/{UnitTest1.cs => Epub/ScrapingHelperTest.cs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename KoeBook.Test/{UnitTest1.cs => Epub/ScrapingHelperTest.cs} (95%) diff --git a/KoeBook.Test/UnitTest1.cs b/KoeBook.Test/Epub/ScrapingHelperTest.cs similarity index 95% rename from KoeBook.Test/UnitTest1.cs rename to KoeBook.Test/Epub/ScrapingHelperTest.cs index 1a4ef89..aa5e7e6 100644 --- a/KoeBook.Test/UnitTest1.cs +++ b/KoeBook.Test/Epub/ScrapingHelperTest.cs @@ -1,8 +1,8 @@ using KoeBook.Epub.Utility; -namespace KoeBook.Test; +namespace KoeBook.Test.Epub; -public class UnitTest1 +public class ScrapingHelperTest { public static IEnumerable Data() { From 58a8b1539ed8e25cade3550f15c5b31766943e7e Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 17:25:24 +0900 Subject: [PATCH 09/45] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/Epub/ScrapingHelperTest.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/KoeBook.Test/Epub/ScrapingHelperTest.cs b/KoeBook.Test/Epub/ScrapingHelperTest.cs index aa5e7e6..6c3a5ec 100644 --- a/KoeBook.Test/Epub/ScrapingHelperTest.cs +++ b/KoeBook.Test/Epub/ScrapingHelperTest.cs @@ -8,6 +8,7 @@ public static IEnumerable Data() { yield return new object[] { "「", new List { "「" } }; yield return new object[] { "」", new List { "」" } }; + yield return new object[] { "a", new List { "a" } }; yield return new object[] { "abc「abc」abc", new List { "abc", "「abc」", "abc" } }; yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; yield return new object[] { "「abc」abc", new List { "「abc」", "abc", } }; @@ -19,6 +20,10 @@ public static IEnumerable Data() yield return new object[] { "abc」abc", new List { "abc」", "abc" } }; yield return new object[] { "abc」", new List { "abc」" } }; yield return new object[] { "」abc", new List { "」", "abc" } }; + yield return new object[] { "abc「abc」abc「abc」abc", new List { "abc", "「abc」", "abc", "「abc」", "abc" } }; + yield return new object[] { "「abc」abc「abc」abc", new List { "「abc」", "abc", "「abc」", "abc" } }; + yield return new object[] { "abc「abc」「abc」abc", new List { "abc", "「abc」", "「abc」", "abc" } }; + yield return new object[] { "abc「abc」abc「abc」", new List { "abc", "「abc」", "abc", "「abc」" } }; } [Theory] From 9a717b9e24a4916ab2e5b09ee5abb3ba27231a79 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 17:30:39 +0900 Subject: [PATCH 10/45] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/Epub/ScrapingHelperTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/KoeBook.Test/Epub/ScrapingHelperTest.cs b/KoeBook.Test/Epub/ScrapingHelperTest.cs index 6c3a5ec..d13cfef 100644 --- a/KoeBook.Test/Epub/ScrapingHelperTest.cs +++ b/KoeBook.Test/Epub/ScrapingHelperTest.cs @@ -24,6 +24,10 @@ public static IEnumerable Data() yield return new object[] { "「abc」abc「abc」abc", new List { "「abc」", "abc", "「abc」", "abc" } }; yield return new object[] { "abc「abc」「abc」abc", new List { "abc", "「abc」", "「abc」", "abc" } }; yield return new object[] { "abc「abc」abc「abc」", new List { "abc", "「abc」", "abc", "「abc」" } }; + yield return new object[] { "abc「abc「abc」abc", new List { "abc", "「abc「abc」abc" } }; + yield return new object[] { "abc「abc」abc」abc", new List { "abc「abc」abc」", "abc" } }; + yield return new object[] { "abc「abc「abc", new List { "abc", "「abc「abc" } }; + yield return new object[] { "abc」abc」abc", new List { "abc」abc」", "abc" } }; } [Theory] From 680a90592d953c468a8966062e18173e2b28e05c Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 18:14:15 +0900 Subject: [PATCH 11/45] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/Epub/ScrapingHelperTest.cs | 51 +++++++++++++------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/KoeBook.Test/Epub/ScrapingHelperTest.cs b/KoeBook.Test/Epub/ScrapingHelperTest.cs index d13cfef..54e41b5 100644 --- a/KoeBook.Test/Epub/ScrapingHelperTest.cs +++ b/KoeBook.Test/Epub/ScrapingHelperTest.cs @@ -4,34 +4,37 @@ namespace KoeBook.Test.Epub; public class ScrapingHelperTest { - public static IEnumerable Data() + public static object[][] TestCases() { - yield return new object[] { "「", new List { "「" } }; - yield return new object[] { "」", new List { "」" } }; - yield return new object[] { "a", new List { "a" } }; - yield return new object[] { "abc「abc」abc", new List { "abc", "「abc」", "abc" } }; - yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; - yield return new object[] { "「abc」abc", new List { "「abc」", "abc", } }; - yield return new object[] { "abc「abc」", new List { "abc", "「abc」" } }; - yield return new object[] { "「abc」", new List { "「abc」", } }; - yield return new object[] { "abc「abc", new List { "abc", "「abc" } }; - yield return new object[] { "abc「", new List { "abc", "「" } }; - yield return new object[] { "「abc", new List { "「abc" } }; - yield return new object[] { "abc」abc", new List { "abc」", "abc" } }; - yield return new object[] { "abc」", new List { "abc」" } }; - yield return new object[] { "」abc", new List { "」", "abc" } }; - yield return new object[] { "abc「abc」abc「abc」abc", new List { "abc", "「abc」", "abc", "「abc」", "abc" } }; - yield return new object[] { "「abc」abc「abc」abc", new List { "「abc」", "abc", "「abc」", "abc" } }; - yield return new object[] { "abc「abc」「abc」abc", new List { "abc", "「abc」", "「abc」", "abc" } }; - yield return new object[] { "abc「abc」abc「abc」", new List { "abc", "「abc」", "abc", "「abc」" } }; - yield return new object[] { "abc「abc「abc」abc", new List { "abc", "「abc「abc」abc" } }; - yield return new object[] { "abc「abc」abc」abc", new List { "abc「abc」abc」", "abc" } }; - yield return new object[] { "abc「abc「abc", new List { "abc", "「abc「abc" } }; - yield return new object[] { "abc」abc」abc", new List { "abc」abc」", "abc" } }; + (string, List)[] cases = [ + ("「", ["「"]), + ("」", ["」"]), + ("a", ["a"]), + ("abc「abc」abc", ["abc", "「abc」", "abc"]), + ("abc「abc」", ["abc", "「abc」"]), + ("「abc」abc", ["「abc」", "abc",]), + ("abc「abc」", ["abc", "「abc」"]), + ("「abc」", ["「abc」",]), + ("abc「abc", ["abc", "「abc"]), + ("abc「", ["abc", "「"]), + ("「abc", ["「abc"]), + ("abc」abc", ["abc」", "abc"]), + ("abc」", ["abc」"]), + ("」abc", ["」", "abc"]), + ("abc「abc」abc「abc」abc", ["abc", "「abc」", "abc", "「abc」", "abc"]), + ("「abc」abc「abc」abc", ["「abc」", "abc", "「abc」", "abc"]), + ("abc「abc」「abc」abc", ["abc", "「abc」", "「abc」", "abc"]), + ("abc「abc」abc「abc」", ["abc", "「abc」", "abc", "「abc」"]), + ("abc「abc「abc」abc", ["abc", "「abc「abc」abc"]), + ("abc「abc」abc」abc", ["abc「abc」abc」", "abc"]), + ("abc「abc「abc", ["abc", "「abc「abc"]), + ("abc」abc」abc", ["abc」abc」", "abc"]) + ]; + return cases.Select(c => new object[] { c.Item1, c.Item2 }).ToArray(); } [Theory] - [MemberData(nameof(Data))] + [MemberData(nameof(TestCases))] public void SplitBraceTest(string text, List expected) { Assert.Equal(expected, ScrapingHelper.SplitBrace(text)); From c41e4af389db0a6f33f3f33baebafc47ffc47e55 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 18:19:55 +0900 Subject: [PATCH 12/45] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/Epub/ScrapingHelperTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/KoeBook.Test/Epub/ScrapingHelperTest.cs b/KoeBook.Test/Epub/ScrapingHelperTest.cs index 54e41b5..394315e 100644 --- a/KoeBook.Test/Epub/ScrapingHelperTest.cs +++ b/KoeBook.Test/Epub/ScrapingHelperTest.cs @@ -25,6 +25,7 @@ public static object[][] TestCases() ("「abc」abc「abc」abc", ["「abc」", "abc", "「abc」", "abc"]), ("abc「abc」「abc」abc", ["abc", "「abc」", "「abc」", "abc"]), ("abc「abc」abc「abc」", ["abc", "「abc」", "abc", "「abc」"]), + ("abc「abc「abc」abc」abc", ["abc", "「abc「abc」abc」", "abc"]), ("abc「abc「abc」abc", ["abc", "「abc「abc」abc"]), ("abc「abc」abc」abc", ["abc「abc」abc」", "abc"]), ("abc「abc「abc", ["abc", "「abc「abc"]), From 73f9424f95454f6616eca029495c5bfb131e52a3 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 18:27:19 +0900 Subject: [PATCH 13/45] =?UTF-8?q?ScrapingHelper=E3=81=AE=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E5=BC=8F=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs index 9838850..f988e9c 100644 --- a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -23,21 +23,18 @@ public static List SplitBrace(string text) for (var i = 0; i < brackets.Length; i++) { brackets[i] -= mn; - if (text[i] == '「' && brackets[i] == 1 && i != 0) + if (text[i] == '「' && brackets[i] == 1 && i != 0 && startIdx != i) { result.Add(text[startIdx..i]); startIdx = i; } - if (text[i] == '」' && brackets[i] == 0 && i != 0) + if (text[i] == '」' && brackets[i] == 0) { result.Add(text[startIdx..(i + 1)]); startIdx = i + 1; } } - if (startIdx != text.Length - 1) - { - result.Add(text[startIdx..]); - } + result.Add(text[startIdx..]); if (result[^1] == "") { result.RemoveAt(result.Count - 1); From 8da4ce76ffc77154f93d3071d939b022a6d5039a Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sun, 3 Mar 2024 18:47:16 +0900 Subject: [PATCH 14/45] =?UTF-8?q?TryCreateEpubAsync=E3=82=92=E3=82=B5?= =?UTF-8?q?=E3=83=BC=E3=83=93=E3=82=B9=E3=81=A8=E3=81=97=E3=81=A6=E5=88=87?= =?UTF-8?q?=E3=82=8A=E5=87=BA=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contracts/Services/IEpubGeneration.cs | 8 ++ Epub/KoeBook.Epub/Models/EpubDocument.cs | 110 ++---------------- Epub/KoeBook.Epub/Services/EpubGeneration.cs | 104 +++++++++++++++++ 3 files changed, 120 insertions(+), 102 deletions(-) create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs create mode 100644 Epub/KoeBook.Epub/Services/EpubGeneration.cs diff --git a/Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs b/Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs new file mode 100644 index 0000000..c06b3d4 --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs @@ -0,0 +1,8 @@ +using KoeBook.Epub.Models; + +namespace KoeBook.Epub.Contracts.Services; + +public interface IEpubGeneration +{ + ValueTask TryCreateEpubAsync(EpubDocument epubDocument, string tmpDirectory, CancellationToken ct); +} diff --git a/Epub/KoeBook.Epub/Models/EpubDocument.cs b/Epub/KoeBook.Epub/Models/EpubDocument.cs index 583d0ce..e46ac21 100644 --- a/Epub/KoeBook.Epub/Models/EpubDocument.cs +++ b/Epub/KoeBook.Epub/Models/EpubDocument.cs @@ -6,15 +6,6 @@ namespace KoeBook.Epub.Models; public class EpubDocument(string title, string author, string coverFilePath, Guid id) { - readonly string _containerXml = """ - - - - - - - """; - public string Title { get; set; } = title; public string Author { get; set; } = author; @@ -214,97 +205,12 @@ public string CreateOpf() return builder.ToString(); } - public async Task TryCreateEpubAsync(string tmpDirectory, string name, CancellationToken ct) - { - if (!File.Exists(CoverFilePath)) - { - throw new FileNotFoundException("指定されたカバーファイルが存在しません", CoverFilePath); - } - try - { - using var fs = File.Create(Path.Combine(tmpDirectory, $"{name}.epub")); - using var archive = new ZipArchive(fs, ZipArchiveMode.Create); - - var mimeTypeEntry = archive.CreateEntry("mimetype", CompressionLevel.NoCompression); - using (var mimeTypeStream = new StreamWriter(mimeTypeEntry.Open())) - { - await mimeTypeStream.WriteAsync("application/epub+zip").ConfigureAwait(false); - await mimeTypeStream.FlushAsync(ct).ConfigureAwait(false); - } - - var containerEntry = archive.CreateEntry("META-INF/container.xml"); - using (var containerStream = new StreamWriter(containerEntry.Open())) - { - await containerStream.WriteLineAsync(_containerXml).ConfigureAwait(false); - await containerStream.FlushAsync(ct).ConfigureAwait(false); - } - - archive.CreateEntryFromFile(CoverFilePath, $"OEBPS/{Path.GetFileName(CoverFilePath)}"); - - var cssEntry = archive.CreateEntry("OEBPS/style.css"); - using (var cssStream = new StreamWriter(cssEntry.Open())) - { - await cssStream.WriteLineAsync(CreateCssText()).ConfigureAwait(false); - await cssStream.FlushAsync(ct).ConfigureAwait(false); - } - - var navEntry = archive.CreateEntry("OEBPS/nav.xhtml"); - using (var navStream = new StreamWriter(navEntry.Open())) - { - await navStream.WriteLineAsync(CreateNavXhtml()).ConfigureAwait(false); - await navStream.FlushAsync(ct).ConfigureAwait(false); - } - - var opfEntry = archive.CreateEntry("OEBPS/book.opf"); - using (var opfStream = new StreamWriter(opfEntry.Open())) - { - await opfStream.WriteLineAsync(CreateOpf()).ConfigureAwait(false); - await opfStream.FlushAsync(ct).ConfigureAwait(false); - } - - for (var i = 0; i < Chapters.Count; i++) - { - for (var j = 0; j < Chapters[i].Sections.Count; j++) - { - var sectionXhtmlEntry = archive.CreateEntry($"OEBPS/{Chapters[i].Sections[j].Id}.xhtml"); - using (var sectionXhtmlStream = new StreamWriter(sectionXhtmlEntry.Open())) - { - await sectionXhtmlStream.WriteLineAsync(Chapters[i].Sections[j].CreateSectionXhtml()).ConfigureAwait(false); - await sectionXhtmlStream.FlushAsync(ct).ConfigureAwait(false); - } - var sectionSmilEntry = archive.CreateEntry($"OEBPS/{Chapters[i].Sections[j].Id}_audio.smil"); - using (var sectionSmilStream = new StreamWriter(sectionSmilEntry.Open())) - { - await sectionSmilStream.WriteLineAsync(Chapters[i].Sections[j].CreateSectionSmil()).ConfigureAwait(false); - await sectionSmilStream.FlushAsync(ct).ConfigureAwait(false); - } - for (var k = 0; k < Chapters[i].Sections[j].Elements.Count; k++) - { - var element = Chapters[i].Sections[j].Elements[k]; - if (element is Paragraph para && para.Audio != null) - { - var audioEntry = archive.CreateEntry($"OEBPS/{Chapters[i].Sections[j].Id}_p{k}.mp3"); - using var audioStream = para.Audio.GetStream(); - using var audioEntryStream = audioEntry.Open(); - await audioStream.CopyToAsync(audioEntryStream, ct).ConfigureAwait(false); - await audioEntryStream.FlushAsync(ct).ConfigureAwait(false); - } - else if (element is Picture pic && File.Exists(pic.PictureFilePath)) - { - archive.CreateEntryFromFile(pic.PictureFilePath, $"OEBPS/{Chapters[i].Sections[j].Id}_p{k}{Path.GetExtension(pic.PictureFilePath)}"); - } - } - } - } - return true; - } - catch (OperationCanceledException) - { - throw; - } - catch - { - return false; - } - } + public string CreateContainerXml() => """ + + + + + + + """; } diff --git a/Epub/KoeBook.Epub/Services/EpubGeneration.cs b/Epub/KoeBook.Epub/Services/EpubGeneration.cs new file mode 100644 index 0000000..9a724eb --- /dev/null +++ b/Epub/KoeBook.Epub/Services/EpubGeneration.cs @@ -0,0 +1,104 @@ +using KoeBook.Epub.Contracts.Services; +using KoeBook.Epub.Models; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO.Compression; +using System.Text; + +namespace KoeBook.Epub.Services; +public class EpubGeneration : IEpubGeneration +{ + public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument,string tmpDirectory, CancellationToken ct) + { + if (!File.Exists(epubDocument.CoverFilePath)) + { + throw new FileNotFoundException("指定されたカバーファイルが存在しません", epubDocument.CoverFilePath); + } + try + { + using var fs = File.Create(Path.Combine(tmpDirectory, $"{epubDocument.Id}.epub")); + using var archive = new ZipArchive(fs, ZipArchiveMode.Create); + + var mimeTypeEntry = archive.CreateEntry("mimetype", CompressionLevel.NoCompression); + using (var mimeTypeStream = new StreamWriter(mimeTypeEntry.Open())) + { + await mimeTypeStream.WriteAsync("application/epub+zip").ConfigureAwait(false); + await mimeTypeStream.FlushAsync(ct).ConfigureAwait(false); + } + + var containerEntry = archive.CreateEntry("META-INF/container.xml"); + using (var containerStream = new StreamWriter(containerEntry.Open())) + { + await containerStream.WriteLineAsync(epubDocument.CreateContainerXml()).ConfigureAwait(false); + await containerStream.FlushAsync(ct).ConfigureAwait(false); + } + + archive.CreateEntryFromFile(epubDocument.CoverFilePath, $"OEBPS/{Path.GetFileName(epubDocument.CoverFilePath)}"); + + var cssEntry = archive.CreateEntry("OEBPS/style.css"); + using (var cssStream = new StreamWriter(cssEntry.Open())) + { + await cssStream.WriteLineAsync(epubDocument.CreateCssText()).ConfigureAwait(false); + await cssStream.FlushAsync(ct).ConfigureAwait(false); + } + + var navEntry = archive.CreateEntry("OEBPS/nav.xhtml"); + using (var navStream = new StreamWriter(navEntry.Open())) + { + await navStream.WriteLineAsync(epubDocument.CreateNavXhtml()).ConfigureAwait(false); + await navStream.FlushAsync(ct).ConfigureAwait(false); + } + + var opfEntry = archive.CreateEntry("OEBPS/book.opf"); + using (var opfStream = new StreamWriter(opfEntry.Open())) + { + await opfStream.WriteLineAsync(epubDocument.CreateOpf()).ConfigureAwait(false); + await opfStream.FlushAsync(ct).ConfigureAwait(false); + } + + for (var i = 0; i < epubDocument.Chapters.Count; i++) + { + for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) + { + var sectionXhtmlEntry = archive.CreateEntry($"OEBPS/{epubDocument.Chapters[i].Sections[j].Id}.xhtml"); + using (var sectionXhtmlStream = new StreamWriter(sectionXhtmlEntry.Open())) + { + await sectionXhtmlStream.WriteLineAsync(epubDocument.Chapters[i].Sections[j].CreateSectionXhtml()).ConfigureAwait(false); + await sectionXhtmlStream.FlushAsync(ct).ConfigureAwait(false); + } + var sectionSmilEntry = archive.CreateEntry($"OEBPS/{epubDocument.Chapters[i].Sections[j].Id}_audio.smil"); + using (var sectionSmilStream = new StreamWriter(sectionSmilEntry.Open())) + { + await sectionSmilStream.WriteLineAsync(epubDocument.Chapters[i].Sections[j].CreateSectionSmil()).ConfigureAwait(false); + await sectionSmilStream.FlushAsync(ct).ConfigureAwait(false); + } + for (var k = 0; k < epubDocument.Chapters[i].Sections[j].Elements.Count; k++) + { + var element = epubDocument.Chapters[i].Sections[j].Elements[k]; + if (element is Paragraph para && para.Audio != null) + { + var audioEntry = archive.CreateEntry($"OEBPS/{epubDocument.Chapters[i].Sections[j].Id}_p{k}.mp3"); + using var audioStream = para.Audio.GetStream(); + using var audioEntryStream = audioEntry.Open(); + await audioStream.CopyToAsync(audioEntryStream, ct).ConfigureAwait(false); + await audioEntryStream.FlushAsync(ct).ConfigureAwait(false); + } + else if (element is Picture pic && File.Exists(pic.PictureFilePath)) + { + archive.CreateEntryFromFile(pic.PictureFilePath, $"OEBPS/{epubDocument.Chapters[i].Sections[j].Id}_p{k}{Path.GetExtension(pic.PictureFilePath)}"); + } + } + } + } + return true; + } + catch (OperationCanceledException) + { + throw; + } + catch + { + return false; + } + } +} From 00ed0be0e36d8736137a46d8763bc76671191bc9 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sun, 3 Mar 2024 19:05:07 +0900 Subject: [PATCH 15/45] =?UTF-8?q?=E5=90=8D=E7=A7=B0=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=A8DI=E7=99=BB=E9=8C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/{IEpubGeneration.cs => IEpubCreateService.cs} | 2 +- .../Services/{EpubGeneration.cs => EpubCreateService.cs} | 4 ++-- KoeBook.Core/Services/EpubGenerateService.cs | 6 ++++-- KoeBook/App.xaml.cs | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) rename Epub/KoeBook.Epub/Contracts/Services/{IEpubGeneration.cs => IEpubCreateService.cs} (84%) rename Epub/KoeBook.Epub/Services/{EpubGeneration.cs => EpubCreateService.cs} (97%) diff --git a/Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs b/Epub/KoeBook.Epub/Contracts/Services/IEpubCreateService.cs similarity index 84% rename from Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs rename to Epub/KoeBook.Epub/Contracts/Services/IEpubCreateService.cs index c06b3d4..6b9df0a 100644 --- a/Epub/KoeBook.Epub/Contracts/Services/IEpubGeneration.cs +++ b/Epub/KoeBook.Epub/Contracts/Services/IEpubCreateService.cs @@ -2,7 +2,7 @@ namespace KoeBook.Epub.Contracts.Services; -public interface IEpubGeneration +public interface IEpubCreateService { ValueTask TryCreateEpubAsync(EpubDocument epubDocument, string tmpDirectory, CancellationToken ct); } diff --git a/Epub/KoeBook.Epub/Services/EpubGeneration.cs b/Epub/KoeBook.Epub/Services/EpubCreateService.cs similarity index 97% rename from Epub/KoeBook.Epub/Services/EpubGeneration.cs rename to Epub/KoeBook.Epub/Services/EpubCreateService.cs index 9a724eb..4bfe3e0 100644 --- a/Epub/KoeBook.Epub/Services/EpubGeneration.cs +++ b/Epub/KoeBook.Epub/Services/EpubCreateService.cs @@ -6,9 +6,9 @@ using System.Text; namespace KoeBook.Epub.Services; -public class EpubGeneration : IEpubGeneration +public class EpubCreateService : IEpubCreateService { - public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument,string tmpDirectory, CancellationToken ct) + public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, string tmpDirectory, CancellationToken ct) { if (!File.Exists(epubDocument.CoverFilePath)) { diff --git a/KoeBook.Core/Services/EpubGenerateService.cs b/KoeBook.Core/Services/EpubGenerateService.cs index 6052d8c..2810e8d 100644 --- a/KoeBook.Core/Services/EpubGenerateService.cs +++ b/KoeBook.Core/Services/EpubGenerateService.cs @@ -1,14 +1,16 @@ using KoeBook.Core.Contracts.Services; using KoeBook.Core.Models; using KoeBook.Epub; +using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; namespace KoeBook.Core.Services; -public class EpubGenerateService(ISoundGenerationService soundGenerationService, IEpubDocumentStoreService epubDocumentStoreService) : IEpubGenerateService +public class EpubGenerateService(ISoundGenerationService soundGenerationService, IEpubDocumentStoreService epubDocumentStoreService, IEpubCreateService epubCreateService) : IEpubGenerateService { private ISoundGenerationService _soundGenerationService = soundGenerationService; private IEpubDocumentStoreService _documentStoreService = epubDocumentStoreService; + private IEpubCreateService _createService = epubCreateService; public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string tempDirectory, CancellationToken cancellationToken) { @@ -22,7 +24,7 @@ public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string scriptLine.Paragraph.Audio = new Audio(await _soundGenerationService.GenerateLineSoundAsync(scriptLine, bookScripts.Options, cancellationToken).ConfigureAwait(false)); } - if (await document.TryCreateEpubAsync(tempDirectory, bookScripts.BookProperties.Id.ToString(), cancellationToken).ConfigureAwait(false)) + if (await _createService.TryCreateEpubAsync(document, tempDirectory, cancellationToken).ConfigureAwait(false)) { _documentStoreService.Unregister(bookScripts.BookProperties.Id); return Path.Combine(tempDirectory, $"{bookScripts.BookProperties.Id}.epub"); diff --git a/KoeBook/App.xaml.cs b/KoeBook/App.xaml.cs index ddde655..3b1e62f 100644 --- a/KoeBook/App.xaml.cs +++ b/KoeBook/App.xaml.cs @@ -102,6 +102,7 @@ public App() services.AddSingleton() .AddSingleton() .AddSingleton(); + services.AddSingleton(); // Views and ViewModels services.AddTransient(); From 326914a4d8e21f3375f7bfc581ae45cda495038c Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 19:31:02 +0900 Subject: [PATCH 16/45] =?UTF-8?q?SplitBrace=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs index f988e9c..e70067c 100644 --- a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -34,10 +34,9 @@ public static List SplitBrace(string text) startIdx = i + 1; } } - result.Add(text[startIdx..]); - if (result[^1] == "") + if (startIdx != brackets.Length) { - result.RemoveAt(result.Count - 1); + result.Add(text[startIdx..]); } return result; From 1307ed91a48ba55fba59282a1336ad27f7f12d56 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Sun, 3 Mar 2024 19:37:42 +0900 Subject: [PATCH 17/45] =?UTF-8?q?brackets.Length=20=E3=82=92=20text.Length?= =?UTF-8?q?=20=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs index e70067c..7b7a337 100644 --- a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -34,7 +34,7 @@ public static List SplitBrace(string text) startIdx = i + 1; } } - if (startIdx != brackets.Length) + if (startIdx != text.Length) { result.Add(text[startIdx..]); } From f7e8ce1c29c802b119625b625d785cb56e014751 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:58:52 +0900 Subject: [PATCH 18/45] =?UTF-8?q?#5-1=20EpubDocument=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E4=BD=9C=E6=88=90=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/Epub/EpubDocumentTest.cs | 132 ++++++++++++++++++++++++++ KoeBook.Test/KoeBook.Test.csproj | 4 + KoeBook.Test/Proxies/EpubModels.cs | 7 ++ 3 files changed, 143 insertions(+) create mode 100644 KoeBook.Test/Epub/EpubDocumentTest.cs create mode 100644 KoeBook.Test/Proxies/EpubModels.cs diff --git a/KoeBook.Test/Epub/EpubDocumentTest.cs b/KoeBook.Test/Epub/EpubDocumentTest.cs new file mode 100644 index 0000000..008b136 --- /dev/null +++ b/KoeBook.Test/Epub/EpubDocumentTest.cs @@ -0,0 +1,132 @@ +using KoeBook.Epub.Models; +using KoeBook.Test.Proxies; + +namespace KoeBook.Test.Epub; + +public class EpubDocumentTest +{ + #region Ensure List + [Fact] + public void EnsureChapter() + { + var document = new EpubDocument("title", "author", "cover", default); + + Assert.Empty(document.Chapters); + + // 空のときは追加 + document.AsPrivateProxy().EnsureChapter(); + + var chapter = Assert.Single(document.Chapters); + Assert.Null(chapter.Title); + Assert.Empty(chapter.Sections); + + // 空でないときは無視 + document.AsPrivateProxy().EnsureChapter(); + + var chapter2 = Assert.Single(document.Chapters); + Assert.Same(chapter, chapter2); + } + + [Fact] + public void EnsureSection() + { + var document = new EpubDocument("title", "author", "cover", default); + + Assert.Empty(document.Chapters); + + // 空のときは追加される + document.AsPrivateProxy().EnsureSection(0); + + var chapter = Assert.Single(document.Chapters); + Assert.Null(chapter.Title); + var section = Assert.Single(chapter.Sections); + Assert.Equal("title", section.Title); + Assert.Empty(section.Elements); + + // 空でないときは無視 + document.Chapters = [ + new() { + Title = "chapter1", + Sections = [ + new("section1"), + new("section2"), + new("section3"), + ], + }, + new() { + Title = "chapter2", + Sections = [], + }, + ]; + + document.AsPrivateProxy().EnsureSection(0); + + Assert.Equal(3, document.Chapters[0].Sections.Count); + + document.AsPrivateProxy().EnsureSection(1); + + Assert.Equal("chapter2", document.Chapters[1].Sections[0].Title); + + // インデックスは正しく指定する必要がある + var exception = Record.Exception(() => document.AsPrivateProxy().EnsureSection(5)); + + Assert.IsType(exception); + } + + [Fact] + public void EnsureParagraph() + { + var document = new EpubDocument("title", "author", "cover", default); + + Assert.Empty(document.Chapters); + + // 空のときは追加される + document.AsPrivateProxy().EnsureParagraph(0, 0); + + var chapter = Assert.Single(document.Chapters); + var section = Assert.Single(chapter.Sections); + var element = Assert.Single(section.Elements); + var paragraph = Assert.IsType(element); + Assert.Null(paragraph.Audio); + Assert.Null(paragraph.Text); + Assert.Null(paragraph.ClassName); + + // 空でないときは無視 + document.Chapters = [ + new() { + Title = "chapter1", + Sections = [ + new("section1") { + Elements = [ + new Paragraph() { + Text = "paragraph1", + }, + ] + }, + ], + }, + ]; + + document.AsPrivateProxy().EnsureParagraph(0, 0); + + chapter = Assert.Single(document.Chapters); + section = Assert.Single(chapter.Sections); + element = Assert.Single(section.Elements); + paragraph = Assert.IsType(element); + Assert.Equal("paragraph1", paragraph.Text); + + document.AsPrivateProxy().EnsureParagraph(0, 1); + + element = Assert.Single(document.Chapters[0].Sections[1].Elements); + paragraph = Assert.IsType(element); + Assert.Null(paragraph.Audio); + Assert.Null(paragraph.Text); + Assert.Null(paragraph.ClassName); + + // インデックスは正しく指定する必要がある + var exception = Record.Exception(() => document.AsPrivateProxy().EnsureParagraph(0, 5)); + + Assert.IsType(exception); + } + #endregion +} diff --git a/KoeBook.Test/KoeBook.Test.csproj b/KoeBook.Test/KoeBook.Test.csproj index c085021..2658a8d 100644 --- a/KoeBook.Test/KoeBook.Test.csproj +++ b/KoeBook.Test/KoeBook.Test.csproj @@ -12,6 +12,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/KoeBook.Test/Proxies/EpubModels.cs b/KoeBook.Test/Proxies/EpubModels.cs new file mode 100644 index 0000000..7a9822f --- /dev/null +++ b/KoeBook.Test/Proxies/EpubModels.cs @@ -0,0 +1,7 @@ +using KoeBook.Epub.Models; +using PrivateProxy; + +namespace KoeBook.Test.Proxies; + +[GeneratePrivateProxy(typeof(EpubDocument))] +partial struct EpubDocumentProxy; From 2171f647c6998b4d6664397f754f2214edf304f4 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sun, 3 Mar 2024 20:50:45 +0900 Subject: [PATCH 19/45] =?UTF-8?q?#5-1=20IVT=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Properties/AssemblyInfo.cs | 4 +++ KoeBook.Core/Properties/AssemblyInfo.cs | 4 +++ KoeBook.Test/Epub/EpubDocumentTest.cs | 30 +++++++++++--------- 3 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 Epub/KoeBook.Epub/Properties/AssemblyInfo.cs create mode 100644 KoeBook.Core/Properties/AssemblyInfo.cs diff --git a/Epub/KoeBook.Epub/Properties/AssemblyInfo.cs b/Epub/KoeBook.Epub/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9a2272a --- /dev/null +++ b/Epub/KoeBook.Epub/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +// PrivateProxyが外部プロジェクトに対応していないため +[assembly: InternalsVisibleTo("KoeBook.Test")] diff --git a/KoeBook.Core/Properties/AssemblyInfo.cs b/KoeBook.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9a2272a --- /dev/null +++ b/KoeBook.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +// PrivateProxyが外部プロジェクトに対応していないため +[assembly: InternalsVisibleTo("KoeBook.Test")] diff --git a/KoeBook.Test/Epub/EpubDocumentTest.cs b/KoeBook.Test/Epub/EpubDocumentTest.cs index 008b136..69138f1 100644 --- a/KoeBook.Test/Epub/EpubDocumentTest.cs +++ b/KoeBook.Test/Epub/EpubDocumentTest.cs @@ -14,14 +14,14 @@ public void EnsureChapter() Assert.Empty(document.Chapters); // 空のときは追加 - document.AsPrivateProxy().EnsureChapter(); + document.EnsureChapter(); var chapter = Assert.Single(document.Chapters); Assert.Null(chapter.Title); Assert.Empty(chapter.Sections); // 空でないときは無視 - document.AsPrivateProxy().EnsureChapter(); + document.EnsureChapter(); var chapter2 = Assert.Single(document.Chapters); Assert.Same(chapter, chapter2); @@ -35,7 +35,7 @@ public void EnsureSection() Assert.Empty(document.Chapters); // 空のときは追加される - document.AsPrivateProxy().EnsureSection(0); + document.EnsureSection(0); var chapter = Assert.Single(document.Chapters); Assert.Null(chapter.Title); @@ -59,18 +59,18 @@ public void EnsureSection() }, ]; - document.AsPrivateProxy().EnsureSection(0); + document.EnsureSection(0); Assert.Equal(3, document.Chapters[0].Sections.Count); - document.AsPrivateProxy().EnsureSection(1); + document.EnsureSection(1); Assert.Equal("chapter2", document.Chapters[1].Sections[0].Title); // インデックスは正しく指定する必要がある - var exception = Record.Exception(() => document.AsPrivateProxy().EnsureSection(5)); + var exception = Record.Exception(() => document.EnsureSection(5)); - Assert.IsType(exception); + Assert.IsType(exception); } [Fact] @@ -81,7 +81,7 @@ public void EnsureParagraph() Assert.Empty(document.Chapters); // 空のときは追加される - document.AsPrivateProxy().EnsureParagraph(0, 0); + document.EnsureParagraph(0, 0); var chapter = Assert.Single(document.Chapters); var section = Assert.Single(chapter.Sections); @@ -103,19 +103,23 @@ public void EnsureParagraph() }, ] }, + new("section1") { + Elements = [] + }, ], }, ]; - document.AsPrivateProxy().EnsureParagraph(0, 0); + document.EnsureParagraph(0, 0); chapter = Assert.Single(document.Chapters); - section = Assert.Single(chapter.Sections); + Assert.Equal(2, chapter.Sections.Count); + section = chapter.Sections[0]; element = Assert.Single(section.Elements); paragraph = Assert.IsType(element); Assert.Equal("paragraph1", paragraph.Text); - document.AsPrivateProxy().EnsureParagraph(0, 1); + document.EnsureParagraph(0, 1); element = Assert.Single(document.Chapters[0].Sections[1].Elements); paragraph = Assert.IsType(element); @@ -124,9 +128,9 @@ public void EnsureParagraph() Assert.Null(paragraph.ClassName); // インデックスは正しく指定する必要がある - var exception = Record.Exception(() => document.AsPrivateProxy().EnsureParagraph(0, 5)); + var exception = Record.Exception(() => document.EnsureParagraph(0, 5)); - Assert.IsType(exception); + Assert.IsType(exception); } #endregion } From fb8d6bc10a0fcec5990fbe42235b26913b108539 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sun, 3 Mar 2024 20:51:45 +0900 Subject: [PATCH 20/45] fmt --- KoeBook.Test/Epub/EpubDocumentTest.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/KoeBook.Test/Epub/EpubDocumentTest.cs b/KoeBook.Test/Epub/EpubDocumentTest.cs index 69138f1..e9209a8 100644 --- a/KoeBook.Test/Epub/EpubDocumentTest.cs +++ b/KoeBook.Test/Epub/EpubDocumentTest.cs @@ -45,7 +45,8 @@ public void EnsureSection() // 空でないときは無視 document.Chapters = [ - new() { + new() + { Title = "chapter1", Sections = [ new("section1"), @@ -53,7 +54,8 @@ public void EnsureSection() new("section3"), ], }, - new() { + new() + { Title = "chapter2", Sections = [], }, @@ -93,17 +95,21 @@ public void EnsureParagraph() // 空でないときは無視 document.Chapters = [ - new() { + new() + { Title = "chapter1", Sections = [ - new("section1") { + new("section1") + { Elements = [ - new Paragraph() { + new Paragraph() + { Text = "paragraph1", }, ] }, - new("section1") { + new("section1") + { Elements = [] }, ], From 3864450a5edbe99cd79dbab17a2359ab2536b0ce Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:17:28 +0900 Subject: [PATCH 21/45] =?UTF-8?q?#5-1=20PrivateProxy=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KoeBook.Test/Epub/EpubDocumentTest.cs | 1 - KoeBook.Test/KoeBook.Test.csproj | 4 ---- KoeBook.Test/Proxies/EpubModels.cs | 7 ------- 3 files changed, 12 deletions(-) delete mode 100644 KoeBook.Test/Proxies/EpubModels.cs diff --git a/KoeBook.Test/Epub/EpubDocumentTest.cs b/KoeBook.Test/Epub/EpubDocumentTest.cs index e9209a8..6ce0f15 100644 --- a/KoeBook.Test/Epub/EpubDocumentTest.cs +++ b/KoeBook.Test/Epub/EpubDocumentTest.cs @@ -1,5 +1,4 @@ using KoeBook.Epub.Models; -using KoeBook.Test.Proxies; namespace KoeBook.Test.Epub; diff --git a/KoeBook.Test/KoeBook.Test.csproj b/KoeBook.Test/KoeBook.Test.csproj index 2658a8d..c085021 100644 --- a/KoeBook.Test/KoeBook.Test.csproj +++ b/KoeBook.Test/KoeBook.Test.csproj @@ -12,10 +12,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/KoeBook.Test/Proxies/EpubModels.cs b/KoeBook.Test/Proxies/EpubModels.cs deleted file mode 100644 index 7a9822f..0000000 --- a/KoeBook.Test/Proxies/EpubModels.cs +++ /dev/null @@ -1,7 +0,0 @@ -using KoeBook.Epub.Models; -using PrivateProxy; - -namespace KoeBook.Test.Proxies; - -[GeneratePrivateProxy(typeof(EpubDocument))] -partial struct EpubDocumentProxy; From f3652d06e2eccb47c0b174d200644d1dc1c3788c Mon Sep 17 00:00:00 2001 From: TakenPt Date: Tue, 5 Mar 2024 21:15:38 +0900 Subject: [PATCH 22/45] wip --- .../KoeBook.Epub/Services/ScrapingNaroService.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index a1e00ec..87c7d2e 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -66,7 +66,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } var document = new EpubDocument(bookTitle.InnerHtml, bookAuther.InnerHtml, coverFilePath, id); - if (isRensai) + if (isRensai) // 連載の時 { List SectionWithChapterTitleList = new List(); for (int i = 1; i <= allNum; i++) @@ -93,6 +93,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } else { + document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); } } @@ -106,7 +107,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } } } - else + else // 短編の時 { var load = await ReadPageAsync(url, isRensai, imageDirectory, ct).ConfigureAwait(false); if (load != null) @@ -128,13 +129,16 @@ private async Task ReadPageAsync(string url, bool isRen using var context = BrowsingContext.New(config); var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); - var chapterTitleElement = doc.QuerySelector(".chapter_title"); string? chapterTitle = null; - if (chapterTitleElement != null) + if (!isRensai) { - if (chapterTitleElement.InnerHtml != null) + var chapterTitleElement = doc.QuerySelector(".chapter_title"); + if (chapterTitleElement != null) { - chapterTitle = chapterTitleElement.InnerHtml; + if (chapterTitleElement.InnerHtml != null) + { + chapterTitle = chapterTitleElement.InnerHtml; + } } } From 16a7f932d7d90fee59728292e86a2552968633e3 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Wed, 6 Mar 2024 14:19:17 +0900 Subject: [PATCH 23/45] =?UTF-8?q?=E4=BC=9A=E8=A9=B1=E6=96=87=E3=81=AE?= =?UTF-8?q?=E9=96=8B=E5=A7=8B=E3=80=81=E7=B5=82=E4=BA=86=E8=A8=98=E5=8F=B7?= =?UTF-8?q?=E3=81=AB=E3=80=8E=E5=8F=8A=E3=81=B3=E3=80=8F=E3=82=92=E8=AA=8D?= =?UTF-8?q?=E3=82=81=E3=82=8B=E3=82=88=E3=81=86=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ScrapingNaroService.cs | 6 +-- Epub/KoeBook.Epub/Utility/ScrapingHelper.cs | 10 ++-- KoeBook.Test/Epub/ScrapingHelperTest.cs | 47 ++++++++++++++++++- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index 87c7d2e..ba33b8a 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -71,8 +71,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP List SectionWithChapterTitleList = new List(); for (int i = 1; i <= allNum; i++) { - Console.WriteLine(i); - await Task.Delay(500, ct); + await Task.Delay(1500, ct); var pageUrl = Path.Combine(url, i.ToString()); var load = await ReadPageAsync(pageUrl, isRensai, imageDirectory, ct).ConfigureAwait(false); SectionWithChapterTitleList.Add(load); @@ -93,7 +92,6 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } else { - document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); } } @@ -123,7 +121,7 @@ public record BookInfo(int? allcount, int? noveltype, int? general_all_no); private record SectionWithChapterTitle(string? title, Section section); - private async Task ReadPageAsync(string url, bool isRensai, string imageDirectory, CancellationToken ct) + private static async Task ReadPageAsync(string url, bool isRensai, string imageDirectory, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); using var context = BrowsingContext.New(config); diff --git a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs index 7b7a337..45991a9 100644 --- a/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs +++ b/Epub/KoeBook.Epub/Utility/ScrapingHelper.cs @@ -4,7 +4,7 @@ public static class ScrapingHelper { public static List SplitBrace(string text) { - if (text.Length == 1 && (text == "「" || text == "」")) + if (text.Length == 1 && (text == "「" || text == "『" || text == "」" || text == "』")) return [text]; var bracket = 0; @@ -12,8 +12,8 @@ public static List SplitBrace(string text) for (var i = 0; i < text.Length; i++) { var c = text[i]; - if (c == '「') bracket++; - else if (c == '」') bracket--; + if (c == '「' || c == '『') bracket++; + else if (c == '」' || c == '』') bracket--; brackets[i] = bracket; } @@ -23,12 +23,12 @@ public static List SplitBrace(string text) for (var i = 0; i < brackets.Length; i++) { brackets[i] -= mn; - if (text[i] == '「' && brackets[i] == 1 && i != 0 && startIdx != i) + if ((text[i] == '「' || text[i] == '『') && brackets[i] == 1 && i != 0 && startIdx != i) { result.Add(text[startIdx..i]); startIdx = i; } - if (text[i] == '」' && brackets[i] == 0) + if ((text[i] == '」' || text[i] == '』') && brackets[i] == 0) { result.Add(text[startIdx..(i + 1)]); startIdx = i + 1; diff --git a/KoeBook.Test/Epub/ScrapingHelperTest.cs b/KoeBook.Test/Epub/ScrapingHelperTest.cs index 394315e..055cceb 100644 --- a/KoeBook.Test/Epub/ScrapingHelperTest.cs +++ b/KoeBook.Test/Epub/ScrapingHelperTest.cs @@ -7,6 +7,7 @@ public class ScrapingHelperTest public static object[][] TestCases() { (string, List)[] cases = [ + // '「''」'のみの場合のケース ("「", ["「"]), ("」", ["」"]), ("a", ["a"]), @@ -29,7 +30,51 @@ public static object[][] TestCases() ("abc「abc「abc」abc", ["abc", "「abc「abc」abc"]), ("abc「abc」abc」abc", ["abc「abc」abc」", "abc"]), ("abc「abc「abc", ["abc", "「abc「abc"]), - ("abc」abc」abc", ["abc」abc」", "abc"]) + ("abc」abc」abc", ["abc」abc」", "abc"]), + // '『''』'のみの場合のケース + ("『", ["『"]), + ("』", ["』"]), + ("a", ["a"]), + ("abc『abc』abc", ["abc", "『abc』", "abc"]), + ("abc『abc』", ["abc", "『abc』"]), + ("『abc』abc", ["『abc』", "abc",]), + ("abc『abc』", ["abc", "『abc』"]), + ("『abc』", ["『abc』",]), + ("abc『abc", ["abc", "『abc"]), + ("abc『", ["abc", "『"]), + ("『abc", ["『abc"]), + ("abc』abc", ["abc』", "abc"]), + ("abc』", ["abc』"]), + ("』abc", ["』", "abc"]), + ("abc『abc』abc『abc』abc", ["abc", "『abc』", "abc", "『abc』", "abc"]), + ("『abc』abc『abc』abc", ["『abc』", "abc", "『abc』", "abc"]), + ("abc『abc』『abc』abc", ["abc", "『abc』", "『abc』", "abc"]), + ("abc『abc』abc『abc』", ["abc", "『abc』", "abc", "『abc』"]), + ("abc『abc『abc』abc』abc", ["abc", "『abc『abc』abc』", "abc"]), + ("abc『abc『abc』abc", ["abc", "『abc『abc』abc"]), + ("abc『abc』abc』abc", ["abc『abc』abc』", "abc"]), + ("abc『abc『abc", ["abc", "『abc『abc"]), + ("abc』abc』abc", ["abc』abc』", "abc"]), + // '「''」''『''』'が混在するパターン + ("abc「abc」abc『abc』abc", ["abc", "「abc」", "abc", "『abc』", "abc"]), + ("abc『abc』abc「abc」abc", ["abc", "『abc』", "abc", "「abc」", "abc"]), + ("「abc」abc『abc』abc", ["「abc」", "abc", "『abc』", "abc"]), + ("『abc』abc「abc」abc", ["『abc』", "abc", "「abc」", "abc"]), + ("abc「abc」『abc』abc", ["abc", "「abc」", "『abc』", "abc"]), + ("abc『abc』「abc」abc", ["abc", "『abc』", "「abc」", "abc"]), + ("abc「abc」abc『abc』", ["abc", "「abc」", "abc", "『abc』"]), + ("abc『abc』abc「abc」", ["abc", "『abc』", "abc", "「abc」"]), + ("abc「abc『abc』abc」abc", ["abc", "「abc『abc』abc」", "abc"]), + ("abc『abc「abc」abc』abc", ["abc", "『abc「abc」abc』", "abc"]), + ("abc「abc『abc』abc", ["abc", "「abc『abc』abc"]), + ("abc『abc「abc」abc", ["abc", "『abc「abc」abc"]), + ("abc「abc」abc』abc", ["abc「abc」abc』", "abc"]), + ("abc『abc』abc」abc", ["abc『abc』abc」", "abc"]), + ("abc「abc『abc", ["abc", "「abc『abc"]), + ("abc『abc「abc", ["abc", "『abc「abc"]), + ("abc」abc』abc", ["abc」abc』", "abc"]), + ("abc』abc」abc", ["abc』abc」", "abc"]), + ("abc』abc』abc", ["abc』abc』", "abc"]) ]; return cases.Select(c => new object[] { c.Item1, c.Item2 }).ToArray(); } From c6f891194839106d78a165450faf517676900100 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Wed, 6 Mar 2024 23:07:09 +0900 Subject: [PATCH 24/45] =?UTF-8?q?EpubDocument=E5=86=85=E3=81=AE.epub?= =?UTF-8?q?=E7=94=9F=E6=88=90=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E3=82=B3?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/EpubCreateHelper.cs | 17 -- Epub/KoeBook.Epub/Models/EpubDocument.cs | 179 +----------------- .../Services/EpubCreateService.cs | 171 ++++++++++++++++- 3 files changed, 175 insertions(+), 192 deletions(-) delete mode 100644 Epub/KoeBook.Epub/EpubCreateHelper.cs diff --git a/Epub/KoeBook.Epub/EpubCreateHelper.cs b/Epub/KoeBook.Epub/EpubCreateHelper.cs deleted file mode 100644 index 936a546..0000000 --- a/Epub/KoeBook.Epub/EpubCreateHelper.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace KoeBook.Epub; - -internal static class EpubCreateHelper -{ - internal static string GetImagesMediaType(string path) - { - return Path.GetExtension(path) switch - { - ".gif" => "image/gif", - ".jpg" or ".jpeg" => "image/jpeg", - ".png" => "image/png", - ".svg" => "image/svg+xml", - ".webp" => "image/webp", - _ => "" - }; - } -} diff --git a/Epub/KoeBook.Epub/Models/EpubDocument.cs b/Epub/KoeBook.Epub/Models/EpubDocument.cs index e46ac21..e32192f 100644 --- a/Epub/KoeBook.Epub/Models/EpubDocument.cs +++ b/Epub/KoeBook.Epub/Models/EpubDocument.cs @@ -14,17 +14,17 @@ public class EpubDocument(string title, string author, string coverFilePath, Gui public Guid Id { get; } = id; public List CssClasses { get; set; } = [ - new CssClass("-epub-media-overlay-active", """ - .-epub-media-overlay-active *{ - background-color: yellow; - color: black !important; - } - """), + new CssClass("-epub-media-overlay-active", """ + .-epub-media-overlay-active *{ + background-color: yellow; + color: black !important; + } + """), new CssClass("-epub-media-overlay-unactive", """ - .-epub-media-overlay-unactive * { - color: gray; - } - """), + .-epub-media-overlay-unactive * { + color: gray; + } + """), ]; public List Chapters { get; set; } = []; @@ -54,163 +54,4 @@ internal void EnsureParagraph(int chapterIndex, int sectionIndex) if (Chapters[chapterIndex].Sections[sectionIndex].Elements.Count == 0) Chapters[chapterIndex].Sections[sectionIndex].Elements.Add(new Paragraph()); } - - public string CreateNavXhtml() - { - var builder = new StringBuilder($""" - - - - - {Title} - - - - - - """); - return builder.ToString(); - } - - public string CreateCssText() - { - var builder = new StringBuilder(); - foreach (var cssClass in CssClasses) - { - builder.AppendLine(cssClass.Text); - } - return builder.ToString(); - } - - public string CreateOpf() - { - var builder = new StringBuilder($""" - - - {Title} - {Author} - aut - urn:uuid:{Guid.NewGuid()} - ja - {DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)} - {DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)} - -epub-media-overlay-active - -epub-media-overlay-unactive - - """); - - var totalTime = TimeSpan.Zero; - for (var i = 0; i < Chapters.Count; i++) - { - for (var j = 0; j < Chapters[i].Sections.Count; j++) - { - var time = Chapters[i].Sections[j].GetTotalTime(); - totalTime += time; - builder.AppendLine($""" - {time} - """); - } - } - builder.AppendLine($""" - {totalTime} - - - - - - """); - - for (var i = 0; i < Chapters.Count; i++) - { - for (var j = 0; j < Chapters[i].Sections.Count; j++) - { - builder.AppendLine($""" - - - """); - for (var k = 0; k < Chapters[i].Sections[j].Elements.Count; k++) - { - var element = Chapters[i].Sections[j].Elements[k]; - if (element is Paragraph para && para.Audio != null) - { - builder.AppendLine(@$" "); - } - else if (element is Picture pic && File.Exists(pic.PictureFilePath)) - { - builder.AppendLine(@$" "); - } - } - } - } - - builder.AppendLine($""" - - - """); - - for (var i = 0; i < Chapters.Count; i++) - { - for (var j = 0; j < Chapters[i].Sections.Count; j++) - { - builder.AppendLine($""" - - """); - } - } - - builder.AppendLine($""" - - - """); - return builder.ToString(); - } - - public string CreateContainerXml() => """ - - - - - - - """; } diff --git a/Epub/KoeBook.Epub/Services/EpubCreateService.cs b/Epub/KoeBook.Epub/Services/EpubCreateService.cs index 4bfe3e0..02e4054 100644 --- a/Epub/KoeBook.Epub/Services/EpubCreateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubCreateService.cs @@ -1,13 +1,13 @@ using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO.Compression; using System.Text; namespace KoeBook.Epub.Services; -public class EpubCreateService : IEpubCreateService +public class EpubCreateService(IFileExtensionService fileExtensionService) : IEpubCreateService { + private readonly IFileExtensionService _fileExtensionService = fileExtensionService; public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, string tmpDirectory, CancellationToken ct) { if (!File.Exists(epubDocument.CoverFilePath)) @@ -29,7 +29,7 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin var containerEntry = archive.CreateEntry("META-INF/container.xml"); using (var containerStream = new StreamWriter(containerEntry.Open())) { - await containerStream.WriteLineAsync(epubDocument.CreateContainerXml()).ConfigureAwait(false); + await containerStream.WriteLineAsync(CreateContainerXml()).ConfigureAwait(false); await containerStream.FlushAsync(ct).ConfigureAwait(false); } @@ -38,21 +38,21 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin var cssEntry = archive.CreateEntry("OEBPS/style.css"); using (var cssStream = new StreamWriter(cssEntry.Open())) { - await cssStream.WriteLineAsync(epubDocument.CreateCssText()).ConfigureAwait(false); + await cssStream.WriteLineAsync(CreateCssText(epubDocument)).ConfigureAwait(false); await cssStream.FlushAsync(ct).ConfigureAwait(false); } var navEntry = archive.CreateEntry("OEBPS/nav.xhtml"); using (var navStream = new StreamWriter(navEntry.Open())) { - await navStream.WriteLineAsync(epubDocument.CreateNavXhtml()).ConfigureAwait(false); + await navStream.WriteLineAsync(CreateNavXhtml(epubDocument)).ConfigureAwait(false); await navStream.FlushAsync(ct).ConfigureAwait(false); } var opfEntry = archive.CreateEntry("OEBPS/book.opf"); using (var opfStream = new StreamWriter(opfEntry.Open())) { - await opfStream.WriteLineAsync(epubDocument.CreateOpf()).ConfigureAwait(false); + await opfStream.WriteLineAsync(CreateOpf(epubDocument)).ConfigureAwait(false); await opfStream.FlushAsync(ct).ConfigureAwait(false); } @@ -101,4 +101,163 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin return false; } } + + internal static string CreateNavXhtml(EpubDocument epubDocument) + { + var builder = new StringBuilder($""" + + + + + {epubDocument.Title} + + + + + + """); + return builder.ToString(); + } + + internal static string CreateCssText(EpubDocument epubDocument) + { + var builder = new StringBuilder(); + foreach (var cssClass in epubDocument.CssClasses) + { + builder.AppendLine(cssClass.Text); + } + return builder.ToString(); + } + + internal string CreateOpf(EpubDocument epubDocument) + { + var builder = new StringBuilder($""" + + + {epubDocument.Title} + {epubDocument.Author} + aut + urn:uuid:{Guid.NewGuid()} + ja + {DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)} + {DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)} + -epub-media-overlay-active + -epub-media-overlay-unactive + + """); + + var totalTime = TimeSpan.Zero; + for (var i = 0; i < epubDocument.Chapters.Count; i++) + { + for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) + { + var time = epubDocument.Chapters[i].Sections[j].GetTotalTime(); + totalTime += time; + builder.AppendLine($""" + {time} + """); + } + } + builder.AppendLine($""" + {totalTime} + + + + + + """); + + for (var i = 0; i < epubDocument.Chapters.Count; i++) + { + for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) + { + builder.AppendLine($""" + + + """); + for (var k = 0; k < epubDocument.Chapters[i].Sections[j].Elements.Count; k++) + { + var element = epubDocument.Chapters[i].Sections[j].Elements[k]; + if (element is Paragraph para && para.Audio != null) + { + builder.AppendLine(@$" "); + } + else if (element is Picture pic && File.Exists(pic.PictureFilePath)) + { + builder.AppendLine(@$" "); + } + } + } + } + + builder.AppendLine($""" + + + """); + + for (var i = 0; i < epubDocument.Chapters.Count; i++) + { + for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) + { + builder.AppendLine($""" + + """); + } + } + + builder.AppendLine($""" + + + """); + return builder.ToString(); + } + + internal static string CreateContainerXml() => """ + + + + + + + """; } From 718b8fd7e784f9226c1e86713ff84f82d2549638 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Thu, 7 Mar 2024 03:55:06 +0900 Subject: [PATCH 25/45] =?UTF-8?q?=E8=91=97=E8=80=85=E5=90=8D=E3=81=8Ca?= =?UTF-8?q?=E3=82=BF=E3=82=B0=E3=81=A7=E3=81=AA=E3=81=8F=E3=81=A6=E3=82=82?= =?UTF-8?q?=E8=91=97=E8=80=85=E5=90=8D=E3=82=92=E5=8F=96=E5=BE=97=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ScrapingNaroService.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index ba33b8a..ccfd70a 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -25,12 +25,23 @@ public async ValueTask ScrapingAsync(string url, string coverFileP var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); // title の取得 - var bookTitle = doc.QuerySelector(".novel_title") + var bookTitleElement = doc.QuerySelector(".novel_title") ?? throw new EpubDocumentException($"Failed to get title properly.\nUrl may be not collect"); + var bookTitle = bookTitleElement.InnerHtml; // auther の取得 - var bookAuther = doc.QuerySelector(".novel_writername a") + var bookAutherElement = doc.QuerySelector(".novel_writername") ?? throw new EpubDocumentException($"Failed to get auther properly.\nUrl may be not collect"); + var bookAuther = string.Empty; + if (bookAutherElement.QuerySelector("a") is IHtmlAnchorElement bookAutherAnchorElement) + { + bookAuther = bookAutherAnchorElement.InnerHtml; + } + else + { + bookAuther = bookAutherElement.InnerHtml.Replace("作者:", ""); + } + bool isRensai = true; int allNum = 0; @@ -65,7 +76,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP throw new EpubDocumentException("faild to get data by Narou API"); } - var document = new EpubDocument(bookTitle.InnerHtml, bookAuther.InnerHtml, coverFilePath, id); + var document = new EpubDocument(bookTitle, bookAuther, coverFilePath, id); if (isRensai) // 連載の時 { List SectionWithChapterTitleList = new List(); From f91fbf6ca0f7bad306240ea091726ba3203aed28 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Fri, 8 Mar 2024 03:43:29 +0900 Subject: [PATCH 26/45] =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AA=E8=A1=8C(?= =?UTF-8?q?=E5=87=A6=E7=90=86)=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/ScrapingNaroService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index ccfd70a..6e93cbe 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -161,11 +161,10 @@ private static async Task ReadPageAsync(string url, boo sectionTitleElement = doc.QuerySelector(".novel_title"); } - string sectionTitle = ""; if (sectionTitleElement == null) throw new EpubDocumentException("Can not find title of page"); - sectionTitle = sectionTitleElement.InnerHtml; + var sectionTitle = sectionTitleElement.InnerHtml; var section = new Section(sectionTitleElement.InnerHtml); @@ -236,6 +235,7 @@ private static async Task ReadPageAsync(string url, boo if (tags.TagName != "RUBY") { isAllRuby = false; + break; } } From f7135a4b012c485de4db52b938c1350d209bcaf6 Mon Sep 17 00:00:00 2001 From: TakenPt Date: Fri, 8 Mar 2024 23:40:46 +0900 Subject: [PATCH 27/45] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/ScrapingNaroService.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index 6e93cbe..576c422 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -40,8 +40,8 @@ public async ValueTask ScrapingAsync(string url, string coverFileP else { bookAuther = bookAutherElement.InnerHtml.Replace("作者:", ""); - } - + } + bool isRensai = true; int allNum = 0; @@ -254,8 +254,6 @@ private static async Task ReadPageAsync(string url, boo return new SectionWithChapterTitle(chapterTitle, section); } - - [System.Text.RegularExpressions.GeneratedRegex(@"https://.{5,7}.syosetu.com/(.{7}).?")] private static partial System.Text.RegularExpressions.Regex UrlToNcode(); From b2816769a30d7fe01ee092f3260692e5f7cf84d3 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sun, 10 Mar 2024 18:17:36 +0900 Subject: [PATCH 28/45] =?UTF-8?q?=E3=82=BB=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E9=83=A8=E5=88=86=E3=82=92=E4=BD=9C=E6=88=90=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Models/Section.cs | 67 ----------------- .../Services/EpubCreateService.cs | 71 ++++++++++++++++++- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Epub/KoeBook.Epub/Models/Section.cs b/Epub/KoeBook.Epub/Models/Section.cs index 81da7ec..bd283d9 100644 --- a/Epub/KoeBook.Epub/Models/Section.cs +++ b/Epub/KoeBook.Epub/Models/Section.cs @@ -8,73 +8,6 @@ public sealed class Section(string title) public string Title { get; set; } = title; public List Elements { get; set; } = []; - public string CreateSectionXhtml() - { - var builder = new StringBuilder($""" - - - - - {Title} - - - """); - - for (var i = 0; i < Elements.Count; i++) - { - if (Elements[i] is Paragraph para) - { - builder.AppendLine($""" -

      - {para.Text} -

      - """); - } - else if (Elements[i] is Picture pic && File.Exists(pic.PictureFilePath)) - { - builder.AppendLine($""" -

      - - """); - } - } - - builder.AppendLine(""" - - - """); - return builder.ToString(); - } - - public string CreateSectionSmil() - { - var builder = new StringBuilder($""" - - - - """); - - for (var i = 0; i < Elements.Count; i++) - { - if (Elements[i] is Paragraph para && para.Audio != null) - { - builder.AppendLine($""" - - - - """); - } - } - - builder.AppendLine(""" - - - """); - return builder.ToString(); - } - public TimeSpan GetTotalTime() { var time = TimeSpan.Zero; diff --git a/Epub/KoeBook.Epub/Services/EpubCreateService.cs b/Epub/KoeBook.Epub/Services/EpubCreateService.cs index 02e4054..d47c018 100644 --- a/Epub/KoeBook.Epub/Services/EpubCreateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubCreateService.cs @@ -63,13 +63,13 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin var sectionXhtmlEntry = archive.CreateEntry($"OEBPS/{epubDocument.Chapters[i].Sections[j].Id}.xhtml"); using (var sectionXhtmlStream = new StreamWriter(sectionXhtmlEntry.Open())) { - await sectionXhtmlStream.WriteLineAsync(epubDocument.Chapters[i].Sections[j].CreateSectionXhtml()).ConfigureAwait(false); + await sectionXhtmlStream.WriteLineAsync(CreateSectionXhtml(epubDocument.Chapters[i].Sections[j])).ConfigureAwait(false); await sectionXhtmlStream.FlushAsync(ct).ConfigureAwait(false); } var sectionSmilEntry = archive.CreateEntry($"OEBPS/{epubDocument.Chapters[i].Sections[j].Id}_audio.smil"); using (var sectionSmilStream = new StreamWriter(sectionSmilEntry.Open())) { - await sectionSmilStream.WriteLineAsync(epubDocument.Chapters[i].Sections[j].CreateSectionSmil()).ConfigureAwait(false); + await sectionSmilStream.WriteLineAsync(CreateSectionSmil(epubDocument.Chapters[i].Sections[j])).ConfigureAwait(false); await sectionSmilStream.FlushAsync(ct).ConfigureAwait(false); } for (var k = 0; k < epubDocument.Chapters[i].Sections[j].Elements.Count; k++) @@ -260,4 +260,71 @@ internal static string CreateContainerXml() => """ """; + + internal static string CreateSectionXhtml(Section section) + { + var builder = new StringBuilder($""" + + + + + {section.Title} + + + """); + + for (var i = 0; i < section.Elements.Count; i++) + { + if (section.Elements[i] is Paragraph para) + { + builder.AppendLine($""" +

      + {para.Text} +

      + """); + } + else if (section.Elements[i] is Picture pic && File.Exists(pic.PictureFilePath)) + { + builder.AppendLine($""" +

      + + """); + } + } + + builder.AppendLine(""" + + + """); + return builder.ToString(); + } + + internal static string CreateSectionSmil(Section section) + { + var builder = new StringBuilder($""" + + + + """); + + for (var i = 0; i < section.Elements.Count; i++) + { + if (section.Elements[i] is Paragraph para && para.Audio != null) + { + builder.AppendLine($""" + + + + """); + } + } + + builder.AppendLine(""" + + + """); + return builder.ToString(); + } } From 4a7ea0f7ae6b1bcff5404cb04e1c2e4153be835b Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sun, 10 Mar 2024 18:27:14 +0900 Subject: [PATCH 29/45] =?UTF-8?q?StringBuilder=E3=81=AE=E4=B8=80=E6=9C=AC?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/EpubCreateService.cs | 75 ++++++++++--------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/EpubCreateService.cs b/Epub/KoeBook.Epub/Services/EpubCreateService.cs index d47c018..53b5998 100644 --- a/Epub/KoeBook.Epub/Services/EpubCreateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubCreateService.cs @@ -8,6 +8,8 @@ namespace KoeBook.Epub.Services; public class EpubCreateService(IFileExtensionService fileExtensionService) : IEpubCreateService { private readonly IFileExtensionService _fileExtensionService = fileExtensionService; + private readonly StringBuilder _builder = new(); + public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, string tmpDirectory, CancellationToken ct) { if (!File.Exists(epubDocument.CoverFilePath)) @@ -102,9 +104,10 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin } } - internal static string CreateNavXhtml(EpubDocument epubDocument) + internal string CreateNavXhtml(EpubDocument epubDocument) { - var builder = new StringBuilder($""" + _builder.Clear(); + _builder.AppendLine($""" @@ -114,13 +117,13 @@ internal static string CreateNavXhtml(EpubDocument epubDocument)

      """); - return builder.ToString(); + return _builder.ToString(); } - internal static string CreateCssText(EpubDocument epubDocument) + internal string CreateCssText(EpubDocument epubDocument) { - var builder = new StringBuilder(); + _builder.Clear(); foreach (var cssClass in epubDocument.CssClasses) { - builder.AppendLine(cssClass.Text); + _builder.AppendLine(cssClass.Text); } - return builder.ToString(); + return _builder.ToString(); } internal string CreateOpf(EpubDocument epubDocument) { - var builder = new StringBuilder($""" + _builder.Clear(); + _builder.AppendLine($""" {epubDocument.Title} @@ -183,7 +187,6 @@ internal string CreateOpf(EpubDocument epubDocument) {DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)} -epub-media-overlay-active -epub-media-overlay-unactive - """); var totalTime = TimeSpan.Zero; @@ -193,12 +196,12 @@ internal string CreateOpf(EpubDocument epubDocument) { var time = epubDocument.Chapters[i].Sections[j].GetTotalTime(); totalTime += time; - builder.AppendLine($""" + _builder.AppendLine($""" {time} """); } } - builder.AppendLine($""" + _builder.AppendLine($""" {totalTime} @@ -211,7 +214,7 @@ internal string CreateOpf(EpubDocument epubDocument) { for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) { - builder.AppendLine($""" + _builder.AppendLine($""" """); @@ -220,17 +223,17 @@ internal string CreateOpf(EpubDocument epubDocument) var element = epubDocument.Chapters[i].Sections[j].Elements[k]; if (element is Paragraph para && para.Audio != null) { - builder.AppendLine(@$" "); + _builder.AppendLine(@$" "); } else if (element is Picture pic && File.Exists(pic.PictureFilePath)) { - builder.AppendLine(@$" "); + _builder.AppendLine(@$" "); } } } } - builder.AppendLine($""" + _builder.AppendLine($""" """); @@ -239,17 +242,17 @@ internal string CreateOpf(EpubDocument epubDocument) { for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) { - builder.AppendLine($""" + _builder.AppendLine($""" """); } } - builder.AppendLine($""" + _builder.AppendLine($""" """); - return builder.ToString(); + return _builder.ToString(); } internal static string CreateContainerXml() => """ @@ -261,9 +264,10 @@ internal static string CreateContainerXml() => """ """; - internal static string CreateSectionXhtml(Section section) + internal string CreateSectionXhtml(Section section) { - var builder = new StringBuilder($""" + _builder.Clear(); + _builder.AppendLine($""" @@ -277,7 +281,7 @@ internal static string CreateSectionXhtml(Section section) { if (section.Elements[i] is Paragraph para) { - builder.AppendLine($""" + _builder.AppendLine($"""

      {para.Text}

      @@ -285,7 +289,7 @@ internal static string CreateSectionXhtml(Section section) } else if (section.Elements[i] is Picture pic && File.Exists(pic.PictureFilePath)) { - builder.AppendLine($""" + _builder.AppendLine($"""

      @@ -293,16 +297,17 @@ internal static string CreateSectionXhtml(Section section) } } - builder.AppendLine(""" + _builder.AppendLine(""" """); - return builder.ToString(); + return _builder.ToString(); } - internal static string CreateSectionSmil(Section section) + internal string CreateSectionSmil(Section section) { - var builder = new StringBuilder($""" + _builder.Clear(); + _builder.AppendLine($""" @@ -312,7 +317,7 @@ internal static string CreateSectionSmil(Section section) { if (section.Elements[i] is Paragraph para && para.Audio != null) { - builder.AppendLine($""" + _builder.AppendLine($""" """); - return builder.ToString(); + return _builder.ToString(); } } From 409d868fc7ed96bc60bae16c0f0e454cce5b1a20 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sun, 10 Mar 2024 19:36:19 +0900 Subject: [PATCH 30/45] =?UTF-8?q?CreateContainerXml=E3=81=AE=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/EpubCreateService.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/EpubCreateService.cs b/Epub/KoeBook.Epub/Services/EpubCreateService.cs index 53b5998..f96cac2 100644 --- a/Epub/KoeBook.Epub/Services/EpubCreateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubCreateService.cs @@ -10,6 +10,15 @@ public class EpubCreateService(IFileExtensionService fileExtensionService) : IEp private readonly IFileExtensionService _fileExtensionService = fileExtensionService; private readonly StringBuilder _builder = new(); + internal const string ContainerXml = """ + + + + + + + """; + public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, string tmpDirectory, CancellationToken ct) { if (!File.Exists(epubDocument.CoverFilePath)) @@ -31,7 +40,7 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin var containerEntry = archive.CreateEntry("META-INF/container.xml"); using (var containerStream = new StreamWriter(containerEntry.Open())) { - await containerStream.WriteLineAsync(CreateContainerXml()).ConfigureAwait(false); + await containerStream.WriteLineAsync(ContainerXml).ConfigureAwait(false); await containerStream.FlushAsync(ct).ConfigureAwait(false); } @@ -255,15 +264,6 @@ internal string CreateOpf(EpubDocument epubDocument) return _builder.ToString(); } - internal static string CreateContainerXml() => """ - - - - - - - """; - internal string CreateSectionXhtml(Section section) { _builder.Clear(); From abd0a66946325b78878d64044f0bb0f1e582986d Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sun, 10 Mar 2024 19:52:27 +0900 Subject: [PATCH 31/45] =?UTF-8?q?revert=20StringBuilder=E3=81=AE=E4=B8=80?= =?UTF-8?q?=E6=9C=AC=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/EpubCreateService.cs | 75 +++++++++---------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/EpubCreateService.cs b/Epub/KoeBook.Epub/Services/EpubCreateService.cs index f96cac2..b34d590 100644 --- a/Epub/KoeBook.Epub/Services/EpubCreateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubCreateService.cs @@ -8,7 +8,6 @@ namespace KoeBook.Epub.Services; public class EpubCreateService(IFileExtensionService fileExtensionService) : IEpubCreateService { private readonly IFileExtensionService _fileExtensionService = fileExtensionService; - private readonly StringBuilder _builder = new(); internal const string ContainerXml = """ @@ -113,10 +112,9 @@ public async ValueTask TryCreateEpubAsync(EpubDocument epubDocument, strin } } - internal string CreateNavXhtml(EpubDocument epubDocument) + internal static string CreateNavXhtml(EpubDocument epubDocument) { - _builder.Clear(); - _builder.AppendLine($""" + var builder = new StringBuilder($""" @@ -126,13 +124,13 @@ internal string CreateNavXhtml(EpubDocument epubDocument)

      """); - return _builder.ToString(); + return builder.ToString(); } - internal string CreateCssText(EpubDocument epubDocument) + internal static string CreateCssText(EpubDocument epubDocument) { - _builder.Clear(); + var builder = new StringBuilder(); foreach (var cssClass in epubDocument.CssClasses) { - _builder.AppendLine(cssClass.Text); + builder.AppendLine(cssClass.Text); } - return _builder.ToString(); + return builder.ToString(); } internal string CreateOpf(EpubDocument epubDocument) { - _builder.Clear(); - _builder.AppendLine($""" + var builder = new StringBuilder($""" {epubDocument.Title} @@ -196,6 +193,7 @@ internal string CreateOpf(EpubDocument epubDocument) {DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)} -epub-media-overlay-active -epub-media-overlay-unactive + """); var totalTime = TimeSpan.Zero; @@ -205,12 +203,12 @@ internal string CreateOpf(EpubDocument epubDocument) { var time = epubDocument.Chapters[i].Sections[j].GetTotalTime(); totalTime += time; - _builder.AppendLine($""" + builder.AppendLine($""" {time} """); } } - _builder.AppendLine($""" + builder.AppendLine($""" {totalTime} @@ -223,7 +221,7 @@ internal string CreateOpf(EpubDocument epubDocument) { for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) { - _builder.AppendLine($""" + builder.AppendLine($""" """); @@ -232,17 +230,17 @@ internal string CreateOpf(EpubDocument epubDocument) var element = epubDocument.Chapters[i].Sections[j].Elements[k]; if (element is Paragraph para && para.Audio != null) { - _builder.AppendLine(@$" "); + builder.AppendLine(@$" "); } else if (element is Picture pic && File.Exists(pic.PictureFilePath)) { - _builder.AppendLine(@$" "); + builder.AppendLine(@$" "); } } } } - _builder.AppendLine($""" + builder.AppendLine($""" """); @@ -251,23 +249,23 @@ internal string CreateOpf(EpubDocument epubDocument) { for (var j = 0; j < epubDocument.Chapters[i].Sections.Count; j++) { - _builder.AppendLine($""" + builder.AppendLine($""" """); } } - _builder.AppendLine($""" + builder.AppendLine($""" """); - return _builder.ToString(); + return builder.ToString(); } - internal string CreateSectionXhtml(Section section) + + internal static string CreateSectionXhtml(Section section) { - _builder.Clear(); - _builder.AppendLine($""" + var builder = new StringBuilder($""" @@ -281,7 +279,7 @@ internal string CreateSectionXhtml(Section section) { if (section.Elements[i] is Paragraph para) { - _builder.AppendLine($""" + builder.AppendLine($"""

      {para.Text}

      @@ -289,7 +287,7 @@ internal string CreateSectionXhtml(Section section) } else if (section.Elements[i] is Picture pic && File.Exists(pic.PictureFilePath)) { - _builder.AppendLine($""" + builder.AppendLine($"""

      @@ -297,17 +295,16 @@ internal string CreateSectionXhtml(Section section) } } - _builder.AppendLine(""" + builder.AppendLine(""" """); - return _builder.ToString(); + return builder.ToString(); } - internal string CreateSectionSmil(Section section) + internal static string CreateSectionSmil(Section section) { - _builder.Clear(); - _builder.AppendLine($""" + var builder = new StringBuilder($""" @@ -317,7 +314,7 @@ internal string CreateSectionSmil(Section section) { if (section.Elements[i] is Paragraph para && para.Audio != null) { - _builder.AppendLine($""" + builder.AppendLine($""" """); - return _builder.ToString(); + return builder.ToString(); } } From a56c8bbcf2140c041ae3529f883f432c644204a6 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:05:55 +0900 Subject: [PATCH 32/45] =?UTF-8?q?#15=20=E3=83=97=E3=83=AD=E3=82=B8?= =?UTF-8?q?=E3=82=A7=E3=82=AF=E3=83=88=E3=81=AE=E4=BE=9D=E5=AD=98=E9=96=A2?= =?UTF-8?q?=E4=BF=82=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Contracts/Services/IEpubDocumentStoreService.cs | 2 +- Epub/KoeBook.Epub/KoeBook.Epub.csproj | 6 +++++- Epub/KoeBook.Epub/Models/Paragraph.cs | 7 +++++-- .../Services/AnalyzerService _CoverFileBytes.cs | 2 +- .../KoeBook.Epub}/Services/AnalyzerService.cs | 7 +++++-- .../Services/EpubDocumentStoreService.cs | 4 ++-- .../KoeBook.Epub}/Services/EpubGenerateService.cs | 7 ++++--- KoeBook.Core/KoeBook.Core.csproj | 7 ++----- {Epub/KoeBook.Epub => KoeBook.Core}/Models/Audio.cs | 0 KoeBook.Core/Models/ScriptLine.cs | 13 ++++++++----- KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs | 6 +++--- 11 files changed, 36 insertions(+), 25 deletions(-) rename {KoeBook.Core => Epub/KoeBook.Epub}/Contracts/Services/IEpubDocumentStoreService.cs (84%) rename {KoeBook.Core => Epub/KoeBook.Epub}/Services/AnalyzerService _CoverFileBytes.cs (99%) rename {KoeBook.Core => Epub/KoeBook.Epub}/Services/AnalyzerService.cs (94%) rename {KoeBook.Core => Epub/KoeBook.Epub}/Services/EpubDocumentStoreService.cs (93%) rename {KoeBook.Core => Epub/KoeBook.Epub}/Services/EpubGenerateService.cs (85%) rename {Epub/KoeBook.Epub => KoeBook.Core}/Models/Audio.cs (100%) diff --git a/KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs b/Epub/KoeBook.Epub/Contracts/Services/IEpubDocumentStoreService.cs similarity index 84% rename from KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs rename to Epub/KoeBook.Epub/Contracts/Services/IEpubDocumentStoreService.cs index 8ba00b9..187e9a4 100644 --- a/KoeBook.Core/Contracts/Services/IEpubDocumentStoreService.cs +++ b/Epub/KoeBook.Epub/Contracts/Services/IEpubDocumentStoreService.cs @@ -1,6 +1,6 @@ using KoeBook.Epub.Models; -namespace KoeBook.Core.Contracts.Services; +namespace KoeBook.Epub.Contracts.Services; public interface IEpubDocumentStoreService { diff --git a/Epub/KoeBook.Epub/KoeBook.Epub.csproj b/Epub/KoeBook.Epub/KoeBook.Epub.csproj index 8f3071f..2241bce 100644 --- a/Epub/KoeBook.Epub/KoeBook.Epub.csproj +++ b/Epub/KoeBook.Epub/KoeBook.Epub.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -11,5 +11,9 @@ + + + + diff --git a/Epub/KoeBook.Epub/Models/Paragraph.cs b/Epub/KoeBook.Epub/Models/Paragraph.cs index ea1956c..9faffe9 100644 --- a/Epub/KoeBook.Epub/Models/Paragraph.cs +++ b/Epub/KoeBook.Epub/Models/Paragraph.cs @@ -1,7 +1,10 @@ -namespace KoeBook.Epub.Models; +using KoeBook.Core.Models; + +namespace KoeBook.Epub.Models; public sealed class Paragraph : Element { - public Audio? Audio { get; set; } + public ScriptLine? ScriptLine { get; set; } + public Audio? Audio => ScriptLine?.Audio; public string? Text { get; set; } } diff --git a/KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs b/Epub/KoeBook.Epub/Services/AnalyzerService _CoverFileBytes.cs similarity index 99% rename from KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs rename to Epub/KoeBook.Epub/Services/AnalyzerService _CoverFileBytes.cs index 97c30e8..6e0673c 100644 --- a/KoeBook.Core/Services/AnalyzerService _CoverFileBytes.cs +++ b/Epub/KoeBook.Epub/Services/AnalyzerService _CoverFileBytes.cs @@ -5,7 +5,7 @@ using KoeBook.Epub; using KoeBook.Epub.Services; -namespace KoeBook.Core.Services; +namespace KoeBook.Epub.Services; public partial class AnalyzerService { diff --git a/KoeBook.Core/Services/AnalyzerService.cs b/Epub/KoeBook.Epub/Services/AnalyzerService.cs similarity index 94% rename from KoeBook.Core/Services/AnalyzerService.cs rename to Epub/KoeBook.Epub/Services/AnalyzerService.cs index 59b72a6..b8fa5cb 100644 --- a/KoeBook.Core/Services/AnalyzerService.cs +++ b/Epub/KoeBook.Epub/Services/AnalyzerService.cs @@ -1,11 +1,12 @@ using System.Text; using System.Text.RegularExpressions; +using KoeBook.Core; using KoeBook.Core.Contracts.Services; using KoeBook.Core.Models; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -namespace KoeBook.Core.Services; +namespace KoeBook.Epub.Services; public partial class AnalyzerService(IScraperSelectorService scrapingService, IEpubDocumentStoreService epubDocumentStoreService, ILlmAnalyzerService llmAnalyzerService) : IAnalyzerService { @@ -56,7 +57,9 @@ public async ValueTask AnalyzeAsync(BookProperties bookProperties, // ルビを置換 line = ReplaceBaseTextWithRuby(line, rubyDict); - scriptLines.Add(new ScriptLine(paragraph, line, "", "")); + var scriptLine = new ScriptLine(line, "", ""); + paragraph.ScriptLine = scriptLine; + scriptLines.Add(scriptLine); } } } diff --git a/KoeBook.Core/Services/EpubDocumentStoreService.cs b/Epub/KoeBook.Epub/Services/EpubDocumentStoreService.cs similarity index 93% rename from KoeBook.Core/Services/EpubDocumentStoreService.cs rename to Epub/KoeBook.Epub/Services/EpubDocumentStoreService.cs index 4c51415..0ef2725 100644 --- a/KoeBook.Core/Services/EpubDocumentStoreService.cs +++ b/Epub/KoeBook.Epub/Services/EpubDocumentStoreService.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; -using KoeBook.Core.Contracts.Services; +using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -namespace KoeBook.Core.Services; +namespace KoeBook.Epub.Services; public class EpubDocumentStoreService : IEpubDocumentStoreService { diff --git a/KoeBook.Core/Services/EpubGenerateService.cs b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs similarity index 85% rename from KoeBook.Core/Services/EpubGenerateService.cs rename to Epub/KoeBook.Epub/Services/EpubGenerateService.cs index 2810e8d..058795d 100644 --- a/KoeBook.Core/Services/EpubGenerateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs @@ -1,10 +1,11 @@ -using KoeBook.Core.Contracts.Services; +using KoeBook.Core; +using KoeBook.Core.Contracts.Services; using KoeBook.Core.Models; using KoeBook.Epub; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; -namespace KoeBook.Core.Services; +namespace KoeBook.Epub.Services; public class EpubGenerateService(ISoundGenerationService soundGenerationService, IEpubDocumentStoreService epubDocumentStoreService, IEpubCreateService epubCreateService) : IEpubGenerateService { @@ -21,7 +22,7 @@ public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string foreach (var scriptLine in bookScripts.ScriptLines) { - scriptLine.Paragraph.Audio = new Audio(await _soundGenerationService.GenerateLineSoundAsync(scriptLine, bookScripts.Options, cancellationToken).ConfigureAwait(false)); + scriptLine.Audio = new Audio(await _soundGenerationService.GenerateLineSoundAsync(scriptLine, bookScripts.Options, cancellationToken).ConfigureAwait(false)); } if (await _createService.TryCreateEpubAsync(document, tempDirectory, cancellationToken).ConfigureAwait(false)) diff --git a/KoeBook.Core/KoeBook.Core.csproj b/KoeBook.Core/KoeBook.Core.csproj index 92b37e0..3963f59 100644 --- a/KoeBook.Core/KoeBook.Core.csproj +++ b/KoeBook.Core/KoeBook.Core.csproj @@ -1,4 +1,4 @@ - + net8.0 KoeBook.Core @@ -11,10 +11,7 @@ + - - - - diff --git a/Epub/KoeBook.Epub/Models/Audio.cs b/KoeBook.Core/Models/Audio.cs similarity index 100% rename from Epub/KoeBook.Epub/Models/Audio.cs rename to KoeBook.Core/Models/Audio.cs diff --git a/KoeBook.Core/Models/ScriptLine.cs b/KoeBook.Core/Models/ScriptLine.cs index 7f7cbec..3fcb5e4 100644 --- a/KoeBook.Core/Models/ScriptLine.cs +++ b/KoeBook.Core/Models/ScriptLine.cs @@ -5,12 +5,15 @@ namespace KoeBook.Core.Models; ///

      /// 読み上げ1行分 /// -public class ScriptLine(Paragraph paragraph, string text, string character, string style) +//public class ScriptLine(Paragraph paragraph, string text, string character, string style) +//{ +// /// +// /// 読み上げ位置との関連付け +// /// +// public Paragraph Paragraph { get; } = paragraph; +public class ScriptLine(string text, string character, string style) { - /// - /// 読み上げ位置との関連付け - /// - public Paragraph Paragraph { get; } = paragraph; + public Audio? Audio { get; set; } /// /// 読み上げテキスト diff --git a/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs b/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs index 6e6432f..04468bf 100644 --- a/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs +++ b/KoeBook/Services/CoreMocks/AnalyzerServiceMock.cs @@ -40,9 +40,9 @@ public async ValueTask AnalyzeAsync(BookProperties bookProperties, return new(bookProperties, new(characterMapping)) { ScriptLines = [ - new(new Paragraph { Text = "a" }, "読み上げテキスト1", "Hoge", "Angry"), - new(new Paragraph { Text = "b" }, "読み上げテキスト2", "Fuga", "Sad"), - new(new Paragraph { Text = "c" }, "読み上げテキスト3", "Narration", "Narration"), + new("読み上げテキスト1", "Hoge", "Angry"), + new("読み上げテキスト2", "Fuga", "Sad"), + new("読み上げテキスト3", "Narration", "Narration"), ], }; } From 67e01a1254655c0e9e5618552f3172fe41eb161d Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Fri, 15 Mar 2024 17:24:35 +0900 Subject: [PATCH 33/45] =?UTF-8?q?#17-1=20Core=E3=81=AE=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E3=82=92Epub=E3=82=BD?= =?UTF-8?q?=E3=83=AA=E3=83=A5=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub.sln | 51 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/Epub/KoeBook.Epub.sln b/Epub/KoeBook.Epub.sln index dc90218..732b485 100644 --- a/Epub/KoeBook.Epub.sln +++ b/Epub/KoeBook.Epub.sln @@ -1,4 +1,5 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.10.34607.79 MinimumVisualStudioVersion = 10.0.40219.1 @@ -6,20 +7,68 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KoeBook.Epub", "KoeBook.Epu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EpubConsoleApp", "EpubConsoleApp\EpubConsoleApp.csproj", "{DE1F3CE9-4F3B-4428-9293-D18446F333D8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KoeBook.Core", "..\KoeBook.Core\KoeBook.Core.csproj", "{9F11B1EF-8330-400E-A60C-CFC98E827AA7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|arm64 = Debug|arm64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|arm64 = Release|arm64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|arm64.ActiveCfg = Debug|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|arm64.Build.0 = Debug|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|x64.ActiveCfg = Debug|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|x64.Build.0 = Debug|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|x86.ActiveCfg = Debug|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Debug|x86.Build.0 = Debug|Any CPU {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|Any CPU.ActiveCfg = Release|Any CPU {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|Any CPU.Build.0 = Release|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|arm64.ActiveCfg = Release|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|arm64.Build.0 = Release|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|x64.ActiveCfg = Release|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|x64.Build.0 = Release|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|x86.ActiveCfg = Release|Any CPU + {E8E224F2-D1E9-437E-8222-FF2F33DB3FCD}.Release|x86.Build.0 = Release|Any CPU {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|arm64.ActiveCfg = Debug|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|arm64.Build.0 = Debug|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|x64.Build.0 = Debug|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|x86.ActiveCfg = Debug|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Debug|x86.Build.0 = Debug|Any CPU {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|Any CPU.Build.0 = Release|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|arm64.ActiveCfg = Release|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|arm64.Build.0 = Release|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|x64.ActiveCfg = Release|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|x64.Build.0 = Release|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|x86.ActiveCfg = Release|Any CPU + {DE1F3CE9-4F3B-4428-9293-D18446F333D8}.Release|x86.Build.0 = Release|Any CPU + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|arm64.ActiveCfg = Debug|arm64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|arm64.Build.0 = Debug|arm64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|x64.ActiveCfg = Debug|x64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|x64.Build.0 = Debug|x64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|x86.ActiveCfg = Debug|x86 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Debug|x86.Build.0 = Debug|x86 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|Any CPU.Build.0 = Release|Any CPU + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|arm64.ActiveCfg = Release|arm64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|arm64.Build.0 = Release|arm64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|x64.ActiveCfg = Release|x64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|x64.Build.0 = Release|x64 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|x86.ActiveCfg = Release|x86 + {9F11B1EF-8330-400E-A60C-CFC98E827AA7}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From bca75d9e465cc3713143cbc3ba846f184d6fa306 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:55:37 +0900 Subject: [PATCH 34/45] =?UTF-8?q?#17-1=20Epub=E4=BD=9C=E6=88=90=E9=83=A8?= =?UTF-8?q?=E5=88=86=E3=81=AEException=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/EpubGenerateService.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/EpubGenerateService.cs b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs index 058795d..513b89e 100644 --- a/Epub/KoeBook.Epub/Services/EpubGenerateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs @@ -9,10 +9,10 @@ namespace KoeBook.Epub.Services; public class EpubGenerateService(ISoundGenerationService soundGenerationService, IEpubDocumentStoreService epubDocumentStoreService, IEpubCreateService epubCreateService) : IEpubGenerateService { - private ISoundGenerationService _soundGenerationService = soundGenerationService; - private IEpubDocumentStoreService _documentStoreService = epubDocumentStoreService; - private IEpubCreateService _createService = epubCreateService; - + private readonly ISoundGenerationService _soundGenerationService = soundGenerationService; + private readonly IEpubDocumentStoreService _documentStoreService = epubDocumentStoreService; + private readonly IEpubCreateService _createService = epubCreateService; + public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string tempDirectory, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -32,7 +32,8 @@ public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string } else { - throw new EbookException(ExceptionType.EpubCreateError); + EbookException.Throw(ExceptionType.EpubCreateError); + return ""; } } } From 8f2900eeb357170de49ef02ef020171700adab21 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Fri, 15 Mar 2024 18:56:50 +0900 Subject: [PATCH 35/45] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E?= =?UTF-8?q?=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/EpubGenerateService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Epub/KoeBook.Epub/Services/EpubGenerateService.cs b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs index 513b89e..c43dc74 100644 --- a/Epub/KoeBook.Epub/Services/EpubGenerateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs @@ -12,7 +12,7 @@ public class EpubGenerateService(ISoundGenerationService soundGenerationService, private readonly ISoundGenerationService _soundGenerationService = soundGenerationService; private readonly IEpubDocumentStoreService _documentStoreService = epubDocumentStoreService; private readonly IEpubCreateService _createService = epubCreateService; - + public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string tempDirectory, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); From fa3f28ce4097fcdfa32ee427117799b0219823ed Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Fri, 15 Mar 2024 22:05:10 +0900 Subject: [PATCH 36/45] =?UTF-8?q?#17-1=20DI=E8=BF=BD=E5=8A=A0&throw=20exce?= =?UTF-8?q?ption=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/EpubGenerateService.cs | 3 +-- KoeBook/App.xaml.cs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/EpubGenerateService.cs b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs index c43dc74..1543f30 100644 --- a/Epub/KoeBook.Epub/Services/EpubGenerateService.cs +++ b/Epub/KoeBook.Epub/Services/EpubGenerateService.cs @@ -32,8 +32,7 @@ public async ValueTask GenerateEpubAsync(BookScripts bookScripts, string } else { - EbookException.Throw(ExceptionType.EpubCreateError); - return ""; + throw new EbookException(ExceptionType.EpubCreateError); } } } diff --git a/KoeBook/App.xaml.cs b/KoeBook/App.xaml.cs index 3b1e62f..0ed636f 100644 --- a/KoeBook/App.xaml.cs +++ b/KoeBook/App.xaml.cs @@ -103,6 +103,7 @@ public App() .AddSingleton() .AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Views and ViewModels services.AddTransient(); From 29394aadad0ec26a0820d821ec85afef5445e93e Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Fri, 15 Mar 2024 23:34:21 +0900 Subject: [PATCH 37/45] =?UTF-8?q?#17-1=20EpubDocumentException=E3=81=AE?= =?UTF-8?q?=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/EpubDocumentException.cs | 13 --------- .../Services/ScrapingAozoraService.cs | 9 ++++--- .../Services/ScrapingNaroService.cs | 27 ++++++++++--------- 3 files changed, 19 insertions(+), 30 deletions(-) delete mode 100644 Epub/KoeBook.Epub/EpubDocumentException.cs diff --git a/Epub/KoeBook.Epub/EpubDocumentException.cs b/Epub/KoeBook.Epub/EpubDocumentException.cs deleted file mode 100644 index d83bb62..0000000 --- a/Epub/KoeBook.Epub/EpubDocumentException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace KoeBook.Epub -{ - public class EpubDocumentException : Exception - { - public EpubDocumentException(string? message) : base(message) { } - } -} diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs index 203bcaa..e1ad47f 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs @@ -2,6 +2,7 @@ using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Io; +using KoeBook.Core; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; using static KoeBook.Epub.Utility.ScrapingHelper; @@ -29,11 +30,11 @@ public async ValueTask ScrapingAsync(string url, string coverFileP // title の取得 var bookTitle = doc.QuerySelector(".title") - ?? throw new EpubDocumentException($"Failed to get title properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get title properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); // auther の取得 var bookAuther = doc.QuerySelector(".author") - ?? throw new EpubDocumentException($"Failed to get auther properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get auther properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); // EpubDocument の生成 var document = new EpubDocument(TextReplace(bookTitle.InnerHtml), TextReplace(bookAuther.InnerHtml), coverFilePath, id) @@ -111,10 +112,10 @@ public async ValueTask ScrapingAsync(string url, string coverFileP if (midashi != null) { if (midashi.Id == null) - throw new EpubDocumentException("Unecpected structure of HTML File: div tag with class=\"midashi_anchor\", but id=\"midashi___\" exist"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Unecpected structure of HTML File: div tag with class=\"midashi_anchor\", but id=\"midashi___\" exist"); if (!int.TryParse(midashi.Id.Replace("midashi", ""), out var midashiId)) - throw new EpubDocumentException($"Unexpected id of Anchor tag was found: id = {midashi.Id}"); + throw new EbookException(ExceptionType.WebScrapingFailed, $"Unexpected id of Anchor tag was found: id = {midashi.Id}"); if (contentsIds.Contains(midashiId)) { diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index 576c422..9c63cf3 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -3,6 +3,7 @@ using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Io; +using KoeBook.Core; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; using static KoeBook.Epub.Utility.ScrapingHelper; @@ -26,12 +27,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP // title の取得 var bookTitleElement = doc.QuerySelector(".novel_title") - ?? throw new EpubDocumentException($"Failed to get title properly.\nUrl may be not collect"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get title properly.\nUrl may be not collect"); var bookTitle = bookTitleElement.InnerHtml; // auther の取得 var bookAutherElement = doc.QuerySelector(".novel_writername") - ?? throw new EpubDocumentException($"Failed to get auther properly.\nUrl may be not collect"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get auther properly.\nUrl may be not collect"); var bookAuther = string.Empty; if (bookAutherElement.QuerySelector("a") is IHtmlAnchorElement bookAutherAnchorElement) { @@ -54,13 +55,13 @@ public async ValueTask ScrapingAsync(string url, string coverFileP var result = await client.SendAsync(message, ct).ConfigureAwait(false); var test = await result.Content.ReadAsStringAsync(ct).ConfigureAwait(false); if (!result.IsSuccessStatusCode) - throw new EpubDocumentException("Url may be not Correct"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Url may be not Correct"); var content = await result.Content.ReadFromJsonAsync(ct).ConfigureAwait(false); if (content != null) { if (content[1].noveltype == null) - throw new EpubDocumentException("faild to get data by Narou API"); + throw new EbookException(ExceptionType.WebScrapingFailed, "faild to get data by Narou API"); if (content[1].noveltype == 2) { @@ -73,7 +74,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } if (allNum == 0) - throw new EpubDocumentException("faild to get data by Narou API"); + throw new EbookException(ExceptionType.WebScrapingFailed, "faild to get data by Narou API"); } var document = new EpubDocument(bookTitle, bookAuther, coverFilePath, id); @@ -91,7 +92,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP foreach (var sectionWithChapterTitle in SectionWithChapterTitleList) { if (sectionWithChapterTitle == null) - throw new EpubDocumentException("failed to get page"); + throw new EbookException(ExceptionType.WebScrapingFailed, "failed to get page"); if (sectionWithChapterTitle.title != null) { @@ -162,7 +163,7 @@ private static async Task ReadPageAsync(string url, boo } if (sectionTitleElement == null) - throw new EpubDocumentException("Can not find title of page"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Can not find title of page"); var sectionTitle = sectionTitleElement.InnerHtml; @@ -170,12 +171,12 @@ private static async Task ReadPageAsync(string url, boo var main_text = doc.QuerySelector("#novel_honbun") - ?? throw new EpubDocumentException("There is no honbun."); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, "There is no honbun."); foreach (var item in main_text.Children) { if (item is not IHtmlParagraphElement) - throw new EpubDocumentException("Unexpected structure"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); if (item.ChildElementCount == 0) { @@ -192,12 +193,12 @@ private static async Task ReadPageAsync(string url, boo if (item.Children[0] is IHtmlAnchorElement aElement) { if (aElement.ChildElementCount != 1) - throw new EpubDocumentException("Unexpected structure"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); if (aElement.Children[0] is IHtmlImageElement img) { if (img.Source == null) - throw new EpubDocumentException("Unexpected structure"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); // 画像のダウンロード var loader = context.GetService(); @@ -225,7 +226,7 @@ private static async Task ReadPageAsync(string url, boo } } else if (item.Children[0] is not IHtmlBreakRowElement) - throw new EpubDocumentException("Unexpected structure"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); } else { @@ -240,7 +241,7 @@ private static async Task ReadPageAsync(string url, boo } if (!isAllRuby) - throw new EpubDocumentException("Unexpected structure"); + throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); if (!string.IsNullOrWhiteSpace(item.InnerHtml)) { From 7ff9bd36efd4a1af2e4f3658bf129b7227bdb220 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Fri, 15 Mar 2024 23:54:49 +0900 Subject: [PATCH 38/45] =?UTF-8?q?#17-1=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=AE=E6=97=A5?= =?UTF-8?q?=E6=9C=AC=E8=AA=9E=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ScrapingAozoraService.cs | 8 +++--- .../Services/ScrapingNaroService.cs | 26 +++++++++---------- KoeBook.Core/EbookException.cs | 8 +++++- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs index e1ad47f..3516dd9 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs @@ -30,11 +30,11 @@ public async ValueTask ScrapingAsync(string url, string coverFileP // title の取得 var bookTitle = doc.QuerySelector(".title") - ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get title properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"タイトルの取得に失敗しました。\n以下のリンクから正しい小説のリンクを取得してください。\n{GetCardUrl(url)}"); // auther の取得 var bookAuther = doc.QuerySelector(".author") - ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get auther properly.\nYou may be able to get proper URL at {GetCardUrl(url)}"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"著者の取得に失敗しました。\n以下のリンクから正しい小説のリンクを取得してください。\n{GetCardUrl(url)}"); // EpubDocument の生成 var document = new EpubDocument(TextReplace(bookTitle.InnerHtml), TextReplace(bookAuther.InnerHtml), coverFilePath, id) @@ -112,10 +112,10 @@ public async ValueTask ScrapingAsync(string url, string coverFileP if (midashi != null) { if (midashi.Id == null) - throw new EbookException(ExceptionType.WebScrapingFailed, "Unecpected structure of HTML File: div tag with class=\"midashi_anchor\", but id=\"midashi___\" exist"); + throw new EbookException(ExceptionType.WebScrapingFailed, "予期しないHTMLの構造です。\nclass=\"midashi_anchor\"ではなくid=\"midashi___\"が存在します。"); if (!int.TryParse(midashi.Id.Replace("midashi", ""), out var midashiId)) - throw new EbookException(ExceptionType.WebScrapingFailed, $"Unexpected id of Anchor tag was found: id = {midashi.Id}"); + throw new EbookException(ExceptionType.WebScrapingFailed, $"予期しないアンカータグが見つかりました。id = {midashi.Id}"); if (contentsIds.Contains(midashiId)) { diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index 9c63cf3..a7dc353 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -27,12 +27,12 @@ public async ValueTask ScrapingAsync(string url, string coverFileP // title の取得 var bookTitleElement = doc.QuerySelector(".novel_title") - ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get title properly.\nUrl may be not collect"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"タイトルを取得できませんでした"); var bookTitle = bookTitleElement.InnerHtml; // auther の取得 var bookAutherElement = doc.QuerySelector(".novel_writername") - ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"Failed to get auther properly.\nUrl may be not collect"); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"著者を取得できませんでした"); var bookAuther = string.Empty; if (bookAutherElement.QuerySelector("a") is IHtmlAnchorElement bookAutherAnchorElement) { @@ -55,13 +55,13 @@ public async ValueTask ScrapingAsync(string url, string coverFileP var result = await client.SendAsync(message, ct).ConfigureAwait(false); var test = await result.Content.ReadAsStringAsync(ct).ConfigureAwait(false); if (!result.IsSuccessStatusCode) - throw new EbookException(ExceptionType.WebScrapingFailed, "Url may be not Correct"); + throw new EbookException(ExceptionType.WebScrapingFailed, "URLが正しくありません"); var content = await result.Content.ReadFromJsonAsync(ct).ConfigureAwait(false); if (content != null) { if (content[1].noveltype == null) - throw new EbookException(ExceptionType.WebScrapingFailed, "faild to get data by Narou API"); + throw new EbookException(ExceptionType.NarouApiFailed); if (content[1].noveltype == 2) { @@ -74,7 +74,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } if (allNum == 0) - throw new EbookException(ExceptionType.WebScrapingFailed, "faild to get data by Narou API"); + throw new EbookException(ExceptionType.NarouApiFailed); } var document = new EpubDocument(bookTitle, bookAuther, coverFilePath, id); @@ -92,7 +92,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP foreach (var sectionWithChapterTitle in SectionWithChapterTitleList) { if (sectionWithChapterTitle == null) - throw new EbookException(ExceptionType.WebScrapingFailed, "failed to get page"); + throw new EbookException(ExceptionType.WebScrapingFailed, "ページの取得に失敗しました"); if (sectionWithChapterTitle.title != null) { @@ -163,7 +163,7 @@ private static async Task ReadPageAsync(string url, boo } if (sectionTitleElement == null) - throw new EbookException(ExceptionType.WebScrapingFailed, "Can not find title of page"); + throw new EbookException(ExceptionType.WebScrapingFailed, "ページのタイトルが見つかりません"); var sectionTitle = sectionTitleElement.InnerHtml; @@ -171,12 +171,12 @@ private static async Task ReadPageAsync(string url, boo var main_text = doc.QuerySelector("#novel_honbun") - ?? throw new EbookException(ExceptionType.WebScrapingFailed, "There is no honbun."); + ?? throw new EbookException(ExceptionType.WebScrapingFailed, "本文がありません"); foreach (var item in main_text.Children) { if (item is not IHtmlParagraphElement) - throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); + throw new EbookException(ExceptionType.UnexpectedStructure); if (item.ChildElementCount == 0) { @@ -193,12 +193,12 @@ private static async Task ReadPageAsync(string url, boo if (item.Children[0] is IHtmlAnchorElement aElement) { if (aElement.ChildElementCount != 1) - throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); + throw new EbookException(ExceptionType.UnexpectedStructure); if (aElement.Children[0] is IHtmlImageElement img) { if (img.Source == null) - throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); + throw new EbookException(ExceptionType.UnexpectedStructure); // 画像のダウンロード var loader = context.GetService(); @@ -226,7 +226,7 @@ private static async Task ReadPageAsync(string url, boo } } else if (item.Children[0] is not IHtmlBreakRowElement) - throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); + throw new EbookException(ExceptionType.UnexpectedStructure); } else { @@ -241,7 +241,7 @@ private static async Task ReadPageAsync(string url, boo } if (!isAllRuby) - throw new EbookException(ExceptionType.WebScrapingFailed, "Unexpected structure"); + throw new EbookException(ExceptionType.UnexpectedStructure); if (!string.IsNullOrWhiteSpace(item.InnerHtml)) { diff --git a/KoeBook.Core/EbookException.cs b/KoeBook.Core/EbookException.cs index e8590e8..474eb5d 100644 --- a/KoeBook.Core/EbookException.cs +++ b/KoeBook.Core/EbookException.cs @@ -45,5 +45,11 @@ public enum ExceptionType Gpt4TalkerAndStyleSettingFailed, [EnumMember(Value = "webページの解析に失敗しました")] - WebScrapingFailed + WebScrapingFailed, + + [EnumMember(Value = "小説情報の取得に失敗しました")] + NarouApiFailed, + + [EnumMember(Value = "予期しない構造です")] + UnexpectedStructure } From 3adc1d44d2a8219d7114efa123422eaa08234105 Mon Sep 17 00:00:00 2001 From: aiueo-1234 <130837816+aiueo-1234@users.noreply.github.com> Date: Sat, 16 Mar 2024 09:03:07 +0900 Subject: [PATCH 39/45] =?UTF-8?q?#17-1=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=83=A1=E3=83=83=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=AE=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/ScrapingNaroService.cs | 2 +- KoeBook.Core/EbookException.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index a7dc353..c04694f 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -55,7 +55,7 @@ public async ValueTask ScrapingAsync(string url, string coverFileP var result = await client.SendAsync(message, ct).ConfigureAwait(false); var test = await result.Content.ReadAsStringAsync(ct).ConfigureAwait(false); if (!result.IsSuccessStatusCode) - throw new EbookException(ExceptionType.WebScrapingFailed, "URLが正しくありません"); + throw new EbookException(ExceptionType.HttpResponseError, $"URLが正しいかどうかやインターネットに正常に接続されているかどうかを確認してください。\nステータスコード: {result.StatusCode}\nメッセージ:{test}"); var content = await result.Content.ReadFromJsonAsync(ct).ConfigureAwait(false); if (content != null) diff --git a/KoeBook.Core/EbookException.cs b/KoeBook.Core/EbookException.cs index 474eb5d..9b5b4e1 100644 --- a/KoeBook.Core/EbookException.cs +++ b/KoeBook.Core/EbookException.cs @@ -50,6 +50,9 @@ public enum ExceptionType [EnumMember(Value = "小説情報の取得に失敗しました")] NarouApiFailed, - [EnumMember(Value = "予期しない構造です")] - UnexpectedStructure + [EnumMember(Value = "Webページが予期しない構造です")] + UnexpectedStructure, + + [EnumMember(Value = "HTTPリクエストエラー")] + HttpResponseError } From d9e347db35cc8ea3ac310c3da11db808adf5f7cb Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Fri, 15 Mar 2024 23:32:36 +0900 Subject: [PATCH 40/45] =?UTF-8?q?#19=20=E3=82=B9=E3=82=AF=E3=83=AC?= =?UTF-8?q?=E3=82=A4=E3=83=94=E3=83=B3=E3=82=B0=E7=94=A8=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/IScrapingClientService.cs | 10 +++++++++ .../Services/ScrapingAozoraService.cs | 5 ++++- .../Services/ScrapingClientService.cs | 22 +++++++++++++++++++ .../Services/ScrapingNaroService.cs | 7 ++++-- KoeBook/App.xaml.cs | 9 +++++++- 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs create mode 100644 Epub/KoeBook.Epub/Services/ScrapingClientService.cs diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs new file mode 100644 index 0000000..44cff57 --- /dev/null +++ b/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs @@ -0,0 +1,10 @@ +namespace KoeBook.Epub.Contracts.Services; + +public interface IScrapingClientService +{ + /// + /// スクレイピングでGETする用 + /// APIは不要 + /// + ValueTask GetAsync(string url, CancellationToken ct); +} diff --git a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs index 3516dd9..7ea75c7 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingAozoraService.cs @@ -5,13 +5,16 @@ using KoeBook.Core; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; +using Microsoft.Extensions.DependencyInjection; using static KoeBook.Epub.Utility.ScrapingHelper; namespace KoeBook.Epub.Services { - public partial class ScrapingAozoraService : IScrapingService + public partial class ScrapingAozoraService([FromKeyedServices(nameof(ScrapingAozoraService))] IScrapingClientService scrapingClientService) : IScrapingService { + private readonly IScrapingClientService _scrapingClientService = scrapingClientService; + public bool IsMatchSite(Uri uri) { return uri.Host == "www.aozora.gr.jp"; diff --git a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs new file mode 100644 index 0000000..9a0ab20 --- /dev/null +++ b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs @@ -0,0 +1,22 @@ +using KoeBook.Epub.Contracts.Services; + +namespace KoeBook.Epub.Services; + +public sealed class ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider timeProvider) : IScrapingClientService, IDisposable +{ + private readonly IHttpClientFactory _httpClientFactory = httpClientFactory; + private readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(10), timeProvider); + + public void Dispose() + { + _periodicTimer.Dispose(); + } + + public async ValueTask GetAsync(string url, CancellationToken ct) + { + await _periodicTimer.WaitForNextTickAsync(ct).ConfigureAwait(false); + + var httpClient = _httpClientFactory.CreateClient(); + return await httpClient.GetAsync(url, ct).ConfigureAwait(false); + } +} diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index c04694f..c631a3d 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -6,13 +6,15 @@ using KoeBook.Core; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; +using Microsoft.Extensions.DependencyInjection; using static KoeBook.Epub.Utility.ScrapingHelper; namespace KoeBook.Epub.Services { - public partial class ScrapingNaroService(IHttpClientFactory httpClientFactory) : IScrapingService + public partial class ScrapingNaroService(IHttpClientFactory httpClientFactory, [FromKeyedServices(nameof(ScrapingNaroService))] IScrapingClientService scrapingClientService) : IScrapingService { private readonly IHttpClientFactory _httpCliantFactory = httpClientFactory; + private readonly IScrapingClientService _scrapingClientService = scrapingClientService; public bool IsMatchSite(Uri uri) { @@ -22,6 +24,7 @@ public bool IsMatchSite(Uri uri) public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); + using var context = BrowsingContext.New(config); var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); @@ -133,7 +136,7 @@ public record BookInfo(int? allcount, int? noveltype, int? general_all_no); private record SectionWithChapterTitle(string? title, Section section); - private static async Task ReadPageAsync(string url, bool isRensai, string imageDirectory, CancellationToken ct) + private async Task ReadPageAsync(string url, bool isRensai, string imageDirectory, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); using var context = BrowsingContext.New(config); diff --git a/KoeBook/App.xaml.cs b/KoeBook/App.xaml.cs index 0ed636f..10b0523 100644 --- a/KoeBook/App.xaml.cs +++ b/KoeBook/App.xaml.cs @@ -60,6 +60,9 @@ public App() .UseContentRoot(AppContext.BaseDirectory) .ConfigureServices((context, services) => { + // System + services.AddSingleton(TimeProvider.System); + // Default Activation Handler services.AddTransient, DefaultActivationHandler>(); @@ -99,7 +102,11 @@ public App() services.AddSingleton(); services.AddSingleton(); - services.AddSingleton() + // Epub Services + services + .AddKeyedSingleton(nameof(ScrapingAozoraService)) + .AddKeyedSingleton(nameof(ScrapingNaroService)) + .AddSingleton() .AddSingleton() .AddSingleton(); services.AddSingleton(); From 0441c1dbed8029e24e9991872b5967fd34c2fa61 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Fri, 15 Mar 2024 23:34:08 +0900 Subject: [PATCH 41/45] =?UTF-8?q?#19=20=E4=B8=8D=E8=A6=81=E3=81=AA?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/ScrapingNaroService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index c631a3d..70dd1e4 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -24,7 +24,6 @@ public bool IsMatchSite(Uri uri) public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); - using var context = BrowsingContext.New(config); var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); @@ -136,7 +135,7 @@ public record BookInfo(int? allcount, int? noveltype, int? general_all_no); private record SectionWithChapterTitle(string? title, Section section); - private async Task ReadPageAsync(string url, bool isRensai, string imageDirectory, CancellationToken ct) + private static async Task ReadPageAsync(string url, bool isRensai, string imageDirectory, CancellationToken ct) { var config = Configuration.Default.WithDefaultLoader(); using var context = BrowsingContext.New(config); From 119de8e163bc1b8c1b85b4ec1a3cd1748bfc5ea0 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 16 Mar 2024 01:19:13 +0900 Subject: [PATCH 42/45] =?UTF-8?q?#19=20Queue-Consumer=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/IScrapingClientService.cs | 14 ++- .../Services/ScrapingClientService.cs | 97 +++++++++++++++++-- 2 files changed, 100 insertions(+), 11 deletions(-) diff --git a/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs b/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs index 44cff57..c2a0b81 100644 --- a/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs +++ b/Epub/KoeBook.Epub/Contracts/Services/IScrapingClientService.cs @@ -1,10 +1,18 @@ -namespace KoeBook.Epub.Contracts.Services; +using System.Net.Http.Headers; + +namespace KoeBook.Epub.Contracts.Services; public interface IScrapingClientService { /// /// スクレイピングでGETする用 - /// APIは不要 + /// APIを叩く際は不要 + /// + Task GetAsStringAsync(string url, CancellationToken ct); + + /// + /// スクレイピングでGETする用 + /// APIを叩く際は不要 /// - ValueTask GetAsync(string url, CancellationToken ct); + Task GetAsStreamAsync(string url, Stream destination, CancellationToken ct); } diff --git a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs index 9a0ab20..1367980 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs @@ -1,22 +1,103 @@ -using KoeBook.Epub.Contracts.Services; +using System.Net.Http.Headers; +using KoeBook.Epub.Contracts.Services; namespace KoeBook.Epub.Services; -public sealed class ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider timeProvider) : IScrapingClientService, IDisposable +public sealed class ScrapingClientService : IScrapingClientService, IDisposable { - private readonly IHttpClientFactory _httpClientFactory = httpClientFactory; - private readonly PeriodicTimer _periodicTimer = new(TimeSpan.FromSeconds(10), timeProvider); + private readonly IHttpClientFactory _httpClientFactory; + private readonly PeriodicTimer _periodicTimer; + private readonly Queue> _actionQueue = []; + private bool _workerActivated; + + public ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider timeProvider) + { + _httpClientFactory = httpClientFactory; + _periodicTimer = new(TimeSpan.FromSeconds(10), timeProvider); + + Worker(); + } public void Dispose() { _periodicTimer.Dispose(); } - public async ValueTask GetAsync(string url, CancellationToken ct) + private async void Worker() + { + lock (_actionQueue) + { + _workerActivated = true; + } + + while (await _periodicTimer.WaitForNextTickAsync().ConfigureAwait(false) && _actionQueue.Count > 0) + { + if (_actionQueue.TryDequeue(out var action)) + { + await action(_httpClientFactory.CreateClient()).ConfigureAwait(false); + } + } + + lock (_actionQueue) + { + _workerActivated = false; + } + } + + public Task GetAsStringAsync(string url, CancellationToken ct) { - await _periodicTimer.WaitForNextTickAsync(ct).ConfigureAwait(false); + var taskCompletion = new TaskCompletionSource(); + _actionQueue.Enqueue(async httpClient => + { + if (ct.IsCancellationRequested) + taskCompletion.SetCanceled(ct); + + try + { + var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); + taskCompletion.SetResult(await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false)); + } + catch (Exception ex) + { + taskCompletion.SetException(ex); + } + }); + + lock (_actionQueue) + { + if (!_workerActivated) + Worker(); + } + + return taskCompletion.Task; + } + + public Task GetAsStreamAsync(string url, Stream destination, CancellationToken ct) + { + var taskCompletion = new TaskCompletionSource(); + _actionQueue.Enqueue(async httpClient => + { + if (ct.IsCancellationRequested) + taskCompletion.SetCanceled(ct); + + try + { + var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); + await response.Content.CopyToAsync(destination, ct).ConfigureAwait(false); + taskCompletion.SetResult(response.Content.Headers.ContentDisposition); + } + catch (Exception ex) + { + taskCompletion.SetException(ex); + } + }); + + lock (_actionQueue) + { + if (!_workerActivated) + Worker(); + } - var httpClient = _httpClientFactory.CreateClient(); - return await httpClient.GetAsync(url, ct).ConfigureAwait(false); + return taskCompletion.Task; } } From d63ea333f0c9952348b206bd717538ba82c58534 Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 16 Mar 2024 13:14:32 +0900 Subject: [PATCH 43/45] =?UTF-8?q?#19=20lock=E6=96=87=E3=81=AE=E7=AE=87?= =?UTF-8?q?=E6=89=80=E3=82=92=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/ScrapingClientService.cs | 137 ++++++++++-------- 1 file changed, 76 insertions(+), 61 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs index 1367980..4a6ac00 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs @@ -7,7 +7,7 @@ public sealed class ScrapingClientService : IScrapingClientService, IDisposable { private readonly IHttpClientFactory _httpClientFactory; private readonly PeriodicTimer _periodicTimer; - private readonly Queue> _actionQueue = []; + private readonly Queue> _actionQueue = []; private bool _workerActivated; public ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider timeProvider) @@ -18,86 +18,101 @@ public ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider Worker(); } - public void Dispose() + public Task GetAsStringAsync(string url, CancellationToken ct) { - _periodicTimer.Dispose(); - } + var taskCompletion = new TaskCompletionSource(); - private async void Worker() - { lock (_actionQueue) - { - _workerActivated = true; - } - - while (await _periodicTimer.WaitForNextTickAsync().ConfigureAwait(false) && _actionQueue.Count > 0) - { - if (_actionQueue.TryDequeue(out var action)) + _actionQueue.Enqueue(async httpClient => { - await action(_httpClientFactory.CreateClient()).ConfigureAwait(false); - } - } + if (ct.IsCancellationRequested) + taskCompletion.SetCanceled(ct); + + try + { + var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); + taskCompletion.SetResult(await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false)); + } + catch (Exception ex) + { + taskCompletion.SetException(ex); + } + }); + + EnsureWorkerActivated(); - lock (_actionQueue) - { - _workerActivated = false; - } + return taskCompletion.Task; } - public Task GetAsStringAsync(string url, CancellationToken ct) + public Task GetAsStreamAsync(string url, Stream destination, CancellationToken ct) { - var taskCompletion = new TaskCompletionSource(); - _actionQueue.Enqueue(async httpClient => - { - if (ct.IsCancellationRequested) - taskCompletion.SetCanceled(ct); - - try - { - var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); - taskCompletion.SetResult(await response.Content.ReadAsStringAsync(ct).ConfigureAwait(false)); - } - catch (Exception ex) - { - taskCompletion.SetException(ex); - } - }); + var taskCompletion = new TaskCompletionSource(); lock (_actionQueue) - { - if (!_workerActivated) - Worker(); - } + _actionQueue.Enqueue(async httpClient => + { + if (ct.IsCancellationRequested) + taskCompletion.SetCanceled(ct); + + try + { + var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); + await response.Content.CopyToAsync(destination, ct).ConfigureAwait(false); + taskCompletion.SetResult(response.Content.Headers.ContentDisposition); + } + catch (Exception ex) + { + taskCompletion.SetException(ex); + } + }); + + EnsureWorkerActivated(); return taskCompletion.Task; } - public Task GetAsStreamAsync(string url, Stream destination, CancellationToken ct) + /// + /// が起動していない場合は起動します + /// + private void EnsureWorkerActivated() { - var taskCompletion = new TaskCompletionSource(); - _actionQueue.Enqueue(async httpClient => - { - if (ct.IsCancellationRequested) - taskCompletion.SetCanceled(ct); + bool activateWorker; + lock (_actionQueue) activateWorker = !_workerActivated; - try - { - var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); - await response.Content.CopyToAsync(destination, ct).ConfigureAwait(false); - taskCompletion.SetResult(response.Content.Headers.ContentDisposition); - } - catch (Exception ex) - { - taskCompletion.SetException(ex); - } - }); + if (activateWorker) + Worker(); + } + /// + /// のConsumer + /// 別スレッドでループさせるためにvoid + /// + private async void Worker() + { lock (_actionQueue) + _workerActivated = true; + + try { - if (!_workerActivated) - Worker(); + while (await _periodicTimer.WaitForNextTickAsync().ConfigureAwait(false) && _actionQueue.Count > 0) + { + Func? action; + lock (_actionQueue) + if (!_actionQueue.TryDequeue(out action)) + continue; + + await action(_httpClientFactory.CreateClient()).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); + } } + finally + { + lock (_actionQueue) + _workerActivated = false; + } + } - return taskCompletion.Task; + public void Dispose() + { + _periodicTimer.Dispose(); } } From e93ac8e5962151c6e84bdccf50d4e1cab59a577f Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 16 Mar 2024 13:15:11 +0900 Subject: [PATCH 44/45] =?UTF-8?q?#19=20=E3=82=B3=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=83=A9=E3=82=AF=E3=82=BF=E3=81=A7=E3=81=AEWorker?= =?UTF-8?q?=E3=81=AE=E8=B5=B7=E5=8B=95=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Services/ScrapingClientService.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs index 4a6ac00..164898e 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingClientService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingClientService.cs @@ -14,8 +14,6 @@ public ScrapingClientService(IHttpClientFactory httpClientFactory, TimeProvider { _httpClientFactory = httpClientFactory; _periodicTimer = new(TimeSpan.FromSeconds(10), timeProvider); - - Worker(); } public Task GetAsStringAsync(string url, CancellationToken ct) From f1579e5ca3749dd2d99734b163c050a2ff4ed54d Mon Sep 17 00:00:00 2001 From: miyaji255 <84168445+miyaji255@users.noreply.github.com> Date: Sat, 16 Mar 2024 17:37:16 +0900 Subject: [PATCH 45/45] =?UTF-8?q?#1-2=20=E3=81=AA=E3=82=8D=E3=81=86API?= =?UTF-8?q?=E3=82=92=E5=8F=A9=E3=81=8F=E9=83=A8=E5=88=86=E3=81=AE=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Epub/KoeBook.Epub/Models/Chapter.cs | 2 +- .../Services/ScrapingNaroService.cs | 156 +++++++++++------- Epub/KoeBook.Epub/Utility/JsonOptions.cs | 10 ++ KoeBook.Core/EbookException.cs | 8 +- 4 files changed, 115 insertions(+), 61 deletions(-) create mode 100644 Epub/KoeBook.Epub/Utility/JsonOptions.cs diff --git a/Epub/KoeBook.Epub/Models/Chapter.cs b/Epub/KoeBook.Epub/Models/Chapter.cs index 8c2e72b..18dcc51 100644 --- a/Epub/KoeBook.Epub/Models/Chapter.cs +++ b/Epub/KoeBook.Epub/Models/Chapter.cs @@ -2,6 +2,6 @@ public class Chapter { - public List
      Sections = []; + public List
      Sections { get; } = []; public string? Title { get; set; } } diff --git a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs index 70dd1e4..fb50c6d 100644 --- a/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs +++ b/Epub/KoeBook.Epub/Services/ScrapingNaroService.cs @@ -1,4 +1,7 @@ -using System.Net.Http.Json; +using System; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; using AngleSharp; using AngleSharp.Dom; using AngleSharp.Html.Dom; @@ -6,6 +9,7 @@ using KoeBook.Core; using KoeBook.Epub.Contracts.Services; using KoeBook.Epub.Models; +using KoeBook.Epub.Utility; using Microsoft.Extensions.DependencyInjection; using static KoeBook.Epub.Utility.ScrapingHelper; @@ -23,6 +27,7 @@ public bool IsMatchSite(Uri uri) public async ValueTask ScrapingAsync(string url, string coverFilePath, string imageDirectory, Guid id, CancellationToken ct) { + var ncode = GetNcode(url); var config = Configuration.Default.WithDefaultLoader(); using var context = BrowsingContext.New(config); var doc = await context.OpenAsync(url, ct).ConfigureAwait(false); @@ -35,63 +40,34 @@ public async ValueTask ScrapingAsync(string url, string coverFileP // auther の取得 var bookAutherElement = doc.QuerySelector(".novel_writername") ?? throw new EbookException(ExceptionType.WebScrapingFailed, $"著者を取得できませんでした"); - var bookAuther = string.Empty; - if (bookAutherElement.QuerySelector("a") is IHtmlAnchorElement bookAutherAnchorElement) - { - bookAuther = bookAutherAnchorElement.InnerHtml; - } - else - { - bookAuther = bookAutherElement.InnerHtml.Replace("作者:", ""); - } - - bool isRensai = true; - int allNum = 0; + var bookAuther = bookAutherElement.QuerySelector("a") is IHtmlAnchorElement bookAutherTag + ? bookAutherTag.InnerHtml + : bookAutherElement.InnerHtml.Replace("作者:", ""); - var apiUrl = $"https://api.syosetu.com/novelapi/api/?of=ga-nt&ncode={UrlToNcode().Replace(url, "$1")}&out=json"; + var novelInfo = await GetNovelInfoAsync(ncode, ct).ConfigureAwait(false); - // APIを利用して、noveltype : 連載(1)か短編(2)か、general_all_no : 全掲載部分数 - var message = new HttpRequestMessage(System.Net.Http.HttpMethod.Get, apiUrl); - message.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"); - var client = _httpCliantFactory.CreateClient(); - var result = await client.SendAsync(message, ct).ConfigureAwait(false); - var test = await result.Content.ReadAsStringAsync(ct).ConfigureAwait(false); - if (!result.IsSuccessStatusCode) - throw new EbookException(ExceptionType.HttpResponseError, $"URLが正しいかどうかやインターネットに正常に接続されているかどうかを確認してください。\nステータスコード: {result.StatusCode}\nメッセージ:{test}"); - - var content = await result.Content.ReadFromJsonAsync(ct).ConfigureAwait(false); - if (content != null) + var document = new EpubDocument(bookTitle, bookAuther, coverFilePath, id); + if (novelInfo.IsShort) // 短編の時 { - if (content[1].noveltype == null) - throw new EbookException(ExceptionType.NarouApiFailed); - - if (content[1].noveltype == 2) - { - isRensai = false; - } - - if (content[1].general_all_no != null) + var load = await ReadPageAsync(url, false, imageDirectory, ct).ConfigureAwait(false); + if (load != null) { - allNum = (int)content[1].general_all_no!; + document.Chapters.Add(new Chapter() { Title = null }); + document.Chapters[^1].Sections.Add(load.section); } - - if (allNum == 0) - throw new EbookException(ExceptionType.NarouApiFailed); } - - var document = new EpubDocument(bookTitle, bookAuther, coverFilePath, id); - if (isRensai) // 連載の時 + else // 連載の時 { - List SectionWithChapterTitleList = new List(); - for (int i = 1; i <= allNum; i++) + var sectionWithChapterTitleList = new List(); + for (int i = 1; i <= novelInfo.GeneralAllNo; i++) { await Task.Delay(1500, ct); var pageUrl = Path.Combine(url, i.ToString()); - var load = await ReadPageAsync(pageUrl, isRensai, imageDirectory, ct).ConfigureAwait(false); - SectionWithChapterTitleList.Add(load); + var load = await ReadPageAsync(pageUrl, true, imageDirectory, ct).ConfigureAwait(false); + sectionWithChapterTitleList.Add(load); } string? chapterTitle = null; - foreach (var sectionWithChapterTitle in SectionWithChapterTitleList) + foreach (var sectionWithChapterTitle in sectionWithChapterTitleList) { if (sectionWithChapterTitle == null) throw new EbookException(ExceptionType.WebScrapingFailed, "ページの取得に失敗しました"); @@ -111,27 +87,89 @@ public async ValueTask ScrapingAsync(string url, string coverFileP } else { - if (document.Chapters.Count == 0) - { - document.Chapters.Add(new Chapter()); - } + document.EnsureChapter(); document.Chapters[^1].Sections.Add(sectionWithChapterTitle.section); } } } - else // 短編の時 + return document; + } + + private async ValueTask GetNovelInfoAsync(string ncode, CancellationToken ct) + { + // APIを利用して、noveltype : 連載(1)か短編(2)か、general_all_no : 全掲載部分数 + var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Get, $"https://api.syosetu.com/novelapi/api/?of=ga-nt-n&out=json&ncode={ncode}"); + request.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"); + + var client = _httpCliantFactory.CreateClient(); + var response = await client.SendAsync(request, ct).ConfigureAwait(false); + + if (!response.IsSuccessStatusCode) + throw new EbookException(ExceptionType.HttpResponseError, $"URLが正しいかどうかやインターネットに正常に接続されているかどうかを確認してください。\nステータスコード: {response.StatusCode}"); + + var result = response.Content.ReadFromJsonAsAsyncEnumerable(ct); + + await using var enumerator = result.GetAsyncEnumerator(ct); + + if (!await enumerator.MoveNextAsync().ConfigureAwait(false)) + throw new EbookException(ExceptionType.NarouApiFailed); + var dataInfo = enumerator.Current.Deserialize(JsonOptions.Web); + if (dataInfo is not { Allcount: 1 }) + throw new EbookException(ExceptionType.NarouApiFailed); + + if (!await enumerator.MoveNextAsync().ConfigureAwait(false)) + throw new EbookException(ExceptionType.NarouApiFailed); + + var novelInfo = enumerator.Current.Deserialize(JsonOptions.Web); + if (novelInfo is null || !novelInfo.Ncode.Equals(ncode, StringComparison.OrdinalIgnoreCase)) + throw new EbookException(ExceptionType.NarouApiFailed); + + if (await enumerator.MoveNextAsync()!.ConfigureAwait(false)) + throw new EbookException(ExceptionType.NarouApiFailed); + + return novelInfo; + } + + private static string GetNcode(string url) + { + var uri = new Uri(url); + if (uri.GetLeftPart(UriPartial.Authority) != "https://ncode.syosetu.com") + throw new EbookException(ExceptionType.InvalidUrl); + + switch (uri.Segments) { - var load = await ReadPageAsync(url, isRensai, imageDirectory, ct).ConfigureAwait(false); - if (load != null) - { - document.Chapters.Add(new Chapter() { Title = null }); - document.Chapters[^1].Sections.Add(load.section); - } + case [var ncode] when IsAscii(ncode): // https://ncode.syosetu.com/n0000a/ のとき + return ncode; + case [var ncode, var num] when IsAscii(ncode) && num.All(char.IsAsciiDigit): // https://ncode.syosetu.com/n0000a/12 のとき + return ncode; + case ["novelview", "infotop", "ncode", var ncode] when IsAscii(ncode): // https://ncode.syosetu.com/novelview/infotop/ncode/n0000a/ のとき + return ncode; } - return document; + + throw new EbookException(ExceptionType.InvalidUrl); + + static bool IsAscii(string str) + => str.All(char.IsAscii); } - public record BookInfo(int? allcount, int? noveltype, int? general_all_no); + private record NaroResponseFirstElement(int Allcount); + + /// + /// 小説情報 + /// + /// ncode + /// 1: 連載, 2: 短編 + /// 話数 (短編の場合は1) + private record NovelInfo( + [property: JsonRequired] string Ncode, + [property: JsonRequired] int Noveltype, + [property: JsonPropertyName("general_all_no"), JsonRequired] int GeneralAllNo) + { + /// + /// 短編であるときtrue + /// + public bool IsShort => Noveltype == 2; + } private record SectionWithChapterTitle(string? title, Section section); diff --git a/Epub/KoeBook.Epub/Utility/JsonOptions.cs b/Epub/KoeBook.Epub/Utility/JsonOptions.cs new file mode 100644 index 0000000..05e9530 --- /dev/null +++ b/Epub/KoeBook.Epub/Utility/JsonOptions.cs @@ -0,0 +1,10 @@ +using System.Text.Json; + +namespace KoeBook.Epub.Utility; + +internal static class JsonOptions +{ + public static JsonSerializerOptions Default => JsonSerializerOptions.Default; + + public static JsonSerializerOptions Web { get; } = new JsonSerializerOptions(JsonSerializerDefaults.Web); +} diff --git a/KoeBook.Core/EbookException.cs b/KoeBook.Core/EbookException.cs index 9b5b4e1..85ad230 100644 --- a/KoeBook.Core/EbookException.cs +++ b/KoeBook.Core/EbookException.cs @@ -54,5 +54,11 @@ public enum ExceptionType UnexpectedStructure, [EnumMember(Value = "HTTPリクエストエラー")] - HttpResponseError + HttpResponseError, + + /// + /// 無効なURLです + /// + [EnumMember(Value = "無効なURLです")] + InvalidUrl, }