UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

113 lines (112 loc) • 4.37 kB
import path from "node:path"; import async from "async"; import { ProgressBarSymbol } from "../console/progressBar.js"; import { PlaylistMode } from "../models/options.js"; import ArrayUtil from "../utils/arrayUtil.js"; import FsUtil from "../utils/fsUtil.js"; import GameGrouper from "./dats/utils/gameGrouper.js"; import Module from "./module.js"; class PlaylistCreator extends Module { options; constructor(options, progressBar) { super(progressBar, PlaylistCreator.name); this.options = options; } /** * Creates playlists. */ async write(dat, candidates) { if (!this.options.shouldPlaylist()) { return []; } if (candidates.length === 0) { this.prefixedLogger.trace(`${dat.getName()}: no candidates to create playlists for`); return []; } this.prefixedLogger.trace(`${dat.getName()}: writing playlists`); this.progressBar.setSymbol(ProgressBarSymbol.WRITING); this.progressBar.resetProgress(candidates.length); const writtenPlaylistPaths = []; let remainingCandidates = candidates; remainingCandidates = (await async.mapLimit( remainingCandidates, this.options.getWriterThreads(), async (candidate) => { const writtenFile = await this.maybeWritePlaylist( dat, candidate, candidate.getGame().getName() ); if (writtenFile === void 0) { return candidate; } writtenPlaylistPaths.push(writtenFile); } )).filter((candidate) => candidate !== void 0); const gameNamesToCandidates = GameGrouper.groupMultiDiscGames( remainingCandidates, (candidate) => candidate.getGame().getName() ); await async.mapLimit( [...gameNamesToCandidates], this.options.getWriterThreads(), async ([gameName, candidates2]) => { const writtenFile = await this.maybeWritePlaylist(dat, candidates2, gameName); if (writtenFile === void 0) { return; } writtenPlaylistPaths.push(writtenFile); } ); this.prefixedLogger.trace(`${dat.getName()}: done writing playlists`); return writtenPlaylistPaths; } async maybeWritePlaylist(dat, candidates, playlistBasename) { const playlistFiles = (Array.isArray(candidates) ? candidates : [candidates]).flatMap((candidate) => candidate.getRomsWithFiles()).flatMap( (romWithFiles) => this.options.shouldWrite() ? romWithFiles.getOutputFile() : romWithFiles.getInputFile() ).filter( (outputFile) => this.options.getPlaylistExtensions().some((ext) => outputFile.getFilePath().toLowerCase().endsWith(ext.toLowerCase())) ).filter(ArrayUtil.filterUniqueMapped((file) => file.getFilePath())); if (playlistFiles.length === 0) { return void 0; } if (this.options.getPlaylistMode() === PlaylistMode.MULTIPLE && playlistFiles.length < 2) { return void 0; } this.progressBar.incrementInProgress(); const commonDirectory = PlaylistCreator.getCommonDirectory(playlistFiles); const playlistLines = `${playlistFiles.map( (file) => file.getFilePath().slice(commonDirectory.length).replace(/^[\\/]/, "").replaceAll("\\", "/") ).toSorted((a, b) => a.localeCompare(b)).join("\n")} `; if (!await FsUtil.exists(commonDirectory)) { await FsUtil.mkdir(commonDirectory, { recursive: true }); } const playlistLocation = path.join(commonDirectory, `${playlistBasename}.m3u`); this.prefixedLogger.info(`${dat.getName()}: creating playlist '${playlistLocation}'`); await FsUtil.writeFile(playlistLocation, playlistLines); return playlistLocation; } static getCommonDirectory(files) { if (files.length === 1) { return path.dirname(files[0].getFilePath()); } const fileDirsSplit = files.map((file) => path.dirname(file.getFilePath()).split(path.sep)); const maxDepth = fileDirsSplit.reduce((max, file) => Math.max(max, file.length), 0); let lastCommonDir = ""; let depth = 0; while (depth <= maxDepth) { const fileSubPaths = fileDirsSplit.map((split) => split.slice(0, depth).join(path.sep)); if (fileSubPaths.some((p) => p !== fileSubPaths[0])) { break; } lastCommonDir = fileSubPaths[0]; depth += 1; } return lastCommonDir; } } export { PlaylistCreator as default }; //# sourceMappingURL=playlistCreator.js.map