Fix compressed .mxl handling
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"clsx": "catalog:",
|
||||
"common": "workspace:^",
|
||||
"effect": "catalog:",
|
||||
"jszip": "catalog:",
|
||||
"lucide-react": "catalog:",
|
||||
"opensheetmusicdisplay": "catalog:",
|
||||
"react": "catalog:",
|
||||
|
||||
@@ -67,13 +67,21 @@ const router = createBrowserRouter([
|
||||
path: "/login",
|
||||
Component: Login,
|
||||
},
|
||||
]);
|
||||
], {
|
||||
future: {
|
||||
v7_fetcherPersist: true,
|
||||
v7_normalizeFormMethod: true,
|
||||
v7_partialHydration: true,
|
||||
v7_relativeSplatPath: true,
|
||||
v7_skipActionErrorRevalidation: true,
|
||||
},
|
||||
});
|
||||
|
||||
const rootElement = document.getElementById("root") as HTMLDivElement;
|
||||
const root = createRoot(rootElement);
|
||||
|
||||
root.render(
|
||||
<StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
<RouterProvider router={router} future={{ v7_startTransition: true }} />
|
||||
</StrictMode>,
|
||||
);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { client } from "@/client";
|
||||
import { useLoading } from "@/hooks/useLoading.ts";
|
||||
import { AttachmentId, PieceId } from "common";
|
||||
import JSZip from "jszip";
|
||||
import { OpenSheetMusicDisplay } from "opensheetmusicdisplay";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
export default function Attachment() {
|
||||
@@ -14,33 +15,81 @@ export default function Attachment() {
|
||||
const { isLoading, error, data } = useLoading(() => client.piece({ pieceId }).attachment({ attachmentId }).get(), [pieceId, attachmentId]);
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const renderFn = useRef<null | (() => void)>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading || error !== null) return;
|
||||
|
||||
const url = URL.createObjectURL(data);
|
||||
let musixXmlBlob: Blob = data;
|
||||
|
||||
const render = () => osmd.render();
|
||||
/* If the file is the compressed .mxl file, we do the uncompression
|
||||
* ourselves, because apparently OpenSheetMusicDisplay is incapable.
|
||||
*/
|
||||
if (data.type === "application/vnd.recordare.musicxml") {
|
||||
const zip = new JSZip();
|
||||
await zip.loadAsync(data);
|
||||
|
||||
const containerFile = zip.file("META-INF/container.xml");
|
||||
if (containerFile === null) {
|
||||
console.error("Missing META-INF/container.xml in the .mxl file");
|
||||
return;
|
||||
}
|
||||
|
||||
const containerText = await containerFile.async("text");
|
||||
const containerXml = new DOMParser().parseFromString(containerText, "application/xml");
|
||||
|
||||
const rootFile = containerXml.querySelector("rootfile[media-type=\"application/vnd.recordare.musicxml+xml\"], rootfile:not([media-type])");
|
||||
if (rootFile === null) {
|
||||
console.error("Missing root MusicXML file in META-INF/container.xml");
|
||||
return;
|
||||
}
|
||||
|
||||
const fullPath = rootFile.getAttribute("full-path");
|
||||
if (fullPath === null) {
|
||||
console.error("Missing full-path attribute in rootfile element");
|
||||
return;
|
||||
}
|
||||
|
||||
const musicXmlFile = zip.file(fullPath);
|
||||
if (musicXmlFile === null) {
|
||||
console.error(`Missing ${fullPath} file in the .mxl archive`);
|
||||
return;
|
||||
}
|
||||
|
||||
musixXmlBlob = await musicXmlFile.async("blob");
|
||||
}
|
||||
|
||||
const musicXml = await musixXmlBlob.text();
|
||||
|
||||
const osmd = new OpenSheetMusicDisplay(containerRef.current!, {
|
||||
autoResize: false,
|
||||
drawTitle: false,
|
||||
drawComposer: false,
|
||||
drawMeasureNumbers: true,
|
||||
drawMeasureNumbersOnlyAtSystemStart: true,
|
||||
//measureNumberInterval: 5,
|
||||
//renderSingleHorizontalStaffline: true,
|
||||
drawMeasureNumbersOnlyAtSystemStart: false,
|
||||
measureNumberInterval: 1,
|
||||
renderSingleHorizontalStaffline: true,
|
||||
});
|
||||
|
||||
osmd.load(url).then(render, (error) => console.error(error));
|
||||
const render = () => osmd.render();
|
||||
|
||||
await osmd.load(musicXml);
|
||||
render();
|
||||
|
||||
renderFn.current = render;
|
||||
}, [data, error, isLoading]);
|
||||
|
||||
useEffect(() => void load(), [load]);
|
||||
|
||||
useEffect(() => {
|
||||
const render = () => renderFn.current?.();
|
||||
|
||||
window.addEventListener("resize", render);
|
||||
|
||||
return () => {
|
||||
URL.revokeObjectURL(url);
|
||||
window.removeEventListener("resize", render);
|
||||
};
|
||||
}, [data, error, isLoading]);
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -66,6 +66,9 @@ catalogs:
|
||||
eslint-plugin-react-hooks:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
jszip:
|
||||
specifier: ^3.10.1
|
||||
version: 3.10.1
|
||||
kysely:
|
||||
specifier: ^0.27.4
|
||||
version: 0.27.4
|
||||
@@ -192,6 +195,9 @@ importers:
|
||||
effect:
|
||||
specifier: 'catalog:'
|
||||
version: 3.11.4
|
||||
jszip:
|
||||
specifier: 'catalog:'
|
||||
version: 3.10.1
|
||||
lucide-react:
|
||||
specifier: 'catalog:'
|
||||
version: 0.462.0(react@18.3.1)
|
||||
|
||||
@@ -22,6 +22,7 @@ catalog:
|
||||
effect: '^3.11.4'
|
||||
elysia: '^1.1.25'
|
||||
eslint-plugin-react-hooks: '^5.1.0'
|
||||
jszip: '^3.10.1'
|
||||
kysely: '^0.27.4'
|
||||
kysely-bun-sqlite: '^0.3.2'
|
||||
lucide-react: '^0.462.0'
|
||||
|
||||
Reference in New Issue
Block a user