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

@ -1,26 +1,25 @@
@page @page
@model ErrorModel @model ErrorModel
@{ @{
ViewData["Title"] = "Error"; ViewData["Title"] = "Error";
} }
<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>
} }
<h3>Development Mode</h3> <h3>Development Mode</h3>
<p> <p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred. Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p> </p>
<p> <p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong> <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users. It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app. and restarting the app.
</p> </p>

View file

@ -1,23 +1,16 @@
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>
<h2>@Model.displaypath <span id="state"></span> <span id="flags"></span></h2> @Model.Displaypath <span id="state"></span> <span id="flags"></span>
<a class="action-muted">[..]</a> </h2>
<a href="?@Model.path_oneup" class="entry-muted cfont"> Go back</a><br/> <a class="action-muted">[..]</a>
<a class="action-muted">[--]</a> <a href="?@Model.PathOneup" class="entry-muted cfont"> Go back</a>
<a href="/playlist/@(Request.QueryString).m3u" class="entry-muted cfont"> Download playlist</a><br/><br/> <br/>
@foreach (var dir in Model.dirs) <a class="action-muted">[--]</a>
{ <a href="/playlist/@(Request.QueryString).m3u" class="entry-muted cfont"> Download playlist</a>
<a class="action" href="#">[--]</a> <br/>
<a href="?@Model.path/@dir"> @dir</a> <br/>
<br/> @foreach (var dir in Model.Dirs) {
} <a class="action" href="#">[--]</a>
@foreach (var file in Model.files) <a href="?@Model.Path/@dir"> @dir</a>
{ <br/>
var jspath = Model.Encode(Model.fullpath); }
var jsfile = jspath + "/" + Model.Encode(file); @foreach (var file in Model.Files) {
var jspath = IndexModel.Encode(Model.Fullpath);
<script>queue.push('@Html.Raw(jsfile)')</script> var jsfile = jspath + "/" + IndexModel.Encode(file);
<a class="action" href="@Html.Raw(Model.Encode(Model.fullpath+"/"+file))" download>[DL]</a> var basename = System.IO.Path.GetFileNameWithoutExtension(file);
<a href="#" onclick="playSong('@Html.Raw(jsfile)')"> @file</a> var lrcfile = basename + ".lrc";
<br/> var lrcpath = System.IO.Path.Combine(Model.Fullpath, lrcfile);
}
<script>queue.push('@Html.Raw(jsfile)')</script>
<a class="action" href="@Html.Raw(IndexModel.Encode(Model.Fullpath + "/" + file))" download>[DL]</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/>
}
} }

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

View file

@ -1,37 +1,37 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<title>@ViewData["Title"]</title> <title>@ViewData["Title"]</title>
<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono" rel="stylesheet">
<script src="/howler.core.js"></script> <script src="/howler.core.js"></script>
<script src="/webmusic.js"></script> <script src="/webmusic.js"></script>
<link href="/webmusic.css" rel="stylesheet"/> <link href="/webmusic.css" rel="stylesheet"/>
<link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-icon-57x57.png"> <link rel="apple-touch-icon" sizes="57x57" href="/favicon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-icon-60x60.png"> <link rel="apple-touch-icon" sizes="60x60" href="/favicon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-icon-72x72.png"> <link rel="apple-touch-icon" sizes="72x72" href="/favicon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-icon-76x76.png"> <link rel="apple-touch-icon" sizes="76x76" href="/favicon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-icon-114x114.png"> <link rel="apple-touch-icon" sizes="114x114" href="/favicon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-icon-120x120.png"> <link rel="apple-touch-icon" sizes="120x120" href="/favicon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-icon-144x144.png"> <link rel="apple-touch-icon" sizes="144x144" href="/favicon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-icon-152x152.png"> <link rel="apple-touch-icon" sizes="152x152" href="/favicon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-icon-180x180.png"> <link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-icon-192x192.png"> <link rel="icon" type="image/png" sizes="192x192" href="/favicon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png"> <link rel="icon" type="image/png" sizes="96x96" href="/favicon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
<link rel="manifest" href="/manifest.json"> <link rel="manifest" href="/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff"> <meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/favicon/ms-icon-144x144.png"> <meta name="msapplication-TileImage" content="/favicon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
</head> </head>
<body> <body>
<div id="container"> <div id="container">
@RenderBody() @RenderBody()
</div> </div>
</body> </body>
</html> </html>

View file

@ -1,3 +1,3 @@
@using webmusic @using webmusic
@namespace webmusic.Pages @namespace webmusic.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View file

@ -1,3 +1,3 @@
@{ @{
Layout = "_Layout"; Layout = "_Layout";
} }

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

@ -2,15 +2,13 @@
@using System.Web @using System.Web
@model IndexModel @model IndexModel
@{ @{
var path = HttpUtility.UrlDecode(Request.QueryString.Value.TrimStart('?')); var path = HttpUtility.UrlDecode(Request.QueryString.Value.TrimStart('?'));
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)
{
@Html.Raw("https://" + Request.Host + "/" + Model.fullpath + "/" + file.Replace("?", "%3F") +"\n")
} }
@foreach (var file in Model.Files) {
@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) {
{ CreateWebHostBuilder(args).Build().Run();
public static void Main(string[] args) }
{
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,68 +1,45 @@
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 // This method gets called by the runtime. Use this method to add services to the container.
{ public static void ConfigureServices(IServiceCollection services) {
public Startup(IConfiguration configuration) services.Configure<CookiePolicyOptions>(options => {
{ // This lambda determines whether user consent for non-essential cookies is needed for a given request.
Configuration = configuration; options.CheckConsentNeeded = context => true;
} options.MinimumSameSitePolicy = SameSiteMode.None;
});
public IConfiguration Configuration { get; } services.Configure<MvcOptions>(options => { options.EnableEndpointRouting = false; });
// This method gets called by the runtime. Use this method to add services to the container. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest);
public void ConfigureServices(IServiceCollection services) }
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".flac"] = "audio/flac";
provider.Mappings[".m3u"] = "audio/m3u";
provider.Mappings[".opus"] = "audio/opus";
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
else {
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.
app.UseHsts();
}
services.Configure<MvcOptions>(options => { options.EnableEndpointRouting = false; }); app.UseStaticFiles(new StaticFileOptions {ContentTypeProvider = provider});
app.UseCookiePolicy();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest); app.UseMvc();
} }
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".flac"] = "audio/flac";
provider.Mappings[".m3u"] = "audio/m3u";
provider.Mappings[".opus"] = "audio/opus";
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
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.
app.UseHsts();
}
app.UseStaticFiles(new StaticFileOptions()
{
ContentTypeProvider = provider
});
app.UseCookiePolicy();
app.UseMvc();
}
}
} }