Spaces:
Running
Running
| import SparkMD5 from 'spark-md5'; | |
| //import JSZip from 'jszip'; | |
| import * as starry from '../../src/starry'; | |
| //import { encodeFindResource } from '../../src/isomorphic/converter'; | |
| import sharp, { FormatEnum } from 'sharp'; | |
| import got from 'got'; | |
| //import { Logger } from './ZeroClient'; | |
| import type { SolutionStore, SaveIssueMeasure } from './store'; | |
| import { ScoreJSON } from '../../src/isomorphic/types'; | |
| const SYSTEM_MARGIN = 4; | |
| export const constructSystem = ({ page, backgroundImage, detection, imageSize, position }) => { | |
| const systemWidth = (detection.phi2 - detection.phi1) / detection.interval; | |
| const systemHeight = imageSize.height / detection.interval; | |
| const lastSystem = page.systems[page.systems.length - 1]; | |
| const top = position ? position.y : (lastSystem ? lastSystem.top + lastSystem.height : 0) + SYSTEM_MARGIN; | |
| const left = position ? position.x : SYSTEM_MARGIN; | |
| const stavesTops = [ | |
| 0, | |
| ...Array(detection.middleRhos.length - 1) | |
| .fill(0) | |
| .map((_, i) => (detection.middleRhos[i] + detection.middleRhos[i + 1]) / 2 / detection.interval), | |
| ]; | |
| const measureBars = [systemWidth]; | |
| const staves = stavesTops.map( | |
| (top, i) => | |
| new starry.Staff({ | |
| top, | |
| height: (stavesTops[i + 1] || systemHeight) - top, | |
| staffY: detection.middleRhos[i] / detection.interval - top, | |
| measureBars, | |
| }) | |
| ); | |
| //console.log("detection:", detection, options, stavesTops); | |
| const imagePosition = { | |
| x: -detection.phi1 / detection.interval, | |
| y: 0, | |
| width: imageSize.width / detection.interval, | |
| height: imageSize.height / detection.interval, | |
| }; | |
| return new starry.System({ | |
| staves, | |
| left, | |
| top, | |
| width: systemWidth, | |
| backgroundImage, | |
| imagePosition, | |
| measureBars, | |
| }); | |
| }; | |
| export interface ConvertOption { | |
| format?: keyof FormatEnum; | |
| quality?: number; | |
| maxHeight?: number; | |
| } | |
| const toBuffer = async (url: string | Buffer): Promise<Buffer> => { | |
| if (typeof url === 'string') { | |
| if (/^https?:\/\//.test(url)) { | |
| return (await got(url, { responseType: 'buffer', decompress: true, https: { rejectUnauthorized: false } })).body; | |
| } | |
| if (/^data:image\//.test(url)) { | |
| return Buffer.from(url.split(',')[1], 'base64'); | |
| } | |
| return Buffer.from(url); | |
| } | |
| return url; | |
| }; | |
| /** | |
| * 转换图片格式,默认webp、最大高度1080,高度小于1080自动不做尺寸变换 | |
| * @param url | |
| * @param format | |
| * @param maxHeight | |
| * @param quality | |
| */ | |
| export async function convertImage(url: string | Buffer, { format = 'webp', maxHeight = 1080, quality = 80 }: ConvertOption = {}) { | |
| let buf = await toBuffer(url); | |
| const webpBuffer = await new Promise<Buffer>((resolve) => { | |
| sharp(buf) | |
| .resize({ | |
| width: maxHeight, | |
| height: maxHeight, | |
| fit: 'inside', | |
| withoutEnlargement: true, | |
| }) | |
| .toFormat(format, { quality }) | |
| .toBuffer((err, buf) => { | |
| resolve(buf); | |
| }); | |
| }); | |
| const md5 = SparkMD5.ArrayBuffer.hash(webpBuffer); | |
| return { | |
| buffer: webpBuffer, | |
| filename: `${md5}.${format}`, | |
| }; | |
| } | |
| /** | |
| * 替换scoreJson图片地址 | |
| * @param scoreJson | |
| * @param onReplaceImage | |
| */ | |
| export const replaceScoreJsonImages = (scoreJson: ScoreJSON, onReplaceImage: (src: string) => string = (src) => src) => { | |
| const json = JSON.parse(JSON.stringify(scoreJson)); | |
| json.pages.forEach((page) => { | |
| page?.src && (page.src = onReplaceImage(page?.src)); | |
| }); | |
| json.lines.forEach((system) => { | |
| system.lineStaves.forEach((line) => { | |
| line.imgs.forEach((staff) => { | |
| staff?.src && (staff.src = onReplaceImage(staff.src)); | |
| }); | |
| }); | |
| }); | |
| return json; | |
| }; | |
| /** | |
| * 获取scoreJson图片资源列表 | |
| * @param scoreJson | |
| */ | |
| export const getScoreJsonImages = (scoreJson: ScoreJSON) => { | |
| return [ | |
| ...scoreJson.pages.map((page) => page?.src), | |
| ...scoreJson.lines | |
| .map((system) => system.lineStaves.map((staff) => staff.imgs)) | |
| .flat(2) | |
| .map((staff) => staff?.src) | |
| .filter(Boolean), | |
| ]; | |
| }; | |
| interface ScorePatchesUpdateOptions { | |
| solutionStore?: SolutionStore; | |
| } | |
| export const updateScorePatches = (score: starry.Score, measures: starry.SpartitoMeasure[], options: ScorePatchesUpdateOptions = {}): void => { | |
| console.assert( | |
| measures.every((measure) => measure.validRegulated), | |
| '[updateScorePatches] some measures not valid regulated:', | |
| measures.filter((measure) => !measure.validRegulated) | |
| ); | |
| score.patches = measures.map((measure) => measure.createPatch()); | |
| if (options?.solutionStore) { | |
| score.assemble(); | |
| const spartito = score.makeSpartito(); | |
| measures.forEach((measure) => { | |
| options.solutionStore!.set(measure.regulationHash, { ...measure.asSolution(), priority: 1 }); | |
| if (measure.regulationHash0 !== measure.regulationHash) { | |
| const originMeasure = spartito.measures.find((m) => m.measureIndex === measure.measureIndex); | |
| options.solutionStore!.set(measure.regulationHash0, { ...measure.asSolution(originMeasure), priority: 1 }); | |
| } | |
| }); | |
| } | |
| }; | |
| interface EditableMeasuresSaveOptions { | |
| status?: number; | |
| solutionStore?: SolutionStore; | |
| } | |
| export const saveEditableMeasures = async ( | |
| score: starry.Score, | |
| measureIndices: number[], | |
| saveMeasure: SaveIssueMeasure, | |
| { status = 2, solutionStore }: EditableMeasuresSaveOptions = {} | |
| ): Promise<void> => { | |
| score.assemble(); | |
| const spartito = score.spartito || score.makeSpartito(); | |
| const measures = measureIndices | |
| .map((index) => spartito.measures.find((measure) => measure.measureIndex === index)) | |
| .filter(Boolean) as starry.SpartitoMeasure[]; | |
| if (solutionStore) { | |
| const solutions = await solutionStore.batchGet(measures.map((measure) => measure.regulationHash0)); | |
| measures.forEach((measure, i) => { | |
| const solution = solutions[i]; | |
| if (solution) measure.applySolution(solution); | |
| }); | |
| } | |
| measures.forEach((measure) => { | |
| saveMeasure({ | |
| measureIndex: measure.measureIndex, | |
| measure: new starry.EditableMeasure(measure), | |
| status, | |
| }); | |
| }); | |
| }; | |