<?php
setlocale(LC_CTYPE, "pl_PL.UTF-8", "en_US.UTF-8", "C.UTF-8");
require_once 'functions.php';

$shareHash = $_GET['h'] ?? '';
$isPublicShare = false;

if ($shareHash !== '') {
    if (!preg_match('/^(?:[a-f0-9]{12}|[a-f0-9]{32})$/', $shareHash)) die('Błędny lub wygasły link.');
    $db = getDb();
    if (!isset($db['shares'][$shareHash])) die('Błędny lub wygasły link.');

    $share = $db['shares'][$shareHash];
    if (time() > (int)($share['expires'] ?? 0)) die('Ten link tymczasowy wygasł (minęły 24h).');

    $folderKey = $share['folder'] ?? '';
    $subPath = $share['path'] ?? '';
    $fileName = $share['file'] ?? '';
    $resolved = resolveAllowedFile($folderKey, $subPath, $fileName);
    if ($resolved === false) die('Plik usunięty, przeniesiony lub znajduje się poza dozwolonym folderem.');
    $isPublicShare = true;
} else {
    checkAuth();
    $folderKey = $_GET['folder'] ?? '';
    $subPath = $_GET['path'] ?? '';
    $fileName = $_GET['file'] ?? '';
    $resolved = resolveAllowedFile($folderKey, $subPath, $fileName);
    if ($resolved === false) die('Odmowa dostępu lub plik nie istnieje.');
}

// Zwalnia sesję, abyś mógł otwierać inne karty
session_write_close();

// KRYTYCZNE: domyślny limit czasu wykonania PHP (często 30-60s) zabija
// CAŁY skrypt po cichu, gdy ffmpeg/ekstrakcja napisów trwa dłużej (wolny
// mount MEGA) - proces potomny (ffmpeg) ginie razem z PHP, ale żaden wpis
// nie zdąży trafić do logu, bo PHP jest ubijane w połowie wykonania. To
// tłumaczy przypadki "film się zatrzymuje, a log jest pusty". Streaming i
// ekstrakcja z natury mogą trwać długo - nie ma tu sensu na górny limit.
set_time_limit(0);

$start = isset($_GET['start']) ? max(0, floatval($_GET['start'])) : 0;

$subPath = $resolved['path'];
$fileName = $resolved['file'];
$filePath = $resolved['full'];

header("Content-Type: video/mp4");
header("Cache-Control: no-cache, no-store, must-revalidate");
header("Pragma: no-cache");
header("Expires: 0");
header("X-Accel-Buffering: no");

while (ob_get_level()) ob_end_clean();
ignore_user_abort(true);

$startParam = $start > 0 ? "-ss " . escapeshellarg((string)$start) : "";

$logFile = __DIR__ . '/data/ffmpeg_error.log';

$aid = isset($_GET['aid']) ? max(0, intval($_GET['aid'])) : 0;

$thumbDir = __DIR__ . '/data/thumbnails';
if (!is_dir($thumbDir)) @mkdir($thumbDir, 0777, true);
$fileSignature = (@filemtime($filePath) ?: 0) . '|' . (@filesize($filePath) ?: 0);
$metaHash = md5('video-meta-v1|' . $filePath . '|' . $fileSignature);
$metaFile = $thumbDir . '/meta_' . $metaHash . '.json';
$probeData = readJsonFile($metaFile, []);
if (empty($probeData)) {
    $probeCmd = "/usr/bin/ffprobe -v error -print_format json -show_entries format=duration:stream=codec_type,codec_name:stream_tags=language,title " . escapeshellarg($filePath);
    $probeData = json_decode((string)@shell_exec($probeCmd), true);
    if (!is_array($probeData)) $probeData = [];
    if (!empty($probeData)) writeJsonFile($metaFile, $probeData);
}
$duration = isset($probeData['format']['duration']) ? (float)$probeData['format']['duration'] : 0;
$videoCodec = '';
$audioCodecs = [];
if (isset($probeData['streams']) && is_array($probeData['streams'])) {
    foreach ($probeData['streams'] as $stream) {
        if (($stream['codec_type'] ?? '') === 'video' && $videoCodec === '') {
            $videoCodec = strtolower((string)($stream['codec_name'] ?? ''));
        }
        if (($stream['codec_type'] ?? '') === 'audio') {
            $audioCodecs[] = strtolower((string)($stream['codec_name'] ?? ''));
        }
    }
}

