lyrics support, code cleanup

This commit is contained in:
Laura Hausmann 2020-12-21 20:42:29 +01:00
parent 193cc39ce2
commit b9794110cd
Signed by untrusted user: zotan
GPG key ID: 5EC1D38FFC321311
12 changed files with 245 additions and 340 deletions

View file

@ -1,21 +0,0 @@
# http://docs.gitlab.com/ce/ci/docker/using_docker_build.html#using-the-gitlab-container-registry
# The docker tag is the first 6 letters of the Git commit id
job_build_dotnet:
stage: build
image: archlinux/base:latest
variables:
GIT_SUBMODULE_STRATEGY: recursive
script:
- pacman-key --init
- pacman-key --recv-keys 3FABB87C7C9F7E5FF2B6CB7B11A7E7E4DB9351DE
- pacman-key --lsign-key 3FABB87C7C9F7E5FF2B6CB7B11A7E7E4DB9351DE
- bash -c "echo -e '"'[zotancc]\nServer = https://arch.prod.zotan.network/zotancc/os/$arch'"' >> /etc/pacman.conf"
- pacman -Syu --needed dotnet-sdk-bin --noconfirm
- curl -Lo warp-packer https://github.com/dgiagio/warp/releases/download/v0.3.0/linux-x64.warp-packer && chmod +x warp-packer
- dotnet publish -c Release -r linux-x64
- ./warp-packer --arch linux-x64 --input_dir bin/Release/netcoreapp3.1/linux-x64/publish --exec webmusic --output webmusic.linux.run
- chmod +x webmusic.linux.run
artifacts:
paths:
- webmusic.linux.run

View file

