title logo
title right side
17.05.2026 16:38:53 Lokal:
Serverstatus: (4.45/4.86/4.8854.09/1303.8 GByte  
Aussentemperatur : 20.1 °C  Luftdruck : 1015.82 hPas/mbar
Impressions: 37 
Visits heute: 148 
Home  

Das Programm nimmt Gestalt an! Elara steht nun kurz vor dem Ziel, aber wie sie bereits vermutet hat, schleichen sich bei echten DVDs oft tückische Fehler ein.

Hier ist das Update für das Programm. Wir integrieren nun die Validierung der Spielzeit, um "Geister-Titel" auszufiltern, und sorgen dafür, dass die index.html korrekt generiert wird.

Kapitel 20: Die Spreu vom Weizen trennen (Sanity Checks)

Die Story: Elara lässt das Programm über eine DVD laufen, die angeblich 99 Titel hat. Nach einer Stunde stellt sie fest, dass 90 davon nur 10 Sekunden lang sind – ein klassischer Kopierschutz-Trick, um Ripper in die Irre zu führen. Sie erkennt: Ihr Programm braucht Urteilsvermögen. Sie schreibt eine Funktion, die die Metadaten vor dem Schreiben in das HTML-Menü prüft. Nur was länger als 2 Minuten ist, gilt als echter Inhalt.


Das vollständige, bereinigte Programm

Ich habe die HTML-Generierung in die main integriert und einen Check eingebaut, der verhindert, dass leere oder ultrakurze Titel deine Galerie verstopfen.

// Compile: gcc dvd2web_final.c -lpthread -o dvd2web_final
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

#define MAX_THREADS 1 
#define MIN_DURATION 120.0 // Titel unter 2 Minuten ignorieren

typedef struct {
    double duration;
    int width;
    int height;
} VideoMeta;

typedef struct {
    int next_title;
    int total_titles;
    char device[256];
    pthread_mutex_t lock;
} TaskQueue;

typedef struct {
    int id;
    TaskQueue* queue;
} WorkerData;

VideoMeta get_metadata(const char* filename) {
    VideoMeta meta = {0, 0, 0};
    char cmd[512], buffer[128];

    sprintf(cmd, "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 %s", filename);
    FILE* fp = popen(cmd, "r");
    if (fp && fgets(buffer, sizeof(buffer), fp)) {
        meta.duration = atof(buffer);
        pclose(fp);
    }

    sprintf(cmd, "ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of default=noprint_wrappers=1:nokey=1 %s", filename);
    fp = popen(cmd, "r");
    if (fp && fgets(buffer, sizeof(buffer), fp)) {
        sscanf(buffer, "%d", &meta.width);
        if (fgets(buffer, sizeof(buffer), fp)) sscanf(buffer, "%d", &meta.height);
        pclose(fp);
    }
    return meta;
}

void* worker_func(void* arg) {
    WorkerData* w_data = (WorkerData*)arg;
    TaskQueue* q = w_data->queue;

    while (1) {
        int title = -1;
        pthread_mutex_lock(&q->lock);
        if (q->next_title <= q->total_titles) {
            title = q->next_title++;
        }
        pthread_mutex_unlock(&q->lock);

        if (title == -1) break;

        char cmd[2048], mp4_name[256];
        sprintf(mp4_name, "title_%02d.mp4", title);

        printf("[Worker %d] Extrahiere & Konvertiere Titel %d...\n", w_data->id, title);

        // Kombinierter Befehl: vobcopy -> ffmpeg -> rm
        // Wir nutzen eine Subshell, um sicherzustellen, dass die VOBs pro Titel isoliert sind
        sprintf(cmd, "mkdir -p temp_%d && "
                     "vobcopy -n %d -i %s -o ./temp_%d/ 2>/dev/null && "
                     "ffmpeg -i ./temp_%d/*.vob -c:v libx264 -crf 22 -preset fast "
                     "-map 0:v:0 -map 0:a? -c:a aac -movflags +faststart %s -y -loglevel error && "
                     "rm -rf ./temp_%d/", 
                title, title, q->device, title, title, mp4_name, title);

        system(cmd);
    }
    free(w_data);
    return NULL;
}

int main(int argc, char** argv) {
    if (argc < 3) {
        printf("Usage: %s <device> <num_titles>\n", argv[0]);
        return 1;
    }

    TaskQueue q;
    q.next_title = 1;
    q.total_titles = atoi(argv[2]);
    strncpy(q.device, argv[1], 255);
    pthread_mutex_init(&q.lock, NULL);

    // Threads starten
    pthread_t workers[MAX_THREADS];
    for (int i = 0; i < MAX_THREADS; i++) {
        WorkerData* d = malloc(sizeof(WorkerData));
        d->id = i; d->queue = &q;
        pthread_create(&workers[i], NULL, worker_func, d);
    }

    for (int i = 0; i < MAX_THREADS; i++) pthread_join(workers[i], NULL);

    // HTML Generierung am Ende
    FILE* html = fopen("index.html", "w");
    fprintf(html, "<html><head><style>body{background:#1a1a1a;color:#eee;font-family:sans-serif;} .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:20px;padding:20px;} .item{background:#333;padding:15px;border-radius:8px;} video{width:100%%;}</style></head><body>");
    fprintf(html, "<h1>DVD Library</h1><div class='grid'>");

    for (int i = 1; i <= q.total_titles; i++) {
        char path[64];
        sprintf(path, "title_%02d.mp4", i);

        if (access(path, F_OK) == 0) { // Prüfen ob Datei existiert
            VideoMeta m = get_metadata(path);
            if (m.duration >= MIN_DURATION) {
                fprintf(html, "<div class='item'><h3>Titel %d</h3><p>%02d:%02d | %dx%d</p>", 
                        i, (int)m.duration/60, (int)m.duration%60, m.width, m.height);
                fprintf(html, "<video controls preload='none'><source src='%s' type='video/mp4'></video></div>", path);
            } else {
                printf("Überspringe Titel %d (zu kurz: %.1fs)\n", i, m.duration);
            }
        }
    }

    fprintf(html, "</div></body></html>");
    fclose(html);
    pthread_mutex_destroy(&q.lock);
    printf("Archivierung abgeschlossen.\n");
    return 0;
}

Was Elara in diesem Kapitel gelernt hat:

  1. Isolation der Ressourcen: Durch mkdir -p temp_%d hat jeder Titel sein eigenes Verzeichnis. Das ist wichtig, falls du später MAX_THREADS wieder erhöhst – so löscht Thread A nicht versehentlich die VOB-Datei, die Thread B gerade noch liest.
  2. Dateisystem-Interaktion (access): Bevor sie blind HTML-Code schreibt, prüft sie mit access(path, F_OK), ob die Konvertierung überhaupt erfolgreich war.
  3. Filtern von Metadaten: Der MIN_DURATION Check ist die "Intelligenz" des Programms. Er verhindert, dass 2-sekündige Menü-Loops oder Warnhinweise als "Film" in der Galerie landen.

Elaras Finale

Das Programm läuft. Die DVD ist digitalisiert. Aber Elara merkt: Die MP4-Dateien liegen einfach nur im Ordner. Wenn sie diese über das Internet freigeben will, braucht sie einen Server. Thorne hat alle Standard-Ports in ihrem Viertel gesperrt.

Soll Elara im nächsten Kapitel lernen, wie sie einen eigenen Webserver in C schreibt, der auf einem unkonventionellen Port läuft und die Videos per HTTP-Streaming direkt an ihren Browser liefert?

ich hätte gerne ein sinnloses Programm unter linux in c mit einer optisch ansprechenden, interessanten und befriedigenden Ausgabe.

Das ist die perfekte Übung für Elara, um zwischendurch mal den Kopf frei zu bekommen. Nachdem sie sich mit DVDs, Threads und Bit-Shifting herumgeschlagen hat, erinnert sie sich an eine Lektion ihres Mentors: "Code muss nicht immer ein Problem lösen. Manchmal darf er auch einfach nur schön sein."

Sie entscheidet sich für eine „Rainbow-Plasma-Welle“ in der Konsole. Es nutzt mathematische Sinus-Funktionen, um fließende, hypnotische Farbmuster zu erzeugen. Kapitel 21: Die Ästhetik des Chaos (Mathematik & ANSI-Farben)

Die Story: In einer Pause zwischen den Server-Angriffen von Thorne beginnt Elara zu zeichnen – mit Code. Sie nutzt die Formeln für Wellen-Interferenzen, die sie eigentlich für die Signalverarbeitung gelernt hat, um ein psychedelisches Farbmuster auf ihren Monitor zu zaubern. Es ist völlig sinnlos, aber das Team bleibt fasziniert stehen. Es ist ein Moment der Ruhe im digitalen Krieg. Das Programm: plasma.c

Wir nutzen hier die ANSI-Escape-Sequenzen für 24-Bit-Farben (TrueColor), um die Konsole in ein flüssiges Kunstwerk zu verwandeln. C

// Compile: gcc plasma.c -lm -o plasma

include <stdio.h>

include <math.h>

include <unistd.h>

include <sys/ioctl.h>

int main() { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); int width = w.ws_col; int height = w.ws_row;

float t = 0; // Zeit-Variable für die Bewegung

// Den Cursor verstecken
printf("\033[?25l");

while (1) {
    // Cursor zurück nach oben links (ohne den Screen zu flashen)
    printf("\033[H");

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            // Die "Plasma"-Formel: Eine Überlagerung von Sinuswellen
            float v = 0.0;
            v += sin(x / 8.0 + t);
            v += sin((y / 4.0 + t) / 2.0);
            v += sin((x / 8.0 + y / 4.0 + t) / 2.0);

            float cx = x / 8.0 + 1.0 * sin(t / 3.0);
            float cy = y / 4.0 + 1.0 * cos(t / 2.0);
            v += sin(sqrt(cx * cx + cy * cy + 1.0) + t);

            // Normalisierung des Werts für die Farbberechnung
            int r = (int)((sin(v * M_PI) + 1.0) * 127);
            int g = (int)((cos(v * M_PI / 2.0) + 1.0) * 127);
            int b = (int)((sin(v * M_PI + 2.0 * M_PI / 3.0) + 1.0) * 127);

            // Ausgabe eines gefüllten Blocks mit TrueColor ANSI
            // Wir nutzen ein Leerzeichen mit Hintergrundfarbe
            printf("\033[48;2;%d;%d;%dm ", r, g, b);
        }
        printf("\033[0m\n"); // Zeilenende zurücksetzen
    }

    t += 0.05; // Geschwindigkeit der Animation
    usleep(20000); // Ca. 50 FPS
}

