Upload via GUI (324 Dateien)
This commit is contained in:
+26
-3
@@ -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
@@ -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
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user