release: publish Twitch VOD Manager 1.0.3

Polish navigation, settings, streamer and queue workflows; add safe pause and partial-file lifecycle handling; apply the product identity across Windows surfaces; and replace the public README with a complete English product guide and isolated screenshot.
This commit is contained in:
Sucukdeluxe
2026-08-10 19:25:08 +02:00
parent f9e415da88
commit b3c2a8b9fd
48 changed files with 4035 additions and 826 deletions
+71
View File
@@ -0,0 +1,71 @@
import * as fs from 'fs';
import * as path from 'path';
import * as ResEdit from 'resedit';
import { writeFileAtomicSync } from './infra/fs-atomic';
export interface WindowsDevExecutableOptions {
sourcePath: string;
destinationPath: string;
iconPath: string;
version: string;
}
export async function prepareWindowsDevExecutable(options: WindowsDevExecutableOptions): Promise<string> {
const source = path.resolve(options.sourcePath);
const destination = path.resolve(options.destinationPath);
const icon = path.resolve(options.iconPath);
const stampPath = `${destination}.json`;
const sourceStats = fs.statSync(source);
const iconStats = fs.statSync(icon);
const fingerprint = JSON.stringify({
sourceSize: sourceStats.size,
sourceModified: sourceStats.mtimeMs,
iconSize: iconStats.size,
iconModified: iconStats.mtimeMs,
version: options.version
});
if (fs.existsSync(destination) && fs.existsSync(stampPath) && fs.readFileSync(stampPath, 'utf8') === fingerprint) {
return destination;
}
fs.mkdirSync(path.dirname(destination), { recursive: true });
const temporaryDestination = `${destination}.tmp`;
fs.copyFileSync(source, temporaryDestination);
try {
const executable = ResEdit.NtExecutable.from(fs.readFileSync(temporaryDestination), { ignoreCert: true });
const resources = ResEdit.NtExecutableResource.from(executable);
const versionEntries = ResEdit.Resource.VersionInfo.fromEntries(resources.entries);
const versionInfo = versionEntries[0] || ResEdit.Resource.VersionInfo.createEmpty();
const languages = versionInfo.getAllLanguagesForStringValues();
const language = languages[0] || { lang: 0x0409, codepage: 1200 };
versionInfo.setStringValues(language, {
FileDescription: 'Twitch VOD Manager',
ProductName: 'Twitch VOD Manager',
InternalName: 'Twitch VOD Manager',
OriginalFilename: 'Twitch VOD Manager.exe'
});
versionInfo.setFileVersion(options.version);
versionInfo.setProductVersion(options.version);
versionInfo.outputToResourceEntries(resources.entries);
const iconFile = ResEdit.Data.IconFile.from(fs.readFileSync(icon));
ResEdit.Resource.IconGroupEntry.replaceIconsForResource(
resources.entries,
1,
language.lang,
iconFile.icons.map((entry) => entry.data)
);
resources.outputResource(executable);
fs.writeFileSync(temporaryDestination, Buffer.from(executable.generate()));
fs.rmSync(destination, { force: true });
fs.renameSync(temporaryDestination, destination);
writeFileAtomicSync(stampPath, fingerprint);
return destination;
} catch (error) {
fs.rmSync(temporaryDestination, { force: true });
throw error;
}
}