From ab47a424dd9ad350aed634b5af7f8dfdc5daf3ca Mon Sep 17 00:00:00 2001 From: ato lash Date: Sun, 31 Dec 2023 17:29:36 +0900 Subject: [PATCH] refactor: split the process to move to the main process --- src/renderer/main/core.ts | 284 ++++++++++--------- src/renderer/main/package.ts | 536 ++++++++++++++++++----------------- 2 files changed, 418 insertions(+), 402 deletions(-) diff --git a/src/renderer/main/core.ts b/src/renderer/main/core.ts index de83fcb0..1ac5b569 100644 --- a/src/renderer/main/core.ts +++ b/src/renderer/main/core.ts @@ -40,26 +40,55 @@ async function initCore() { store.set('installationPath', instPath); } } +/** + * Set the version of the manually installed program. + * @param {string} instPath - An installation path. + */ +async function detectManuallyInstalledProgram(instPath: string) { + const coreInfo = await getCoreInfo(); + for (const program of programs) { + const progInfo: Program = coreInfo[program]; + + if (!(await apmJson.has(instPath, 'core.' + program))) { + for (const release of progInfo.releases) { + if (await checkIntegrity(instPath, release.integrity.file)) + await apmJson.setCore(instPath, program, release.version); + } + } + } +} + +/** + * Add a shortcut to the Start menu. + * @param {string} instPath - An installation path. + */ +async function updateAviUtlShortcut(instPath: string) { + const appDataPath = await app.getPath('appData'); + const apmPath = await app.getPath('exe'); + const aviutlPath = path.join(instPath, 'aviutl.exe'); + if ( + fs.existsSync(aviutlPath) && + apmPath.includes(path.dirname(appDataPath)) // Verify that it is the installed version of apm + ) { + addAviUtlShortcut(appDataPath, aviutlPath); + } else { + removeAviUtlShortcut(appDataPath); + } +} /** * Displays installed version. * @param {string} instPath - An installation path. */ async function displayInstalledVersion(instPath: string) { + await detectManuallyInstalledProgram(instPath); + const coreInfo = await getCoreInfo(); const isInstalled = { aviutl: false, exedit: false }; if (instPath && coreInfo) { for (const program of programs) { const progInfo: Program = coreInfo[program]; - // Set the version of the manually installed program - if (!(await apmJson.has(instPath, 'core.' + program))) { - for (const release of progInfo.releases) { - if (await checkIntegrity(instPath, release.integrity.file)) - await apmJson.setCore(instPath, program, release.version); - } - } - if (await apmJson.has(instPath, 'core.' + program)) { const installedVersion = (await apmJson.get( instPath, @@ -113,19 +142,8 @@ async function displayInstalledVersion(instPath: string) { replaceText('core-check-date', '未確認'); } - // Add a shortcut to the Start menu if (process.platform === 'win32') { - const appDataPath = await app.getPath('appData'); - const apmPath = await app.getPath('exe'); - const aviutlPath = path.join(instPath, 'aviutl.exe'); - if ( - fs.existsSync(aviutlPath) && - apmPath.includes(path.dirname(appDataPath)) // Verify that it is the installed version of apm - ) { - addAviUtlShortcut(appDataPath, aviutlPath); - } else { - removeAviUtlShortcut(appDataPath); - } + await updateAviUtlShortcut(instPath); } } @@ -215,6 +233,19 @@ async function setCoreVersions(instPath: string) { } } +/** + * + */ +async function _checkLatestVersion() { + await download(await modList.getCoreDataUrl(), { + subDir: 'core', + }); + await modList.updateInfo(); + store.set('checkDate.core', Date.now()); + const modInfo = await modList.getInfo(); + store.set('modDate.core', new Date(modInfo.core.modified).getTime()); +} + /** * Checks the latest versionof programs. * @param {string} instPath - An installation path. @@ -226,13 +257,8 @@ async function checkLatestVersion(instPath: string) { const { enableButton } = buttonTransition.loading(btn, '更新'); try { - await download(await modList.getCoreDataUrl(), { - subDir: 'core', - }); - await modList.updateInfo(); - store.set('checkDate.core', Date.now()); - const modInfo = await modList.getInfo(); - store.set('modDate.core', new Date(modInfo.core.modified).getTime()); + await _checkLatestVersion(); + await displayInstalledVersion(instPath); await setCoreVersions(instPath); buttonTransition.message(btn, '更新完了', 'success'); @@ -327,63 +353,21 @@ async function changeInstallationPath(instPath: string) { } /** - * Installs a program to installation path. - * @param {HTMLButtonElement} btn - A HTMLElement of clicked button. + * * @param {string} program - A program name to install. * @param {string} version - A version to install. * @param {string} instPath - An installation path. */ -async function installProgram( - btn: HTMLButtonElement, +async function _installProgram( program: (typeof programs)[number], version: string, instPath: string, ) { - const { enableButton } = btn - ? buttonTransition.loading(btn) - : { enableButton: null }; - - if (!instPath) { - log.error('An installation path is not selected.'); - if (btn) { - buttonTransition.message( - btn, - 'インストール先フォルダを指定してください。', - 'danger', - ); - setTimeout(() => { - enableButton(); - }, 3000); - } - return; - } - - if (!version) { - log.error('A version is not selected.'); - if (btn) { - buttonTransition.message(btn, 'バージョンを指定してください。', 'danger'); - setTimeout(() => { - enableButton(); - }, 3000); - } - return; - } - const coreInfo = await getCoreInfo(); if (!coreInfo) { log.error('The version data do not exist.'); - if (btn) { - buttonTransition.message( - btn, - 'バージョンデータが存在しません。', - 'danger', - ); - setTimeout(() => { - enableButton(); - }, 3000); - } - return; + throw new Error('バージョンデータが存在しません。'); } const progInfo = coreInfo[program] as Program; @@ -392,17 +376,7 @@ async function installProgram( if (!archivePath) { log.error('Failed downloading a file.'); - if (btn) { - buttonTransition.message( - btn, - 'ダウンロード中にエラーが発生しました。', - 'danger', - ); - setTimeout(() => { - enableButton(); - }, 3000); - } - return; + throw new Error('ダウンロード中にエラーが発生しました。'); } const integrityForArchive = progInfo.releases.find( @@ -419,45 +393,68 @@ async function installProgram( if (!dialogResult) { log.error(`The downloaded archive file is corrupt. URL:${url}`); - if (btn) { - buttonTransition.message( - btn, - 'ダウンロードされたファイルは破損しています。', - 'danger', - ); - setTimeout(() => { - enableButton(); - }, 3000); - } - return; + throw new Error('ダウンロードされたファイルは破損しています。'); } archivePath = await download(url, { subDir: 'core' }); if (!archivePath) { log.error(`Failed downloading the archive file. URL:${url}`); - if (btn) { - buttonTransition.message( - btn, - 'ファイルのダウンロードに失敗しました。', - 'danger', - ); - setTimeout(() => { - enableButton(); - }, 3000); - return; - } else { - // Throw an error if not executed from the UI. - throw new Error('Failed downloading the archive file.'); - } + throw new Error('ファイルのダウンロードに失敗しました。'); } } } + const unzippedPath = await unzip(archivePath); + await install(unzippedPath, instPath, progInfo.files, true); + await apmJson.setCore(instPath, program, version); +} + +/** + * Installs a program to installation path. + * @param {HTMLButtonElement} btn - A HTMLElement of clicked button. + * @param {string} program - A program name to install. + * @param {string} version - A version to install. + * @param {string} instPath - An installation path. + */ +async function installProgram( + btn: HTMLButtonElement, + program: (typeof programs)[number], + version: string, + instPath: string, +) { + const { enableButton } = btn + ? buttonTransition.loading(btn) + : { enableButton: null }; + + if (!instPath) { + log.error('An installation path is not selected.'); + if (btn) { + buttonTransition.message( + btn, + 'インストール先フォルダを指定してください。', + 'danger', + ); + setTimeout(() => { + enableButton(); + }, 3000); + } + return; + } + + if (!version) { + log.error('A version is not selected.'); + if (btn) { + buttonTransition.message(btn, 'バージョンを指定してください。', 'danger'); + setTimeout(() => { + enableButton(); + }, 3000); + } + return; + } + try { - const unzippedPath = await unzip(archivePath); - await install(unzippedPath, instPath, progInfo.files, true); + await _installProgram(program, version, instPath); - await apmJson.setCore(instPath, program, version); await displayInstalledVersion(instPath); await packageMain.setPackagesList(instPath); await packageMain.displayNicommonsIdList(instPath); @@ -465,13 +462,39 @@ async function installProgram( if (btn) buttonTransition.message(btn, 'インストール完了', 'success'); } catch (e) { log.error(e); - if (btn) buttonTransition.message(btn, 'エラーが発生しました。', 'danger'); + if (btn) buttonTransition.message(btn, e.message, 'danger'); + } finally { + if (btn) + setTimeout(() => { + enableButton(); + }, 3000); } +} - if (btn) - setTimeout(() => { - enableButton(); - }, 3000); +/** + * + * @param {string} instPath - An installation path. + */ +async function _batchInstall(instPath: string) { + const coreInfo = await getCoreInfo(); + for (const program of programs) { + const progInfo = coreInfo[program]; + await installProgram(null, program, progInfo.latestVersion, instPath); + } + const allPackages = ( + await packageUtil.getPackagesExtra( + await packageMain.getPackages(instPath), + instPath, + ) + ).packages; + const packages = allPackages.filter( + (p) => + p.info.directURL && + p.installationStatus === packageUtil.states.notInstalled, + ); + for (const packageItem of packages) { + await packageMain.installPackage(instPath, packageItem, true); + } } /** @@ -501,35 +524,16 @@ async function batchInstall(instPath: string) { } try { - const coreInfo = await getCoreInfo(); - for (const program of programs) { - const progInfo = coreInfo[program]; - await installProgram(null, program, progInfo.latestVersion, instPath); - } - const allPackages = ( - await packageUtil.getPackagesExtra( - await packageMain.getPackages(instPath), - instPath, - ) - ).packages; - const packages = allPackages.filter( - (p) => - p.info.directURL && - p.installationStatus === packageUtil.states.notInstalled, - ); - for (const packageItem of packages) { - await packageMain.installPackage(instPath, packageItem, true); - } - + await _batchInstall(instPath); buttonTransition.message(btn, 'インストール完了', 'success'); } catch (e) { log.error(e); buttonTransition.message(btn, 'エラーが発生しました。', 'danger'); + } finally { + setTimeout(() => { + enableButton(); + }, 3000); } - - setTimeout(() => { - enableButton(); - }, 3000); } const core = { diff --git a/src/renderer/main/package.ts b/src/renderer/main/package.ts index 4ae63ba6..b5f5a177 100644 --- a/src/renderer/main/package.ts +++ b/src/renderer/main/package.ts @@ -478,6 +478,21 @@ async function setPackagesList(instPath: string) { } } +/** + * + * @param {string} instPath - An installation path. + */ +async function _checkPackagesList(instPath: string) { + await modList.updateInfo(); + await packageUtil.downloadRepository(modList.getPackagesDataUrl(instPath)); + store.set('checkDate.packages', Date.now()); + const modInfo = await modList.getInfo(); + store.set( + 'modDate.packages', + Math.max(...modInfo.packages.map((p) => new Date(p.modified).getTime())), + ); +} + /** * Checks the packages list. * @param {string} instPath - An installation path. @@ -497,17 +512,10 @@ async function checkPackagesList(instPath: string) { } try { - await modList.updateInfo(); - await packageUtil.downloadRepository(modList.getPackagesDataUrl(instPath)); - store.set('checkDate.packages', Date.now()); - const modInfo = await modList.getInfo(); - store.set( - 'modDate.packages', - Math.max(...modInfo.packages.map((p) => new Date(p.modified).getTime())), - ); + await _checkPackagesList(instPath); + await setPackagesList(instPath); await displayNicommonsIdList(instPath); - if (btn) buttonTransition.message(btn, '更新完了', 'success'); } catch (e) { log.error(e); @@ -563,6 +571,76 @@ async function getScriptsList(update = false) { return result; } +/** + * + * @param {string} archivePath + * @param {PackageItem} installedPackage + * @param {string} instPath - An installation path. + * @returns {Promise} + */ +async function _installPackage( + archivePath: string, + installedPackage: PackageItem, + instPath: string, +) { + const getUnzippedPath = async () => { + if (['.zip', '.lzh', '.7z', '.rar'].includes(path.extname(archivePath))) { + return await unzip(archivePath, installedPackage.id); + } else { + // In this line, path.dirname(archivePath) always refers to the 'Data/package' folder. + const newFolder = path.join( + path.dirname(archivePath), + installedPackage.id, + ); + await mkdir(newFolder, { recursive: true }); + await rename( + archivePath, + path.join(newFolder, path.basename(archivePath)), + ); + return newFolder; + } + }; + + const unzippedPath = await getUnzippedPath(); + + if (installedPackage.info.installer) { + const searchFiles = async (dirName: string) => { + let result: string[][] = []; + const dirents = await readdir(dirName, { + withFileTypes: true, + }); + for (const dirent of dirents) { + if (dirent.isDirectory()) { + const childResult = await searchFiles( + path.join(dirName, dirent.name), + ); + result = result.concat(childResult); + } else { + if (dirent.name === installedPackage.info.installer) { + result.push([path.join(dirName, dirent.name)]); + break; + } + } + } + return result; + }; + + const exePath = await searchFiles(unzippedPath); + const command = + '"' + + exePath[0][0] + + '" ' + + installedPackage.info.installArg + .replace('"$instpath"', '$instpath') + .replace('$instpath', '"' + instPath + '"'); // Prevent double quoting + execSync(command); + + return verifyFilesByCount(instPath, installedPackage.info.files); + } else { + return await install(unzippedPath, instPath, installedPackage.info.files); + } +} + /** * Installs a package to installation path. * @param {string} instPath - An installation path. @@ -764,66 +842,11 @@ async function installPackage( let installResult = false; try { - const getUnzippedPath = async () => { - if (['.zip', '.lzh', '.7z', '.rar'].includes(path.extname(archivePath))) { - return await unzip(archivePath, installedPackage.id); - } else { - // In this line, path.dirname(archivePath) always refers to the 'Data/package' folder. - const newFolder = path.join( - path.dirname(archivePath), - installedPackage.id, - ); - await mkdir(newFolder, { recursive: true }); - await rename( - archivePath, - path.join(newFolder, path.basename(archivePath)), - ); - return newFolder; - } - }; - - const unzippedPath = await getUnzippedPath(); - - if (installedPackage.info.installer) { - const searchFiles = async (dirName: string) => { - let result: string[][] = []; - const dirents = await readdir(dirName, { - withFileTypes: true, - }); - for (const dirent of dirents) { - if (dirent.isDirectory()) { - const childResult = await searchFiles( - path.join(dirName, dirent.name), - ); - result = result.concat(childResult); - } else { - if (dirent.name === installedPackage.info.installer) { - result.push([path.join(dirName, dirent.name)]); - break; - } - } - } - return result; - }; - - const exePath = await searchFiles(unzippedPath); - const command = - '"' + - exePath[0][0] + - '" ' + - installedPackage.info.installArg - .replace('"$instpath"', '$instpath') - .replace('$instpath', '"' + instPath + '"'); // Prevent double quoting - execSync(command); - - installResult = verifyFilesByCount(instPath, installedPackage.info.files); - } else { - installResult = await install( - unzippedPath, - instPath, - installedPackage.info.files, - ); - } + installResult = await _installPackage( + archivePath, + installedPackage, + instPath, + ); } catch (e) { log.error(e); installResult = false; @@ -851,6 +874,43 @@ async function installPackage( } } +/** + * + * @param {string} instPath - An installation path. + * @param {PackageItem} uninstalledPackage + */ +async function _uninstallPackage( + instPath: string, + uninstalledPackage: PackageItem, +) { + const filesToRemove = []; + for (const file of uninstalledPackage.info.files) { + if (!file.isInstallOnly) + filesToRemove.push(path.join(instPath, file.filename)); + } + + await Promise.all( + filesToRemove.map((filePath) => safeRemove(filePath, instPath)), + ); + + let filesCount = 0; + let notExistCount = 0; + for (const file of uninstalledPackage.info.files) { + if (!file.isInstallOnly) { + filesCount++; + if (!existsSync(path.join(instPath, file.filename))) { + notExistCount++; + } + } + } + + if (filesCount !== notExistCount) { + throw new Error(''); + } + + await apmJson.removePackage(instPath, uninstalledPackage); +} + /** * Uninstalls a package to installation path. * @param {string} instPath - An installation path. @@ -900,38 +960,9 @@ async function uninstallPackage(instPath: string) { const uninstalledPackage = { ...selectedEntry } as PackageItem; - const filesToRemove = []; - for (const file of uninstalledPackage.info.files) { - if (!file.isInstallOnly) - filesToRemove.push(path.join(instPath, file.filename)); - } - try { - await Promise.all( - filesToRemove.map((filePath) => safeRemove(filePath, instPath)), - ); - } catch (e) { - log.error(e); - buttonTransition.message(btn, 'エラーが発生しました。', 'danger'); - setTimeout(() => { - enableButton(); - }, 3000); - return; - } + await _uninstallPackage(instPath, uninstalledPackage); - let filesCount = 0; - let notExistCount = 0; - for (const file of uninstalledPackage.info.files) { - if (!file.isInstallOnly) { - filesCount++; - if (!existsSync(path.join(instPath, file.filename))) { - notExistCount++; - } - } - } - - await apmJson.removePackage(instPath, uninstalledPackage); - if (filesCount === notExistCount) { if (!uninstalledPackage.id.startsWith('script_')) { await setPackagesList(instPath); await displayNicommonsIdList(instPath); @@ -941,16 +972,17 @@ async function uninstallPackage(instPath: string) { uninstalledPackage, ); await checkPackagesList(instPath); - } - buttonTransition.message(btn, 'アンインストール完了', 'success'); - } else { + buttonTransition.message(btn, 'アンインストール完了', 'success'); + } + } catch (e) { + log.error(e); buttonTransition.message(btn, 'エラーが発生しました。', 'danger'); + } finally { + setTimeout(() => { + enableButton(); + }, 3000); } - - setTimeout(() => { - enableButton(); - }, 3000); } /** @@ -1012,39 +1044,16 @@ async function openPackageFolder() { } /** - * Installs a script to installation path. + * + * @param {string} url * @param {string} instPath - An installation path. + * @returns {Promise} */ -async function installScript(instPath: string) { - const btn = document.getElementById('install-package') as HTMLButtonElement; - const { enableButton } = buttonTransition.loading(btn); - const url = (selectedEntry as Scripts['webpage'][number]).url; - - if (!instPath) { - log.error('An installation path is not selected.'); - buttonTransition.message( - btn, - 'インストール先フォルダを指定してください。', - 'danger', - ); - setTimeout(() => { - enableButton(); - }, 3000); - return; - } - +async function _installScript(url: string, instPath: string) { const downloadResult = await openBrowser(url, 'package'); if (!downloadResult) { log.info('The installation was canceled.'); - buttonTransition.message( - btn, - 'インストールがキャンセルされました。', - 'info', - ); - setTimeout(() => { - enableButton(); - }, 3000); - return; + throw new Error('インストールがキャンセルされました。'); } const archivePath = downloadResult.savePath; @@ -1055,11 +1064,7 @@ async function installScript(instPath: string) { if (!matchInfo) { log.error('The script is not supported.'); - buttonTransition.message(btn, '未対応のスクリプトです。', 'danger'); - setTimeout(() => { - enableButton(); - }, 3000); - return; + throw new Error('未対応のスクリプトです。'); } if ('redirect' in matchInfo) { @@ -1081,15 +1086,8 @@ async function installScript(instPath: string) { archivePath, ); } else { - buttonTransition.message( - btn, - '指定されたパッケージは存在しません。', - 'danger', - ); + throw new Error('指定されたパッケージは存在しません。'); } - setTimeout(() => { - enableButton(); - }, 3000); return; } @@ -1134,135 +1132,149 @@ async function installScript(instPath: string) { ).some((e) => e); }; - try { - const getUnzippedPath = async () => { - if (['.zip', '.lzh', '.7z', '.rar'].includes(path.extname(archivePath))) { - return await unzip(archivePath); - } else { - // In this line, path.dirname(archivePath) always refers to the 'Data/package' folder. - const newFolder = path.join( - path.dirname(archivePath), - 'tmp_' + path.basename(archivePath), - ); - await mkdir(newFolder, { recursive: true }); - await rename( - archivePath, - path.join(newFolder, path.basename(archivePath)), - ); - return newFolder; - } - }; - const unzippedPath = await getUnzippedPath(); - - if (!(await extExists(unzippedPath, scriptExtRegex))) { - log.error('No script files are included.'); - buttonTransition.message(btn, 'スクリプトが含まれていません。', 'danger'); - setTimeout(() => { - enableButton(); - }, 3000); - return; - } - if (await extExists(unzippedPath, pluginExtRegex)) { - log.error('Plugin files are included.'); - buttonTransition.message( - btn, - 'プラグインが含まれているためインストールできません。', - 'danger', + const getUnzippedPath = async () => { + if (['.zip', '.lzh', '.7z', '.rar'].includes(path.extname(archivePath))) { + return await unzip(archivePath); + } else { + // In this line, path.dirname(archivePath) always refers to the 'Data/package' folder. + const newFolder = path.join( + path.dirname(archivePath), + 'tmp_' + path.basename(archivePath), ); - setTimeout(() => { - enableButton(); - }, 3000); - return; + await mkdir(newFolder, { recursive: true }); + await rename( + archivePath, + path.join(newFolder, path.basename(archivePath)), + ); + return newFolder; } + }; + const unzippedPath = await getUnzippedPath(); - // Copying files - const denyList = [ - '*readme*', - '*copyright*', - '*.txt', - '*.zip', - '*.aup', - '*.md', - 'doc', - 'old', - 'old_*', - ]; - const scriptRoot = (await searchScriptRoot(unzippedPath))[0]; - const entriesToCopy = ( - await readdir(scriptRoot, { - withFileTypes: true, - }) - ) - .filter((p) => !isMatch([p.name], denyList)) - .map((p) => { - return { - src: path.join(scriptRoot, p.name), - dest: path.join(instPath, 'script', matchInfo.folder, p.name), - filename: path - .join('script', matchInfo.folder, p.name) - .replaceAll('\\', '/'), - isDirectory: p.isDirectory(), - }; - }); - await mkdir(path.join(instPath, 'script', matchInfo.folder), { - recursive: true, - }); - await Promise.all( - entriesToCopy.map((filePath) => copy(filePath.src, filePath.dest)), - ); + if (!(await extExists(unzippedPath, scriptExtRegex))) { + log.error('No script files are included.'); + throw new Error('スクリプトが含まれていません。'); + } + if (await extExists(unzippedPath, pluginExtRegex)) { + log.error('Plugin files are included.'); + throw new Error('プラグインが含まれているためインストールできません。'); + } - // Constructing package information - const files = entriesToCopy.map((i) => { - return { filename: i.filename, isDirectory: i.isDirectory }; + // Copying files + const denyList = [ + '*readme*', + '*copyright*', + '*.txt', + '*.zip', + '*.aup', + '*.md', + 'doc', + 'old', + 'old_*', + ]; + const scriptRoot = (await searchScriptRoot(unzippedPath))[0]; + const entriesToCopy = ( + await readdir(scriptRoot, { + withFileTypes: true, + }) + ) + .filter((p) => !isMatch([p.name], denyList)) + .map((p) => { + return { + src: path.join(scriptRoot, p.name), + dest: path.join(instPath, 'script', matchInfo.folder, p.name), + filename: path + .join('script', matchInfo.folder, p.name) + .replaceAll('\\', '/'), + isDirectory: p.isDirectory(), + }; }); + await mkdir(path.join(instPath, 'script', matchInfo.folder), { + recursive: true, + }); + await Promise.all( + entriesToCopy.map((filePath) => copy(filePath.src, filePath.dest)), + ); - const filteredFiles = files.filter((f) => scriptExtRegex.test(f.filename)); - const name = path.basename( - filteredFiles[0].filename, - path.extname(filteredFiles[0].filename), - ); - const id = 'script_' + getHash(name); - - // Rename the extracted folder - const newPath = path.join(path.dirname(unzippedPath), id); - if (existsSync(newPath)) await rm(newPath, { recursive: true }); - await rename(unzippedPath, newPath); - - // Save package information - const packageItem = { - id: id, - name: name, - overview: 'スクリプト', - description: - 'スクリプト一覧: ' + - filteredFiles.map((f) => path.basename(f.filename)).join(', '), - developer: matchInfo?.developer ?? '-', - dependencies: matchInfo?.dependencies, - pageURL: url, - downloadURLs: [url] as [string, ...string[]], - latestVersion: getDate(), - files: files, - }; + // Constructing package information + const files = entriesToCopy.map((i) => { + return { filename: i.filename, isDirectory: i.isDirectory }; + }); - await parseJson.addPackage( - modList.getLocalPackagesDataUrl(instPath), - packageItem, + const filteredFiles = files.filter((f) => scriptExtRegex.test(f.filename)); + const name = path.basename( + filteredFiles[0].filename, + path.extname(filteredFiles[0].filename), + ); + const id = 'script_' + getHash(name); + + // Rename the extracted folder + const newPath = path.join(path.dirname(unzippedPath), id); + if (existsSync(newPath)) await rm(newPath, { recursive: true }); + await rename(unzippedPath, newPath); + + // Save package information + const packageItem = { + id: id, + name: name, + overview: 'スクリプト', + description: + 'スクリプト一覧: ' + + filteredFiles.map((f) => path.basename(f.filename)).join(', '), + developer: matchInfo?.developer ?? '-', + dependencies: matchInfo?.dependencies, + pageURL: url, + downloadURLs: [url] as [string, ...string[]], + latestVersion: getDate(), + files: files, + }; + + await parseJson.addPackage( + modList.getLocalPackagesDataUrl(instPath), + packageItem, + ); + await apmJson.addPackage(instPath, { + id: packageItem.id, + info: packageItem, + }); +} + +/** + * Installs a script to installation path. + * @param {string} instPath - An installation path. + */ +async function installScript(instPath: string) { + const btn = document.getElementById('install-package') as HTMLButtonElement; + const { enableButton } = buttonTransition.loading(btn); + const url = (selectedEntry as Scripts['webpage'][number]).url; + + if (!instPath) { + log.error('An installation path is not selected.'); + buttonTransition.message( + btn, + 'インストール先フォルダを指定してください。', + 'danger', ); - await apmJson.addPackage(instPath, { - id: packageItem.id, - info: packageItem, - }); + setTimeout(() => { + enableButton(); + }, 3000); + return; + } + + try { + await _installScript(url, instPath); + await checkPackagesList(instPath); buttonTransition.message(btn, 'インストール完了', 'success'); } catch (e) { log.error(e); - buttonTransition.message(btn, 'エラーが発生しました。', 'danger'); + buttonTransition.message(btn, e.message, 'danger'); + } finally { + setTimeout(() => { + enableButton(); + }, 3000); } - - setTimeout(() => { - enableButton(); - }, 3000); } const filterButtons: Set = new Set();