Playlist TV: mostrar el límite de peso REAL, no el del código

Junto al botón de subir aparece ahora el límite por archivo. No es un número
escrito a mano: se calcula como el menor entre el tope del código (500 MB) y
lo que permita el php.ini de ese servidor (upload_max_filesize y
post_max_size) — un letrero fijo mentiría si el servidor admite menos. El
pre-chequeo del navegador usa ese mismo valor, y el rechazo dice cuánto pesa
el archivo y cuál es el límite.

Dato relevante: PHP trae de fábrica 2 MB de límite de subida. Si el letrero
en producción muestra «2 MB», esa es la verdadera causa de los videos que no
entraron, y la solución es subir upload_max_filesize y post_max_size en el
php.ini del servidor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-27 11:48:02 -05:00
co-authored by Claude Fable 5
parent 587c23adcd
commit 398ea98b0b
2 changed files with 37 additions and 3 deletions
+34 -2
View File
@@ -13,6 +13,28 @@ require_once __DIR__ . '/../../../config/config.php';
if (!isUserLoggedIn()) { header('Location: ' . BASE_URL . 'login.php'); exit; } if (!isUserLoggedIn()) { header('Location: ' . BASE_URL . 'login.php'); exit; }
require_once __DIR__ . '/../_acceso.php'; require_once __DIR__ . '/../_acceso.php';
// Límite real de subida para la playlist: el menor entre el tope del código
// (500 MB) y lo que permita el php.ini de ESTE servidor. Mostrar un número
// fijo mentiría si el servidor admite menos.
function _bytesDeIni(string $v): int {
$v = trim($v);
$n = (float) $v;
switch (strtoupper(substr($v, -1))) {
case 'G': $n *= 1024; // sigue
case 'M': $n *= 1024;
case 'K': $n *= 1024;
}
return (int) $n;
}
$_tvLimiteBytes = min(
500 * 1024 * 1024,
_bytesDeIni(ini_get('upload_max_filesize') ?: '500M'),
_bytesDeIni(ini_get('post_max_size') ?: '500M')
);
$_tvLimiteTxt = $_tvLimiteBytes >= 1024 * 1024 * 1024
? round($_tvLimiteBytes / (1024 * 1024 * 1024), 1) . ' GB'
: round($_tvLimiteBytes / (1024 * 1024)) . ' MB';
turneroExigirRol(['supervisor']); turneroExigirRol(['supervisor']);
$db = Database::getInstance(); $db = Database::getInstance();
@@ -1196,6 +1218,11 @@ $tab = $_GET['tab'] ?? 'lugares';
<button class="btn btn-primary btn-sm" onclick="guardarOrdenTv()"> <button class="btn btn-primary btn-sm" onclick="guardarOrdenTv()">
<i class="fas fa-save me-1"></i>Guardar orden / duración <i class="fas fa-save me-1"></i>Guardar orden / duración
</button> </button>
<div class="text-muted small mt-1">
<i class="fas fa-info-circle me-1"></i>Videos MP4 o WebM e imágenes JPG/PNG ·
máximo <b><?= $_tvLimiteTxt ?></b> por archivo.
Los videos de iPhone (.mov) deben convertirse a MP4 primero.
</div>
</div> </div>
<input type="file" id="tv-file-input" accept="video/mp4,video/webm,image/jpeg,image/png,image/webp" class="d-none" multiple> <input type="file" id="tv-file-input" accept="video/mp4,video/webm,image/jpeg,image/png,image/webp" class="d-none" multiple>
<div id="tv-upload-resumen" style="display:none"></div> <div id="tv-upload-resumen" style="display:none"></div>
@@ -1647,6 +1674,10 @@ async function cambiarSesion(accion, sesionId) {
// timeout, avisando con un toast que desaparecía en segundos. El cliente veía // timeout, avisando con un toast que desaparecía en segundos. El cliente veía
// «subí 5» y la playlist mostraba 2. Ahora van de a uno, un fallo no frena a // «subí 5» y la playlist mostraba 2. Ahora van de a uno, un fallo no frena a
// los demás, y al final queda un resumen fijo con qué entró y qué no. // los demás, y al final queda un resumen fijo con qué entró y qué no.
// El mismo límite que muestra la interfaz: calculado en el servidor
const TV_LIMITE_BYTES = <?= (int) $_tvLimiteBytes ?>;
const TV_LIMITE_TXT = '<?= $_tvLimiteTxt ?>';
const escHtml = x => String(x ?? '').replace(/[<>&"]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c])); const escHtml = x => String(x ?? '').replace(/[<>&"]/g, c => ({'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c]));
async function subirTvMediaEnCola(files) { async function subirTvMediaEnCola(files) {
@@ -1678,8 +1709,9 @@ function subirTvMedia(file) {
if (!allowedMime.includes(file.type)) { if (!allowedMime.includes(file.type)) {
return Promise.resolve({ ok: false, nombre: file.name, motivo: 'formato no admitido (use MP4, WebM, JPG o PNG)' }); return Promise.resolve({ ok: false, nombre: file.name, motivo: 'formato no admitido (use MP4, WebM, JPG o PNG)' });
} }
if (file.size > 500 * 1024 * 1024) { if (file.size > TV_LIMITE_BYTES) {
return Promise.resolve({ ok: false, nombre: file.name, motivo: 'supera el límite de 500 MB' }); return Promise.resolve({ ok: false, nombre: file.name,
motivo: 'pesa ' + Math.round(file.size / 1048576) + ' MB y el límite es ' + TV_LIMITE_TXT });
} }
const wrap = document.getElementById('tv-progress-wrap'); const wrap = document.getElementById('tv-progress-wrap');
+3 -1
View File
@@ -74,8 +74,10 @@ ok('el reproductor salta al siguiente si un video falla',
// ── 2. Los pre-chequeos rechazan claro y ANTES de tocar la red ────── // ── 2. Los pre-chequeos rechazan claro y ANTES de tocar la red ──────
const docTrampa = { getElementById: () => { throw new Error('tocó el DOM'); } }; const docTrampa = { getElementById: () => { throw new Error('tocó el DOM'); } };
const subir = new Function('document', 'XMLHttpRequest', 'FormData', 'API', const subir = new Function('document', 'XMLHttpRequest', 'FormData', 'API',
'TV_LIMITE_BYTES', 'TV_LIMITE_TXT',
subirSrc + '; return subirTvMedia;')( subirSrc + '; return subirTvMedia;')(
docTrampa, function () { throw new Error('tocó la red'); }, function () {}, ''); docTrampa, function () { throw new Error('tocó la red'); }, function () {}, '',
500 * 1024 * 1024, '500 MB');
Promise.all([ Promise.all([
subir({ name: 'clip.mov', type: 'video/quicktime', size: 1000 }), subir({ name: 'clip.mov', type: 'video/quicktime', size: 1000 }),