Browse Source

add: loadable resource module; WIP: blazor WASM ui module

HOME 2 years ago
parent
commit
3edbdc2ace

+ 39 - 0
FNZCM/FNZCM.BlazorWasm/App.razor

@@ -0,0 +1,39 @@
+<h1>Brrrrrrrr</h1>
+@MyProperty
+<button @onclick="brr">asf</button>
+
+
+<h1>@currentHeading</h1>
+
+<p>
+    <label>
+        New title
+        <input @bind="newHeading" />
+    </label>
+    <button @onclick="UpdateHeading">
+        Update heading
+    </button>
+</p>
+
+<p>
+    <label>
+        <input type="checkbox" @onchange="CheckChanged" />
+        @checkedMessage
+    </label>
+</p>
+
+@code {
+    private string currentHeading = "Initial heading";
+    private string? newHeading;
+    private string checkedMessage = "Not changed yet";
+
+    private void UpdateHeading()
+    {
+        currentHeading = $"{newHeading}!!!";
+    }
+
+    private void CheckChanged()
+    {
+        checkedMessage = $"Last changed at {DateTime.Now}";
+    }
+}

+ 14 - 0
FNZCM/FNZCM.BlazorWasm/App.razor.cs

@@ -0,0 +1,14 @@
+using System.Diagnostics;
+
+namespace FNZCM.BlazorWasm
+{
+    public partial class App
+    {
+        public string MyProperty => DateTime.Now.ToString();
+
+        public void brr()
+        {
+            Console.WriteLine("brr");
+        }
+    }
+}

+ 14 - 0
FNZCM/FNZCM.BlazorWasm/FNZCM.BlazorWasm.csproj

@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+  <PropertyGroup>
+    <TargetFramework>net7.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.0-preview.4.22251.1" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.0-preview.4.22251.1" PrivateAssets="all" />
+  </ItemGroup>
+
+</Project>

+ 11 - 0
FNZCM/FNZCM.BlazorWasm/Program.cs

@@ -0,0 +1,11 @@
+using Microsoft.AspNetCore.Components.Web;
+using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+using FNZCM.BlazorWasm;
+
+var builder = WebAssemblyHostBuilder.CreateDefault(args);
+builder.RootComponents.Add<App>("#app");
+builder.RootComponents.Add<HeadOutlet>("head::after");
+
+builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
+
+await builder.Build().RunAsync();

+ 29 - 0
FNZCM/FNZCM.BlazorWasm/Properties/launchSettings.json

@@ -0,0 +1,29 @@
+{
+  "profiles": {
+    "FNZCM.BlazorWasm": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "dotnetRunMessages": true,
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+      "applicationUrl": "http://localhost:5263"
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}"
+    }
+  },
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:57625",
+      "sslPort": 0
+    }
+  }
+}

+ 9 - 0
FNZCM/FNZCM.BlazorWasm/_Imports.razor

@@ -0,0 +1,9 @@
+@using System.Net.Http
+@using System.Net.Http.Json
+@using Microsoft.AspNetCore.Components.Forms
+@using Microsoft.AspNetCore.Components.Routing
+@using Microsoft.AspNetCore.Components.Web
+@using Microsoft.AspNetCore.Components.Web.Virtualization
+@using Microsoft.AspNetCore.Components.WebAssembly.Http
+@using Microsoft.JSInterop
+@using FNZCM.BlazorWasm

BIN
FNZCM/FNZCM.BlazorWasm/wwwroot/favicon.ico


BIN
FNZCM/FNZCM.BlazorWasm/wwwroot/icon-192.png


+ 16 - 0
FNZCM/FNZCM.BlazorWasm/wwwroot/index.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <link rel="icon" type="image/x-icon" href="favicon.ico">
+    <title>Blazor WASM UI</title>
+</head>
+
+<body>
+    <div id="app">Loading...</div>
+    <script src="_framework/blazor.webassembly.js"></script>
+</body>
+
+</html>

+ 1 - 0
FNZCM/FNZCM.ConHost/FNZCM.ConHost.csproj

@@ -17,6 +17,7 @@
 
   <ItemGroup>
     <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+    <PackageReference Include="SharpCompress" Version="0.32.2" />
     <PackageReference Include="taglib-sharp-netstandard2.0" Version="2.1.0" />
   </ItemGroup>
 

+ 13 - 0
FNZCM/FNZCM.ConHost/Ver2/ModuleModels.cs

@@ -0,0 +1,13 @@
+using System.Collections.Generic;
+
+namespace FNZCM.ConHost.Ver2
+{
+    internal class LoadedModule
+    {
+        public string VirtualPath { get; set; }
+        public string DisplayText { get; set; }
+        public string DefaultDocument { get; set; }
+
+        public Dictionary<string, byte[]> Files { get; set; }
+    }
+}

+ 98 - 6
FNZCM/FNZCM.ConHost/Ver2/Program2.cs

@@ -1,5 +1,6 @@
 using FNZCM.Core;
 using Microsoft.VisualBasic.FileIO;
