mirror of
https://github.com/Polaris-Entertainment/bytefy.git
synced 2026-07-07 16:15:09 +00:00
46
.gitignore
vendored
46
.gitignore
vendored
@@ -1,42 +1,4 @@
|
|||||||
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
tools/.vscode
|
||||||
|
services/bytefy.image/bytefy.image/obj
|
||||||
# Compiled output
|
services/bytefy.image/bytefy.image/bin
|
||||||
/dist
|
services/bytefy.image/.idea
|
||||||
/tmp
|
|
||||||
/out-tsc
|
|
||||||
/bazel-out
|
|
||||||
|
|
||||||
# Node
|
|
||||||
/node_modules
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
.idea/
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.c9/
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# Visual Studio Code
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.history/*
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
/.angular/cache
|
|
||||||
.sass-cache/
|
|
||||||
/connect.lock
|
|
||||||
/coverage
|
|
||||||
/libpeerconnection.log
|
|
||||||
testem.log
|
|
||||||
/typings
|
|
||||||
|
|
||||||
# System files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|||||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
|
||||||
"recommendations": ["angular.ng-template"]
|
|
||||||
}
|
|
||||||
53
.vscode/launch.json
vendored
53
.vscode/launch.json
vendored
@@ -1,20 +1,35 @@
|
|||||||
{
|
{
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
"version": "0.2.0",
|
||||||
"version": "0.2.0",
|
"configurations": [
|
||||||
"configurations": [
|
{
|
||||||
{
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
"name": "ng serve",
|
// Use hover for the description of the existing attributes
|
||||||
"type": "chrome",
|
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
|
||||||
"request": "launch",
|
"name": ".NET Core Launch (web)",
|
||||||
"preLaunchTask": "npm: start",
|
"type": "coreclr",
|
||||||
"url": "http://localhost:4200/"
|
"request": "launch",
|
||||||
},
|
"preLaunchTask": "build",
|
||||||
{
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
"name": "ng test",
|
"program": "${workspaceFolder}/services/bytefy.image/bytefy.image/bin/Debug/net8.0/bytefy.image.dll",
|
||||||
"type": "chrome",
|
"args": [],
|
||||||
"request": "launch",
|
"cwd": "${workspaceFolder}/services/bytefy.image/bytefy.image",
|
||||||
"preLaunchTask": "npm: test",
|
"stopAtEntry": false,
|
||||||
"url": "http://localhost:9876/debug.html"
|
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
||||||
}
|
"serverReadyAction": {
|
||||||
]
|
"action": "openExternally",
|
||||||
}
|
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"sourceFileMap": {
|
||||||
|
"/Views": "${workspaceFolder}/Views"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
79
.vscode/tasks.json
vendored
79
.vscode/tasks.json
vendored
@@ -1,42 +1,41 @@
|
|||||||
{
|
{
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
"version": "2.0.0",
|
||||||
"version": "2.0.0",
|
"tasks": [
|
||||||
"tasks": [
|
{
|
||||||
{
|
"label": "build",
|
||||||
"type": "npm",
|
"command": "dotnet",
|
||||||
"script": "start",
|
"type": "process",
|
||||||
"isBackground": true,
|
"args": [
|
||||||
"problemMatcher": {
|
"build",
|
||||||
"owner": "typescript",
|
"${workspaceFolder}/services/bytefy.image/bytefy.image.sln",
|
||||||
"pattern": "$tsc",
|
"/property:GenerateFullPaths=true",
|
||||||
"background": {
|
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||||
"activeOnStart": true,
|
],
|
||||||
"beginsPattern": {
|
"problemMatcher": "$msCompile"
|
||||||
"regexp": "(.*?)"
|
},
|
||||||
},
|
{
|
||||||
"endsPattern": {
|
"label": "publish",
|
||||||
"regexp": "bundle generation complete"
|
"command": "dotnet",
|
||||||
}
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/services/bytefy.image/bytefy.image.sln",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"--project",
|
||||||
|
"${workspaceFolder}/services/bytefy.image/bytefy.image.sln"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
},
|
}
|
||||||
{
|
|
||||||
"type": "npm",
|
|
||||||
"script": "test",
|
|
||||||
"isBackground": true,
|
|
||||||
"problemMatcher": {
|
|
||||||
"owner": "typescript",
|
|
||||||
"pattern": "$tsc",
|
|
||||||
"background": {
|
|
||||||
"activeOnStart": true,
|
|
||||||
"beginsPattern": {
|
|
||||||
"regexp": "(.*?)"
|
|
||||||
},
|
|
||||||
"endsPattern": {
|
|
||||||
"regexp": "bundle generation complete"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
25
services/bytefy.image/.dockerignore
Normal file
25
services/bytefy.image/.dockerignore
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/.idea
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
21
services/bytefy.image/bytefy.image.sln
Normal file
21
services/bytefy.image/bytefy.image.sln
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "bytefy.image", "bytefy.image\bytefy.image.csproj", "{FEB931BB-C67F-4754-A05F-ECAAC83628C4}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E72677F8-7D41-48DB-AF14-D3A188D11AFA}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
docker-compose.yml = docker-compose.yml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{FEB931BB-C67F-4754-A05F-ECAAC83628C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{FEB931BB-C67F-4754-A05F-ECAAC83628C4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{FEB931BB-C67F-4754-A05F-ECAAC83628C4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{FEB931BB-C67F-4754-A05F-ECAAC83628C4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using ImageMagick;
|
||||||
|
|
||||||
|
namespace bytefy.image;
|
||||||
|
|
||||||
|
public class ConversionTask
|
||||||
|
{
|
||||||
|
public byte[] ImageData { get; set; }
|
||||||
|
public MagickFormat Format { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConversionQueueService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly ConcurrentQueue<(ConversionTask Task, TaskCompletionSource<(byte[], string)> CompletionSource)> _queue = new();
|
||||||
|
|
||||||
|
public void QueueConversion(ConversionTask task, TaskCompletionSource<(byte[], string)> completionSource)
|
||||||
|
{
|
||||||
|
_queue.Enqueue((task, completionSource));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (_queue.TryDequeue(out var item))
|
||||||
|
{
|
||||||
|
var result = await ProcessConversionAsync(item.Task);
|
||||||
|
item.CompletionSource.SetResult(result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Task.Delay(50, stoppingToken); // Shorten the delay to check the queue more frequently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<(byte[], string)> ProcessConversionAsync(ConversionTask task)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var magickImage = new MagickImage(task.ImageData);
|
||||||
|
magickImage.Format = task.Format;
|
||||||
|
var resultStream = new MemoryStream();
|
||||||
|
magickImage.Write(resultStream);
|
||||||
|
resultStream.Position = 0;
|
||||||
|
|
||||||
|
var mimeType = MimeTypes.MimeTypeMap.GetMimeType($"image/{task.Format.ToString().ToLower()}");
|
||||||
|
return Task.FromResult((resultStream.ToArray(), mimeType));
|
||||||
|
}
|
||||||
|
catch (MagickImageErrorException ex)
|
||||||
|
{
|
||||||
|
// Log the error message
|
||||||
|
Console.WriteLine($"Image conversion failed: {ex.Message}");
|
||||||
|
|
||||||
|
// Return a default value or handle the error as appropriate for your application
|
||||||
|
return Task.FromResult<(byte[], string)>((null, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
services/bytefy.image/bytefy.image/Dockerfile
Normal file
23
services/bytefy.image/bytefy.image/Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER $APP_UID
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
EXPOSE 8081
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["bytefy.image/bytefy.image.csproj", "bytefy.image/"]
|
||||||
|
RUN dotnet restore "bytefy.image/bytefy.image.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/bytefy.image"
|
||||||
|
RUN dotnet build "bytefy.image.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "bytefy.image.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "bytefy.image.dll"]
|
||||||
833
services/bytefy.image/bytefy.image/MimeTypeMap.cs
Normal file
833
services/bytefy.image/bytefy.image/MimeTypeMap.cs
Normal file
@@ -0,0 +1,833 @@
|
|||||||
|
namespace MimeTypes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class MimeTypeMap.
|
||||||
|
/// </summary>
|
||||||
|
public static class MimeTypeMap
|
||||||
|
{
|
||||||
|
private const string Dot = ".";
|
||||||
|
private const string QuestionMark = "?";
|
||||||
|
private const string DefaultMimeType = "application/octet-stream";
|
||||||
|
private static readonly Lazy<IDictionary<string, string>> Mappings = new(BuildMappings);
|
||||||
|
|
||||||
|
private static IDictionary<string, string> BuildMappings()
|
||||||
|
{
|
||||||
|
var mappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
|
||||||
|
|
||||||
|
#region Big freaking list of mime types
|
||||||
|
|
||||||
|
// maps both ways,
|
||||||
|
// extension -> mime type
|
||||||
|
// and
|
||||||
|
// mime type -> extension
|
||||||
|
//
|
||||||
|
// any mime types on left side not pre-loaded on right side, are added automatically
|
||||||
|
// some mime types can map to multiple extensions, so to get a deterministic mapping,
|
||||||
|
// add those to the dictionary specifically
|
||||||
|
//
|
||||||
|
// combination of values from Windows 7 Registry and
|
||||||
|
// from C:\Windows\System32\inetsrv\config\applicationHost.config
|
||||||
|
// some added, including .7z and .dat
|
||||||
|
//
|
||||||
|
// Some added based on http://www.iana.org/assignments/media-types/media-types.xhtml
|
||||||
|
// which lists mime types, but not extensions
|
||||||
|
//
|
||||||
|
{".323", "text/h323"},
|
||||||
|
{".3g2", "video/3gpp2"},
|
||||||
|
{".3gp", "video/3gpp"},
|
||||||
|
{".3gp2", "video/3gpp2"},
|
||||||
|
{".3gpp", "video/3gpp"},
|
||||||
|
{".7z", "application/x-7z-compressed"},
|
||||||
|
{".aa", "audio/audible"},
|
||||||
|
{".AAC", "audio/aac"},
|
||||||
|
{".aaf", "application/octet-stream"},
|
||||||
|
{".aax", "audio/vnd.audible.aax"},
|
||||||
|
{".ac3", "audio/ac3"},
|
||||||
|
{".aca", "application/octet-stream"},
|
||||||
|
{".accda", "application/msaccess.addin"},
|
||||||
|
{".accdb", "application/msaccess"},
|
||||||
|
{".accdc", "application/msaccess.cab"},
|
||||||
|
{".accde", "application/msaccess"},
|
||||||
|
{".accdr", "application/msaccess.runtime"},
|
||||||
|
{".accdt", "application/msaccess"},
|
||||||
|
{".accdw", "application/msaccess.webapplication"},
|
||||||
|
{".accft", "application/msaccess.ftemplate"},
|
||||||
|
{".acx", "application/internet-property-stream"},
|
||||||
|
{".AddIn", "text/xml"},
|
||||||
|
{".ade", "application/msaccess"},
|
||||||
|
{".adobebridge", "application/x-bridge-url"},
|
||||||
|
{".adp", "application/msaccess"},
|
||||||
|
{".ADT", "audio/vnd.dlna.adts"},
|
||||||
|
{".ADTS", "audio/aac"},
|
||||||
|
{".afm", "application/octet-stream"},
|
||||||
|
{".ai", "application/postscript"},
|
||||||
|
{".aif", "audio/aiff"},
|
||||||
|
{".aifc", "audio/aiff"},
|
||||||
|
{".aiff", "audio/aiff"},
|
||||||
|
{".air", "application/vnd.adobe.air-application-installer-package+zip"},
|
||||||
|
{".amc", "application/mpeg"},
|
||||||
|
{".anx", "application/annodex"},
|
||||||
|
{".apk", "application/vnd.android.package-archive"},
|
||||||
|
{".apng", "image/apng"},
|
||||||
|
{".application", "application/x-ms-application"},
|
||||||
|
{".art", "image/x-jg"},
|
||||||
|
{".asa", "application/xml"},
|
||||||
|
{".asax", "application/xml"},
|
||||||
|
{".ascx", "application/xml"},
|
||||||
|
{".asd", "application/octet-stream"},
|
||||||
|
{".asf", "video/x-ms-asf"},
|
||||||
|
{".ashx", "application/xml"},
|
||||||
|
{".asi", "application/octet-stream"},
|
||||||
|
{".asm", "text/plain"},
|
||||||
|
{".asmx", "application/xml"},
|
||||||
|
{".aspx", "application/xml"},
|
||||||
|
{".asr", "video/x-ms-asf"},
|
||||||
|
{".asx", "video/x-ms-asf"},
|
||||||
|
{".atom", "application/atom+xml"},
|
||||||
|
{".au", "audio/basic"},
|
||||||
|
{".avci", "image/avci"},
|
||||||
|
{".avcs", "image/avcs"},
|
||||||
|
{".avi", "video/x-msvideo"},
|
||||||
|
{".avif", "image/avif"},
|
||||||
|
{".avifs", "image/avif-sequence"},
|
||||||
|
{".axa", "audio/annodex"},
|
||||||
|
{".axs", "application/olescript"},
|
||||||
|
{".axv", "video/annodex"},
|
||||||
|
{".bas", "text/plain"},
|
||||||
|
{".bcpio", "application/x-bcpio"},
|
||||||
|
{".bin", "application/octet-stream"},
|
||||||
|
{".bmp", "image/bmp"},
|
||||||
|
{".c", "text/plain"},
|
||||||
|
{".cab", "application/octet-stream"},
|
||||||
|
{".caf", "audio/x-caf"},
|
||||||
|
{".calx", "application/vnd.ms-office.calx"},
|
||||||
|
{".cat", "application/vnd.ms-pki.seccat"},
|
||||||
|
{".cc", "text/plain"},
|
||||||
|
{".cd", "text/plain"},
|
||||||
|
{".cdda", "audio/aiff"},
|
||||||
|
{".cdf", "application/x-cdf"},
|
||||||
|
{".cer", "application/x-x509-ca-cert"},
|
||||||
|
{".cfg", "text/plain"},
|
||||||
|
{".chm", "application/octet-stream"},
|
||||||
|
{".class", "application/x-java-applet"},
|
||||||
|
{".clp", "application/x-msclip"},
|
||||||
|
{".cmd", "text/plain"},
|
||||||
|
{".cmx", "image/x-cmx"},
|
||||||
|
{".cnf", "text/plain"},
|
||||||
|
{".cod", "image/cis-cod"},
|
||||||
|
{".config", "application/xml"},
|
||||||
|
{".contact", "text/x-ms-contact"},
|
||||||
|
{".coverage", "application/xml"},
|
||||||
|
{".cpio", "application/x-cpio"},
|
||||||
|
{".cpp", "text/plain"},
|
||||||
|
{".crd", "application/x-mscardfile"},
|
||||||
|
{".crl", "application/pkix-crl"},
|
||||||
|
{".crt", "application/x-x509-ca-cert"},
|
||||||
|
{".cs", "text/plain"},
|
||||||
|
{".csdproj", "text/plain"},
|
||||||
|
{".csh", "application/x-csh"},
|
||||||
|
{".csproj", "text/plain"},
|
||||||
|
{".css", "text/css"},
|
||||||
|
{".csv", "text/csv"},
|
||||||
|
{".cur", "application/octet-stream"},
|
||||||
|
{".czx", "application/x-czx"},
|
||||||
|
{".cxx", "text/plain"},
|
||||||
|
{".dat", "application/octet-stream"},
|
||||||
|
{".datasource", "application/xml"},
|
||||||
|
{".dbproj", "text/plain"},
|
||||||
|
{".dcr", "application/x-director"},
|
||||||
|
{".def", "text/plain"},
|
||||||
|
{".deploy", "application/octet-stream"},
|
||||||
|
{".der", "application/x-x509-ca-cert"},
|
||||||
|
{".dgml", "application/xml"},
|
||||||
|
{".dib", "image/bmp"},
|
||||||
|
{".dif", "video/x-dv"},
|
||||||
|
{".dir", "application/x-director"},
|
||||||
|
{".disco", "text/xml"},
|
||||||
|
{".divx", "video/divx"},
|
||||||
|
{".dll", "application/x-msdownload"},
|
||||||
|
{".dll.config", "text/xml"},
|
||||||
|
{".dlm", "text/dlm"},
|
||||||
|
{".doc", "application/msword"},
|
||||||
|
{".docm", "application/vnd.ms-word.document.macroEnabled.12"},
|
||||||
|
{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
|
||||||
|
{".dot", "application/msword"},
|
||||||
|
{".dotm", "application/vnd.ms-word.template.macroEnabled.12"},
|
||||||
|
{".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
|
||||||
|
{".dsp", "application/octet-stream"},
|
||||||
|
{".dsw", "text/plain"},
|
||||||
|
{".dtd", "text/xml"},
|
||||||
|
{".dtsConfig", "text/xml"},
|
||||||
|
{".dv", "video/x-dv"},
|
||||||
|
{".dvi", "application/x-dvi"},
|
||||||
|
{".dwf", "drawing/x-dwf"},
|
||||||
|
{".dwg", "application/acad"},
|
||||||
|
{".dwp", "application/octet-stream"},
|
||||||
|
{".dxf", "application/x-dxf"},
|
||||||
|
{".dxr", "application/x-director"},
|
||||||
|
{".eml", "message/rfc822"},
|
||||||
|
{".emf", "image/emf"},
|
||||||
|
{".emz", "application/octet-stream"},
|
||||||
|
{".eot", "application/vnd.ms-fontobject"},
|
||||||
|
{".eps", "application/postscript"},
|
||||||
|
{".es", "application/ecmascript"},
|
||||||
|
{".etl", "application/etl"},
|
||||||
|
{".etx", "text/x-setext"},
|
||||||
|
{".evy", "application/envoy"},
|
||||||
|
{".exe", "application/vnd.microsoft.portable-executable"},
|
||||||
|
{".exe.config", "text/xml"},
|
||||||
|
{".f4v", "video/mp4"},
|
||||||
|
{".fdf", "application/vnd.fdf"},
|
||||||
|
{".fif", "application/fractals"},
|
||||||
|
{".filters", "application/xml"},
|
||||||
|
{".fla", "application/octet-stream"},
|
||||||
|
{".flac", "audio/flac"},
|
||||||
|
{".flr", "x-world/x-vrml"},
|
||||||
|
{".flv", "video/x-flv"},
|
||||||
|
{".fsscript", "application/fsharp-script"},
|
||||||
|
{".fsx", "application/fsharp-script"},
|
||||||
|
{".generictest", "application/xml"},
|
||||||
|
{".geojson", "application/geo+json"},
|
||||||
|
{".gif", "image/gif"},
|
||||||
|
{".gml", "application/gml+xml"},
|
||||||
|
{".gpx", "application/gpx+xml"},
|
||||||
|
{".group", "text/x-ms-group"},
|
||||||
|
{".gsm", "audio/x-gsm"},
|
||||||
|
{".gtar", "application/x-gtar"},
|
||||||
|
{".gz", "application/x-gzip"},
|
||||||
|
{".h", "text/plain"},
|
||||||
|
{".hdf", "application/x-hdf"},
|
||||||
|
{".hdml", "text/x-hdml"},
|
||||||
|
{".heic", "image/heic"},
|
||||||
|
{".heics", "image/heic-sequence"},
|
||||||
|
{".heif", "image/heif"},
|
||||||
|
{".heifs", "image/heif-sequence"},
|
||||||
|
{".hhc", "application/x-oleobject"},
|
||||||
|
{".hhk", "application/octet-stream"},
|
||||||
|
{".hhp", "application/octet-stream"},
|
||||||
|
{".hlp", "application/winhlp"},
|
||||||
|
{".hpp", "text/plain"},
|
||||||
|
{".hqx", "application/mac-binhex40"},
|
||||||
|
{".hta", "application/hta"},
|
||||||
|
{".htc", "text/x-component"},
|
||||||
|
{".htm", "text/html"},
|
||||||
|
{".html", "text/html"},
|
||||||
|
{".htt", "text/webviewhtml"},
|
||||||
|
{".hxa", "application/xml"},
|
||||||
|
{".hxc", "application/xml"},
|
||||||
|
{".hxd", "application/octet-stream"},
|
||||||
|
{".hxe", "application/xml"},
|
||||||
|
{".hxf", "application/xml"},
|
||||||
|
{".hxh", "application/octet-stream"},
|
||||||
|
{".hxi", "application/octet-stream"},
|
||||||
|
{".hxk", "application/xml"},
|
||||||
|
{".hxq", "application/octet-stream"},
|
||||||
|
{".hxr", "application/octet-stream"},
|
||||||
|
{".hxs", "application/octet-stream"},
|
||||||
|
{".hxt", "text/html"},
|
||||||
|
{".hxv", "application/xml"},
|
||||||
|
{".hxw", "application/octet-stream"},
|
||||||
|
{".hxx", "text/plain"},
|
||||||
|
{".i", "text/plain"},
|
||||||
|
{".ical", "text/calendar"},
|
||||||
|
{".icalendar", "text/calendar"},
|
||||||
|
{".ico", "image/x-icon"},
|
||||||
|
{".ics", "text/calendar"},
|
||||||
|
{".idl", "text/plain"},
|
||||||
|
{".ief", "image/ief"},
|
||||||
|
{".ifb", "text/calendar"},
|
||||||
|
{".iii", "application/x-iphone"},
|
||||||
|
{".inc", "text/plain"},
|
||||||
|
{".inf", "application/octet-stream"},
|
||||||
|
{".ini", "text/plain"},
|
||||||
|
{".inl", "text/plain"},
|
||||||
|
{".ins", "application/x-internet-signup"},
|
||||||
|
{".ipa", "application/x-itunes-ipa"},
|
||||||
|
{".ipg", "application/x-itunes-ipg"},
|
||||||
|
{".ipproj", "text/plain"},
|
||||||
|
{".ipsw", "application/x-itunes-ipsw"},
|
||||||
|
{".iqy", "text/x-ms-iqy"},
|
||||||
|
{".isp", "application/x-internet-signup"},
|
||||||
|
{".isma", "application/octet-stream"},
|
||||||
|
{".ismv", "application/octet-stream"},
|
||||||
|
{".ite", "application/x-itunes-ite"},
|
||||||
|
{".itlp", "application/x-itunes-itlp"},
|
||||||
|
{".itms", "application/x-itunes-itms"},
|
||||||
|
{".itpc", "application/x-itunes-itpc"},
|
||||||
|
{".IVF", "video/x-ivf"},
|
||||||
|
{".jar", "application/java-archive"},
|
||||||
|
{".java", "application/octet-stream"},
|
||||||
|
{".jck", "application/liquidmotion"},
|
||||||
|
{".jcz", "application/liquidmotion"},
|
||||||
|
{".jfif", "image/pjpeg"},
|
||||||
|
{".jnlp", "application/x-java-jnlp-file"},
|
||||||
|
{".jpb", "application/octet-stream"},
|
||||||
|
{".jpe", "image/jpeg"},
|
||||||
|
{".jpeg", "image/jpeg"},
|
||||||
|
{".jpg", "image/jpeg"},
|
||||||
|
{".js", "application/javascript"},
|
||||||
|
{".json", "application/json"},
|
||||||
|
{".jsx", "text/jscript"},
|
||||||
|
{".jsxbin", "text/plain"},
|
||||||
|
{".key", "application/vnd.apple.keynote"},
|
||||||
|
{".latex", "application/x-latex"},
|
||||||
|
{".library-ms", "application/windows-library+xml"},
|
||||||
|
{".lit", "application/x-ms-reader"},
|
||||||
|
{".loadtest", "application/xml"},
|
||||||
|
{".lpk", "application/octet-stream"},
|
||||||
|
{".lsf", "video/x-la-asf"},
|
||||||
|
{".lst", "text/plain"},
|
||||||
|
{".lsx", "video/x-la-asf"},
|
||||||
|
{".lzh", "application/octet-stream"},
|
||||||
|
{".m13", "application/x-msmediaview"},
|
||||||
|
{".m14", "application/x-msmediaview"},
|
||||||
|
{".m1v", "video/mpeg"},
|
||||||
|
{".m2t", "video/vnd.dlna.mpeg-tts"},
|
||||||
|
{".m2ts", "video/vnd.dlna.mpeg-tts"},
|
||||||
|
{".m2v", "video/mpeg"},
|
||||||
|
{".m3u", "audio/x-mpegurl"},
|
||||||
|
{".m3u8", "audio/x-mpegurl"},
|
||||||
|
{".m4a", "audio/m4a"},
|
||||||
|
{".m4b", "audio/m4b"},
|
||||||
|
{".m4p", "audio/m4p"},
|
||||||
|
{".m4r", "audio/x-m4r"},
|
||||||
|
{".m4v", "video/x-m4v"},
|
||||||
|
{".mac", "image/x-macpaint"},
|
||||||
|
{".mak", "text/plain"},
|
||||||
|
{".man", "application/x-troff-man"},
|
||||||
|
{".manifest", "application/x-ms-manifest"},
|
||||||
|
{".map", "text/plain"},
|
||||||
|
{".master", "application/xml"},
|
||||||
|
{".mbox", "application/mbox"},
|
||||||
|
{".mda", "application/msaccess"},
|
||||||
|
{".mdb", "application/x-msaccess"},
|
||||||
|
{".mde", "application/msaccess"},
|
||||||
|
{".mdp", "application/octet-stream"},
|
||||||
|
{".me", "application/x-troff-me"},
|
||||||
|
{".mfp", "application/x-shockwave-flash"},
|
||||||
|
{".mht", "message/rfc822"},
|
||||||
|
{".mhtml", "message/rfc822"},
|
||||||
|
{".mid", "audio/mid"},
|
||||||
|
{".midi", "audio/mid"},
|
||||||
|
{".mix", "application/octet-stream"},
|
||||||
|
{".mk", "text/plain"},
|
||||||
|
{".mk3d", "video/x-matroska-3d"},
|
||||||
|
{".mka", "audio/x-matroska"},
|
||||||
|
{".mkv", "video/x-matroska"},
|
||||||
|
{".mmf", "application/x-smaf"},
|
||||||
|
{".mno", "text/xml"},
|
||||||
|
{".mny", "application/x-msmoney"},
|
||||||
|
{".mod", "video/mpeg"},
|
||||||
|
{".mov", "video/quicktime"},
|
||||||
|
{".movie", "video/x-sgi-movie"},
|
||||||
|
{".mp2", "video/mpeg"},
|
||||||
|
{".mp2v", "video/mpeg"},
|
||||||
|
{".mp3", "audio/mpeg"},
|
||||||
|
{".mp4", "video/mp4"},
|
||||||
|
{".mp4v", "video/mp4"},
|
||||||
|
{".mpa", "video/mpeg"},
|
||||||
|
{".mpe", "video/mpeg"},
|
||||||
|
{".mpeg", "video/mpeg"},
|
||||||
|
{".mpf", "application/vnd.ms-mediapackage"},
|
||||||
|
{".mpg", "video/mpeg"},
|
||||||
|
{".mpp", "application/vnd.ms-project"},
|
||||||
|
{".mpv2", "video/mpeg"},
|
||||||
|
{".mqv", "video/quicktime"},
|
||||||
|
{".ms", "application/x-troff-ms"},
|
||||||
|
{".msg", "application/vnd.ms-outlook"},
|
||||||
|
{".msi", "application/octet-stream"},
|
||||||
|
{".mso", "application/octet-stream"},
|
||||||
|
{".mts", "video/vnd.dlna.mpeg-tts"},
|
||||||
|
{".mtx", "application/xml"},
|
||||||
|
{".mvb", "application/x-msmediaview"},
|
||||||
|
{".mvc", "application/x-miva-compiled"},
|
||||||
|
{".mxf", "application/mxf"},
|
||||||
|
{".mxp", "application/x-mmxp"},
|
||||||
|
{".nc", "application/x-netcdf"},
|
||||||
|
{".nsc", "video/x-ms-asf"},
|
||||||
|
{".numbers", "application/vnd.apple.numbers"},
|
||||||
|
{".nws", "message/rfc822"},
|
||||||
|
{".ocx", "application/octet-stream"},
|
||||||
|
{".oda", "application/oda"},
|
||||||
|
{".odb", "application/vnd.oasis.opendocument.database"},
|
||||||
|
{".odc", "application/vnd.oasis.opendocument.chart"},
|
||||||
|
{".odf", "application/vnd.oasis.opendocument.formula"},
|
||||||
|
{".odg", "application/vnd.oasis.opendocument.graphics"},
|
||||||
|
{".odh", "text/plain"},
|
||||||
|
{".odi", "application/vnd.oasis.opendocument.image"},
|
||||||
|
{".odl", "text/plain"},
|
||||||
|
{".odm", "application/vnd.oasis.opendocument.text-master"},
|
||||||
|
{".odp", "application/vnd.oasis.opendocument.presentation"},
|
||||||
|
{".ods", "application/vnd.oasis.opendocument.spreadsheet"},
|
||||||
|
{".odt", "application/vnd.oasis.opendocument.text"},
|
||||||
|
{".oga", "audio/ogg"},
|
||||||
|
{".ogg", "audio/ogg"},
|
||||||
|
{".ogv", "video/ogg"},
|
||||||
|
{".ogx", "application/ogg"},
|
||||||
|
{".one", "application/onenote"},
|
||||||
|
{".onea", "application/onenote"},
|
||||||
|
{".onepkg", "application/onenote"},
|
||||||
|
{".onetmp", "application/onenote"},
|
||||||
|
{".onetoc", "application/onenote"},
|
||||||
|
{".onetoc2", "application/onenote"},
|
||||||
|
{".opus", "audio/ogg"},
|
||||||
|
{".orderedtest", "application/xml"},
|
||||||
|
{".osdx", "application/opensearchdescription+xml"},
|
||||||
|
{".otf", "application/font-sfnt"},
|
||||||
|
{".otg", "application/vnd.oasis.opendocument.graphics-template"},
|
||||||
|
{".oth", "application/vnd.oasis.opendocument.text-web"},
|
||||||
|
{".otp", "application/vnd.oasis.opendocument.presentation-template"},
|
||||||
|
{".ots", "application/vnd.oasis.opendocument.spreadsheet-template"},
|
||||||
|
{".ott", "application/vnd.oasis.opendocument.text-template"},
|
||||||
|
{".oxps", "application/oxps"},
|
||||||
|
{".oxt", "application/vnd.openofficeorg.extension"},
|
||||||
|
{".p10", "application/pkcs10"},
|
||||||
|
{".p12", "application/x-pkcs12"},
|
||||||
|
{".p7b", "application/x-pkcs7-certificates"},
|
||||||
|
{".p7c", "application/pkcs7-mime"},
|
||||||
|
{".p7m", "application/pkcs7-mime"},
|
||||||
|
{".p7r", "application/x-pkcs7-certreqresp"},
|
||||||
|
{".p7s", "application/pkcs7-signature"},
|
||||||
|
{".pages", "application/vnd.apple.pages"},
|
||||||
|
{".pbm", "image/x-portable-bitmap"},
|
||||||
|
{".pcast", "application/x-podcast"},
|
||||||
|
{".pct", "image/pict"},
|
||||||
|
{".pcx", "application/octet-stream"},
|
||||||
|
{".pcz", "application/octet-stream"},
|
||||||
|
{".pdf", "application/pdf"},
|
||||||
|
{".pfb", "application/octet-stream"},
|
||||||
|
{".pfm", "application/octet-stream"},
|
||||||
|
{".pfx", "application/x-pkcs12"},
|
||||||
|
{".pgm", "image/x-portable-graymap"},
|
||||||
|
{".pic", "image/pict"},
|
||||||
|
{".pict", "image/pict"},
|
||||||
|
{".pkgdef", "text/plain"},
|
||||||
|
{".pkgundef", "text/plain"},
|
||||||
|
{".pko", "application/vnd.ms-pki.pko"},
|
||||||
|
{".pls", "audio/scpls"},
|
||||||
|
{".pma", "application/x-perfmon"},
|
||||||
|
{".pmc", "application/x-perfmon"},
|
||||||
|
{".pml", "application/x-perfmon"},
|
||||||
|
{".pmr", "application/x-perfmon"},
|
||||||
|
{".pmw", "application/x-perfmon"},
|
||||||
|
{".png", "image/png"},
|
||||||
|
{".pnm", "image/x-portable-anymap"},
|
||||||
|
{".pnt", "image/x-macpaint"},
|
||||||
|
{".pntg", "image/x-macpaint"},
|
||||||
|
{".pnz", "image/png"},
|
||||||
|
{".pot", "application/vnd.ms-powerpoint"},
|
||||||
|
{".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"},
|
||||||
|
{".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"},
|
||||||
|
{".ppa", "application/vnd.ms-powerpoint"},
|
||||||
|
{".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"},
|
||||||
|
{".ppm", "image/x-portable-pixmap"},
|
||||||
|
{".pps", "application/vnd.ms-powerpoint"},
|
||||||
|
{".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
|
||||||
|
{".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
|
||||||
|
{".ppt", "application/vnd.ms-powerpoint"},
|
||||||
|
{".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
|
||||||
|
{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
|
||||||
|
{".prf", "application/pics-rules"},
|
||||||
|
{".prm", "application/octet-stream"},
|
||||||
|
{".prx", "application/octet-stream"},
|
||||||
|
{".ps", "application/postscript"},
|
||||||
|
{".psc1", "application/PowerShell"},
|
||||||
|
{".psd", "application/octet-stream"},
|
||||||
|
{".psess", "application/xml"},
|
||||||
|
{".psm", "application/octet-stream"},
|
||||||
|
{".psp", "application/octet-stream"},
|
||||||
|
{".pst", "application/vnd.ms-outlook"},
|
||||||
|
{".pub", "application/x-mspublisher"},
|
||||||
|
{".pwz", "application/vnd.ms-powerpoint"},
|
||||||
|
{".qht", "text/x-html-insertion"},
|
||||||
|
{".qhtm", "text/x-html-insertion"},
|
||||||
|
{".qt", "video/quicktime"},
|
||||||
|
{".qti", "image/x-quicktime"},
|
||||||
|
{".qtif", "image/x-quicktime"},
|
||||||
|
{".qtl", "application/x-quicktimeplayer"},
|
||||||
|
{".qxd", "application/octet-stream"},
|
||||||
|
{".ra", "audio/x-pn-realaudio"},
|
||||||
|
{".ram", "audio/x-pn-realaudio"},
|
||||||
|
{".rar", "application/x-rar-compressed"},
|
||||||
|
{".ras", "image/x-cmu-raster"},
|
||||||
|
{".rat", "application/rat-file"},
|
||||||
|
{".rc", "text/plain"},
|
||||||
|
{".rc2", "text/plain"},
|
||||||
|
{".rct", "text/plain"},
|
||||||
|
{".rdlc", "application/xml"},
|
||||||
|
{".reg", "text/plain"},
|
||||||
|
{".resx", "application/xml"},
|
||||||
|
{".rf", "image/vnd.rn-realflash"},
|
||||||
|
{".rgb", "image/x-rgb"},
|
||||||
|
{".rgs", "text/plain"},
|
||||||
|
{".rm", "application/vnd.rn-realmedia"},
|
||||||
|
{".rmi", "audio/mid"},
|
||||||
|
{".rmp", "application/vnd.rn-rn_music_package"},
|
||||||
|
{".rmvb", "application/vnd.rn-realmedia-vbr"},
|
||||||
|
{".roff", "application/x-troff"},
|
||||||
|
{".rpm", "audio/x-pn-realaudio-plugin"},
|
||||||
|
{".rqy", "text/x-ms-rqy"},
|
||||||
|
{".rtf", "application/rtf"},
|
||||||
|
{".rtx", "text/richtext"},
|
||||||
|
{".rvt", "application/octet-stream"},
|
||||||
|
{".ruleset", "application/xml"},
|
||||||
|
{".s", "text/plain"},
|
||||||
|
{".safariextz", "application/x-safari-safariextz"},
|
||||||
|
{".scd", "application/x-msschedule"},
|
||||||
|
{".scr", "text/plain"},
|
||||||
|
{".sct", "text/scriptlet"},
|
||||||
|
{".sd2", "audio/x-sd2"},
|
||||||
|
{".sdp", "application/sdp"},
|
||||||
|
{".sea", "application/octet-stream"},
|
||||||
|
{".searchConnector-ms", "application/windows-search-connector+xml"},
|
||||||
|
{".setpay", "application/set-payment-initiation"},
|
||||||
|
{".setreg", "application/set-registration-initiation"},
|
||||||
|
{".settings", "application/xml"},
|
||||||
|
{".sgimb", "application/x-sgimb"},
|
||||||
|
{".sgml", "text/sgml"},
|
||||||
|
{".sh", "application/x-sh"},
|
||||||
|
{".shar", "application/x-shar"},
|
||||||
|
{".shtml", "text/html"},
|
||||||
|
{".sit", "application/x-stuffit"},
|
||||||
|
{".sitemap", "application/xml"},
|
||||||
|
{".skin", "application/xml"},
|
||||||
|
{".skp", "application/x-koan"},
|
||||||
|
{".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"},
|
||||||
|
{".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"},
|
||||||
|
{".slk", "application/vnd.ms-excel"},
|
||||||
|
{".sln", "text/plain"},
|
||||||
|
{".slupkg-ms", "application/x-ms-license"},
|
||||||
|
{".smd", "audio/x-smd"},
|
||||||
|
{".smi", "application/octet-stream"},
|
||||||
|
{".smx", "audio/x-smd"},
|
||||||
|
{".smz", "audio/x-smd"},
|
||||||
|
{".snd", "audio/basic"},
|
||||||
|
{".snippet", "application/xml"},
|
||||||
|
{".snp", "application/octet-stream"},
|
||||||
|
{".sql", "application/sql"},
|
||||||
|
{".sol", "text/plain"},
|
||||||
|
{".sor", "text/plain"},
|
||||||
|
{".spc", "application/x-pkcs7-certificates"},
|
||||||
|
{".spl", "application/futuresplash"},
|
||||||
|
{".spx", "audio/ogg"},
|
||||||
|
{".src", "application/x-wais-source"},
|
||||||
|
{".srf", "text/plain"},
|
||||||
|
{".SSISDeploymentManifest", "text/xml"},
|
||||||
|
{".ssm", "application/streamingmedia"},
|
||||||
|
{".sst", "application/vnd.ms-pki.certstore"},
|
||||||
|
{".stl", "application/vnd.ms-pki.stl"},
|
||||||
|
{".sv4cpio", "application/x-sv4cpio"},
|
||||||
|
{".sv4crc", "application/x-sv4crc"},
|
||||||
|
{".svc", "application/xml"},
|
||||||
|
{".svg", "image/svg+xml"},
|
||||||
|
{".swf", "application/x-shockwave-flash"},
|
||||||
|
{".step", "application/step"},
|
||||||
|
{".stp", "application/step"},
|
||||||
|
{".t", "application/x-troff"},
|
||||||
|
{".tar", "application/x-tar"},
|
||||||
|
{".tcl", "application/x-tcl"},
|
||||||
|
{".testrunconfig", "application/xml"},
|
||||||
|
{".testsettings", "application/xml"},
|
||||||
|
{".tex", "application/x-tex"},
|
||||||
|
{".texi", "application/x-texinfo"},
|
||||||
|
{".texinfo", "application/x-texinfo"},
|
||||||
|
{".tgz", "application/x-compressed"},
|
||||||
|
{".thmx", "application/vnd.ms-officetheme"},
|
||||||
|
{".thn", "application/octet-stream"},
|
||||||
|
{".tif", "image/tiff"},
|
||||||
|
{".tiff", "image/tiff"},
|
||||||
|
{".tlh", "text/plain"},
|
||||||
|
{".tli", "text/plain"},
|
||||||
|
{".toc", "application/octet-stream"},
|
||||||
|
{".tr", "application/x-troff"},
|
||||||
|
{".trm", "application/x-msterminal"},
|
||||||
|
{".trx", "application/xml"},
|
||||||
|
{".ts", "video/vnd.dlna.mpeg-tts"},
|
||||||
|
{".tsv", "text/tab-separated-values"},
|
||||||
|
{".ttf", "application/font-sfnt"},
|
||||||
|
{".tts", "video/vnd.dlna.mpeg-tts"},
|
||||||
|
{".txt", "text/plain"},
|
||||||
|
{".u32", "application/octet-stream"},
|
||||||
|
{".uls", "text/iuls"},
|
||||||
|
{".user", "text/plain"},
|
||||||
|
{".ustar", "application/x-ustar"},
|
||||||
|
{".vb", "text/plain"},
|
||||||
|
{".vbdproj", "text/plain"},
|
||||||
|
{".vbk", "video/mpeg"},
|
||||||
|
{".vbproj", "text/plain"},
|
||||||
|
{".vbs", "text/vbscript"},
|
||||||
|
{".vcf", "text/x-vcard"},
|
||||||
|
{".vcproj", "application/xml"},
|
||||||
|
{".vcs", "text/plain"},
|
||||||
|
{".vcxproj", "application/xml"},
|
||||||
|
{".vddproj", "text/plain"},
|
||||||
|
{".vdp", "text/plain"},
|
||||||
|
{".vdproj", "text/plain"},
|
||||||
|
{".vdx", "application/vnd.ms-visio.viewer"},
|
||||||
|
{".vml", "text/xml"},
|
||||||
|
{".vscontent", "application/xml"},
|
||||||
|
{".vsct", "text/xml"},
|
||||||
|
{".vsd", "application/vnd.visio"},
|
||||||
|
{".vsdx", "application/vnd.ms-visio.viewer"},
|
||||||
|
{".vsi", "application/ms-vsi"},
|
||||||
|
{".vsix", "application/vsix"},
|
||||||
|
{".vsixlangpack", "text/xml"},
|
||||||
|
{".vsixmanifest", "text/xml"},
|
||||||
|
{".vsmdi", "application/xml"},
|
||||||
|
{".vspscc", "text/plain"},
|
||||||
|
{".vss", "application/vnd.visio"},
|
||||||
|
{".vsscc", "text/plain"},
|
||||||
|
{".vssettings", "text/xml"},
|
||||||
|
{".vssscc", "text/plain"},
|
||||||
|
{".vst", "application/vnd.visio"},
|
||||||
|
{".vstemplate", "text/xml"},
|
||||||
|
{".vsto", "application/x-ms-vsto"},
|
||||||
|
{".vsw", "application/vnd.visio"},
|
||||||
|
{".vsx", "application/vnd.visio"},
|
||||||
|
{".vtt", "text/vtt"},
|
||||||
|
{".vtx", "application/vnd.visio"},
|
||||||
|
{".wasm", "application/wasm"},
|
||||||
|
{".wav", "audio/wav"},
|
||||||
|
{".wave", "audio/wav"},
|
||||||
|
{".wax", "audio/x-ms-wax"},
|
||||||
|
{".wbk", "application/msword"},
|
||||||
|
{".wbmp", "image/vnd.wap.wbmp"},
|
||||||
|
{".wcm", "application/vnd.ms-works"},
|
||||||
|
{".wdb", "application/vnd.ms-works"},
|
||||||
|
{".wdp", "image/vnd.ms-photo"},
|
||||||
|
{".webarchive", "application/x-safari-webarchive"},
|
||||||
|
{".webm", "video/webm"},
|
||||||
|
{".webp", "image/webp"}, /* https://en.wikipedia.org/wiki/WebP */
|
||||||
|
{".webtest", "application/xml"},
|
||||||
|
{".wiq", "application/xml"},
|
||||||
|
{".wiz", "application/msword"},
|
||||||
|
{".wks", "application/vnd.ms-works"},
|
||||||
|
{".WLMP", "application/wlmoviemaker"},
|
||||||
|
{".wlpginstall", "application/x-wlpg-detect"},
|
||||||
|
{".wlpginstall3", "application/x-wlpg3-detect"},
|
||||||
|
{".wm", "video/x-ms-wm"},
|
||||||
|
{".wma", "audio/x-ms-wma"},
|
||||||
|
{".wmd", "application/x-ms-wmd"},
|
||||||
|
{".wmf", "application/x-msmetafile"},
|
||||||
|
{".wml", "text/vnd.wap.wml"},
|
||||||
|
{".wmlc", "application/vnd.wap.wmlc"},
|
||||||
|
{".wmls", "text/vnd.wap.wmlscript"},
|
||||||
|
{".wmlsc", "application/vnd.wap.wmlscriptc"},
|
||||||
|
{".wmp", "video/x-ms-wmp"},
|
||||||
|
{".wmv", "video/x-ms-wmv"},
|
||||||
|
{".wmx", "video/x-ms-wmx"},
|
||||||
|
{".wmz", "application/x-ms-wmz"},
|
||||||
|
{".woff", "application/font-woff"},
|
||||||
|
{".woff2", "application/font-woff2"},
|
||||||
|
{".wpl", "application/vnd.ms-wpl"},
|
||||||
|
{".wps", "application/vnd.ms-works"},
|
||||||
|
{".wri", "application/x-mswrite"},
|
||||||
|
{".wrl", "x-world/x-vrml"},
|
||||||
|
{".wrz", "x-world/x-vrml"},
|
||||||
|
{".wsc", "text/scriptlet"},
|
||||||
|
{".wsdl", "text/xml"},
|
||||||
|
{".wvx", "video/x-ms-wvx"},
|
||||||
|
{".x", "application/directx"},
|
||||||
|
{".xaf", "x-world/x-vrml"},
|
||||||
|
{".xaml", "application/xaml+xml"},
|
||||||
|
{".xap", "application/x-silverlight-app"},
|
||||||
|
{".xbap", "application/x-ms-xbap"},
|
||||||
|
{".xbm", "image/x-xbitmap"},
|
||||||
|
{".xdr", "text/plain"},
|
||||||
|
{".xht", "application/xhtml+xml"},
|
||||||
|
{".xhtml", "application/xhtml+xml"},
|
||||||
|
{".xla", "application/vnd.ms-excel"},
|
||||||
|
{".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"},
|
||||||
|
{".xlc", "application/vnd.ms-excel"},
|
||||||
|
{".xld", "application/vnd.ms-excel"},
|
||||||
|
{".xlk", "application/vnd.ms-excel"},
|
||||||
|
{".xll", "application/vnd.ms-excel"},
|
||||||
|
{".xlm", "application/vnd.ms-excel"},
|
||||||
|
{".xls", "application/vnd.ms-excel"},
|
||||||
|
{".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
|
||||||
|
{".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"},
|
||||||
|
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
|
||||||
|
{".xlt", "application/vnd.ms-excel"},
|
||||||
|
{".xltm", "application/vnd.ms-excel.template.macroEnabled.12"},
|
||||||
|
{".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
|
||||||
|
{".xlw", "application/vnd.ms-excel"},
|
||||||
|
{".xml", "text/xml"},
|
||||||
|
{".xmp", "application/octet-stream"},
|
||||||
|
{".xmta", "application/xml"},
|
||||||
|
{".xof", "x-world/x-vrml"},
|
||||||
|
{".XOML", "text/plain"},
|
||||||
|
{".xpm", "image/x-xpixmap"},
|
||||||
|
{".xps", "application/vnd.ms-xpsdocument"},
|
||||||
|
{".xrm-ms", "text/xml"},
|
||||||
|
{".xsc", "application/xml"},
|
||||||
|
{".xsd", "text/xml"},
|
||||||
|
{".xsf", "text/xml"},
|
||||||
|
{".xsl", "text/xml"},
|
||||||
|
{".xslt", "text/xml"},
|
||||||
|
{".xsn", "application/octet-stream"},
|
||||||
|
{".xss", "application/xml"},
|
||||||
|
{".xspf", "application/xspf+xml"},
|
||||||
|
{".xtp", "application/octet-stream"},
|
||||||
|
{".xwd", "image/x-xwindowdump"},
|
||||||
|
{".z", "application/x-compress"},
|
||||||
|
{".zip", "application/zip"},
|
||||||
|
|
||||||
|
{"application/fsharp-script", ".fsx"},
|
||||||
|
{"application/msaccess", ".adp"},
|
||||||
|
{"application/msword", ".doc"},
|
||||||
|
{"application/octet-stream", ".bin"},
|
||||||
|
{"application/onenote", ".one"},
|
||||||
|
{"application/postscript", ".eps"},
|
||||||
|
{"application/step", ".step"},
|
||||||
|
{"application/vnd.apple.keynote", ".key"},
|
||||||
|
{"application/vnd.apple.numbers", ".numbers"},
|
||||||
|
{"application/vnd.apple.pages", ".pages"},
|
||||||
|
{"application/vnd.ms-excel", ".xls"},
|
||||||
|
{"application/vnd.ms-powerpoint", ".ppt"},
|
||||||
|
{"application/vnd.ms-works", ".wks"},
|
||||||
|
{"application/vnd.visio", ".vsd"},
|
||||||
|
{"application/x-director", ".dir"},
|
||||||
|
{"application/x-msdos-program", ".exe"},
|
||||||
|
{"application/x-shockwave-flash", ".swf"},
|
||||||
|
{"application/x-x509-ca-cert", ".cer"},
|
||||||
|
{"application/x-zip-compressed", ".zip"},
|
||||||
|
{"application/xhtml+xml", ".xhtml"},
|
||||||
|
{"application/x-iwork-keynote-sffkey", ".key"},
|
||||||
|
{"application/x-iwork-numbers-sffnumbers", ".numbers"},
|
||||||
|
{"application/x-iwork-pages-sffpages", ".pages"},
|
||||||
|
{"application/xml", ".xml"}, // anomaly, .xml -> text/xml, but application/xml -> many things, but all are xml, so safest is .xml
|
||||||
|
{"audio/aac", ".AAC"},
|
||||||
|
{"audio/aiff", ".aiff"},
|
||||||
|
{"audio/basic", ".snd"},
|
||||||
|
{"audio/mid", ".midi"},
|
||||||
|
{"audio/mp4", ".m4a"}, // one way mapping only, mime -> ext
|
||||||
|
{"audio/ogg", ".ogg"},
|
||||||
|
{"audio/ogg; codecs=opus", ".opus"},
|
||||||
|
{"audio/wav", ".wav"},
|
||||||
|
{"audio/x-m4a", ".m4a"},
|
||||||
|
{"audio/x-mpegurl", ".m3u"},
|
||||||
|
{"audio/x-pn-realaudio", ".ra"},
|
||||||
|
{"audio/x-smd", ".smd"},
|
||||||
|
{"image/bmp", ".bmp"},
|
||||||
|
{"image/heic", ".heic"},
|
||||||
|
{"image/heif", ".heif"},
|
||||||
|
{"image/jpeg", ".jpg"},
|
||||||
|
{"image/pict", ".pic"},
|
||||||
|
{"image/png", ".png"}, // Defined in [RFC-2045], [RFC-2048]
|
||||||
|
{"image/x-png", ".png"}, // See https://www.w3.org/TR/PNG/#A-Media-type :"It is recommended that implementations also recognize the media type "image/x-png"."
|
||||||
|
{"image/tiff", ".tiff"},
|
||||||
|
{"image/x-macpaint", ".mac"},
|
||||||
|
{"image/x-quicktime", ".qti"},
|
||||||
|
{"message/rfc822", ".eml"},
|
||||||
|
{"text/calendar", ".ics"},
|
||||||
|
{"text/html", ".html"},
|
||||||
|
{"text/plain", ".txt"},
|
||||||
|
{"text/scriptlet", ".wsc"},
|
||||||
|
{"text/xml", ".xml"},
|
||||||
|
{"video/3gpp", ".3gp"},
|
||||||
|
{"video/3gpp2", ".3gp2"},
|
||||||
|
{"video/mp4", ".mp4"},
|
||||||
|
{"video/mpeg", ".mpg"},
|
||||||
|
{"video/quicktime", ".mov"},
|
||||||
|
{"video/vnd.dlna.mpeg-tts", ".m2t"},
|
||||||
|
{"video/x-dv", ".dv"},
|
||||||
|
{"video/x-la-asf", ".lsf"},
|
||||||
|
{"video/x-ms-asf", ".asf"},
|
||||||
|
{"x-world/x-vrml", ".xof"},
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
var cache = mappings.ToList(); // need ToList() to avoid modifying while still enumerating
|
||||||
|
|
||||||
|
foreach (var mapping in cache)
|
||||||
|
{
|
||||||
|
if (!mappings.ContainsKey(mapping.Value))
|
||||||
|
{
|
||||||
|
mappings.Add(mapping.Value, mapping.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get the type of the MIME from the provided string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">The filename or extension.</param>
|
||||||
|
/// <param name="mimeType">The variable to store the MIME type.</param>
|
||||||
|
/// <returns>The MIME type.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException" />
|
||||||
|
public static bool TryGetMimeType(string str, out string mimeType)
|
||||||
|
{
|
||||||
|
if (str == null) {
|
||||||
|
throw new ArgumentNullException(nameof(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexQuestionMark = str.IndexOf(QuestionMark, StringComparison.Ordinal);
|
||||||
|
if (indexQuestionMark != -1)
|
||||||
|
{
|
||||||
|
str = str.Remove(indexQuestionMark);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!str.StartsWith(Dot))
|
||||||
|
{
|
||||||
|
var index = str.LastIndexOf(Dot);
|
||||||
|
if (index != -1 && str.Length > index + 1)
|
||||||
|
{
|
||||||
|
str = str.Substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
str = Dot + str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Mappings.Value.TryGetValue(str, out mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the MIME from the provided string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="str">The filename or extension.</param>
|
||||||
|
/// <returns>The MIME type.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException" />
|
||||||
|
public static string GetMimeType(string str)
|
||||||
|
{
|
||||||
|
return MimeTypeMap.TryGetMimeType(str, out var result) ? result : DefaultMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the extension from the provided MIME type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mimeType">Type of the MIME.</param>
|
||||||
|
/// <param name="throwErrorIfNotFound">if set to <c>true</c>, throws error if extension's not found.</param>
|
||||||
|
/// <returns>The extension.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException" />
|
||||||
|
/// <exception cref="ArgumentException" />
|
||||||
|
public static string GetExtension(string mimeType, bool throwErrorIfNotFound = true)
|
||||||
|
{
|
||||||
|
if (mimeType == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(mimeType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mimeType.StartsWith(Dot))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Requested mime type is not valid: " + mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mappings.Value.TryGetValue(mimeType, out string extension))
|
||||||
|
{
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (throwErrorIfNotFound)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Requested mime type is not registered: " + mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
services/bytefy.image/bytefy.image/Program.cs
Normal file
86
services/bytefy.image/bytefy.image/Program.cs
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
using bytefy.image;
|
||||||
|
using ImageMagick;
|
||||||
|
using Microsoft.AspNetCore.Antiforgery;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
builder.Services.AddAntiforgery(options => options.HeaderName = "2311d8d8-607d-4747-8939-1bde65643254");
|
||||||
|
builder.Services.AddSingleton<ConversionQueueService>();
|
||||||
|
builder.Services.AddHostedService(provider => provider.GetRequiredService<ConversionQueueService>());
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("AllowSpecificOrigin",
|
||||||
|
builder => builder.WithOrigins("http://localhost:4200")
|
||||||
|
.AllowAnyMethod()
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowCredentials());
|
||||||
|
});
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
app.UseAntiforgery();
|
||||||
|
app.UseCors("AllowSpecificOrigin"); // Use the CORS policy
|
||||||
|
var conversionQueue = app.Services.GetRequiredService<ConversionQueueService>();
|
||||||
|
|
||||||
|
app.MapPost("/convert/{format}", async (IFormFile file, string format) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Enum.TryParse(format, true, out MagickFormat magickFormat) || magickFormat == MagickFormat.Unknown)
|
||||||
|
return Results.BadRequest("Invalid format");
|
||||||
|
|
||||||
|
var formatInfo = MagickNET.SupportedFormats.FirstOrDefault(f => f.Format == magickFormat);
|
||||||
|
if (formatInfo == null || !formatInfo.SupportsReading || !formatInfo.SupportsWriting)
|
||||||
|
return Results.BadRequest("Unsupported format");
|
||||||
|
|
||||||
|
if (file == null || file.Length == 0)
|
||||||
|
return Results.BadRequest("No image provided");
|
||||||
|
|
||||||
|
if (file.Length > 20 * 1024 * 1024)
|
||||||
|
throw new Exception("Image size too large");
|
||||||
|
|
||||||
|
using var memoryStream = new MemoryStream();
|
||||||
|
await file.CopyToAsync(memoryStream);
|
||||||
|
|
||||||
|
var conversionTask = new ConversionTask
|
||||||
|
{
|
||||||
|
ImageData = memoryStream.ToArray(),
|
||||||
|
Format = magickFormat
|
||||||
|
};
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<(byte[], string)>();
|
||||||
|
conversionQueue.QueueConversion(conversionTask, tcs);
|
||||||
|
|
||||||
|
var (imageData, mimeType) = await tcs.Task;
|
||||||
|
|
||||||
|
return Results.File(new MemoryStream(imageData), mimeType, $"{Path.GetFileNameWithoutExtension(file.FileName)}.{magickFormat.ToString().ToLower()}");
|
||||||
|
}
|
||||||
|
catch (ImageMagick.MagickImageErrorException e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
return Results.BadRequest("Invalid image");
|
||||||
|
}
|
||||||
|
}).DisableAntiforgery(); // should get this removed by getting antiforgery working with angular. Doesn't find Cookie.
|
||||||
|
|
||||||
|
app.MapGet("/antiforgery/token", (IAntiforgery forgeryService, HttpContext context) =>
|
||||||
|
{
|
||||||
|
var tokens = forgeryService.GetAndStoreTokens(context);
|
||||||
|
var xsrfToken = tokens.RequestToken!;
|
||||||
|
return TypedResults.Content(xsrfToken, "text/plain");
|
||||||
|
}).DisableAntiforgery();
|
||||||
|
|
||||||
|
app.MapGet("/formats", () =>
|
||||||
|
{
|
||||||
|
var formats = MagickNET.SupportedFormats
|
||||||
|
.Where(f => f.SupportsReading && f.SupportsWriting)
|
||||||
|
.Select(f => f.Format.ToString())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return Results.Ok(formats);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapGet("/mimetype/{format}", (string format) =>
|
||||||
|
{
|
||||||
|
var mimeType = MimeTypes.MimeTypeMap.GetMimeType($".{format.ToLower()}");
|
||||||
|
return Results.Ok(mimeType);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Run();
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "todos",
|
||||||
|
"applicationUrl": "http://localhost:1337",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
9
services/bytefy.image/bytefy.image/appsettings.json
Normal file
9
services/bytefy.image/bytefy.image/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
23
services/bytefy.image/bytefy.image/bytefy.image.csproj
Normal file
23
services/bytefy.image/bytefy.image/bytefy.image.csproj
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<PublishAot>false</PublishAot>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="..\.dockerignore">
|
||||||
|
<Link>.dockerignore</Link>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="14.0.0" />
|
||||||
|
<PackageReference Include="Magick.NET.Core" Version="14.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
11
services/bytefy.image/bytefy.image/bytefy.image.http
Normal file
11
services/bytefy.image/bytefy.image/bytefy.image.http
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
@bytefy.image_HostAddress = http://localhost:1337
|
||||||
|
|
||||||
|
GET {{bytefy.image_HostAddress}}/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET {{bytefy.image_HostAddress}}/1
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
6
services/bytefy.image/docker-compose.yml
Normal file
6
services/bytefy.image/docker-compose.yml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
services:
|
||||||
|
bytefy.image:
|
||||||
|
image: bytefy.image
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: bytefy.image/Dockerfile
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// .main-content {
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: row;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// router-outlet {
|
|
||||||
// flex: 1;
|
|
||||||
// padding: 20px;
|
|
||||||
// }
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import { Routes } from '@angular/router';
|
|
||||||
import { AsciiToTextComponent } from '../tools/ascii-to-text/ascii-to-text.component';
|
|
||||||
import { GuidComponent } from '../tools/guid/guid.component';
|
|
||||||
import { Base64ConverterComponent } from '../tools/base64-converter/base64-converter.component';
|
|
||||||
import { JwtToJsonComponent } from '../tools/jwt-to-json/jwt-to-json.component';
|
|
||||||
import { TextToCronComponent } from '../tools/text-to-cron/text-to-cron.component';
|
|
||||||
import { DdsToPngComponent } from '../tools/dds-to-png/dds-to-png.component';
|
|
||||||
|
|
||||||
export const routes: Routes = [
|
|
||||||
{
|
|
||||||
path: 'ascii-to-text',
|
|
||||||
pathMatch: 'full',
|
|
||||||
component: AsciiToTextComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'guid',
|
|
||||||
pathMatch: 'full',
|
|
||||||
component: GuidComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'base64-converter',
|
|
||||||
pathMatch: 'full',
|
|
||||||
component: Base64ConverterComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'jwt-decoder',
|
|
||||||
pathMatch: 'full',
|
|
||||||
component: JwtToJsonComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'text-to-cron',
|
|
||||||
pathMatch: 'full',
|
|
||||||
component: TextToCronComponent
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dds-to-png',
|
|
||||||
pathMatch: 'full',
|
|
||||||
component: DdsToPngComponent
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<div class="card flex justify-center">
|
|
||||||
<p-panel [header]="title">
|
|
||||||
<p-fileUpload
|
|
||||||
name="file"
|
|
||||||
url="./upload"
|
|
||||||
(onSelect)="onFileSelect($event)"
|
|
||||||
[auto]="true"
|
|
||||||
[accept]="accept"
|
|
||||||
[previewWidth]="isPreview ? '50px' : '0px'"
|
|
||||||
>
|
|
||||||
</p-fileUpload>
|
|
||||||
<p-table [value]="processedFiles" *ngIf="processedFiles.length != 0">
|
|
||||||
<ng-template pTemplate="header">
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Format</th>
|
|
||||||
<th>Download</th>
|
|
||||||
</tr>
|
|
||||||
</ng-template>
|
|
||||||
<ng-template pTemplate="body" let-file>
|
|
||||||
<tr>
|
|
||||||
<td>{{file.name}}</td>
|
|
||||||
<td>{{file.format}}</td>
|
|
||||||
<td><a [href]="file.link" download>{{file.name}}</a></td>
|
|
||||||
</tr>
|
|
||||||
</ng-template>
|
|
||||||
</p-table>
|
|
||||||
</p-panel>
|
|
||||||
</div>
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { FileSelectEvent, FileUploadModule } from 'primeng/fileupload';
|
|
||||||
import { ButtonModule } from 'primeng/button';
|
|
||||||
import { PanelModule } from 'primeng/panel';
|
|
||||||
import { TableModule } from 'primeng/table';
|
|
||||||
|
|
||||||
interface ProcessedFile {
|
|
||||||
name: string;
|
|
||||||
link: string;
|
|
||||||
format: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-file-converter',
|
|
||||||
templateUrl: 'file-converter.component.html',
|
|
||||||
styleUrls: ['file-converter.component.scss'],
|
|
||||||
standalone: true,
|
|
||||||
imports: [
|
|
||||||
CommonModule,
|
|
||||||
FormsModule,
|
|
||||||
FileUploadModule,
|
|
||||||
ButtonModule,
|
|
||||||
PanelModule,
|
|
||||||
TableModule
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class FileConverterComponent {
|
|
||||||
_fileFormats: string[] = [];
|
|
||||||
accept: string = '';
|
|
||||||
|
|
||||||
@Output() fileSelected = new EventEmitter<File[]>();
|
|
||||||
@Input() isPreview: boolean = true;
|
|
||||||
@Input () title: string = 'File Converter';
|
|
||||||
@Input() processedFiles: ProcessedFile[] = [];
|
|
||||||
@Input()
|
|
||||||
set fileFormats(formats: string[]) {
|
|
||||||
this._fileFormats = formats;
|
|
||||||
this.accept = formats.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
get fileFormats(): string[] {
|
|
||||||
return this._fileFormats;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedFile: File[] | null = null;
|
|
||||||
|
|
||||||
onFileSelect(event: FileSelectEvent): void {
|
|
||||||
this.selectedFile = event.files;
|
|
||||||
this.fileSelected.emit(this.selectedFile!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
/* You can add global styles to this file, and also import other style files */
|
|
||||||
@import "primeicons/primeicons.css";
|
|
||||||
42
tools/.gitignore
vendored
Normal file
42
tools/.gitignore
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
|
||||||
|
|
||||||
|
# Compiled output
|
||||||
|
/dist
|
||||||
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
/bazel-out
|
||||||
|
|
||||||
|
# Node
|
||||||
|
/node_modules
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
.idea/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.history/*
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
/.angular/cache
|
||||||
|
.sass-cache/
|
||||||
|
/connect.lock
|
||||||
|
/coverage
|
||||||
|
/libpeerconnection.log
|
||||||
|
testem.log
|
||||||
|
/typings
|
||||||
|
|
||||||
|
# System files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
0
package-lock.json → tools/package-lock.json
generated
0
package-lock.json → tools/package-lock.json
generated
@@ -1,10 +1,10 @@
|
|||||||
import { ApplicationConfig } from '@angular/core';
|
import { ApplicationConfig, importProvidersFrom } from '@angular/core';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||||
import { provideNgIconsConfig } from '@ng-icons/core';
|
import { provideNgIconsConfig } from '@ng-icons/core';
|
||||||
import { provideHttpClient } from '@angular/common/http';
|
import { HttpClientXsrfModule, provideHttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -13,6 +13,10 @@ export const appConfig: ApplicationConfig = {
|
|||||||
provideNgIconsConfig({
|
provideNgIconsConfig({
|
||||||
size: '1.5em',
|
size: '1.5em',
|
||||||
}),
|
}),
|
||||||
provideHttpClient()
|
provideHttpClient(),
|
||||||
|
importProvidersFrom(HttpClientXsrfModule.withOptions({
|
||||||
|
cookieName: 'X-XSRF-TOKEN',
|
||||||
|
headerName: '2311d8d8-607d-4747-8939-1bde65643254',
|
||||||
|
}))
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
47
tools/src/app/app.routes.ts
Normal file
47
tools/src/app/app.routes.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { Routes } from '@angular/router';
|
||||||
|
import { AsciiToTextComponent } from '../tools/client-side/ascii-to-text/ascii-to-text.component';
|
||||||
|
import { GuidComponent } from '../tools/client-side/guid/guid.component';
|
||||||
|
import { Base64ConverterComponent } from '../tools/client-side/base64-converter/base64-converter.component';
|
||||||
|
import { JwtToJsonComponent } from '../tools/client-side/jwt-to-json/jwt-to-json.component';
|
||||||
|
import { TextToCronComponent } from '../tools/client-side/text-to-cron/text-to-cron.component';
|
||||||
|
import { DdsToPngComponent } from '../tools/client-side/dds-to-png/dds-to-png.component';
|
||||||
|
import { ImageConverterComponent } from '../tools/server-side/image-converter/image-converter.component';
|
||||||
|
|
||||||
|
export const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'ascii-to-text',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: AsciiToTextComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'guid',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: GuidComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'base64-converter',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: Base64ConverterComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'jwt-decoder',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: JwtToJsonComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'text-to-cron',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: TextToCronComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dds-to-png',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: DdsToPngComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'image-converter',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: ImageConverterComponent
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
@@ -65,6 +65,11 @@ export class HeaderComponent implements OnInit {
|
|||||||
label: 'DDS to PNG',
|
label: 'DDS to PNG',
|
||||||
routerLink: 'dds-to-png',
|
routerLink: 'dds-to-png',
|
||||||
routerLinkActiveOptions: { exact: true }
|
routerLinkActiveOptions: { exact: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Image Converter',
|
||||||
|
routerLink: 'image-converter',
|
||||||
|
routerLinkActiveOptions: { exact: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
12
tools/src/app/models/conversion.model.ts
Normal file
12
tools/src/app/models/conversion.model.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Contains all models used for conversion
|
||||||
|
|
||||||
|
export interface ProcessedFile {
|
||||||
|
name: string;
|
||||||
|
link: string;
|
||||||
|
format: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Format {
|
||||||
|
name: string;
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
<div class="card flex justify-center">
|
<div class="card flex justify-center">
|
||||||
<p-panel [header]="title">
|
<p-panel [header]="title">
|
||||||
|
<ng-template pTemplate="header">
|
||||||
|
<p-tag *ngIf="isBeta" severity="warn" value="Beta"></p-tag>
|
||||||
|
</ng-template>
|
||||||
<textarea
|
<textarea
|
||||||
(keyup)="onTopChange($event)"
|
(keyup)="onTopChange($event)"
|
||||||
[disabled]="topDisabled"
|
[disabled]="topDisabled"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
margin-top: 20px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 98vw;
|
width: 98vw;
|
||||||
|
|
||||||
@@ -39,4 +39,13 @@
|
|||||||
background-color: var(--primary-contrast);
|
background-color: var(--primary-contrast);
|
||||||
color: var(--text-color)
|
color: var(--text-color)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
::ng-deep .p-panel-header {
|
||||||
|
justify-content: unset !important;
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms';
|
|||||||
import { FloatLabelModule } from 'primeng/floatlabel';
|
import { FloatLabelModule } from 'primeng/floatlabel';
|
||||||
import { InputTextareaModule } from 'primeng/inputtextarea';
|
import { InputTextareaModule } from 'primeng/inputtextarea';
|
||||||
import { PanelModule } from 'primeng/panel';
|
import { PanelModule } from 'primeng/panel';
|
||||||
|
import { TagModule } from 'primeng/tag';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dual-textarea',
|
selector: 'app-dual-textarea',
|
||||||
@@ -15,7 +16,8 @@ import { PanelModule } from 'primeng/panel';
|
|||||||
InputTextareaModule,
|
InputTextareaModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
PanelModule,
|
PanelModule,
|
||||||
CommonModule
|
CommonModule,
|
||||||
|
TagModule
|
||||||
]
|
]
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -27,6 +29,7 @@ export class DualTextareaComponent {
|
|||||||
@Input() bottomPlaceholder: string = 'Right Textarea';
|
@Input() bottomPlaceholder: string = 'Right Textarea';
|
||||||
@Input() topValue: string = '';
|
@Input() topValue: string = '';
|
||||||
@Input() bottomValue: string = '';
|
@Input() bottomValue: string = '';
|
||||||
|
@Input() isBeta: boolean = false;
|
||||||
@Output() topChange = new EventEmitter<string>();
|
@Output() topChange = new EventEmitter<string>();
|
||||||
@Output() bottomChange = new EventEmitter<string>();
|
@Output() bottomChange = new EventEmitter<string>();
|
||||||
|
|
||||||
76
tools/src/app/shared/upload/file-converter.component.html
Normal file
76
tools/src/app/shared/upload/file-converter.component.html
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<div class="card flex justify-center">
|
||||||
|
<p-panel [header]="title">
|
||||||
|
<ng-template pTemplate="header">
|
||||||
|
<p-tag *ngIf="isBeta" severity="warn" value="Beta"></p-tag>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<p-fileUpload
|
||||||
|
name="file"
|
||||||
|
(onSelect)="onFileSelect($event)"
|
||||||
|
[auto]="true"
|
||||||
|
[accept]="accept"
|
||||||
|
[previewWidth]="isPreview ? '50px' : '0px'"
|
||||||
|
mode="advanced"
|
||||||
|
[url]="url"
|
||||||
|
[withCredentials]="true"
|
||||||
|
[method]="method"
|
||||||
|
[headers]="requestHeaders"
|
||||||
|
>
|
||||||
|
<ng-template
|
||||||
|
*ngIf="fileTypeSelector"
|
||||||
|
pTemplate="header"
|
||||||
|
let-files
|
||||||
|
let-chooseCallback="chooseCallback"
|
||||||
|
let-clearCallback="clearCallback"
|
||||||
|
let-uploadCallback="uploadCallback"
|
||||||
|
>
|
||||||
|
<p-button
|
||||||
|
(onClick)="choose($event, chooseCallback)"
|
||||||
|
icon="pi pi-images"
|
||||||
|
[rounded]="true"
|
||||||
|
[outlined]="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p-autoComplete
|
||||||
|
*ngIf="fileTypeSelector"
|
||||||
|
(onSelect)="onAutoCompleteDropdownClick($event)"
|
||||||
|
[virtualScroll]="true"
|
||||||
|
[suggestions]="filteredFiles"
|
||||||
|
[virtualScrollItemSize]="34"
|
||||||
|
(completeMethod)="onAutoComplete($event)"
|
||||||
|
optionLabel="name"
|
||||||
|
[dropdown]="true"
|
||||||
|
placeholder="Select a output format"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p-button
|
||||||
|
(onClick)="onUploadEvent()"
|
||||||
|
icon="pi pi-file-arrow-up"
|
||||||
|
[rounded]="true"
|
||||||
|
[outlined]="true"
|
||||||
|
/>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template *ngIf="fileTypeSelector" pTemplate="empty">
|
||||||
|
<div>Drag and drop files to here to upload.</div>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
</p-fileUpload>
|
||||||
|
<p-table [value]="processedFiles" *ngIf="processedFiles.length != 0">
|
||||||
|
<ng-template pTemplate="header">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Format</th>
|
||||||
|
<th>Download</th>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template pTemplate="body" let-file>
|
||||||
|
<tr>
|
||||||
|
<td>{{file.name}}</td>
|
||||||
|
<td>{{file.format}}</td>
|
||||||
|
<td><a [href]="file.link" [download]="file.name">{{file.name}}</a></td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</p-table>
|
||||||
|
</p-panel>
|
||||||
|
</div>
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
margin-top: 20px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 98vw;
|
width: 98vw;
|
||||||
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -17,4 +16,12 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
::ng-deep .p-panel-header {
|
||||||
|
justify-content: unset !important;
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
97
tools/src/app/shared/upload/file-converter.component.ts
Normal file
97
tools/src/app/shared/upload/file-converter.component.ts
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { FileSelectEvent, FileUploadEvent, FileUploadModule } from 'primeng/fileupload';
|
||||||
|
import { ButtonModule } from 'primeng/button';
|
||||||
|
import { PanelModule } from 'primeng/panel';
|
||||||
|
import { TableModule } from 'primeng/table';
|
||||||
|
import { AutoCompleteCompleteEvent, AutoCompleteModule, AutoCompleteSelectEvent } from 'primeng/autocomplete';
|
||||||
|
import { BadgeModule } from 'primeng/badge';
|
||||||
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
|
import { TagModule } from 'primeng/tag';
|
||||||
|
|
||||||
|
interface ProcessedFile {
|
||||||
|
name: string;
|
||||||
|
link: string;
|
||||||
|
format: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-file-converter',
|
||||||
|
templateUrl: 'file-converter.component.html',
|
||||||
|
styleUrls: ['file-converter.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
|
FileUploadModule,
|
||||||
|
ButtonModule,
|
||||||
|
PanelModule,
|
||||||
|
TableModule,
|
||||||
|
AutoCompleteModule,
|
||||||
|
BadgeModule,
|
||||||
|
TagModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class FileConverterComponent implements OnInit {
|
||||||
|
_fileFormats: string[] = [];
|
||||||
|
accept: string = '';
|
||||||
|
selected = '';
|
||||||
|
invalidFileTypeMessageSummary: string = '';
|
||||||
|
url: string = '';
|
||||||
|
requestHeaders: any;
|
||||||
|
selectedFile: File[] | null = null;
|
||||||
|
|
||||||
|
@Output() fileSelected = new EventEmitter<File[]>();
|
||||||
|
@Input() isBeta: boolean = false;
|
||||||
|
@Input() filteredFiles: string[] = [];
|
||||||
|
@Input() isPreview: boolean = true;
|
||||||
|
@Input() title: string = 'File Converter';
|
||||||
|
@Input() processedFiles: ProcessedFile[] = [];
|
||||||
|
@Input()
|
||||||
|
set fileFormats(formats: string[]) {
|
||||||
|
this._fileFormats = formats;
|
||||||
|
this.accept = formats.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// File type selector
|
||||||
|
@Output() autoComplete = new EventEmitter<AutoCompleteCompleteEvent>();
|
||||||
|
@Output() selectedFormat = new EventEmitter<string>();
|
||||||
|
@Input() fileTypeSelector: boolean = false;
|
||||||
|
|
||||||
|
// Upload file to server
|
||||||
|
@Input() baseUrl = '';
|
||||||
|
@Input() method : 'post' | 'put' = 'post';
|
||||||
|
@Input() headers: HttpHeaders = new HttpHeaders();
|
||||||
|
@Output() upload = new EventEmitter<FileUploadEvent>();
|
||||||
|
|
||||||
|
get fileFormats(): string[] {
|
||||||
|
return this._fileFormats;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.requestHeaders = this.headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
choose(_: any, callback: () => void) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileSelect(event: FileSelectEvent): void {
|
||||||
|
this.selectedFile = event.currentFiles;
|
||||||
|
this.fileSelected.emit(this.selectedFile!);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAutoComplete(event: AutoCompleteCompleteEvent): void {
|
||||||
|
this.autoComplete.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAutoCompleteDropdownClick(event: AutoCompleteSelectEvent): void {
|
||||||
|
this.selectedFormat.emit(event.value.name);
|
||||||
|
this.selected = event.value.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadEvent() {
|
||||||
|
this.upload.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
4
tools/src/environments/environment.ts
Normal file
4
tools/src/environments/environment.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const environment = {
|
||||||
|
production: false,
|
||||||
|
uploadServiceBaseUrl: 'http://localhost:1337'
|
||||||
|
};
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -6,6 +6,9 @@
|
|||||||
<base href="/">
|
<base href="/">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@304&display=swap" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
11
tools/src/styles.scss
Normal file
11
tools/src/styles.scss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
@import "primeicons/primeicons.css";
|
||||||
|
body,
|
||||||
|
body .p-component
|
||||||
|
{
|
||||||
|
|
||||||
|
font-family: "Roboto Mono", monospace;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
font-weight: 304;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component';
|
import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-ascii-to-text',
|
selector: 'app-ascii-to-text',
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component';
|
import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-base64-converter',
|
selector: 'app-base64-converter',
|
||||||
@@ -1,12 +1,7 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FileConverterComponent } from '../../app/shared/upload/file-converter.component';
|
import { FileConverterComponent } from '../../../app/shared/upload/file-converter.component';
|
||||||
import { DdsToPngService } from './dds-to-png.service';
|
import { DdsToPngService } from './dds-to-png.service';
|
||||||
|
import { ProcessedFile } from '../../../app/models/conversion.model';
|
||||||
interface ProcessedFile {
|
|
||||||
name: string;
|
|
||||||
link: string;
|
|
||||||
format: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dds-to-png',
|
selector: 'app-dds-to-png',
|
||||||
@@ -4,8 +4,6 @@ import { Injectable } from '@angular/core';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DdsToPngService {
|
export class DdsToPngService {
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
parseHeaders(arrayBuffer: ArrayBuffer) {
|
parseHeaders(arrayBuffer: ArrayBuffer) {
|
||||||
const header = new DataView(arrayBuffer, 0, 128);
|
const header = new DataView(arrayBuffer, 0, 128);
|
||||||
const height = header.getUint32(12, true);
|
const height = header.getUint32(12, true);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component';
|
import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-jwt-to-json',
|
selector: 'app-jwt-to-json',
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
bottomPlaceholder="*/5 * * * *"
|
bottomPlaceholder="*/5 * * * *"
|
||||||
[bottomDisabled]="true"
|
[bottomDisabled]="true"
|
||||||
[bottomValue]="cronExpression"
|
[bottomValue]="cronExpression"
|
||||||
|
[isBeta]="true"
|
||||||
(topChange)="getCronExpression($event)">
|
(topChange)="getCronExpression($event)">
|
||||||
</app-dual-textarea>
|
</app-dual-textarea>
|
||||||
|
|
||||||
<p>Still in beta, don't rely on this tool!</p>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component';
|
import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-text-to-cron',
|
selector: 'app-text-to-cron',
|
||||||
@@ -9,7 +9,6 @@ import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-texta
|
|||||||
imports: [DualTextareaComponent]
|
imports: [DualTextareaComponent]
|
||||||
})
|
})
|
||||||
export class TextToCronComponent {
|
export class TextToCronComponent {
|
||||||
|
|
||||||
cronExpression: string = '';
|
cronExpression: string = '';
|
||||||
|
|
||||||
getCronExpression(description: string): string {
|
getCronExpression(description: string): string {
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<app-file-converter
|
||||||
|
title="Image converter"
|
||||||
|
method="post"
|
||||||
|
[isPreview]="false"
|
||||||
|
[fileTypeSelector]="true"
|
||||||
|
[processedFiles]="processedFiles"
|
||||||
|
[fileFormats]="fileFormats"
|
||||||
|
[filteredFiles]="filteredFormats"
|
||||||
|
[baseUrl]="url"
|
||||||
|
[headers]="headers"
|
||||||
|
[isBeta]="true"
|
||||||
|
(autoComplete)="filterFormats($event)"
|
||||||
|
(fileSelected)="onFileSelected($event)"
|
||||||
|
(selectedFormat)="onFormatSelected($event)"
|
||||||
|
(upload)="onUploadClicked()"
|
||||||
|
>
|
||||||
|
</app-file-converter>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ImageConverterComponent } from './image-converter.component';
|
||||||
|
|
||||||
|
describe('ImageConverterComponent', () => {
|
||||||
|
let component: ImageConverterComponent;
|
||||||
|
let fixture: ComponentFixture<ImageConverterComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ ImageConverterComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ImageConverterComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
|
import { map, Subscription } from 'rxjs';
|
||||||
|
import { DropdownModule } from 'primeng/dropdown';
|
||||||
|
import { AutoCompleteCompleteEvent, AutoCompleteModule } from 'primeng/autocomplete';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { ImageService } from './image-converter.service';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FileConverterComponent } from "../../../app/shared/upload/file-converter.component";
|
||||||
|
import { Format, ProcessedFile } from '../../../app/models/conversion.model';
|
||||||
|
import { HttpHeaders } from '@angular/common/http';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-image-converter',
|
||||||
|
templateUrl: 'image-converter.component.html',
|
||||||
|
styleUrls: ['image-converter.component.scss'],
|
||||||
|
standalone: true,
|
||||||
|
imports: [DropdownModule, AutoCompleteModule, FormsModule, CommonModule, FileConverterComponent]
|
||||||
|
})
|
||||||
|
export class ImageConverterComponent implements OnInit, OnDestroy {
|
||||||
|
constructor(private ImageService: ImageService) { }
|
||||||
|
|
||||||
|
url = 'http://localhost:1337/convert';
|
||||||
|
filteredFormats: string[] = [];
|
||||||
|
formats: Format[] = [];
|
||||||
|
selectedFormat: string | undefined;
|
||||||
|
subscriptions: Subscription[] = [];
|
||||||
|
selected = '';
|
||||||
|
headers = new HttpHeaders();
|
||||||
|
processedFiles: ProcessedFile[] = [];
|
||||||
|
fileFormats: string[] = ["image/*"];
|
||||||
|
selectedFile: File[] | null = null;
|
||||||
|
|
||||||
|
filterFormats(event: AutoCompleteCompleteEvent) {
|
||||||
|
let filtered: any[] = [];
|
||||||
|
let query = event.query;
|
||||||
|
|
||||||
|
for (let index = 0; index < (this.formats as any[]).length; index++) {
|
||||||
|
let format = (this.formats as any[])[index];
|
||||||
|
if (format.name.toLowerCase().indexOf(query.toLowerCase()) == 0) {
|
||||||
|
filtered.push(format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filteredFormats = filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUploadClicked() {
|
||||||
|
if (this.selectedFormat && this.selectedFile) {
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.ImageService.getMimeType(this.selectedFormat).subscribe((typeResponse) => {
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.ImageService.convertImage(this.selectedFile![0], this.selectedFormat!)
|
||||||
|
.pipe(map((response: any) => {
|
||||||
|
const blob = new Blob([response], { type: typeResponse });
|
||||||
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
const processedFile = {
|
||||||
|
name: this.selectedFile![0].name.replace(/\.[^/.]+$/, `.${this.selectedFormat?.toLowerCase()}`),
|
||||||
|
link: blobUrl,
|
||||||
|
format: this.selectedFormat
|
||||||
|
} as ProcessedFile;
|
||||||
|
this.processedFiles.push(processedFile);
|
||||||
|
}))
|
||||||
|
.subscribe()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onFormatSelected(format: string) {
|
||||||
|
this.selectedFormat = format;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFileSelected(input: File[]): void {
|
||||||
|
this.selectedFile = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.subscriptions.push(this.ImageService.setAntiforgeryToken().subscribe());
|
||||||
|
|
||||||
|
this.subscriptions.push(this.ImageService.getFormats()
|
||||||
|
.pipe(map(formats => {
|
||||||
|
this.formats = formats.map(format => {
|
||||||
|
return {
|
||||||
|
name: format,
|
||||||
|
code: format.toLowerCase()
|
||||||
|
} as Format;
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
.subscribe());
|
||||||
|
|
||||||
|
this.headers = new HttpHeaders({
|
||||||
|
'2311d8d8-607d-4747-8939-1bde65643254': localStorage.getItem('imgToken')!,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subscriptions.forEach(sub => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||||
|
import { map, Observable } from 'rxjs';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ImageService {
|
||||||
|
private baseUrl = environment.uploadServiceBaseUrl;
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) { }
|
||||||
|
|
||||||
|
convertImage(image: File, format: string): Observable<any> {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', image);
|
||||||
|
|
||||||
|
let imgToken = localStorage.getItem('imgToken');
|
||||||
|
|
||||||
|
const headers = new HttpHeaders({
|
||||||
|
'2311d8d8-607d-4747-8939-1bde65643254': imgToken!
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.http.post(`${this.baseUrl}/convert/${format}`, formData, { headers, responseType: 'blob' }); }
|
||||||
|
|
||||||
|
setAntiforgeryToken(): Observable<string> {
|
||||||
|
return this.http.get<string>(`${this.baseUrl}/antiforgery/token`, { responseType: 'text' as 'json' }).pipe(
|
||||||
|
map((token) => {
|
||||||
|
localStorage.setItem('imgToken', token.replace('"', ''));
|
||||||
|
return token;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMimeType(simpleType: string): Observable<string> {
|
||||||
|
return this.http.get<string>(`${this.baseUrl}/mimetype/${simpleType}`, { responseType: 'text' as 'json' });
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormats(): Observable<string[]> {
|
||||||
|
return this.http.get<string[]>(`${this.baseUrl}/formats`);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user