Upload via GUI (324 Dateien)

This commit is contained in:
2026-11-02 21:48:49 +00:00
parent 1a52097201
commit d3838f9d30
3 changed files with 936 additions and 901 deletions
+26 -3
View File
@@ -684,7 +684,8 @@ ipcMain.handle('recording:delete', (_e, instanceName, dateiname) => {
ipcMain.handle('recording:open', (_e, instanceName, dateiname) => {
if (!istDateiname(dateiname)) return false;
const ziel = path.join(aufzeichnungsOrdner(instanceName), dateiname);
if (fs.existsSync(ziel)) shell.openPath(ziel);
// nur Videos öffnen, keine beliebigen Dateien aus dem Aufnahmen-Ordner
if (/\.(mp4|webm|mkv)$/i.test(ziel) && fs.existsSync(ziel)) shell.openPath(ziel);
return true;
});
@@ -3672,7 +3673,8 @@ ipcMain.handle('instance:screenshotData', (_e, id, name) => {
ipcMain.handle('instance:openScreenshot', (_e, id, name) => {
const target = safeSub(id, 'screenshots', name);
if (target && fs.existsSync(target)) shell.openPath(target);
// nur Bilder öffnen – im Ordner abgelegte Programme (.exe, .bat …) würden sonst ausgeführt
if (target && /\.(png|jpe?g|gif|webp)$/i.test(target) && fs.existsSync(target)) shell.openPath(target);
return true;
});
@@ -4086,11 +4088,13 @@ async function importCurseForge(buf, zip, zugang) {
const modsDir = path.join(gameDir, 'mods');
let geladen = 0;
const fehlend = [];
// Dateien, die der Autor nicht über Launcher freigibt: nicht laden, nur melden
const gesperrtIds = new Set((aufgeloest.gesperrt || []).map((g) => g.fileID));
let i = 0;
for (const f of dateien) {
i++;
const info = aufgeloest.get(Number(f.fileID));
if (!info) { fehlend.push(f.fileID); continue; }
if (!info) { if (!gesperrtIds.has(Number(f.fileID))) fehlend.push(f.fileID); continue; }
const dest = path.join(modsDir, path.basename(info.fileName));
if (!inside(dest)) continue;
melde('Mods laden', i, dateien.length);
@@ -4114,12 +4118,28 @@ async function importCurseForge(buf, zip, zugang) {
fs.writeFileSync(dest, forge.readZipEntry(buf, zip[name]));
}
// Liste mit Links zum Selbstladen in den Spielordner legen
const gesperrt = aufgeloest.gesperrt || [];
if (gesperrt.length) {
try {
const zeilen = [
'Diese Mods dürfen laut ihren Autoren nicht über Launcher wie AeroMC geladen werden.',
'Bitte von der jeweiligen CurseForge-Seite laden und in den Ordner "mods" legen:',
'',
...gesperrt.map((g) => `${g.fileName}\n ${g.seite}`),
'',
];
fs.writeFileSync(path.join(gameDir, 'GESPERRTE-MODS.txt'), zeilen.join('\r\n'), 'utf8');
} catch { /* Hinweisdatei ist nur eine Hilfe */ }
}
return {
ok: true,
instance: store.getInstance(created.id),
fileCount: dateien.length,
geladen,
fehlend: fehlend.length,
gesperrt: gesperrt.length,
};
}
@@ -4385,6 +4405,9 @@ async function importInstanceZipFromPath(filePath) {
if (!fs.existsSync(path.join(base, 'instance.json'))) {
const mmc = mmcimport.scanSingle(tmp);
if (mmc.ok) {
// Wie beim AeroMC-Export oben: kein Java-Pfad aus einem fremden Archiv (instance.cfg
// könnte auf ein beliebiges Programm oder einen Netzwerkpfad zeigen, das beim Start läuft)
mmc.instance.javaPath = '';
const done = mmcimport.importOne(store, mmc.instance, false);
const quellen = mmcimport.kopierQuellen(mmc.instance);
if (quellen.length) await verschiebeInhalt(mmc.instance.gameDir, store.gameDir(done.id), quellen.map((q) => q.name));
+3 -2
View File
@@ -508,13 +508,13 @@ const el = (id) => document.getElementById(id);
function show(id) { el(id).classList.remove('hidden'); }
function hide(id) { el(id).classList.add('hidden'); }
function toast(msg, isError = false) {
function toast(msg, isError = false, dauerMs = 2800) {
const t = el('toast');
t.textContent = msg;
t.classList.toggle('err', isError);
t.classList.remove('hidden');
clearTimeout(toast._t);
toast._t = setTimeout(() => t.classList.add('hidden'), 2800);
toast._t = setTimeout(() => t.classList.add('hidden'), dauerMs);
}
function fmtDate(iso) {
@@ -778,6 +778,7 @@ async function runModpackImport() {
toast(`Modpack importiert: ${res.instance.name} (${res.fileCount} Dateien)`);
// CurseForge gibt nicht jede Datei heraus – ehrlich benennen statt verschweigen
if (res.fehlend) toast(`${res.fehlend} Datei(en) konnten nicht geladen werden und fehlen in der Instanz.`, true);
if (res.gesperrt) toast(`${res.gesperrt} Mod(s) dürfen laut Autor nicht über Launcher geladen werden. Liste mit Links: GESPERRTE-MODS.txt im Spielordner der Instanz.`, true, 10000);
return;
}
toast(res.message || 'Modpack-Import fehlgeschlagen.', true);
+25 -14
View File
@@ -567,14 +567,13 @@ function curseforgeZugang(settings) {
}
/*
* Ausweichadresse auf dem CurseForge-CDN. Manche Autoren untersagen den
* Download über die API; dann liefert downloadUrl null, die Datei liegt aber
* unter diesem berechenbaren Pfad.
* Manche Autoren untersagen den Download über Drittanbieter: CurseForge liefert dann
* downloadUrl = null. Das wird respektiert – es gibt bewusst keine selbst gebaute
* CDN-Adresse als Umweg. Stattdessen bekommt der Nutzer einen Link auf die Projektseite,
* von der er die Datei selbst laden kann.
*/
function forgeCdnUrl(fileId, fileName) {
const s = String(fileId || '');
if (s.length < 5 || !fileName) return null;
return `https://edge.forgecdn.net/files/${s.slice(0, 4)}/${Number(s.slice(4))}/${encodeURIComponent(fileName)}`;
function cfProjektSeite(modId) {
return `https://www.curseforge.com/projects/${Number(modId) || 0}`;
}
const CF_LOADER = { forge: 1, fabric: 4, quilt: 5, neoforge: 6 };
@@ -772,12 +771,12 @@ async function curseforgePickFile(zugang, modId, gameVersion, loader, art) {
const match = files.find((f) => cfFileMatches(f, gameVersion, loader, art)) || files[0];
if (!match) return null;
const name = match.fileName || ('file-' + match.id + '.jar');
let dl = match.downloadUrl || null;
if (!dl) dl = forgeCdnUrl(match.id, name);
// keine Download-Adresse = der Autor erlaubt keine Downloads über Drittanbieter
if (!match.downloadUrl) return { gesperrt: true, modId, fileId: match.id, filename: name };
return {
fileId: match.id,
filename: name,
url: dl,
url: match.downloadUrl,
hashes: cfHashes(match),
versionNumber: match.displayName || match.fileName || String(match.id),
dependencies: match.dependencies || [],
@@ -797,6 +796,13 @@ async function curseforgeInstall({ zugang, projectId, title, gameVersion, loader
if (!modId) return { ok: false, message: 'Ungültige CurseForge-Projekt-ID.' };
const file = await curseforgePickFile(zugang, modId, gameVersion, loader, art);
if (file && file.gesperrt) {
return {
ok: false,
reason: 'gesperrt',
message: `„${title || file.filename}“ darf laut Autor nicht über Launcher geladen werden. Bitte von Hand laden: ${cfProjektSeite(modId)}`,
};
}
if (!file || !file.url) {
return {
ok: false,
@@ -844,6 +850,8 @@ async function curseforgeResolveFiles(zugang, files, onProgress) {
if (!zugang) throw new Error('Kein Zugang zu CurseForge eingerichtet.');
const ids = (files || []).map((f) => Number(f.fileID)).filter(Boolean);
const map = new Map();
// Dateien, die der Autor für Drittanbieter gesperrt hat – werden nicht geladen, nur gemeldet
map.gesperrt = [];
const PAKET = 100;
for (let i = 0; i < ids.length; i += PAKET) {
@@ -862,11 +870,14 @@ async function curseforgeResolveFiles(zugang, files, onProgress) {
if (!res.ok) throw new Error('CurseForge-Abfrage fehlgeschlagen: HTTP ' + res.status);
const daten = await res.json();
for (const d of (daten && daten.data) || []) {
const roh = d.downloadUrl || forgeCdnUrl(d.id, d.fileName);
if (!d.fileName || !roh) continue;
if (!d.fileName) continue;
if (!d.downloadUrl) {
map.gesperrt.push({ fileID: Number(d.id), projectID: Number(d.modId) || 0, fileName: d.fileName, seite: cfProjektSeite(d.modId) });
continue;
}
map.set(Number(d.id), {
fileName: d.fileName,
url: zugang.downloadAdresse(roh),
url: zugang.downloadAdresse(d.downloadUrl),
hashes: cfHashes(d),
kopfzeilen: zugang.downloadKopfzeilen,
});
@@ -891,6 +902,6 @@ module.exports = {
getMcVersions,
modrinthSearch, modrinthResolveFile, modrinthResolveModpack, modrinthInstall, modrinthProjectTitle,
modrinthFindUpdates, downloadFile, downloadBuffer, inhaltsArt, INHALTSARTEN,
curseforgeResolveFiles, curseforgeSearch, curseforgeInstall, curseforgeTestKey, forgeCdnUrl, curseforgeZugang,
curseforgeResolveFiles, curseforgeSearch, curseforgeInstall, curseforgeTestKey, cfProjektSeite, curseforgeZugang,
curseforgeResolveModpackFile,
};