Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ test_router.php

# Benchmark / workspace d'analyse (corpus privé de torrents + artefacts) — local only
bench/
.feature-loop/
32 changes: 28 additions & 4 deletions download.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,28 @@ function write_stream_info(array $info): void {
require __DIR__ . '/handlers/keyframe.php';
}

// Thumbnail pour preview seekbar : frame JPEG 160×90 à un instant donné
if (isset($_GET['keyframe_thumb'])) {
$t = max(0, min(86400, (float)$_GET['keyframe_thumb']));
if (!$resolvedPath || !file_exists($resolvedPath)) { http_response_code(204); exit; }
$cmd = 'timeout 8 ffmpeg -hide_banner -loglevel error -ss ' . escapeshellarg(sprintf('%.3f', $t))
. ' -i ' . escapeshellarg($resolvedPath)
. ' -vframes 1 -vf ' . escapeshellarg('scale=160:90:force_original_aspect_ratio=decrease,pad=160:90:(ow-iw)/2:(oh-ih)/2')
. ' -q:v 8 -f image2pipe -vcodec mjpeg pipe:1';
$desc = [0 => ['file', '/dev/null', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$proc = proc_open($cmd, $desc, $pipes);
if (!$proc) { http_response_code(204); exit; }
$jpeg = stream_get_contents($pipes[1]);
stream_get_contents($pipes[2]); // évite le blocage pipe stderr sous charge
fclose($pipes[1]); fclose($pipes[2]);
$ret = proc_close($proc);
if ($ret !== 0 || !$jpeg) { http_response_code(204); exit; }
header('Content-Type: image/jpeg');
header('Cache-Control: max-age=300');
echo $jpeg;
exit;
}

// Mode streaming natif : sert le fichier brut (audio uniquement, ou fallback)
if (isset($_GET['stream']) && $_GET['stream'] === '1') {
write_stream_info([
Expand Down Expand Up @@ -619,8 +641,8 @@ function afficher_listing(string $dirPath, string $basePath, string $token, stri
/* ── Continue Watching ── */
.cw-section{margin-bottom:1.5rem}
.cw-title{font-size:.75rem;font-weight:700;color:var(--text-muted);margin-bottom:.5rem;text-transform:uppercase;letter-spacing:.06em}
.cw-scroll{display:flex;gap:.75rem;overflow-x:auto;padding-bottom:.5rem;scrollbar-width:thin}
.cw-scroll::-webkit-scrollbar{height:4px}.cw-scroll::-webkit-scrollbar-track{background:transparent}.cw-scroll::-webkit-scrollbar-thumb{background:rgba(255,255,255,.1);border-radius:2px}
.cw-scroll{display:flex;gap:.75rem;flex-wrap:wrap}

.cw-card{flex:0 0 180px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:8px;padding:.75rem;text-decoration:none;color:inherit;transition:background .15s,border-color .15s;display:block}
.cw-card:hover{background:rgba(255,255,255,.08);border-color:rgba(240,160,48,.3)}
.cw-filename{font-size:.8rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:.25rem}
Expand Down Expand Up @@ -892,10 +914,12 @@ function escHtml(s){var d=document.createElement('div');d.appendChild(document.c
var cutoff=Date.now()-30*86400000;
items=items.filter(function(i){return i.timestamp>cutoff&&i.duration>0&&i.position>0;});
if(!items.length)return;
items.sort(function(a,b){return b.timestamp-a.timestamp;});
items=items.slice(0,6);
var container=document.getElementById('continue-watching');
if(!container)return;
container.style.display='';
var html='<div class="cw-title">Reprendre</div><div class="cw-scroll">';
var html='<div class="cw-title">Reprendre</div><div class="cw-scroll-wrap"><div class="cw-scroll">';
items.forEach(function(item){
var pct=Math.min(99,Math.round((item.position/item.duration)*100));
var remaining=Math.round((item.duration-item.position)/60);
Expand All @@ -906,7 +930,7 @@ function escHtml(s){var d=document.createElement('div');d.appendChild(document.c
+'<div class="cw-bar"><div class="cw-progress" style="width:'+pct+'%"></div></div>'
+'</a>';
});
html+='</div>';
html+='</div></div>';
container.innerHTML=html;
})();

Expand Down
14 changes: 11 additions & 3 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,20 @@ function sharebox_log(string $msg, string $channel = 'stream'): void {
*/
function find_php_cli(): string {
if (defined('PHP_CLI_BINARY') && PHP_CLI_BINARY) return PHP_CLI_BINARY;
// PHP_BINARY peut être /usr/local/sbin/php-fpm ; on regarde /usr/local/bin/php aussi
// PHP_BINARY en contexte FPM pointe vers php-fpm, pas le CLI.
// Priorité au binaire versionné (ex: php8.2 sur Debian multi-PHP) pour éviter
// de tomber sur /usr/bin/php qui peut être une version différente sans pdo_sqlite.
$ver = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
$binDir = dirname(PHP_BINARY);
foreach ([$binDir . '/php', dirname($binDir) . '/bin/php', '/usr/local/bin/php', '/usr/bin/php'] as $candidate) {
foreach ([
'/usr/local/bin/php', // Alpine/Docker (version unique)
'/usr/bin/php' . $ver, // Debian multi-PHP: php8.2, php8.4…
$binDir . '/php', // même répertoire que PHP_BINARY
'/usr/bin/php', // fallback générique (peut être une version différente)
] as $candidate) {
if (is_executable($candidate)) return $candidate;
}
return 'php'; // fallback sur PATH
return 'php';
}

// Aliases pour compatibilité et lisibilité
Expand Down
15 changes: 10 additions & 5 deletions player.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ body::before {
.player-card:fullscreen video,
.player-card:-webkit-full-screen video { position:absolute; inset:0; width:100%; height:100%; max-height:none; }
.player-card:fullscreen .player-controls,
.player-card:-webkit-full-screen .player-controls { position:absolute; bottom:0; left:0; right:0; z-index:20; background:linear-gradient(transparent 0%, rgba(0,0,0,.75) 100%) !important; padding-top:3rem; transition:opacity .25s; border-top:none !important; padding-left:max(1.5rem, env(safe-area-inset-left)); padding-right:max(1.5rem, env(safe-area-inset-right)); padding-bottom:max(.5rem, env(safe-area-inset-bottom)); }
.player-card:-webkit-full-screen .player-controls { position:absolute; bottom:0; left:0; right:0; z-index:20; background:linear-gradient(transparent 0%, rgba(0,0,0,.75) 100%) !important; padding-top:3rem; transition:opacity .3s ease, transform .3s ease; transform:translateY(0); border-top:none !important; padding-left:max(1.5rem, env(safe-area-inset-left)); padding-right:max(1.5rem, env(safe-area-inset-right)); padding-bottom:max(.5rem, env(safe-area-inset-bottom)); }
.player-card:fullscreen .player-controls .ctrl-row button,
.player-card:-webkit-full-screen .player-controls .ctrl-row button,
.player-card:fullscreen .player-controls .ctrl-row svg,
Expand All @@ -77,7 +77,7 @@ body::before {
.player-card:fullscreen .player-controls .track-bar,
.player-card:-webkit-full-screen .player-controls .track-bar { border-top:none; }
.player-card:fullscreen .player-controls.fs-hidden,
.player-card:-webkit-full-screen .player-controls.fs-hidden { opacity:0; pointer-events:none; }
.player-card:-webkit-full-screen .player-controls.fs-hidden { opacity:0; pointer-events:none; transform:translateY(100%); }
.player-card.hide-cursor,.player-card.hide-cursor * { cursor:none !important; }
video { display:block; width:100%; max-height:78vh; background:#000; object-fit:contain; }
.sub-overlay { position:absolute; left:0; right:0; text-align:center; pointer-events:none; padding:0 6%; z-index:10; font-size:1.5rem; }
Expand All @@ -91,6 +91,9 @@ video { display:block; width:100%; max-height:78vh; background:#000; object-fit:
.play-icon-overlay.pop-play { animation:popPlay .4s ease forwards; }
#vol-osd { position:absolute; top:clamp(1rem,3vh,2rem); right:clamp(1rem,3vw,2rem); z-index:20; background:rgba(0,0,0,.72); color:#fff; padding:clamp(.5rem,1.2vh,.9rem) clamp(1rem,2vw,1.6rem); border-radius:clamp(.5rem,1vh,.75rem); font-size:clamp(1.35rem,2.8vh,2.4rem); font-weight:700; letter-spacing:.03em; pointer-events:none; opacity:0; transition:opacity .2s; -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); border:1px solid rgba(255,255,255,.08); text-shadow:0 1px 4px rgba(0,0,0,.5); }
#vol-osd.visible { opacity:1; }
.dt-flash{position:absolute;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.18);border-radius:8px;padding:.4rem .8rem;font-size:.9rem;color:#fff;font-weight:700;pointer-events:none;opacity:0;transition:opacity .15s;z-index:20}
.dt-flash.dt-flash-active{opacity:1;animation:dt-pop .6s ease-out forwards}
@keyframes dt-pop{0%{opacity:1;transform:translateY(-50%) scale(1)}80%{opacity:.8;transform:translateY(-50%) scale(1.05)}100%{opacity:0;transform:translateY(-50%) scale(.95)}}
audio { display:block; width:100%; padding:2rem 1.5rem; background:rgba(26,29,40,.8); }
.player-hint { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; pointer-events:none; z-index:10; }
.player-hint-text { font-family:var(--font-sans); font-size:.78rem; font-weight:600; padding:.4rem 1rem; border-radius:var(--radius-sm); background:rgba(12,14,20,.82); border:1px solid rgba(255,255,255,.08); color:var(--text-muted); -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); letter-spacing:.01em; transition:color .2s; white-space:nowrap; }
Expand Down Expand Up @@ -138,16 +141,17 @@ audio { display:block; width:100%; padding:2rem 1.5rem; background:rgba(26,29,40
.player-card{border-radius:0;border:none;position:fixed;inset:0;z-index:10}
.player-video-wrap{height:100vh;height:100dvh}
video{max-height:100vh !important;height:100vh !important;max-height:100dvh !important;height:100dvh !important}
.player-controls{position:fixed;bottom:0;left:0;right:0;z-index:20;background:linear-gradient(transparent 0%,rgba(0,0,0,.75) 100%) !important;padding-top:2rem;border-top:none !important;transition:opacity .25s;padding-left:max(1.5rem,env(safe-area-inset-left));padding-right:max(1.5rem,env(safe-area-inset-right));padding-bottom:max(.5rem,env(safe-area-inset-bottom))}
.player-controls{position:fixed;bottom:0;left:0;right:0;z-index:20;background:linear-gradient(transparent 0%,rgba(0,0,0,.75) 100%) !important;padding-top:2rem;border-top:none !important;transition:opacity .3s ease, transform .3s ease;padding-left:max(1.5rem,env(safe-area-inset-left));padding-right:max(1.5rem,env(safe-area-inset-right));padding-bottom:max(.5rem,env(safe-area-inset-bottom))}
.fs-title{display:block;position:fixed;top:0;left:0;right:0;z-index:30;padding:.8rem max(1rem,env(safe-area-inset-left)) .8rem max(1rem,env(safe-area-inset-right));background:linear-gradient(rgba(8,10,18,.8) 0%,transparent 100%);transition:opacity .25s}
.player-controls.fs-hidden{opacity:0;pointer-events:none}
.player-controls.fs-hidden{opacity:0;pointer-events:none;transform:translateY(100%)}
.player-card.hide-cursor .player-toolbar{opacity:0;pointer-events:none}
.track-bar{gap:.25rem !important;padding:.3rem 0 .15rem !important;margin-top:.2rem !important;border-top:none !important;flex-wrap:nowrap !important;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.track-bar::-webkit-scrollbar{display:none}
.track-bar label{display:none !important}
.player-name{display:none}
}
.seek-tooltip { position:absolute; bottom:calc(100% + 6px); background:rgba(12,14,20,.9); border:1px solid rgba(255,255,255,.1); color:var(--text-primary); font-family:var(--font-mono); font-size:.68rem; padding:.18rem .45rem; border-radius:4px; pointer-events:none; white-space:nowrap; transform:translateX(-50%); display:none; z-index:5; }
.seek-tooltip { position:absolute; bottom:calc(100% + 8px); background:rgba(12,14,20,.9); border:1px solid rgba(255,255,255,.1); color:var(--text-primary); font-family:var(--font-mono); font-size:.68rem; padding:.25rem .55rem; border-radius:4px; pointer-events:none; white-space:nowrap; transform:translateX(-50%); display:none; z-index:5; text-align:center; }
.seek-tooltip img { display:block; width:160px; height:90px; object-fit:contain; border-radius:2px; margin-bottom:.2rem; }
.vol-wrap { display:flex; align-items:center; gap:.3rem; }
.vol-slider { -webkit-appearance:none; appearance:none; width:60px; height:28px; background:transparent; outline:none; cursor:pointer; vertical-align:middle; }
.vol-slider::-webkit-slider-runnable-track { height:3px; border-radius:2px; background:linear-gradient(to right,#f0a030 0%,#f0a030 var(--vol-pct,100%),rgba(255,255,255,.15) var(--vol-pct,100%),rgba(255,255,255,.15) 100%); }
Expand Down Expand Up @@ -184,3 +188,4 @@ video{max-height:100vh !important;height:100vh !important;max-height:100dvh !imp
outline: 2px solid var(--accent, #f0a030) !important;
outline-offset: 2px !important;
}
.seek-marker { position:absolute; top:0; bottom:0; width:2px; background:rgba(255,255,255,.6); pointer-events:none; border-radius:1px; z-index:1; }
Loading
Loading