if (!empty($audioCodecs)) {
    if (!array_key_exists($aid, $audioCodecs)) $aid = 0;
    // Gwarantowane mapowanie: V:0 to obraz wideo, a:X to wybrana (relatywna) ścieżka dźwiękowa.
    $mapParam = "-map 0:V:0 -map 0:a:" . $aid;
    // Po konwersji wszystkie ścieżki są AAC. Kopiujemy wybraną ścieżkę bez ponownego kodowania
    // i oznaczamy ją jako domyślną, aby przeglądarka od razu rozpoczęła odtwarzanie.
    $audioParams = ($audioCodecs[$aid] === 'aac' ? "-c:a copy" : "-c:a aac -b:a 128k") . " -disposition:a:0 default";
} else {
    $mapParam = "-map 0:V:0";
    $audioParams = "-an";
}

$remainingDuration = $duration > 0 ? max(0, $duration - $start) : 10800;
$streamTimeout = min(43200, max(1800, (int)ceil($remainingDuration) + 900));

// Jeżeli materiał od początku ma już obraz H.264 zgodny z MP4, kopiujemy obraz bez ponownego kodowania.
// Po przewijaniu pozostaje transkodowanie, aby zachować dokładny punkt startu.
if ($videoCodec === 'h264' && $start <= 0) {
    $videoParams = "-c:v copy";
} else {
    $videoParams = "-c:v libx264 -pix_fmt yuv420p -preset ultrafast -crf 28 -maxrate 3000k -bufsize 6000k";
}

// "exec" przed ffmpeg jest KLUCZOWE: proc_open uruchamia komendę przez
// "/bin/sh -c ...", a proc_terminate() zabija TYLKO ten proces powłoki, nie
// jego dziecko. Bez "exec" ffmpeg zostaje osierocony i działa dalej w tle.
// "exec" sprawia, że powłoka PODMIENIA się na proces ffmpeg (ten sam PID),
// więc SIGKILL wysłany przez proc_terminate() trafia naprawdę we ffmpeg.
// UWAGA: jeśli proces utknie w stanie D (nieprzerywalny sen w jądrze, np.
// czeka na odczyt z mounta MEGA), żaden sygnał - łącznie z SIGKILL - nie
// dotrze do niego, dopóki blokujące wywołanie systemowe się nie zakończy.
// To już nie jest coś, co da się naprawić w PHP - wymaga skonfigurowania
// limitów czasu (timeout/contimeout) na poziomie samego mounta MEGA.
$cmd = "exec /usr/bin/timeout --foreground --signal=TERM --kill-after=10 " . $streamTimeout .
    " /usr/bin/ffmpeg -hide_banner -loglevel warning -nostdin $startParam -i " . escapeshellarg($filePath) .
    " $mapParam $videoParams $audioParams -avoid_negative_ts make_zero -threads 2 -f mp4 -movflags frag_keyframe+empty_moov+default_base_moof pipe:1";

$descriptorspec = [
    0 => ["pipe", "r"],
    1 => ["pipe", "w"],
    2 => ["file", $logFile, "w"]
];

$process = proc_open($cmd, $descriptorspec, $pipes);

if (is_resource($process)) {
    fclose($pipes[0]);

    while (!feof($pipes[1])) {
        $data = fread($pipes[1], 8192);
        if ($data === false || strlen($data) === 0) break;
        echo $data;
        @ob_flush();
        flush();
        
        if (connection_aborted()) break;
    }
    
    fclose($pipes[1]);
    proc_terminate($process, 15);
    usleep(200000);
    proc_terminate($process, 9);
    proc_close($process);
}