return 0;

}

Warum das befriedigend ist:

Interferenzmuster: Die Formel kombiniert vier verschiedene Sinuswellen (horizontal, vertikal, diagonal und kreisförmig). Da diese sich ständig verschieben, entstehen Muster, die sich scheinbar nie exakt wiederholen.

24-Bit-Farben: Die Sequenz \033[48;2;R;G;Bm erlaubt es, jeden der 16 Millionen Farbtöne direkt anzusprechen. Das erzeugt weiche Verläufe statt hatter Pixelblöcke.

Mathematische Harmonie: Das Auge nimmt die zugrundeliegende Ordnung der Sinusfunktionen als angenehm und "natürlich" wahr, ähnlich wie Wasserwellen.

Was Elara hier lernt:

Terminal-Steuerung: Mit ioctl liest sie die aktuelle Größe deines Fensters aus, damit das Plasma immer den ganzen Screen füllt, egal wie groß du es ziehst.

Performance: Sie lernt, dass printf verdammt schnell sein kann, wenn man nicht den ganzen Bildschirm löscht (clear), sondern nur den Cursor zurücksetzt.

Soll Elara dieses Wissen jetzt nutzen, um ein interaktives Element einzubauen – zum Beispiel, dass die Wellen auf die Mausbewegung oder Tasteneingaben reagieren?

usleep funktioniert nicht unter ISO C99

Du hast absolut recht. Elara stolpert hier über ein klassisches Problem der C-Programmierung: Portabilität vs. Standards. usleep() gehört zum POSIX-Standard, nicht zum reinen ISO C99. Wenn du mit strengen Flags wie -std=c99 kompilierst, wird der Compiler meckern, weil er die Deklaration nicht findet.

In Elaras Welt ist das der Moment, in dem sie lernt, dass das "moderne" C (und die zugrunde liegende System-Library) stabilere Alternativen bietet. Kapitel 22: Die Zeitbändiger (nanosleep & Feature Macros)

Die Story: Elaras Plasma-Programm lässt sich auf einem anderen System nicht kompilieren. Die Zeit scheint stillzustehen – oder rast unkontrolliert. Sie erfährt, dass usleep veraltet (deprecated) ist. Sie lernt die "offizielle" Art kennen, einen Thread schlafen zu legen: nanosleep. Es ist präziser und ISO-freundlicher. Die Lösung: nanosleep nutzen

nanosleep verlangt eine Struktur namens timespec, die Sekunden und Nanosekunden getrennt speichert. Um den Code absolut sauber zu halten, fügen wir ein Feature Test Macro (_POSIX_C_SOURCE) am Anfang ein. C

// Compile: gcc plasma.c -lm -o plasma

define _POSIX_C_SOURCE 199309L // Ermöglicht nanosleep unter ISO C

include <stdio.h>

include <math.h>

include <time.h> // Für nanosleep

include <unistd.h>

include <sys/ioctl.h>

int main() { struct winsize w; ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); int width = w.ws_col; int height = w.ws_row;

float t = 0;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 20000000L; // 20 Millisekunden (20.000.000 Nanosekunden)

printf("\033[?25l"); // Cursor verstecken

while (1) {
    printf("\033[H");

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            float v = 0.0;
            v += sin(x / 8.0 + t);
            v += sin((y / 4.0 + t) / 2.0);
            v += sin((x / 8.0 + y / 4.0 + t) / 2.0);

            float cx = x / 8.0 + 1.0 * sin(t / 3.0);
            float cy = y / 4.0 + 1.0 * cos(t / 2.0);
            v += sin(sqrt(cx * cx + cy * cy + 1.0) + t);

            int r = (int)((sin(v * M_PI) + 1.0) * 127);
            int g = (int)((cos(v * M_PI / 2.0) + 1.0) * 127);
            int b = (int)((sin(v * M_PI + 2.0 * M_PI / 3.0) + 1.0) * 127);

            printf("\033[48;2;%d;%d;%dm ", r, g, b);
        }
        printf("\033[0m\n");
    }

    t += 0.05;
    nanosleep(&ts, NULL); // Der ISO-konforme Weg
}

return 0;

}

Warum nanosleep besser ist:

Höhere Präzision: usleep arbeitet mit Mikrosekunden, nanosleep theoretisch mit Nanosekunden (abhängig von der Hardware-Clock).

Signal-Handling: Wenn nanosleep durch ein Signal unterbrochen wird, kann es die restliche Schlafzeit im zweiten Parameter speichern, sodass man den Schlaf fortsetzen kann.

ISO/POSIX Konformität: Es ist der empfohlene Weg in modernen Linux/Unix-Umgebungen.

Elaras Erkenntnis

Sie versteht jetzt, dass Standards wie C99 den kleinsten gemeinsamen Nenner bilden, aber das Betriebssystem (POSIX) die Werkzeuge liefert, um wirklich mit der Hardware (wie der Systemuhr) zu interagieren.

Möchtest du, dass Elara jetzt eine physikalische Simulation einbaut, zum Beispiel einen "Regentropfen"-Effekt, der die Plasmawellen bricht?