+using Newtonsoft.Json;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -9,7 +10,6 @@ using System.Net;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
-using Newtonsoft.Json;
 using SearchOption = Microsoft.VisualBasic.FileIO.SearchOption;
 
 namespace FNZCM.ConHost.Ver2
@@ -29,6 +29,9 @@ namespace FNZCM.ConHost.Ver2
         private static readonly ConcurrentDictionary<string, Library2> Libraries = new();
         private static readonly ConcurrentDictionary<string, string> PathMapping = new();
         private static readonly ConcurrentDictionary<string, MediaTag2> MediaTags = new();
+        private static readonly ConcurrentDictionary<string, LoadedModule> Modules = new();
+
+        private static LoadedModule _defaultModule;
 
         private static bool _isRunning;
         private static bool _isLoading;
@@ -46,7 +49,7 @@ namespace FNZCM.ConHost.Ver2
             _isRunning = true;
             tWorker.Start();
 
-            Task.Run(ScanLibrary);
+            Task.Run(ReloadConfig);
 
             Console.WriteLine("Press ENTER to Stop.");
             Console.ReadLine();
@@ -62,18 +65,68 @@ namespace FNZCM.ConHost.Ver2
             Console.ReadLine();
         }
 
-        private static void ScanLibrary()
+        private static void ReloadConfig()
         {
             if (_isLoading)
             {
-                Console.WriteLine("Still scanning, SKIP");
+                Console.WriteLine("Still loading, SKIP");
                 return;
             }
 
             _isLoading = true;
+
             try
             {
                 ConfigFile.Reload();
+
+                Modules.Clear();
+                _defaultModule = null;
+                if (ConfigFile.Instance.Modules?.Any() == true)
+                {
+                    foreach (var modEnt in ConfigFile.Instance.Modules)
+                    {
+                        Console.WriteLine($"Loading module `{modEnt.Value.DisplayText}'...");
+                        var module = new LoadedModule
+                        {
+                            VirtualPath = modEnt.Key,
+                            DisplayText = modEnt.Value.DisplayText,
+                            DefaultDocument = modEnt.Value.DefaultDocument,
+                            Files = new Dictionary<string, byte[]>()
+                        };
+
+                        if (Directory.Exists(modEnt.Value.Path))
+                        {
+                            //load by fs
+                            var files = Directory.GetFiles(modEnt.Value.Path, "*", System.IO.SearchOption.AllDirectories);
+                            foreach (var item in files)
+                            {
+                                var k = item.Substring(modEnt.Value.Path.Length + 1).Replace("\\", "/").ToLower();
+                                module.Files[k] = File.ReadAllBytes(item);
+                            }
+                        }
+                        else if (File.Exists(modEnt.Value.Path))
+                        {
+                            //load by package
+                            using var arc = SharpCompress.Archives.ArchiveFactory.Open(modEnt.Value.Path);
+                            foreach (var ent in arc.Entries.Where(p => p.IsDirectory == false))
+                            {
+                                var buf = new byte[ent.Size];
+                                using var s = ent.OpenEntryStream();
+                                var r = s.Read(buf, 0, buf.Length);
+                                module.Files[ent.Key.ToLower()] = buf;
+                            }
+                        }
+                        else
+                        {
+                            Console.WriteLine("WARN: resource not found");
+                            continue;
+                        }
+
+                        if (modEnt.Value.IsDefault && _defaultModule == null) _defaultModule = module;
+                        Modules[modEnt.Key] = module;
+                    }
+                }
+
                 Console.WriteLine("Scanning libraries...");
                 MediaTags.Clear();
                 PathMapping.Clear();
@@ -233,6 +286,10 @@ namespace FNZCM.ConHost.Ver2
             var request = context.Request;
             Console.WriteLine($"Request from {request.RemoteEndPoint} {request.HttpMethod} {request.RawUrl}");
 
+#if DEBUG
+            context.Response.AddHeader("Access-Control-Allow-Origin", "*");
+#endif
+
             // GET / show all libraries
 
             // foo=library bar=disc
@@ -284,14 +341,33 @@ namespace FNZCM.ConHost.Ver2
                 }
                 else if (requestPath == "/admin/" && request.QueryString["action"] == "Scan" && request.QueryString["pass"] == ConfigFile.Instance.AdminPassword)
                 {
-                    Task.Run(ScanLibrary);
+                    Task.Run(ReloadConfig);
                     context.Response.Redirect("/");
                 }
                 else if (requestPath == "/admin/")
                 {
                     context.Response.Redirect("/");
                 }
-                else if (requestPath == "/")
+                else if (requestPath.StartsWith("/module/") && pathParts.Count > 1)
+                {
+                    var moduleKey = pathParts[1];
+                    if (Modules.TryGetValue(moduleKey, out var module))
+                    {
+                        var entPath = string.Join("/", pathParts.Skip(2));
+                        if (module.Files.TryGetValue(entPath, out var bin))
+                        {
+                            if (entPath.EndsWith(".js")) context.Response.ContentType = "application/javascript";
+                            context.Response.OutputStream.Write(bin, 0, bin.Length);
+                        }
+                        else context.Response.StatusCode = 404;
+                    }
+                    else context.Response.StatusCode = 404;
+                }
+                else if (requestPath == "/" && _defaultModule != null)
+                {
+                    context.Response.Redirect($"/module/{_defaultModule.VirtualPath}/{_defaultModule.DefaultDocument}");
+                }
+                else if (requestPath == "/" || requestPath == "/classic-index")
                 {
                     var sb = new StringBuilder();
                     sb.Append("<!DOCTYPE html><html lang=\"zh-cn\"><meta charset=\"UTF-8\">");
@@ -301,6 +377,22 @@ namespace FNZCM.ConHost.Ver2
                     if (_isLoading) sb.Append($"<h4 style=position:fixed;right:0px;top:0px;margin:0>{LoadingText}</h4>");
 
                     sb.Append($"<h2>{ConfigFile.Instance.Title}</h2>");
+
+                    if (Modules?.Any() == true)
+                    {
+                        sb.Append($"<h3>Modules</h3>");
+                        sb.Append($"<h4>(Total number of modules: {Modules.Count})</h4>");
+
+                        sb.Append("<ul>");
+                        foreach (var module in Modules)
+                        {
+                            sb.Append("<li>");
+                            sb.Append($"<a href='/module/{module.Key.UrlEscape()}/{module.Value.DefaultDocument}'>{module.Value.DisplayText}</a>");
+                            sb.Append("</li>");
+                        }
+                        sb.Append("</ul>");
+                    }
+
                     sb.Append($"<h3>Libraries</h3>");
                     sb.Append($"<h4>(Total number of disc: {Libraries.Sum(p => p.Value.Discs.Count)})</h4>");
 

+ 12 - 0
FNZCM/FNZCM.Core/ConfigFile.cs

@@ -15,6 +15,8 @@ namespace FNZCM.Core
         string[] BkFilePattern { get; }
         string ListenPrefix { get; }
         string Title { get; }
+
+        IReadOnlyDictionary<string, ModuleEntry> Modules { get; }
     }
 
     public class ConfigFile : IConfigFile
@@ -35,5 +37,15 @@ namespace FNZCM.Core
         public string[] BkFilePattern { get; set; }
 
         public static IConfigFile Instance { get; private set; }
+
+        public IReadOnlyDictionary<string, ModuleEntry> Modules { get; set; }
+    }
+
+    public class ModuleEntry
+    {
+        public bool IsDefault { get; set; }
+        public string DisplayText { get; set; }
+        public string DefaultDocument { get; set; }
+        public string Path { get; set; }
     }
 }

