Add project files.

This commit is contained in:
2025-04-19 20:47:58 +03:00
parent 9e14d137ae
commit bfdcdb1f3b
66 changed files with 2394 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
using aeqw89.DataKeys;
using HtmlAgilityPack;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli {
/// <summary>
/// <para>
/// A collection of specific useful methods and constants that facilitate the use of the application; allows other parts of the application to depend on architecture-specific arbitrary choices without compromising the Single-Responsibility principle or increasing redundant code.
/// </para>
/// </summary>
partial interface IArchitecture {
/// <summary>
/// Gets the metadata associated with a <see cref="TextResource"/>
/// </summary>
/// <param name="web">The web client to use when downloading <see cref="WebResource"/>s</param>
/// <param name="pieceKey">The key of the <see cref="TextResource"/> stored in the <paramref name="sdd"/></param>
/// <param name="sdd">The <see cref="SharedDataDictionary"/> to be used to retrieve information</param>
/// <param name="logger">Optional logger for logging debug information</param>
/// <returns>A <see cref="DownloadContext{T}"/> object with the required information to perform the download</returns>
public DownloadContext<IDocumentMetaData>? GetMeta(HtmlWeb web, DataKey<TextResource> pieceKey, SharedDataDictionary sdd, ILogger? logger = null);
/// <summary>
/// Gets the <see cref="DownloadContext{T}"/> of the text record associated with <see cref="TextResource"/>
/// </summary>
/// <param name="web">The web client to use when downloading <see cref="WebResource"/>s</param>
/// <param name="pieceKey">The key of the <see cref="TextResource"/> stored in the <paramref name="sdd"/></param>
/// <param name="sdd">The <see cref="SharedDataDictionary"/> to be used to retrieve information</param>
/// <param name="metadata">Optional book metadata to include with the final text record</param>
/// <param name="logger">Optional logger for logging debug information</param>
/// <returns>A <see cref="DownloadContext{T}"/> object with the required information to perform the download</returns>
public DownloadContext<IDocument>? GetTextRecord(HtmlWeb web, DataKey<TextResource> pieceKey, SharedDataDictionary sdd, IDocumentMetaData? metadata = null, ILogger? logger = null);
/// <summary>
/// The <see cref="DataKey{IDocumentMetaData}"/> to use when looking for the chapter metadata
/// </summary>
public DataKey<IDocumentMetaData> ChapterKey { get; set; }
/// <summary>
/// The <see cref="DataKey{IDocumentMetaData}"/> to use when looking for the book metadata
/// </summary>
public DataKey<IDocumentMetaData> BookKey { get; set; }
/// <summary>
/// The default architecture
/// </summary>
public static IArchitecture Default => new MainArchitecture();
}
}
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.1" />
<PackageReference Include="Spectre.Console" Version="0.49.2-preview.0.70" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Beam.Dynamic\Beam.Dynamic.csproj" />
<ProjectReference Include="..\Beam.Exports\Beam.Exports.csproj" />
<ProjectReference Include="..\Beam\Beam.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="aeqw89.DataKeys">
<HintPath>..\..\aeqw89.DataKeys\aeqw89.DataKeys\bin\Debug\net9.0\aeqw89.DataKeys.dll</HintPath>
</Reference>
<Reference Include="aeqw89.PersistentData">
<HintPath>..\..\aeqw89.PersistentData\aeqw89.PersistentData\bin\Release\net9.0\aeqw89.PersistentData.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
+30
View File
@@ -0,0 +1,30 @@
namespace Beam.Temporary.Cli {
public class CssData {
// Primary background color (e.g., for the body)
public string PrimaryColor { get; set; } = "#f5f5f5";
// Secondary color (e.g., for header background)
public string SecondaryColor { get; set; } = "#e0e0e0";
// Tertiary color (e.g., for content sections)
public string TertiaryColor { get; set; } = "#ffffff";
// Button background color
public string ButtonColor { get; set; } = "#007bff";
// Foreground text color
public string ForegroundColor { get; set; } = "#333333";
// Font family for main content
public string ContentFont { get; set; } = "Arial, sans-serif";
// Font size for main content
public string ContentFontSize { get; set; } = "16px";
// Font family for titles
public string TitleFont { get; set; } = "Georgia, serif";
// Font size for titles
public string TitleFontSize { get; set; } = "32px";
}
}
+34
View File
@@ -0,0 +1,34 @@
using aeqw89.DataKeys;
namespace Beam.Temporary.Cli {
internal static class DataKeyExtensions {
public static DataKey WithNamespace(this DataKey dk, string @namespace) {
string[] names = @namespace.Split(':');
var agg = (string x, string y) => $"{x}:{y}";
for (int i = 0; i < names.Length; i++) {
string test = names.SkipLast(i).Aggregate(agg);
if (dk.Identifier.StartsWith(test)) {
return new DataKey(dk.Identifier.Replace(test, @namespace));
}
}
return new DataKey(@namespace + ":" + dk.Identifier);
}
public static DataKey<T> WithNamespace<T>(this DataKey<T> dk, string @namespace) {
return ((DataKey)dk).WithNamespace(@namespace).As<T>();
}
public static DataKey<T> WithSuffix<T>(this DataKey<T> dk, string suffix) {
return new DataKey<T>(dk.Identifier + suffix);
}
public static DataKey ToAggregator(this DataKey dk)
=> dk.WithNamespace("aeqw89:document:aggregators");
public static DataKey ToAuxiliary(this DataKey dk)
=> dk.WithNamespace("aeqw89:document:auxillaries");
public static DataKey<T> As<T>(this DataKey dk) => new DataKey<T>(dk.Identifier);
}
}
+6
View File
@@ -0,0 +1,6 @@
namespace Beam.Temporary.Cli {
internal class File(string path, params string[] tags) {
public string Path { get; set; } = path;
public string[] Tags { get; set; } = tags;
}
}
+132
View File
@@ -0,0 +1,132 @@
//using aeqw89.DataKeys;
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//namespace Beam.Temporary.Cli {
// internal class HtmlBook : Document {
// public class Keys {
// public static DataKey<File> ContentPage => new DataKey<File>("content_page");
// public static DataKey<File> NoContentPage => new DataKey<File>("no_content_page");
// public static DataKey<File> TitlePage => new DataKey<File>("title_page");
// public static DataKey<File> StylesPage => new DataKey<File>("styles_page");
// }
// public List<Tracked<IDocument>> Documents { get; set; }
// public IReadOnlyList<string> Pages => _Pages;
// private List<string> _Pages { get; set; } = [];
// private const string EMTPY_PAGE = "EMPTY";
// public CssData CssData { get; }
// public ArticleData BookData { get; set; }
// public HtmlBookTemplates Templates { get; set; }
// public HtmlBook(string bookname, CssData cssData, ArticleData bookData, HtmlBookTemplates templates, List<IDocument>? documents = null, Encoding? encoding = null)
// : base(bookname, encoding) {
// Documents = [];
// CssData = cssData;
// BookData = bookData;
// Templates = templates;
// if (documents is not null)
// Documents = documents.Select((x) => new Tracked<IDocument>(x)).ToList();
// }
// public void Update(bool ignoreDirty = false) {
// if (!Directory.Exists(Filename))
// Directory.CreateDirectory(Filename);
// //System.IO.File.WriteAllLines(Path.Combine(Filename, "styles.css"), Format())
// List<string> newpages = [];
// if (Pages.Count < Documents.Count)
// _Pages.AddRange(Enumerable.Repeat(EMTPY_PAGE, Documents.Count - Pages.Count));
// foreach (var (doc, page) in Documents.Zip(Pages)) {
// if (!doc.IsDirty)
// newpages.Add(page);
// else if (doc.TrackedObject.MetaData.Count == 0)
// newpages.Add(PlainPage(doc.TrackedObject));
// else if (doc.TrackedObject.MetaData.TryGetValue(Program.Architecture.ChapterKey, out var meta) && meta is ArticleData articleData)
// newpages.Add(ArticlePage(doc.TrackedObject, articleData));
// else {
// Console.WriteLine("Unhandlable Metadata detected!");
// newpages.Add(PlainPage(doc.TrackedObject));
// }
// System.IO.File.WriteAllText(Path.Combine(Filename, Path.GetRandomFileName() + ".html"), newpages[^1]);
// doc.IsDirty = false;
// }
// _Pages = newpages;
// }
// public void UpdateCss() {
// }
// public void UpateTitle() {
// }
// private string Format(string template, Dictionary<string, string> table) {
// ArgumentNullException.ThrowIfNull(template);
// ArgumentNullException.ThrowIfNull(table);
// foreach (var kvp in table) {
// template = template.Replace(kvp.Key, kvp.Value);
// }
// return template;
// }
// private Dictionary<string, string> GetDocumentTable(IDocument doc, bool keepPlaceholders = false) {
// var table = new Dictionary<string, string>() {
// { "{" + nameof(doc.Filename) + "}", doc.Filename },
// { "{Content}", doc.ToString() }
// };
// return SolvePlaceholders(table, keepPlaceholders);
// }
// private Dictionary<string, string> GetArticleDataTable(IDocument doc, ArticleData ad, bool keepPlaceholders = false) {
// var table = new Dictionary<string, string>() {
// { "{" + nameof(ad.Language) + "}", ad.Language ?? "" },
// { "{" + nameof(ad.Authors) + "}", ad.Authors.Aggregate("; ")},
// { "{" + nameof(ad.Categories) + "}", ad.Categories.Aggregate("; ") },
// { "{" + nameof(ad.Version) + "}", ad.Version ?? "" },
// { "{" + nameof(ad.Description) + "}", ad.Description ?? "" },
// { "{" + nameof(ad.Name) + "}", ad.Name ?? "" },
// { "{" + nameof(doc.Filename) + "}", doc.Filename },
// { "{Content}", doc.ToString() }
// };
// return SolvePlaceholders(table, keepPlaceholders);
// }
// private Dictionary<string, string> SolvePlaceholders(Dictionary<string, string> table, bool keepPlaceholders) {
// if (keepPlaceholders)
// return table.Select(
// (x) => new KeyValuePair<string, string>(x.Key, x.Value == "" ? $"{x.Key}" : x.Value))
// .ToDictionary();
// return table;
// }
// private string PlainPage(IDocument doc, bool keepPlaceholders = false) {
// return Format(Templates.ContentPageTemplate, GetDocumentTable(doc, keepPlaceholders));
// }
// private string ArticlePage(IDocument doc, ArticleData data, bool keepPlaceholders = false) {
// return Format(Templates.ContentPageTemplate, GetArticleDataTable(doc, data, keepPlaceholders));
// }
// public override byte[] ToBytes() {
// throw new NotImplementedException();
// }
// public override string ToString() {
// throw new NotImplementedException();
// }
// }
//}
+8
View File
@@ -0,0 +1,8 @@
namespace Beam.Temporary.Cli {
internal struct HtmlBookTemplates {
public string TitlePageTemplate { get; set; }
public string ContentPageTemplate { get; set; }
public string CssTemplate { get; set; }
public string NoContentTemplate { get; set; }
}
}
+79
View File
@@ -0,0 +1,79 @@
using aeqw89.DataKeys;
using Beam.Dynamic;
using HtmlAgilityPack;
using Microsoft.Extensions.Logging;
namespace Beam.Temporary.Cli {
partial interface IArchitecture {
private class MainArchitecture : IArchitecture {
public MainArchitecture() { }
public DataKey<IDocumentMetaData> ChapterKey { get; set; } = new("ma:chapter");
public DataKey<IDocumentMetaData> BookKey { get; set; } = new("ma:book");
public DownloadContext<IDocumentMetaData>? GetMeta(HtmlWeb web, DataKey<TextResource> pieceKey, SharedDataDictionary sdd, ILogger? logger = null) {
var piece = sdd.Novels[pieceKey].ToRecord(sdd); // retrieves novel data from the sdd
var auxiliary = piece.AssociatedMetaSource?.ToRecord(sdd); // retrieves novel aux data from the sdd
// null checks
if (auxiliary is null) // aux is required to get metadata
return null;
if (piece?.Resource?.MetaTemplateInitialData is null) // sanity check to avoid null warnings
return null;
// gets the link for the novel's metadata using the auxillary data retrieved from the sdd
var link = sdd.Templates[auxiliary.Resource.Key].GenerateLink(piece?.Resource?.MetaTemplateInitialData!);
var binding = auxiliary.Bindings;
return new DownloadContext<IDocumentMetaData>(web, [link], downloadLogger: logger, transformer: (x) => {
return new ArticleData() {
Authors = [OnlineCleaner.Clean(binding?.Authors?.Resolve(x) ?? "")],
Name = OnlineCleaner.Clean(binding?.Title?.ResolveString(x) ?? ""),
Categories = OnlineCleaner.Clean(binding?.Tags?.ResolveString(x) ?? "").Split(';') ?? [],
Description = OnlineCleaner.Clean(binding?.Description?.ResolveString(x) ?? "")
};
});
}
public DownloadContext<IDocument>? GetTextRecord(HtmlWeb web, DataKey<TextResource> resKey, SharedDataDictionary sdd, IDocumentMetaData? metaData = null, ILogger? logger = null) {
var res = sdd.Novels[resKey].ToRecord(sdd); // retrieves the novel data from the sdd
var aggregator = res.AssociatedSource?.ToRecord(sdd); // retrieves the aggregator (novel web source) from the sdd
if (aggregator is null) // ensure aggergator data was retrieved successfully
return null;
if (res is null) // ensure novel data was retrieved successfully
return null;
var template = sdd.Templates[aggregator.Resource.Key]; // gets the link generator for the specified aggregator
// creates a generative enumerable of type link from 'template'
var sle = SourceLinkEnumerable.FromGenerator(new DataBackedSourceLinkGenerator(
template, res.Resource.TemplateInitialData));
return new DownloadContext<IDocument>(web, sle,
transformer: (x) => {
var resolved = aggregator.Bindings.Resolve(x);
var articleData = new ArticleData() {
Name = OnlineCleaner.Clean(resolved.Title),
};
Dictionary<DataKey<IDocumentMetaData>, IDocumentMetaData> meta = [];
meta.Add(ChapterKey, articleData);
if (metaData is not null)
meta.Add(BookKey, metaData);
return new StringDocument(Path.GetRandomFileName(), OnlineCleaner.Clean(resolved.Content)) {
MetaData = meta
};
},
retryReporter: new Progress<int>((x) => Console.WriteLine($"Retrying download ({x})")),
downloadReporter: new Progress<IDocument>((x) => Console.WriteLine($"Downloaded ({x.Filename})")),
asyncFailurePredicates: [
(x) => Task.FromResult(!x.DocumentNode.InnerHtml.Contains("<div id=\"chapter-container\" class=\"chapter-content\" itemprop=\"description\">"))
],
timeOut: TimeSpan.FromSeconds(15),
downloadLogger: logger
);
}
}
}
}
+144
View File
@@ -0,0 +1,144 @@
using aeqw89.DataKeys;
using Beam.Dynamic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli {
internal static class NovelStatics {
public static void Define_LightNovelWorld_Novel_TheLegendaryMechanic(SharedDataDictionary sdd) {
var lnwAggregator = new DataKey<WebResource>("aeqw89:document:aggregators:light_novel_world");
var lnwAuxiliary = new DataKey<WebResource>("aeqw89:document:auxillaries:light_novel_world");
var novel = new TextResource() {
Key = new DataKey<TextResource>("novels:the_legendary_mechanic"),
AssociatedSource = lnwAggregator,
AssociatedMetaSource = lnwAuxiliary,
TemplateInitialData = ["the-legendary-mechanic-245", "1"],
MetaTemplateInitialData = ["the-legendary-mechanic"]
};
sdd.Novels.TryAdd(novel.Key, novel);
sdd.AggregatorNovels.TryAdd(lnwAggregator, [novel.Key]);
}
public static void Define_LightNovelWorl_Novel_IAloneLevelUp(SharedDataDictionary sdd) {
var lnwAggregator = new DataKey("light_novel_world").ToAggregator().As<WebResource>();
var lnwAuxiliary = new DataKey("light_novel_world").ToAuxiliary().As<WebResource>();
var novel = new TextResource() {
Key = new DataKey<TextResource>("novels:i_alone_level_up"),
AssociatedSource = lnwAggregator,
AssociatedMetaSource = lnwAuxiliary,
TemplateInitialData = ["i-alone-level-up-236", "1"],
MetaTemplateInitialData = ["i-alone-level-up-solo-leveling-05122225"]
};
sdd.Novels.TryAdd(novel.Key, novel);
sdd.AggregatorNovels.TryAdd(lnwAggregator, [novel.Key]);
}
public static void Define_NovelFull(SharedDataDictionary sdd) {
var docNamespace = "aeqw89:document";
var nfAgg = new DataKey<WebResource>("aggregators:novel_full").WithNamespace(docNamespace);
var nfAux = new DataKey<WebResource>("auxillaries:novel_full").WithNamespace(docNamespace);
var nfBindings = new DataKey<DataBindings>("aeqw89:bindings:light_novel_world");
var aggregator = new WebResource(nfAgg) {
Name = "Novel Full",
Description = "A novel aggregator site",
Domain = "https://novelfull.net",
Bindings = nfBindings
};
var auxiliary = new WebResource(nfAux) {
Name = "Novel Full",
Description = "A novel aggregator site",
Domain = "https://novelfull.net",
Bindings = nfBindings.WithSuffix("_aux")
};
sdd.Templates.TryAdd(nfAgg, new() {
Template = ""
});
}
public static void Define_LightNovelWorld(SharedDataDictionary sdd) {
var lnwAggregator = new DataKey<WebResource>("aeqw89:document:aggregators:light_novel_world");
var lnwAuxiliary = new DataKey<WebResource>("aeqw89:document:auxillaries:light_novel_world");
const string lnwBindingsA = "aeqw89:bindings:light_novel_world";
var aggregator = new WebResource(lnwAggregator) {
Name = "Light Novel World",
Description = "A novel aggregator site maintained by NetherClaw",
Domain = "https://www.lightnovelworld.co",
Bindings = new DataKey<DataBindings>(lnwBindingsA)
};
const string lnwBindingsB = "aeqw89:bindings:light_novel_world_aux";
var auxiliary = new WebResource(lnwAuxiliary) {
Name = "Light Novel World",
Description = "A novel aggregator site maintained by NetherClaw",
Domain = "https://www.lightnovelworld.co",
Bindings = new DataKey<DataBindings>(lnwBindingsB)
};
sdd.Templates.TryAdd(lnwAuxiliary, new() {
Template = "https://www.lightnovelworld.co/novel/{0}",
IndexOfChapterIndex = -1
});
sdd.Templates.TryAdd(lnwAggregator, new() {
Template = "https://www.lightnovelworld.co/novel/{0}/chapter-{1}",
IndexOfChapterIndex = 1
});
sdd.Aggregators.TryAdd(aggregator.Key, aggregator);
sdd.Auxillaries.TryAdd(auxiliary.Key, auxiliary);
var lnwBindings = new DataKey<DataBindings>(lnwBindingsA);
var lnwBindingsAux = new DataKey<DataBindings>(lnwBindingsB);
sdd.Bindings.TryAdd(lnwBindings, new DataBindings() {
Title = new Binding("aeqw89:binding:light_novel_world:title") {
XPath = "/html/body/main/article/section/div[1]/h1/span[2]",
Type = BindingType.Single
},
Content = new("aeqw89:binding:light_novel_world:content") {
Provider = new ParagraphedContentDataProvider() {
Content = new Binding() {
XPath = "//*[@id=\"chapter-container\"]"
}
},
Type = BindingType.UseProvider
},
});
sdd.Bindings.TryAdd(lnwBindingsAux, new DataBindings() {
Title = new("aeqw89:binding:light_novel_world_aux:title") {
XPath = "/html/body/main/article/header/div[2]/div[2]/div[1]/h1",
Type = BindingType.Single
},
Authors = new("aeqw89:binding:light_novel_world_aux:authors") {
XPath = "/html/body/main/article/header/div[2]/div[2]/div[1]/div[1]/a",
Type = BindingType.Single
},
Description = new("aeqw89:binding:light_novel_world_aux:description") {
Provider = new ParagraphedContentDataProvider() {
Content = new() {
XPath = "/html/body/main/article/div/section/div[1]/div"
}
},
Type = BindingType.UseProvider
},
Tags = new("aeqw89:binding:light_novel_world_aux:tags") {
Provider = new ListContentDataProvider() {
Content = new() {
XPath = "/html/body/main/article/header/div[2]/div[2]/div[3]/ul"
}
},
Type = BindingType.UseProvider
}
});
}
}
}
+135
View File
@@ -0,0 +1,135 @@
using aeqw89.PersistentData;
using aeqw89.DataKeys;
using Beam.Dynamic;
using HtmlAgilityPack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using Beam.Temporary.Cli.Templates.Classic;
using Beam.Exports;
namespace Beam.Temporary.Cli {
internal class Program {
public static JsonSerializerOptions ConversionOptions { get; internal set; } = new();
public static SharedDataDictionary Shared { get; set; } = [];
public static IArchitecture Architecture = IArchitecture.Default;
const string SharedDataPath = "data/.dat";
static async Task Main(string[] args) {
ConversionOptions.Converters.AddPersistentDataRequiredConverters();
ConversionOptions.WriteIndented = true;
var web = new HtmlWeb();
var lf = LoggerFactory.Create((x) => {
x.AddConsole();
});
ILogger logger = lf
.CreateLogger("Program");
await using var sharedContext = await DataDictionaryContext<SharedDataDictionary>.Create(
SharedDataPath,
DataKind.Shared,
logger,
ConversionOptions
);
Shared = sharedContext.Data;
Shared.Clear();
NovelStatics.Define_LightNovelWorld(Shared);
NovelStatics.Define_LightNovelWorld_Novel_TheLegendaryMechanic(Shared);
NovelStatics.Define_LightNovelWorl_Novel_IAloneLevelUp(Shared);
ClassicTemplates.Register(Shared);
var novel = new DataKey<TextResource>("novels:i_alone_level_up");
var context_aux = Architecture.GetMeta(web, novel, Shared);
var metaDownloader = new DownloadEnumerable<IDocumentMetaData>(
new SequentialFragmentDownloader<IDocumentMetaData>(
context_aux,
(c) => new UnitFragmentDownloader<IDocumentMetaData>(c.Web, c.AsyncTranformer, c.AsyncFailurePredicates, 4, logger),
logger)
.UnwrapFragmented());
var metadata = (await metaDownloader.FirstAsync());
var context = Architecture.GetTextRecord(web, novel, Shared, metadata.Data);
context.DownloadReporter = new Progress<IDocument>((x) => Console.WriteLine(x.Filename));
var downloader = new DownloadEnumerable<IDocument>(
new SequentialFragmentDownloader<IDocument>(
context,
(c) => new UnitFragmentDownloader<IDocument>(c.Web, c.AsyncTranformer, c.AsyncFailurePredicates, 4, logger),
logger)
.UnwrapFragmented());
List<Ordered<IDocument>> documents = [];
await foreach (var download in downloader.Take(20)) {
if (!download.Data.MetaData.TryGetValue(Architecture.ChapterKey, out var meta))
continue;
if (meta is not ArticleData articleMetaData)
continue;
//Console.WriteLine($"Title: {data.Name}");
//Console.WriteLine($"Description: {data.Description}");
//Console.WriteLine($"Categories: {data.Categories.Aggregate((x, y) => $"{x}; {y}")}");
//Console.WriteLine($"Authors: {data.Authors.Aggregate((x,y) => $"{x}; {y}")}");
Console.WriteLine($"Chapter title: {articleMetaData.Name}");
//Console.WriteLine($"Content: {download}");
documents.Add(download);
}
string testDir = Path.Combine("txt", Path.GetRandomFileName());
Directory.CreateDirectory(testDir);
int len = documents.MaxBy((x) => x.Order)?.Order ?? -1;
foreach (var document in documents.OrderBy((x) => x.Order)) {
document.Data.MetaData.TryGetValue(Architecture.ChapterKey, out var chapterMetaData);
Dictionary<string, string> linkButtons = new();
if (document.Order != 0)
linkButtons.Add("Previous", $"{document.Order - 1}.html");
if (document.Order != len)
linkButtons.Add("Next", $"{document.Order + 1}.html");
new HtmlExporter(document.Data, chapterMetaData as ArticleData, linkButtons).Write(Path.Combine(testDir, $"{document.Order}.html"));
}
Console.ReadKey();
//foreach (var download in documents.OrderBy((x) => x.Order)) {
// if (download.Data.TryGetTaggedMetaData<ArticleData>(Architecture.ChapterKey, out var meta))
// Console.WriteLine($"{download.Order}:{meta.Name}");
//}
//string[] templates = new DataKey<File>[] {
// HtmlBook.Keys.ContentPage,
// HtmlBook.Keys.NoContentPage,
// HtmlBook.Keys.TitlePage,
// HtmlBook.Keys.StylesPage,
//}.Select(
// (x) => Shared.Files.ReadToString(x.WithNamespace("aeqw89:files:templates:classic"))
//).ToArray();
//HtmlBook book = new(
// bookname: Path.Combine(Path.GetRandomFileName(), "I Alone Level Up"),
// new CssData(),
// new ArticleData(),
// new HtmlBookTemplates() {
// ContentPageTemplate = templates[0],
// NoContentTemplate = templates[1],
// TitlePageTemplate = templates[2],
// CssTemplate = templates[3],
// },
// documents: documents.Select((x) => x.Data).ToList()
//);
//book.Update();
//Console.WriteLine("One variable!");
}
}
}
@@ -0,0 +1,48 @@
using aeqw89.PersistentData;
using aeqw89.DataKeys;
using Beam.Dynamic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli {
public class SharedDataDictionary : BaseDataDictionary {
public Dictionary<DataKey<WebResource>, PackagedSourceLinkGenerationData> Templates {
get => GetOrCreateDictionary<DataKey<WebResource>, PackagedSourceLinkGenerationData>(nameof(Templates));
set => Data[nameof(Templates)] = value;
}
public Dictionary<DataKey<WebResource>, WebResource> Aggregators {
get => GetOrCreateDictionary<DataKey<WebResource>, WebResource>(nameof(Aggregators));
set => Data[nameof(Aggregators)] = value;
}
public Dictionary<DataKey<WebResource>, WebResource> Auxillaries {
get => GetOrCreateDictionary<DataKey<WebResource>, WebResource>(nameof(Auxillaries));
set => Data[nameof(Auxillaries)] = value;
}
public Dictionary<DataKey<DataBindings>, DataBindings> Bindings {
get => GetOrCreateDictionary<DataKey<DataBindings>, DataBindings>(nameof(Bindings));
set => Data[nameof(Bindings)] = value;
}
public Dictionary<DataKey<WebResource>, HashSet<DataKey<TextResource>>> AggregatorNovels {
get => GetOrCreateDictionary<DataKey<WebResource>, HashSet<DataKey<TextResource>>>(nameof(AggregatorNovels));
set => Data[nameof(AggregatorNovels)] = value;
}
public Dictionary<DataKey<TextResource>, TextResource> Novels {
get => GetOrCreateDictionary<DataKey<TextResource>, TextResource>(nameof(Novels));
set => Data[nameof(Novels)] = value;
}
internal Dictionary<DataKey<File>, File> Files {
get => GetOrCreateDictionary<DataKey<File>, File>(nameof(Files));
set => Data[nameof(Files)] = value;
}
}
}
+15
View File
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli {
public static class StringExtensions {
public static string Aggregate(this IEnumerable<string> str, string separator) {
if (!str.Any())
return string.Empty;
return str.Aggregate((x, y) => $"{x}{separator}{y}");
}
}
}
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli.Templates.Classic {
internal class ClassicTemplates {
public static void Register(SharedDataDictionary sdd) {
sdd.Files.TryAdd(
new("aeqw89:files:templates:classic:content_page"),
new("C:\\Users\\qwsdc\\source\\repos\\Beam\\Beam.Temporary.Cli\\Templates\\Classic\\Content.template.html", "htmlpage", "templates"));
sdd.Files.TryAdd(
new("aeqw89:files:templates:classic:title_page"),
new("C:\\Users\\qwsdc\\source\\repos\\Beam\\Beam.Temporary.Cli\\Templates\\Classic\\Title.template.html", "htmlpage", "templates"));
sdd.Files.TryAdd(
new("aeqw89:files:templates:classic:styles_page"),
new("C:\\Users\\qwsdc\\source\\repos\\Beam\\Beam.Temporary.Cli\\Templates\\Classic\\Styles.template.css", "styles", "templates"));
sdd.Files.TryAdd(
new("aeqw89:files:templates:classic:no_content_page"),
new("C:\\Users\\qwsdc\\source\\repos\\Beam\\Beam.Temporary.Cli\\Templates\\Classic\\NoContent.template.html", "htmlpage", "templates"));
}
}
internal static class DictionaryOfFileExtensions {
public static string ReadToString<T>(this Dictionary<T, File> dict, T key) where T: notnull {
return System.IO.File.ReadAllText(dict[key].Path);
}
}
}
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{Name}</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<h1>{Name}</h1>
<p><em>{Description}</em></p>
<div>
<span><strong>Authors:</strong> {Authors}</span> |
<span><strong>Language:</strong> {Language}</span> |
<span><strong>Categories:</strong> {Categories}</span> |
<span><strong>Version:</strong> {Version}</span>
</div>
</header>
<article>
{Content}
</article>
<div class="navigation">
<button id="prev">Previous</button>
<button id="next">Next</button>
</div>
</body>
</html>
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>404 - Not Found</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="error-container">
<h1>404 - Content Not Found</h1>
<p>The file <strong>{Filename}</strong> was not found.</p>
<p>{Content}</p>
</div>
</body>
</html>
@@ -0,0 +1,60 @@
/* styles.css */
/* Placeholders:
{PrimaryColor}, {SecondaryColor}, {TertiaryColor}, {ButtonColor},
{ForegroundColor}, {ContentFont}, {ContentFontSize}, {TitleFont}, {TitleFontSize}
*/
body {
font-family: {ContentFont};
font-size: {ContentFontSize};
background-color: {PrimaryColor};
color: {ForegroundColor};
margin: 0;
padding: 20px;
}
header {
background-color: {SecondaryColor};
padding: 20px;
text-align: center;
}
header h1 {
font-family: {TitleFont};
font-size: {TitleFontSize};
margin: 0;
}
header p {
font-style: italic;
margin: 5px 0;
}
section, article, nav {
background: {TertiaryColor};
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin: 20px auto;
max-width: 800px;
}
.navigation {
display: flex;
justify-content: space-between;
max-width: 800px;
margin: 20px auto;
}
button {
background-color: {ButtonColor};
color: {ForegroundColor};
border: none;
padding: 10px 20px;
cursor: pointer;
font-size: {ContentFontSize};
border-radius: 4px;
}
nav h2 {
margin-top: 0;
}
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{Name}</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<h1>{Name}</h1>
<p><em>{Description}</em></p>
</header>
<section>
<div><strong>Authors:</strong> {Authors}</div>
<div><strong>Language:</strong> {Language}</div>
<div><strong>Categories:</strong> {Categories}</div>
<div><strong>Version:</strong> {Version}</div>
</section>
<nav>
<h2>Table of Contents</h2>
<ul>
{TOC} <!-- Expected to be a list of items (e.g. <li>Chapter 1</li>, etc.) -->
</ul>
</nav>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
using aeqw89.DataKeys;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli {
public class TextResource : IKeyed<TextResource> {
public required DataKey<TextResource> Key { get; set; }
public DataKey<WebResource>? AssociatedSource { get; set; }
public DataKey<WebResource>? AssociatedMetaSource { get; set; }
public required string[] TemplateInitialData { get; set; }
public string?[]? MetaTemplateInitialData { get; set; }
public TextResourceRecord ToRecord(SharedDataDictionary sdd) {
return new(this,
AssociatedSource is null ? null : sdd.Aggregators[AssociatedSource],
AssociatedMetaSource is null ? null : sdd.Auxillaries[AssociatedMetaSource]);
}
}
public record TextResourceRecord(TextResource Resource, WebResource? AssociatedSource, WebResource? AssociatedMetaSource);
}
+11
View File
@@ -0,0 +1,11 @@
namespace Beam.Temporary.Cli {
internal class Tracked<T>(T obj) {
public T TrackedObject { get; set; } = obj;
public bool IsDirty { get; set; } = true;
public Tracked<T> SetDirty() {
IsDirty = true;
return this;
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using aeqw89.PersistentData;
using aeqw89.DataKeys;
using Beam.Dynamic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Beam.Temporary.Cli {
public class WebResource(DataKey<WebResource> key) : IKeyed<WebResource> {
public DataKey<WebResource> Key { get; set; } = key;
public required DataKey<DataBindings> Bindings { get; set; }
public string? Name { get; set; }
public string? Domain { get; set; }
public string? Description { get; set; }
public WebResource() : this(new(string.Empty)) { }
public WebResourceRecord ToRecord(SharedDataDictionary sdd) {
return new WebResourceRecord(this, sdd.Bindings[Bindings]);
}
}
public record WebResourceRecord(WebResource Resource, DataBindings Bindings);
}