3.8.1: se convierte todo lo que ahorre al menos un 30 %, con la estimación medida por códec de origen
Munka: «si un archivo se puede optimizar así sea un 30 % se convierta». La regla deja de mirar si el clip pasa de 1 o 2 veces la tasa objetivo y mira el ahorro estimado (ajustable, 30 % de entrada). La estimación sale de las 286 conversiones hechas: H.264 y AV1 de cámara quedan en una fracción, H.265 casi igual. Entran los 11 AV1 de Línea Gene; dejan de ofrecerse los H.265 que no adelgazan y lo apartado en Duplicados. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Este commit está contenido en:
@@ -800,38 +800,65 @@ class Convertidor(unittest.TestCase):
|
||||
con.close()
|
||||
return t
|
||||
|
||||
def test_lo_ya_comprimido_no_se_reconvierte(self):
|
||||
"""9 clips que ya eran H.265 se reconvirtieron para quedarse en el 95 % de su peso."""
|
||||
def test_se_convierte_lo_que_ahorra_un_30(self):
|
||||
"""Munka, 14-oct-2026: «si un archivo se puede optimizar así sea un 30 % se convierta». Medido sobre las 286
|
||||
conversiones hechas: un H.265 de cámara se queda en el 85-99 % (6 se reconvirtieron para nada), y en cambio
|
||||
los AV1 de 85 Mb/s que no se ofrecían quedan en la décima parte."""
|
||||
raiz = cliente("Comprimidos")
|
||||
g = "2025/06- Junio/00_Grabacion/"
|
||||
for n in ("A014_06170738_C019.mp4", "C0001.MP4", "C0002.MP4", "C0003.MP4", "C0004.MP4", "C0005.MP4"):
|
||||
for n in ("A014_06170738_C019.mp4", "C0001.MP4", "C0002.MP4", "C0003.MP4", "C0004.MP4", "C0005.MP4", "C0006.MP4",
|
||||
"A015_05250930_C004.mp4", "Duplicados/C0003.MP4"):
|
||||
mk(raiz, g + n, 2025, 6, 17)
|
||||
O.escanear(raiz)
|
||||
self.leidos(raiz, {
|
||||
"A014_06170738_C019.mp4": {"codec": "h264", "perfil": "Main", "ancho": 4608, "alto": 2592, "kbps": 25000},
|
||||
"C0001.MP4": {"codec": "h264", "perfil": "High 4:2:2 Intra", "pix_fmt": "yuv422p10le", "fps": 59.94, "kbps": 596000},
|
||||
"C0002.MP4": {"codec": "hevc", "perfil": "Main 10", "pix_fmt": "yuv420p10le", "fps": 59.94, "kbps": 72000},
|
||||
"C0003.MP4": {"codec": "h264", "perfil": "High", "kbps": 120000},
|
||||
"C0004.MP4": {"codec": "av1", "perfil": "Main", "kbps": 30000},
|
||||
"C0004.MP4": {"codec": "av1", "perfil": "Main", "kbps": 22000},
|
||||
"C0005.MP4": {"codec": "prores", "perfil": "HQ", "pix_fmt": "yuv422p10le", "kbps": 870000},
|
||||
"C0006.MP4": {"codec": "hevc", "perfil": "Main 10", "pix_fmt": "yuv420p10le", "kbps": 200000},
|
||||
"A015_05250930_C004.mp4": {"codec": "av1", "perfil": "Main", "ancho": 2160, "alto": 3840, "fps": 59.94, "kbps": 83000},
|
||||
})
|
||||
con = O.db()
|
||||
with con:
|
||||
con.execute("UPDATE archivos SET analizado = 1, codec = 'h264', perfil = 'High', pix_fmt = 'yuv420p', ancho = 3840, alto = 2160, "
|
||||
"fps = 30, dur = 60, kbps = 120000 WHERE nombre = 'C0003.MP4'") # el clip y su copia en Duplicados
|
||||
con.close()
|
||||
nr = os.path.normpath(raiz)
|
||||
cs = {os.path.basename(c["ruta"]): c["excluido"] for c in O.candidatos(nr, 0, "equilibrado", 0)}
|
||||
self.assertTrue(cs["A014_06170738_C019.mp4"].startswith("ya viene comprimido (H.264 a 25 Mb/s)"), cs) # por debajo del objetivo (30)
|
||||
self.assertTrue(cs["C0002.MP4"].startswith("ya viene comprimido (H.265 a 72 Mb/s)"), cs)
|
||||
self.assertTrue(cs["C0004.MP4"].startswith("ya viene comprimido (AV1 a 30 Mb/s)"), cs)
|
||||
# lo que guarda cada cuadro entero (XAVC-I, ProRes) y el H.264 muy holgado sí adelgazan de verdad
|
||||
cands = {os.path.relpath(c["ruta"], os.path.join(nr, g.replace("/", S))): c for c in O.candidatos(nr, 0, "equilibrado", 0)}
|
||||
cs = {k: c["excluido"] for k, c in cands.items()}
|
||||
# un H.265 de cámara casi no adelgaza: no se ofrece; uno muy holgado (el techo es 1,8 veces el objetivo), sí
|
||||
self.assertTrue(cs["C0002.MP4"].startswith("convertido ahorraría solo un 7 % (H.265 a 72 Mb/s): se convierte desde un 30 %"), cs)
|
||||
self.assertEqual(cs["C0006.MP4"], "")
|
||||
# el H.264 por debajo del objetivo y los AV1 (el de móvil a 83 Mb/s, uno a 22), que antes no se ofrecían, ahora sí
|
||||
self.assertEqual([cs["A014_06170738_C019.mp4"], cs["A015_05250930_C004.mp4"], cs["C0004.MP4"]], ["", "", ""])
|
||||
# lo que guarda cada cuadro entero (XAVC-I, ProRes) y el H.264 muy holgado, como siempre
|
||||
self.assertEqual([cs["C0001.MP4"], cs["C0003.MP4"], cs["C0005.MP4"]], ["", "", ""])
|
||||
# lo apartado como repetido no se convierte: se borra
|
||||
self.assertTrue(cs[S.join(("Duplicados", "C0003.MP4"))].startswith("está apartado como repetido"), cs)
|
||||
# lo que se estima: un AV1 a 83 Mb/s, como mucho el objetivo (49 % menos); un H.264 a 25, como mucho 0,6 veces
|
||||
a = cands["A015_05250930_C004.mp4"]
|
||||
self.assertAlmostEqual(a["estimado"] / (83000 * 1000 / 8 * 60), 1 - 0.49, delta=0.01)
|
||||
# el mínimo se cambia: pidiendo un 45 %, el H.264 a 25 Mb/s (39 %) ya no entra
|
||||
cs45 = {os.path.basename(c["ruta"]): c["excluido"] for c in O.candidatos(nr, 0, "equilibrado", 0, ahorro=45)}
|
||||
self.assertTrue(cs45["A014_06170738_C019.mp4"].startswith("convertido ahorraría solo un 39 %"), cs45)
|
||||
self.assertEqual(cs45["A015_05250930_C004.mp4"], "")
|
||||
# a un formato de edición no se le aplica: ahí no se busca que pese menos
|
||||
cs = {os.path.basename(c["ruta"]): c["excluido"] for c in O.candidatos(nr, 0, "equilibrado", 0, False, "prores_lt")}
|
||||
self.assertEqual(cs["A014_06170738_C019.mp4"], "")
|
||||
# y la lista de acciones cuenta solo lo que se puede aligerar
|
||||
self.assertEqual(cs["C0002.MP4"], "")
|
||||
# la lista de acciones cuenta lo que se puede aligerar
|
||||
ajustes = dict(O.CONFIG["optimizar"])
|
||||
self.addCleanup(lambda: O.CONFIG["optimizar"].update(ajustes))
|
||||
O.CONFIG["optimizar"].update(min_mb=0, meses_minimos=0)
|
||||
conv = [a for a in O.acciones(raiz)["acciones"] if a["clave"] == "convertir"]
|
||||
self.assertEqual(len(conv), 1)
|
||||
self.assertTrue(conv[0]["cifra"].startswith("3 clips"), conv[0])
|
||||
self.assertTrue(conv[0]["cifra"].startswith("7 clips"), conv[0])
|
||||
# y después de convertir se descarta lo que ahorre menos del mínimo con 10 puntos de margen (con 30, del 20 %)
|
||||
t = {"formato": "h265_10bit", "ruta": "C0002.MP4", "bytes_in": 100, "bytes_out": 81}
|
||||
self.assertTrue(O._no_compensa(t).startswith("no compensa: pesa el 81 %"))
|
||||
self.assertEqual(O._no_compensa(dict(t, bytes_out=79)), "")
|
||||
O.CONFIG["optimizar"]["ahorro_minimo"] = 50
|
||||
self.assertTrue(O._no_compensa(dict(t, bytes_out=61)).startswith("no compensa"))
|
||||
|
||||
def test_un_nombre_parecido_no_es_una_conversion(self):
|
||||
"""El sufijo solo no basta: «Entrevista_HQ.mp4» en H.264 no lo hizo este programa."""
|
||||
|
||||
Referencia en una nueva incidencia
Block a user