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
JavaScript
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