fix: Umlaute in README und Konsolenausgaben (ä/ö/ü statt ae/oe/ue)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Administrator 2026-03-17 06:03:30 +01:00
parent c6cca124a6
commit fa0aa4736b
3 changed files with 71 additions and 44 deletions

View File

@ -1,15 +1,15 @@
# Byse.sx Video Manager
CLI-Tool zum Aufraumen deines Byse.sx-Accounts. Scannt alle Videos, findet solche mit **0 Views** die aelter als **3 Monate** sind, und loescht sie auf Wunsch in Batches.
CLI-Tool zum Aufräumen deines Byse.sx-Accounts. Scannt alle Videos, findet solche mit **0 Views** die älter als **3 Monate** sind, und löscht sie auf Wunsch in Batches.
## Features
- Scannt alle Videos deines Accounts (paginiert, ~70K+ Videos kein Problem)
- Filtert nach Alter und Views
- Interaktives Menue: Liste durchblaettern, CSV exportieren, loeschen
- Batch-Loeschung mit Fortschrittsanzeige
- Sicherheitsabfrage vor dem Loeschen (Tippe `LOESCHEN`)
- Optionaler Alters-Filter (z.B. nur Videos aelter als 6 Monate)
- Interaktives Menü: Liste durchblättern, CSV exportieren, löschen
- Batch-Löschung mit Fortschrittsanzeige
- Sicherheitsabfrage vor dem Löschen (Tippe `LOESCHEN`)
- Optionaler Alters-Filter (z.B. nur Videos älter als 6 Monate)
## Voraussetzungen
@ -25,10 +25,10 @@ npm install
### Session-Cookie holen
Das Tool nutzt die interne Byse.sx-API, die einen Session-Cookie benoetigt:
Das Tool nutzt die interne Byse.sx-API, die einen Session-Cookie benötigt:
1. Oeffne [byse.sx](https://byse.sx) im Browser und logge dich ein
2. Oeffne die DevTools (`F12`)
1. Öffne [byse.sx](https://byse.sx) im Browser und logge dich ein
2. Öffne die DevTools (`F12`)
3. Gehe zu **Application** > **Cookies** > `https://byse.sx`
4. Kopiere den Wert des `sid`-Cookies
5. Erstelle eine `.env` Datei im Projektordner:
@ -43,7 +43,7 @@ cp .env.example .env
BYSE_SID=dein_session_cookie_hier
```
> **Hinweis:** Der Session-Cookie laeuft nach einiger Zeit ab. Wenn du eine Fehlermeldung bekommst, hole dir einfach einen neuen aus dem Browser.
> **Hinweis:** Der Session-Cookie läuft nach einiger Zeit ab. Wenn du eine Fehlermeldung bekommst, hole dir einfach einen neuen aus dem Browser.
## Benutzung
@ -51,13 +51,13 @@ BYSE_SID=dein_session_cookie_hier
node index.js
```
### Menue-Optionen
### Menü-Optionen
```
[1] Liste anzeigen - Blaettert durch die Kandidaten (je 50)
[1] Liste anzeigen - Blättert durch die Kandidaten (je 50)
[2] CSV exportieren - Speichert alle Kandidaten als byse-candidates.csv
[3] ALLE loeschen - Loescht alle Kandidaten (Sicherheitsabfrage)
[4] Nach Alter filtern - Nur Videos aelter als X Monate loeschen
[3] ALLE löschen - Löscht alle Kandidaten (Sicherheitsabfrage)
[4] Nach Alter filtern - Nur Videos älter als X Monate löschen
[5] Beenden
```
@ -69,27 +69,27 @@ node index.js
════════════════════════════════════════════════
Gesamt Videos: 69.595
Kandidaten (0 Views,
aelter als 3 Mon.): 17.525
älter als 3 Mon.): 17.525
Speicher freigeben: 13.67 TB
Aeltestes Video: 2025-05-02
Neuestes Kandidat: 2025-12-04
Ältestes Video: 2025-05-02
Neuester Kandidat: 2025-12-04
════════════════════════════════════════════════
```
## API-Details
Das Tool nutzt die **interne** Byse.sx Dashboard-API (nicht die oeffentliche API):
Das Tool nutzt die **interne** Byse.sx Dashboard-API (nicht die öffentliche API):
| Endpoint | Methode | Zweck |
|----------|---------|-------|
| `/api/my_files_overview` | GET | Account-Uebersicht |
| `/api/my_files_overview` | GET | Account-Übersicht |
| `/api/my_files_files` | GET | Video-Liste (Cursor-Pagination) |
| `/api/del_file` | POST | Videos loeschen |
| `/api/del_file` | POST | Videos löschen |
Authentifizierung erfolgt ueber den `sid` Session-Cookie.
Authentifizierung erfolgt über den `sid` Session-Cookie.
## Sicherheit
- Die `.env` Datei mit deinem Session-Cookie wird **nicht** ins Git committed (`.gitignore`)
- Vor dem Loeschen muss `LOESCHEN` getippt werden
- Loeschung ist **unwiderruflich**
- Vor dem Löschen muss `LOESCHEN` getippt werden
- Löschung ist **unwiderruflich**

View File

@ -3,7 +3,7 @@ import readline from 'readline';
import fs from 'fs';
const SID = process.env.BYSE_SID;
if (!SID) { console.error('BYSE_SID fehlt in .env'); process.exit(1); }
if (!SID) { console.error('BYSE_SID fehlt in .env!'); process.exit(1); }
const COOKIE = `sid=${SID}`;
const BASE = 'https://byse.sx/api';
@ -37,7 +37,7 @@ async function apiFetch(path) {
headers: { cookie: COOKIE, 'content-type': 'application/json' }
});
if (res.status === 401 || res.status === 403) {
console.error('\nSession abgelaufen! Hol dir einen neuen SID-Cookie aus dem Browser.');
console.error('\nSession abgelaufen! Hol dir einen neuen SID-Cookie aus dem Browser (F12 → Application → Cookies).');
process.exit(1);
}
return res.json();
@ -62,10 +62,10 @@ function progressBar(current, total, width = 30) {
// ── Scan ──
async function scanAllVideos() {
console.log('\nVerbindung pruefen...');
console.log('\nVerbindung prüfen...');
const overview = await apiFetch('/my_files_overview');
if (overview.status !== 'ok') {
console.error('API-Fehler:', overview);
console.error('API-Fehler (prüfe deinen SID-Cookie):', overview);
process.exit(1);
}
const totalFiles = overview.totals.files;
@ -89,7 +89,7 @@ async function scanAllVideos() {
cursor = data.file_pagination?.next_cursor;
if (!cursor) break;
} catch (err) {
console.warn(`\nFehler bei Batch ${batch}, ueberspringe... (${err.message})`);
console.warn(`\nFehler bei Batch ${batch}, überspringe... (${err.message})`);
break;
}
// kleine Pause um Rate-Limits zu vermeiden
@ -105,7 +105,7 @@ function filterCandidates(files) {
return files.filter(f => {
const created = new Date(f.created);
return f.views === 0 && created < THREE_MONTHS_AGO;
}).sort((a, b) => new Date(a.created) - new Date(b.created)); // aelteste zuerst
}).sort((a, b) => new Date(a.created) - new Date(b.created)); // älteste zuerst
}
// ── Display ──
@ -120,16 +120,16 @@ function showSummary(candidates, totalFiles) {
console.log('════════════════════════════════════════════════');
console.log(` Gesamt Videos: ${totalFiles.toLocaleString('de-DE')}`);
console.log(` Kandidaten (0 Views,`);
console.log(` aelter als 3 Mon.): ${candidates.length.toLocaleString('de-DE')}`);
console.log(` älter als 3 Mon.): ${candidates.length.toLocaleString('de-DE')}`);
console.log(` Speicher freigeben: ${formatSize(totalSize)}`);
console.log(` Aeltestes Video: ${oldest}`);
console.log(` Ältestes Video: ${oldest}`);
console.log(` Neuestes Kandidat: ${newest}`);
console.log('════════════════════════════════════════════════\n');
}
function showTable(candidates, offset = 0, limit = 50) {
const slice = candidates.slice(offset, offset + limit);
const header = `${'#'.padStart(6)} ${'Titel'.padEnd(55)} ${'Alter'.padStart(6)} ${'Groesse'.padStart(10)} ${'Views'.padStart(5)}`;
const header = `${'#'.padStart(6)} ${'Titel'.padEnd(55)} ${'Alter'.padStart(6)} ${'Größe'.padStart(10)} ${'Views'.padStart(5)}`;
console.log(header);
console.log('─'.repeat(header.length));
for (let i = 0; i < slice.length; i++) {
@ -153,12 +153,12 @@ async function deleteVideos(candidates) {
let deleted = 0;
let errors = 0;
console.log(`\nLoesche ${total.toLocaleString('de-DE')} Videos in ${Math.ceil(total / DELETE_BATCH)} Batches...\n`);
console.log(`\nLösche${total.toLocaleString('de-DE')} Videos in ${Math.ceil(total / DELETE_BATCH)} Batches...\n`);
for (let i = 0; i < total; i += DELETE_BATCH) {
const batch = candidates.slice(i, i + DELETE_BATCH);
const ids = batch.map(f => f.id);
process.stdout.write(`\rLoeschen... ${progressBar(Math.min(i + DELETE_BATCH, total), total)} `);
process.stdout.write(`\rLöschen... ${progressBar(Math.min(i + DELETE_BATCH, total), total)} `);
try {
const result = await apiDelete(ids);
@ -178,13 +178,13 @@ async function deleteVideos(candidates) {
}
process.stdout.write('\n');
console.log(`\nFertig! Geloescht: ${deleted.toLocaleString('de-DE')} | Fehler: ${errors}`);
console.log(`\nFertig! Gelöscht: ${deleted.toLocaleString('de-DE')} | Fehler: ${errors}`);
}
// ── CSV Export ──
function exportCSV(candidates, filename = 'byse-candidates.csv') {
const header = 'ID,Code,Titel,Views,Erstellt,Groesse_MB,Alter_Tage\n';
const header = 'ID,Code,Titel,Views,Erstellt,Größe_MB,Alter_Tage\n';
const rows = candidates.map(f => {
const days = Math.floor((Date.now() - new Date(f.created).getTime()) / 86400000);
const sizeMB = ((f.size || 0) / 1048576).toFixed(1);
@ -192,7 +192,7 @@ function exportCSV(candidates, filename = 'byse-candidates.csv') {
return `${f.id},"${f.code}","${title}",${f.views},${f.created},${sizeMB},${days}`;
}).join('\n');
fs.writeFileSync(filename, header + rows, 'utf8');
console.log(`CSV exportiert: ${filename} (${candidates.length} Eintraege)`);
console.log(`CSV exportiert: ${filename} (${candidates.length} Einträge)`);
}
// ── Main ──
@ -207,7 +207,7 @@ async function main() {
const candidates = filterCandidates(allFiles);
if (candidates.length === 0) {
console.log('\nKeine Videos gefunden die aelter als 3 Monate sind UND 0 Views haben.');
console.log('\nKeine Videos gefunden die älter als 3 Monate sind UND 0 Views haben.');
process.exit(0);
}
@ -216,10 +216,10 @@ async function main() {
let offset = 0;
while (true) {
console.log('\nOptionen:');
console.log(' [1] Liste anzeigen (naechste 50)');
console.log(' [1] Liste anzeigen (nächste 50)');
console.log(' [2] CSV exportieren');
console.log(' [3] ALLE Kandidaten loeschen');
console.log(' [4] Nur Videos aelter als X Monate loeschen');
console.log(' [3] ALLE Kandidaten löschen');
console.log(' [4] Nur Videos älter als X Monate löschen');
console.log(' [5] Beenden');
const choice = await ask('\nAuswahl: ');
@ -230,7 +230,7 @@ async function main() {
} else if (choice === '2') {
exportCSV(candidates);
} else if (choice === '3') {
const confirm = await ask(`\nSICHER? ${candidates.length.toLocaleString('de-DE')} Videos werden UNWIDERRUFLICH geloescht!\nTippe "LOESCHEN" zum Bestaetigen: `);
const confirm = await ask(`\nSICHER? ${candidates.length.toLocaleString('de-DE')} Videos werden UNWIDERRUFLICH gelöscht!\nTippe "LOESCHEN" zum Bestätigen: `);
if (confirm === 'LOESCHEN') {
await deleteVideos(candidates);
break;
@ -240,15 +240,15 @@ async function main() {
} else if (choice === '4') {
const months = await ask('Mindest-Alter in Monaten (z.B. 6): ');
const m = parseInt(months);
if (isNaN(m) || m < 1) { console.log('Ungueltige Eingabe.'); continue; }
if (isNaN(m) || m < 1) { console.log('Ungültige Eingabe.'); continue; }
const cutoff = new Date(Date.now() - m * 30 * 24 * 60 * 60 * 1000);
const filtered = candidates.filter(f => new Date(f.created) < cutoff);
console.log(`\n${filtered.length.toLocaleString('de-DE')} Videos aelter als ${m} Monate mit 0 Views.`);
console.log(`\n${filtered.length.toLocaleString('de-DE')} Videos älter als ${m} Monate mit 0 Views.`);
const totalSize = filtered.reduce((sum, f) => sum + (f.size || 0), 0);
console.log(`Speicher: ${formatSize(totalSize)}`);
if (filtered.length === 0) continue;
showTable(filtered, 0, 30);
const confirm = await ask(`\nTippe "LOESCHEN" um diese ${filtered.length.toLocaleString('de-DE')} Videos zu loeschen: `);
const confirm = await ask(`\nTippe "LOESCHEN" um diese ${filtered.length.toLocaleString('de-DE')} Videos zu löschen: `);
if (confirm === 'LOESCHEN') {
await deleteVideos(filtered);
break;

27
package-lock.json generated Normal file
View File

@ -0,0 +1,27 @@
{
"name": "byse-video-manager",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "byse-video-manager",
"version": "1.0.0",
"dependencies": {
"dotenv": "^16.4.7"
}
},
"node_modules/dotenv": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
}
}
}