@ -7,8 +7,7 @@
<h1 class="text-danger">Error.</h1> <h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2> <h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId) @if (Model.ShowRequestId) {
{
<p> <p>
<strong>Request ID:</strong> <code>@Model.RequestId</code> <strong>Request ID:</strong> <code>@Model.RequestId</code>
</p> </p>

View file

@ -1,22 +1,15 @@
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace webmusic.Pages namespace webmusic.Pages {
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel public class ErrorModel : PageModel {
{
public string RequestId { get; set; } public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet() public void OnGet() {
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
} }
} }

View file

@ -1,47 +1,44 @@
@page @page
@using System.Web
@model IndexModel @model IndexModel
@{ @{
ViewData["Title"] = $"webmusic on .NET {Environment.Version}"; ViewData["Title"] = $"webmusic on .NET {Environment.Version}";
var path = HttpUtility.UrlDecode(Request.QueryString.Value.TrimStart('?'));
if (path.EndsWith(".m3u"))
{
Layout = null;
Response.ContentType = "text/plain";
} }
} @if (Model.Path.Contains("/..")) {
@if (Model.path.Contains("/.."))
{
return; return;
} }
@if (path.EndsWith(".m3u")) @if (Model.Path.EndsWith(".lrc")) {
{ <h3>@Model.Path</h3>
@foreach (var file in Model.files) <p>@Html.Raw((await System.IO.File.ReadAllTextAsync("music" + Model.Path)).Replace("\n", "<br/>"))</p>
{
@Html.Raw("https://" + Request.Host + "/" + Model.fullpath + "/" + file.Replace("?", "%3F") +"\n")
} }
} else {
else <h2>
{ @Model.Displaypath <span id="state"></span> <span id="flags"></span>
<h2>@Model.displaypath <span id="state"></span> <span id="flags"></span></h2> </h2>
<a class="action-muted">[..]</a> <a class="action-muted">[..]</a>
<a href="?@Model.path_oneup" class="entry-muted cfont"> Go back</a><br/> <a href="?@Model.PathOneup" class="entry-muted cfont"> Go back</a>
<br/>
<a class="action-muted">[--]</a> <a class="action-muted">[--]</a>
<a href="/playlist/@(Request.QueryString).m3u" class="entry-muted cfont"> Download playlist</a><br/><br/> <a href="/playlist/@(Request.QueryString).m3u" class="entry-muted cfont"> Download playlist</a>
@foreach (var dir in Model.dirs) <br/>
{ <br/>
@foreach (var dir in Model.Dirs) {
<a class="action" href="#">[--]</a> <a class="action" href="#">[--]</a>
<a href="?@Model.path/@dir"> @dir</a> <a href="?@Model.Path/@dir"> @dir</a>
<br/> <br/>
} }
@foreach (var file in Model.files) @foreach (var file in Model.Files) {
{ var jspath = IndexModel.Encode(Model.Fullpath);
var jspath = Model.Encode(Model.fullpath); var jsfile = jspath + "/" + IndexModel.Encode(file);
var jsfile = jspath + "/" + Model.Encode(file); var basename = System.IO.Path.GetFileNameWithoutExtension(file);
var lrcfile = basename + ".lrc";
var lrcpath = System.IO.Path.Combine(Model.Fullpath, lrcfile);
<script>queue.push('@Html.Raw(jsfile)')</script> <script>queue.push('@Html.Raw(jsfile)')</script>
<a class="action" href="@Html.Raw(Model.Encode(Model.fullpath+"/"+file))" download>[DL]</a> <a class="action" href="@Html.Raw(IndexModel.Encode(Model.Fullpath + "/" + file))" download>[DL]</a>
<a href="#" onclick="playSong('@Html.Raw(jsfile)')"> @file</a> <a href="#" onclick="playSong('@Html.Raw(jsfile)')"> @file</a>
@if (System.IO.File.Exists(lrcpath)) {
<a class="action" href="?@Model.Path/@lrcfile" target="_blank">[LRC]</a>
}
<br/> <br/>
} }
} }

View file

@ -1,4 +1,4 @@
using System.Collections; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -6,137 +6,115 @@ using System.Text.RegularExpressions;
using System.Web; using System.Web;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
namespace webmusic.Pages {
public class IndexModel : PageModel {
private const string Root = "music";
public List<string> Dirs = new();
public string Displaypath = "";
public List<string> Files = new();
public string Fullpath = "";
public string Path = "";
public string PathOneup = "";
namespace webmusic.Pages public void OnGet() {
{
public class IndexModel : PageModel
{
public static string root = "music";
public string path = "";
public string displaypath = "";
public string path_oneup = "";
public string fullpath = "";
public List<string> dirs = new List<string>();
public List<string> files = new List<string>();
public void OnGet()
{
if (Request.QueryString.HasValue) if (Request.QueryString.HasValue)
path = HttpUtility.UrlDecode(Request.QueryString.Value.Remove(0,1) if (Request.QueryString.Value != null)
.Replace("+", "%2B")); Path = HttpUtility.UrlDecode(Request.QueryString.Value.TrimStart('?').Replace("+", "%2B"));
if (path.EndsWith(".m3u")) if (Path.Contains("/..")) {
path = path.Substring(0, path.Length - 4);
if (path.Contains("/.."))
{
Response.Redirect("/Error"); Response.Redirect("/Error");
return; return;
} }
path_oneup = Regex.Match(path, @".*(?=\/)").Value;
fullpath = root + path; if (Path.EndsWith(".lrc"))
displaypath = string.IsNullOrWhiteSpace(path) ? "/" : path; return;
dirs = Directory.GetDirectories(fullpath).Select(Path.GetFileName).ToList();
dirs.RemoveAll(p => p.StartsWith(".")); PathOneup = Regex.Match(Path, @".*(?=\/)").Value;
dirs.Sort(); Fullpath = Root + Path;
files = Directory.GetFiles(fullpath).Select(Path.GetFileName).ToList(); Displaypath = string.IsNullOrWhiteSpace(Path) ? "/" : Path;
files.RemoveAll(p => p.EndsWith(".m3u")); Dirs = Directory.GetDirectories(Fullpath).Select(System.IO.Path.GetFileName).ToList();
files.RemoveAll(p => p.StartsWith(".")); Dirs.RemoveAll(p => p.StartsWith("."));
files.Sort(new AlphanumComparatorFast()); Dirs.Sort();
Files = Directory.GetFiles(Fullpath).Select(System.IO.Path.GetFileName).ToList();
Files.RemoveAll(p => p.EndsWith(".m3u"));
Files.RemoveAll(p => p.EndsWith(".lrc"));
Files.RemoveAll(p => p.StartsWith("."));
Files.Sort(new AlphanumComparatorFast());
} }
public string Encode(string str) public static string Encode(string str) => str.Replace("\"", "%22")
{ .Replace("'", "%27")
return str.Replace("\"", "%22").Replace("'", "%27") .Replace("?", "%3F")
.Replace("?", "%3F").Replace("&", "%26") .Replace("&", "%26")
.Replace(" ", "%20"); .Replace(" ", "%20");
}
public class AlphanumComparatorFast : IComparer<string> private class AlphanumComparatorFast : IComparer<string> {
{ public int Compare(string x, string y) {
public int Compare(string x, string y) var s1 = x;
{
string s1 = x;
if (s1 == null) if (s1 == null)
{
return 0; return 0;
}
if (!(y is string s2)) if (!(y is { } s2))
{
return 0; return 0;
}
int len1 = s1.Length; var len1 = s1.Length;
int len2 = s2.Length; var len2 = s2.Length;
int marker1 = 0; var marker1 = 0;
int marker2 = 0; var marker2 = 0;
// Walk through two the strings with two markers. // Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2) while (marker1 < len1 && marker2 < len2) {
{ var ch1 = s1[marker1];
char ch1 = s1[marker1]; var ch2 = s2[marker2];
char ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk. // Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1]; var space1 = new char[len1];
int loc1 = 0; var loc1 = 0;
char[] space2 = new char[len2]; var space2 = new char[len2];
int loc2 = 0; var loc2 = 0;
// Walk through all following characters that are digits or // Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker. // characters in BOTH strings starting at the appropriate marker.
// Collect char arrays. // Collect char arrays.
do do {
{
space1[loc1++] = ch1; space1[loc1++] = ch1;
marker1++; marker1++;
if (marker1 < len1) if (marker1 < len1)
{
ch1 = s1[marker1]; ch1 = s1[marker1];
}
else else
{
break; break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0])); } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do do {
{
space2[loc2++] = ch2; space2[loc2++] = ch2;
marker2++; marker2++;
if (marker2 < len2) if (marker2 < len2)
{
ch2 = s2[marker2]; ch2 = s2[marker2];
}
else else
{
break; break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0])); } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically. // If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically. // Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1); var str1 = new string(space1);
string str2 = new string(space2); var str2 = new string(space2);
int result; int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0])) if (char.IsDigit(space1[0]) && char.IsDigit(space2[0])) {
{ var thisNumericChunk = int.Parse(str1);
int thisNumericChunk = int.Parse(str1); var thatNumericChunk = int.Parse(str2);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk); result = thisNumericChunk.CompareTo(thatNumericChunk);
} }
else else {
{ result = string.Compare(str1, str2, StringComparison.Ordinal);
result = str1.CompareTo(str2);
} }
if (result != 0) if (result != 0)
{
return result; return result;
} }
}
return len1 - len2; return len1 - len2;
} }
} }

