Add media type sniffing by extension
This commit is contained in:
69
packages/common/src/MediaType.ts
Normal file
69
packages/common/src/MediaType.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
const mediaTypeExtension = new Map<string, string>();
|
||||||
|
const extensionMediaType = new Map<string, string>();
|
||||||
|
|
||||||
|
register("application/x-musescore", "mscz");
|
||||||
|
register("application/vnd.recordare.musicxml+xml", "musicxml");
|
||||||
|
register("application/vnd.recordare.musicxml", "mxl");
|
||||||
|
register("application/pdf", "pdf");
|
||||||
|
|
||||||
|
register("audio/aac", "aac");
|
||||||
|
register("audio/flac", "flac");
|
||||||
|
register("audio/midi", "mid", "midi");
|
||||||
|
register("audio/mpeg", "mp3");
|
||||||
|
register("audio/wav", "wav");
|
||||||
|
|
||||||
|
export const ACCEPTED_EXTENSIONS = (() => {
|
||||||
|
const ret = [];
|
||||||
|
for (const extension of extensionMediaType.keys()) {
|
||||||
|
ret.push("." + extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.sort((a, b) => a.localeCompare(b));
|
||||||
|
return ret.join(",");
|
||||||
|
})();
|
||||||
|
|
||||||
|
function register(mediaType: string, ...extensions: [string, ...string[]]) {
|
||||||
|
const [primaryExtension, ...secondaryExtensions] = extensions;
|
||||||
|
|
||||||
|
mediaTypeExtension.set(mediaType, primaryExtension);
|
||||||
|
|
||||||
|
extensionMediaType.set(primaryExtension, mediaType);
|
||||||
|
for (const extension of secondaryExtensions) {
|
||||||
|
extensionMediaType.set(extension, mediaType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMediaTypeForExtension(extension: string): string | undefined {
|
||||||
|
return extensionMediaType.get(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPrimaryExtension(extension: string): string {
|
||||||
|
const mediaType = extensionMediaType.get(extension);
|
||||||
|
if (mediaType === undefined) {
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryExtension = mediaTypeExtension.get(mediaType)!;
|
||||||
|
return primaryExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExtensionFromMediaType(mediaType: string): string | undefined {
|
||||||
|
return mediaTypeExtension.get(mediaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getExtensionFromFilename(filename: string): string | undefined {
|
||||||
|
return filename.match(/(?<=.)[^.]+$/)?.[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMediaTypeForFilename(filename: string): string | undefined {
|
||||||
|
const extension = getExtensionFromFilename(filename);
|
||||||
|
if (extension === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMediaTypeForExtension(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMediaTypeForFile(file: File): string {
|
||||||
|
return getMediaTypeForFilename(file.name) ?? file.type;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { getMediaTypeForFile } from "common/MediaType";
|
||||||
import { mapProp, Update } from "./store";
|
import { mapProp, Update } from "./store";
|
||||||
|
|
||||||
export function FileReducer(prev: FileReducer.State, action: FileReducer.Action): FileReducer.State {
|
export function FileReducer(prev: FileReducer.State, action: FileReducer.Action): FileReducer.State {
|
||||||
@@ -7,10 +8,10 @@ export function FileReducer(prev: FileReducer.State, action: FileReducer.Action)
|
|||||||
case "file":
|
case "file":
|
||||||
if (prev.file !== null) {
|
if (prev.file !== null) {
|
||||||
if (action.file !== null) {
|
if (action.file !== null) {
|
||||||
if (prev.file.name === prev.filename && prev.file.type === prev.mediaType) {
|
if (prev.file.name === prev.filename && getMediaTypeForFile(prev.file) === prev.mediaType) {
|
||||||
return Object.freeze<FileReducer.State>({
|
return Object.freeze<FileReducer.State>({
|
||||||
filename: action.file.name,
|
filename: action.file.name,
|
||||||
mediaType: action.file.type,
|
mediaType: getMediaTypeForFile(action.file),
|
||||||
file: action.file,
|
file: action.file,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -28,7 +29,7 @@ export function FileReducer(prev: FileReducer.State, action: FileReducer.Action)
|
|||||||
if (prev.filename === "" && prev.mediaType === "") {
|
if (prev.filename === "" && prev.mediaType === "") {
|
||||||
return Object.freeze<FileReducer.State>({
|
return Object.freeze<FileReducer.State>({
|
||||||
filename: action.file.name,
|
filename: action.file.name,
|
||||||
mediaType: action.file.type,
|
mediaType: getMediaTypeForFile(action.file),
|
||||||
file: action.file,
|
file: action.file,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Attachment, Piece } from "backend/database";
|
import type { Attachment, Piece } from "backend/database";
|
||||||
import { AttachmentId, PieceId } from "common";
|
import { AttachmentId, PieceId } from "common";
|
||||||
|
import { ACCEPTED_EXTENSIONS } from "common/MediaType";
|
||||||
import { ELYSIA_FORM_DATA } from "elysia";
|
import { ELYSIA_FORM_DATA } from "elysia";
|
||||||
import { FormEventHandler, useId, useReducer, useRef, useState } from "react";
|
import { FormEventHandler, useId, useReducer, useRef, useState } from "react";
|
||||||
import { Link, useParams } from "react-router-dom";
|
import { Link, useParams } from "react-router-dom";
|
||||||
@@ -254,6 +255,7 @@ function AttachmentForm(props: AttachmentForm.Props) {
|
|||||||
const file = e.target.files?.item(0) ?? null;
|
const file = e.target.files?.item(0) ?? null;
|
||||||
reduce(FileReducer.setFile(file));
|
reduce(FileReducer.setFile(file));
|
||||||
}}
|
}}
|
||||||
|
accept={ACCEPTED_EXTENSIONS}
|
||||||
/>
|
/>
|
||||||
<Button type="submit">
|
<Button type="submit">
|
||||||
Dodaj
|
Dodaj
|
||||||
|
|||||||
Reference in New Issue
Block a user