-
Notifications
You must be signed in to change notification settings - Fork 16
fix: 修复导出铺面时的诸多问题 #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
51f62a9
feat: 修复导出铺面时的诸多问题
Symb0x76 a008489
fix(export): 加固 RemoteExport 和 HelperProcess
Symb0x76 c3ceea4
fix: 修复导出功能中的路径处理和错误处理逻辑
Symb0x76 e22c4ad
!refactor(WannaCRI): 使用C#重构Python的USM相关处理,彻底移除python
Symb0x76 ef21165
feat: 优化音乐导出功能,支持并发处理和文件哈希检查
Symb0x76 e20eea6
perf: USM直接打包和ffprobe缓存
Symb0x76 d7ab4f5
fix(MusicTransfer): 修复legacyFormat处理逻辑,优化ma2文件解析
Symb0x76 7e19319
修复一个原有的 bug
clansty f4472b3
移除调试代码和无关修改
clansty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
465 changes: 371 additions & 94 deletions
465
MaiChartManager/Controllers/Music/MusicTransferController.cs
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 0 additions & 98 deletions
98
MaiChartManager/Front/src/components/MusicList/BatchActionButton/remoteExport.ts
This file was deleted.
Oops, something went wrong.
194 changes: 194 additions & 0 deletions
194
MaiChartManager/Front/src/components/MusicList/BatchActionButton/remoteExport.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| import { STEP } from "@/components/MusicList/BatchActionButton/index"; | ||
| import { | ||
| currentProcessItem, | ||
| progressAll, | ||
| progressCurrent, | ||
| } from "@/components/MusicList/BatchActionButton/ProgressDisplay"; | ||
| import { MusicXmlWithABJacket } from "@/client/apiGen"; | ||
| import { BlobWriter, ZipReader } from "@zip.js/zip.js"; | ||
| import getSubDirFile from "@/utils/getSubDirFile"; | ||
| import { | ||
| MAIDATA_SUBDIR, | ||
| OPTIONS, | ||
| } from "@/components/MusicList/BatchActionButton/ChooseAction"; | ||
| import { useNotification } from "naive-ui"; | ||
| import { getUrl } from "@/client/api"; | ||
| import { addVersionList, genreList } from "@/store/refs"; | ||
| import { t } from "@/locales"; | ||
| import { sanitizeFsSegment } from "@/utils/sanitizeFsName"; | ||
|
|
||
| export default async ( | ||
| setStep: (step: STEP) => void, | ||
| musicList: MusicXmlWithABJacket[], | ||
| action: OPTIONS, | ||
| notify: ReturnType<typeof useNotification>, | ||
| dirOption: MAIDATA_SUBDIR, | ||
| ) => { | ||
| let folderHandle: FileSystemDirectoryHandle; | ||
| try { | ||
| folderHandle = await window.showDirectoryPicker({ | ||
| id: "copyToSaveDir", | ||
| mode: "readwrite", | ||
| }); | ||
| } catch (e) { | ||
| console.log(e); | ||
| return; | ||
| } | ||
|
|
||
| const getMaidataExportDir = (music: MusicXmlWithABJacket) => { | ||
| let parentDir = ""; | ||
| switch (dirOption) { | ||
| case MAIDATA_SUBDIR.Genre: | ||
| parentDir = | ||
| genreList.value.find((genre) => genre.id === music.genreId) | ||
| ?.genreName || t("music.list.unknown"); | ||
| break; | ||
| case MAIDATA_SUBDIR.Version: | ||
| parentDir = | ||
| addVersionList.value.find( | ||
| (version) => version.id === music.addVersionId, | ||
| )?.genreName || t("music.list.unknown"); | ||
| break; | ||
| } | ||
|
|
||
| if (parentDir) { | ||
| parentDir = sanitizeFsSegment(parentDir, t("music.list.unknown")); | ||
| } | ||
|
|
||
| const suffix = music.id! > 1e4 && music.id! < 2e4 ? " [DX]" : ""; | ||
| const safeTitle = sanitizeFsSegment( | ||
| music.name || t("music.list.unknown"), | ||
| t("music.list.unknown"), | ||
| ); | ||
| const targetDir = `${safeTitle}${suffix}`; | ||
| return parentDir ? `${parentDir}/${targetDir}` : targetDir; | ||
| }; | ||
|
|
||
| progressCurrent.value = 0; | ||
| progressAll.value = musicList.length; | ||
| currentProcessItem.value = ""; | ||
|
|
||
| setStep(STEP.ProgressDisplay); | ||
|
|
||
| const getExportUrl = (music: MusicXmlWithABJacket) => { | ||
| switch (action) { | ||
| case OPTIONS.CreateNewOpt: | ||
| return `ExportOptApi/${music.assetDir}/${music.id}`; | ||
| case OPTIONS.CreateNewOptCompatible: | ||
| return `ExportOptApi/${music.assetDir}/${music.id}?removeEvents=true`; | ||
| case OPTIONS.CreateNewOptMa2_103: | ||
| return `ExportOptApi/${music.assetDir}/${music.id}?removeEvents=true&legacyFormat=true`; | ||
| case OPTIONS.ConvertToMaidata: | ||
| return `ExportAsMaidataApi/${music.assetDir}/${music.id}`; | ||
| case OPTIONS.ConvertToMaidataIgnoreVideo: | ||
| return `ExportAsMaidataApi/${music.assetDir}/${music.id}?ignoreVideo=true`; | ||
| default: | ||
| throw new Error(`Unsupported export action: ${action}`); | ||
| } | ||
| }; | ||
|
|
||
| const getMaxParallelExports = () => { | ||
| const cpuThreads = Math.max(1, navigator.hardwareConcurrency || 4); | ||
|
|
||
| switch (action) { | ||
| case OPTIONS.ConvertToMaidata: | ||
| return Math.max(1, Math.floor(cpuThreads / 4)); | ||
| case OPTIONS.ConvertToMaidataIgnoreVideo: | ||
| return Math.max(1, Math.floor(cpuThreads / 3)); | ||
| default: | ||
| return Math.max(1, Math.floor(cpuThreads / 2)); | ||
| } | ||
| }; | ||
|
|
||
| const exportOne = async (music: MusicXmlWithABJacket) => { | ||
| const musicName = music.name || t("music.list.unknown"); | ||
| currentProcessItem.value = musicName; | ||
|
|
||
| const maidataRootDir = | ||
| action === OPTIONS.ConvertToMaidata || | ||
| action === OPTIONS.ConvertToMaidataIgnoreVideo | ||
| ? getMaidataExportDir(music) | ||
| : ""; | ||
|
|
||
| try { | ||
| const response = await fetch(getUrl(getExportUrl(music))); | ||
| if (!response.ok || !response.body) { | ||
| throw new Error( | ||
| `Export request failed: ${response.status} ${response.statusText}`, | ||
| ); | ||
| } | ||
|
|
||
| const zipReader = new ZipReader(response.body); | ||
| try { | ||
| let hasEntryError = false; | ||
| const entries = zipReader.getEntriesGenerator(); | ||
| for await (const entry of entries) { | ||
| try { | ||
| if (entry.filename.endsWith("/") || !entry.getData) { | ||
| continue; | ||
| } | ||
|
|
||
| const filename = maidataRootDir | ||
| ? `${maidataRootDir}/${entry.filename}` | ||
| : entry.filename; | ||
|
|
||
| const fileHandle = await getSubDirFile(folderHandle, filename); | ||
| const writable = await fileHandle.createWritable(); | ||
| try { | ||
| const blob = await entry.getData(new BlobWriter()); | ||
| await writable.write(blob); | ||
| } finally { | ||
| await writable.close(); | ||
| } | ||
| } catch (e) { | ||
| hasEntryError = true; | ||
| console.error("Failed to export zip entry", { | ||
| musicName, | ||
| sourceFile: entry.filename, | ||
| error: e, | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| if (hasEntryError) { | ||
| notify.error({ | ||
| title: t("error.exportFailed"), | ||
| content: musicName, | ||
| }); | ||
| } | ||
| } finally { | ||
| await zipReader.close(); | ||
| } | ||
| } catch (e) { | ||
| console.error(e); | ||
| notify.error({ | ||
| title: t("error.exportFailed"), | ||
| content: musicName, | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| let nextIndex = 0; | ||
| let completedCount = 0; | ||
| const workerCount = Math.min(musicList.length, getMaxParallelExports()); | ||
|
|
||
| const worker = async () => { | ||
| while (true) { | ||
| const currentIndex = nextIndex++; | ||
| if (currentIndex >= musicList.length) { | ||
| return; | ||
| } | ||
|
|
||
| await exportOne(musicList[currentIndex]); | ||
| completedCount += 1; | ||
| progressCurrent.value = completedCount; | ||
| } | ||
| }; | ||
|
|
||
| try { | ||
| await Promise.all(Array.from({ length: workerCount }, () => worker())); | ||
| } finally { | ||
| currentProcessItem.value = ""; | ||
| setStep(STEP.None); | ||
| } | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,25 @@ | ||
| export default async (folderHandle: FileSystemDirectoryHandle, fileName: string) => { | ||
| const pathParts = fileName.split('/'); | ||
| import { sanitizeFsSegment } from "@/utils/sanitizeFsName"; | ||
|
|
||
| export default async ( | ||
| folderHandle: FileSystemDirectoryHandle, | ||
| fileName: string, | ||
| ) => { | ||
| const pathParts = fileName | ||
| .split("/") | ||
| .filter((part) => part.length > 0 && part !== "." && part !== "..") | ||
| .map((part) => sanitizeFsSegment(part)); | ||
|
|
||
| if (pathParts.length === 0) { | ||
| throw new Error("Invalid file path"); | ||
| } | ||
|
|
||
| let dirHandle = folderHandle; | ||
| for (let i = 0; i < pathParts.length - 1; i++) { | ||
| dirHandle = await dirHandle.getDirectoryHandle(pathParts[i], {create: true}); | ||
| dirHandle = await dirHandle.getDirectoryHandle(pathParts[i], { | ||
| create: true, | ||
| }); | ||
| } | ||
| return await dirHandle.getFileHandle(pathParts[pathParts.length - 1], {create: true}); | ||
| } | ||
| return await dirHandle.getFileHandle(pathParts[pathParts.length - 1], { | ||
| create: true, | ||
| }); | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.