View file

@ -1,19 +1,15 @@
@page @page
@using System.Web
@model IndexModel @model IndexModel
@{ @{
Layout = null; Layout = null;
Response.ContentType = "text/plain"; Response.ContentType = "text/plain";
} }
@if (Model.path.Contains("..")) @if (Model.Path.Contains("..")) {
{
return; return;
} }
@foreach (var dir in Model.dirs) @foreach (var dir in Model.Dirs) {
{
@Html.Raw(dir + "\n") @Html.Raw(dir + "\n")
} }
@foreach (var file in Model.files) @foreach (var file in Model.Files) {
{
@Html.Raw(file + "\n") @Html.Raw(file + "\n")
} }

View file

@ -6,11 +6,9 @@
Layout = null; Layout = null;
Response.ContentType = "text/plain"; Response.ContentType = "text/plain";
} }
@if (Model.path.Contains("..")) @if (Model.Path.Contains("..")) {
{
return; return;
} }
@foreach (var file in Model.files) @foreach (var file in Model.Files) {
{ @Html.Raw("https://" + Request.Host + "/" + Model.Fullpath + "/" + file.Replace("?", "%3F") + "\n")
@Html.Raw("https://" + Request.Host + "/" + Model.fullpath + "/" + file.Replace("?", "%3F") +"\n")
} }

View file

@ -1,25 +1,13 @@
using System; using Microsoft.AspNetCore;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace webmusic namespace webmusic {
{ public class Program {
public class Program public static void Main(string[] args) {
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run(); CreateWebHostBuilder(args).Build().Run();
} }
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args) WebHost.CreateDefaultBuilder(args).UseWebRoot(".").UseStartup<Startup>();
.UseWebRoot(".")
.UseStartup<Startup>();
} }
} }

View file

@ -1,65 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.StaticFiles; using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace webmusic namespace webmusic {
{ public class Startup {
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container. // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) public static void ConfigureServices(IServiceCollection services) {
{ services.Configure<CookiePolicyOptions>(options => {
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request. // This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true; options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None; options.MinimumSameSitePolicy = SameSiteMode.None;
}); });
services.Configure<MvcOptions>(options => { options.EnableEndpointRouting = false; }); services.Configure<MvcOptions>(options => { options.EnableEndpointRouting = false; });
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest);
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env) public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
{
var provider = new FileExtensionContentTypeProvider(); var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".flac"] = "audio/flac"; provider.Mappings[".flac"] = "audio/flac";
provider.Mappings[".m3u"] = "audio/m3u"; provider.Mappings[".m3u"] = "audio/m3u";
provider.Mappings[".opus"] = "audio/opus"; provider.Mappings[".opus"] = "audio/opus";
if (env.IsDevelopment()) if (env.IsDevelopment()) {
{
app.UseDeveloperExceptionPage(); app.UseDeveloperExceptionPage();
} }
else else {
{
app.UseExceptionHandler("/Error"); app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts(); app.UseHsts();
} }
app.UseStaticFiles(new StaticFileOptions() app.UseStaticFiles(new StaticFileOptions {ContentTypeProvider = provider});
{
ContentTypeProvider = provider
});
app.UseCookiePolicy(); app.UseCookiePolicy();
app.UseMvc(); app.UseMvc();