mirror of
https://github.com/LibreScore/dl-librescore
synced 2025-01-22 01:50:52 +01:00
fix: PDF downloads
This commit is contained in:
parent
c5bd971da5
commit
772e7b37a1
@ -3,13 +3,13 @@ import { hookNative } from "./anti-detection";
|
||||
import type { FileType } from "./file";
|
||||
|
||||
const TYPE_REG = /type=(img|mp3|midi)/;
|
||||
// first page has different URL
|
||||
const INIT_PAGE_REG = /(score_0\.png@0|score_0\.svg)/;
|
||||
const INDEX_REG = /index=(\d+)/;
|
||||
|
||||
/**
|
||||
* I know this is super hacky.
|
||||
*/
|
||||
const magicHookConstr = (() => {
|
||||
const l = {};
|
||||
export const auths = {};
|
||||
|
||||
(() => {
|
||||
if (isNodeJs) {
|
||||
// noop in CLI
|
||||
return () => Promise.resolve("");
|
||||
@ -29,14 +29,20 @@ const magicHookConstr = (() => {
|
||||
|
||||
hookNative(w, "fetch", () => {
|
||||
return function (url, init) {
|
||||
const token = init?.headers?.Authorization;
|
||||
if (typeof url === "string" && token) {
|
||||
const m = url.match(TYPE_REG);
|
||||
console.debug(url, token, m);
|
||||
if (m) {
|
||||
let token = init?.headers?.Authorization;
|
||||
if (
|
||||
typeof url === "string" &&
|
||||
(token || url.match(INIT_PAGE_REG))
|
||||
) {
|
||||
let m = url.match(TYPE_REG);
|
||||
let i = url.match(INDEX_REG);
|
||||
if (m && i) {
|
||||
// console.log(url, token, m[1], i[1]);
|
||||
const type = m[1];
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
l[type]?.(token);
|
||||
const index = i[1];
|
||||
auths[type + index] = token;
|
||||
} else if (url.match(INIT_PAGE_REG)) {
|
||||
auths["img0"] = url;
|
||||
}
|
||||
}
|
||||
return fetch(url, init);
|
||||
@ -50,19 +56,4 @@ const magicHookConstr = (() => {
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return async (type: FileType) => {
|
||||
return new Promise<string>((resolve) => {
|
||||
l[type] = (token) => {
|
||||
resolve(token);
|
||||
magics[type] = token;
|
||||
};
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
export const magics: Record<FileType, Promise<string>> = {
|
||||
img: magicHookConstr("img"),
|
||||
midi: magicHookConstr("midi"),
|
||||
mp3: magicHookConstr("mp3"),
|
||||
};
|
||||
|
106
src/file.ts
106
src/file.ts
@ -2,8 +2,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
|
||||
import isNodeJs from "detect-node";
|
||||
import { useTimeout, getFetch } from "./utils";
|
||||
import { magics } from "./file-magics";
|
||||
import { getFetch } from "./utils";
|
||||
import { auths } from "./file-magics";
|
||||
|
||||
export type FileType = "img" | "mp3" | "midi";
|
||||
|
||||
@ -11,29 +11,17 @@ const getApiUrl = (id: number, type: FileType, index: number): string => {
|
||||
return `/api/jmuse?id=${id}&type=${type}&index=${index}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* hard-coded auth tokens
|
||||
*/
|
||||
const useBuiltinAuth = (type: FileType): string => {
|
||||
switch (type) {
|
||||
case "img":
|
||||
return "8c022bdef45341074ce876ae57a48f64b86cdcf5";
|
||||
case "midi":
|
||||
return "38fb9efaae51b0c83b5bb5791a698b48292129e7";
|
||||
case "mp3":
|
||||
return "63794e5461e4cfa046edfbdddfccc1ac16daffd2";
|
||||
}
|
||||
};
|
||||
let imgInProgress = false;
|
||||
|
||||
const getApiAuth = async (type: FileType, index: number): Promise<string> => {
|
||||
if (isNodeJs) {
|
||||
// we cannot intercept API requests in Node.js (as no requests are sent), so go straightforward to the hard-coded tokens
|
||||
return useBuiltinAuth(type);
|
||||
// fix later
|
||||
throw Error;
|
||||
}
|
||||
|
||||
const magic = magics[type];
|
||||
if (magic instanceof Promise) {
|
||||
// force to retrieve the MAGIC
|
||||
let numPages = 0;
|
||||
let pageCooldown = 25;
|
||||
if (!auths[type + index]) {
|
||||
try {
|
||||
switch (type) {
|
||||
case "midi": {
|
||||
@ -51,10 +39,6 @@ const getApiAuth = async (type: FileType, index: number): Promise<string> => {
|
||||
break;
|
||||
}
|
||||
case "mp3": {
|
||||
// Mobile doesn't support click() function, find another method
|
||||
if (navigator.userAgentData.mobile) {
|
||||
throw Error;
|
||||
}
|
||||
const el = document.querySelector(
|
||||
'button[title="Toggle Play"]'
|
||||
) as HTMLButtonElement;
|
||||
@ -62,23 +46,69 @@ const getApiAuth = async (type: FileType, index: number): Promise<string> => {
|
||||
break;
|
||||
}
|
||||
case "img": {
|
||||
// Use fallback until better method is found
|
||||
throw Error;
|
||||
if (!imgInProgress) {
|
||||
imgInProgress = true;
|
||||
let parentDiv = document.querySelector(
|
||||
"#jmuse-scroller-component"
|
||||
)!;
|
||||
|
||||
numPages = parentDiv.children.length - 3;
|
||||
let i = 0;
|
||||
|
||||
function scrollToNextChild() {
|
||||
let childDiv = parentDiv.children[i];
|
||||
if (childDiv) {
|
||||
childDiv.scrollIntoView();
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
if (i < numPages) {
|
||||
setTimeout(scrollToNextChild, pageCooldown);
|
||||
}
|
||||
}
|
||||
|
||||
scrollToNextChild();
|
||||
}
|
||||
imgInProgress = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return useBuiltinAuth(type);
|
||||
throw Error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return await useTimeout(magic, 5 * 1000 /* 5s */);
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = setTimeout(
|
||||
() => {
|
||||
reject(new Error("token timeout"));
|
||||
},
|
||||
type === "img"
|
||||
? numPages * pageCooldown * 2 + 2100
|
||||
: 5 * 1000 /* 5s */
|
||||
);
|
||||
|
||||
// Check the auths object periodically
|
||||
let interval = setInterval(() => {
|
||||
if (auths.hasOwnProperty(type + index)) {
|
||||
clearTimeout(timer);
|
||||
clearInterval(interval);
|
||||
setInterval(
|
||||
() => {
|
||||
resolve(auths[type + index]);
|
||||
},
|
||||
// long delay for images to give time for them to load fully
|
||||
type === "img" ? 2000 : 100
|
||||
);
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
} catch {
|
||||
console.error(type, "token timeout");
|
||||
// try hard-coded tokens
|
||||
return useBuiltinAuth(type);
|
||||
throw Error;
|
||||
}
|
||||
};
|
||||
|
||||
@ -88,23 +118,19 @@ export const getFileUrl = async (
|
||||
index = 0,
|
||||
_fetch = getFetch()
|
||||
): Promise<string> => {
|
||||
let r;
|
||||
const url = getApiUrl(id, type, index);
|
||||
const auth = await getApiAuth(type, index);
|
||||
|
||||
let r = await _fetch(url, {
|
||||
headers: {
|
||||
Authorization: auth,
|
||||
},
|
||||
});
|
||||
|
||||
if (!r.ok) {
|
||||
r = await _fetch(url + "&v2=1", {
|
||||
if (type === "img" && index === 0) {
|
||||
// auth is the URL for the first page
|
||||
r = await _fetch(auth);
|
||||
} else {
|
||||
r = await _fetch(url, {
|
||||
headers: {
|
||||
Authorization: auth,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const { info } = await r.json();
|
||||
return info.url as string;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user