+ 16 - 2
FNZCM/FNZCM.Core/config.json

@@ -1,6 +1,6 @@
 {
   "ListenPrefix": "http://+:38964/",
-  "AliasPrefix": [] ,
+  "AliasPrefix": [],
   "Title": "FNZ Cloud Music",
   "AdminPassword": "",
   "Libraries": {
@@ -23,5 +23,19 @@
     "*.webp",
     "*.tif",
     "*.bmp"
-  ]
+  ],
+  "Modules": {
+    "blazor": {
+      "IsDefault": true,
+      "DisplayText": "Blazor WASM UI",
+      "DefaultDocument": "index.html",
+      "Path": "z:/bwu"
+    },
+    "package": {
+      //"IsDefault": true,
+      "DisplayText": "Package Debug",
+      "DefaultDocument": "index.html",
+      "Path": "z:/www.zip"
+    }
+  }
 }

+ 9 - 3
FNZCM/FNZCM.sln

@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.32106.194
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.32804.182
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FNZCM.ConHost", "FNZCM.ConHost\FNZCM.ConHost.csproj", "{788B1339-FEA3-446D-9584-A311BD0F5114}"
 EndProject
@@ -10,10 +10,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "@", "@", "{C7D1253A-E6BA-4C
 		NuGet.config = NuGet.config
 	EndProjectSection
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNZCM.Core", "FNZCM.Core\FNZCM.Core.csproj", "{14307F36-0309-4DA1-A05D-9CB21877E366}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FNZCM.Core", "FNZCM.Core\FNZCM.Core.csproj", "{14307F36-0309-4DA1-A05D-9CB21877E366}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNZCM.LyricTools", "FNZCM.LyricTools\FNZCM.LyricTools.csproj", "{D2FB3072-B708-446A-A393-3D9F0306D210}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FNZCM.BlazorWasm", "FNZCM.BlazorWasm\FNZCM.BlazorWasm.csproj", "{CE63EFD6-E86F-43F3-AF70-3A4066AFC37D}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -32,6 +34,10 @@ Global
 		{D2FB3072-B708-446A-A393-3D9F0306D210}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{D2FB3072-B708-446A-A393-3D9F0306D210}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{D2FB3072-B708-446A-A393-3D9F0306D210}.Release|Any CPU.Build.0 = Release|Any CPU
+		{CE63EFD6-E86F-43F3-AF70-3A4066AFC37D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CE63EFD6-E86F-43F3-AF70-3A4066AFC37D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CE63EFD6-E86F-43F3-AF70-3A4066AFC37D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CE63EFD6-E86F-43F3-AF70-3A4066AFC37D}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE