Tecnología · GUÍA PRÁCTICA
Revisa las opciones de subtítulos en la cola de HandBrake sin iniciar la codificación
Separa las solicitudes de búsqueda de las pistas explícitas en un informe guardado y distingue true, false y los valores no registrados.

Una cola por lotes puede contener una solicitud de búsqueda de subtítulos para audio extranjero sin ninguna pista explícita. Tratarlas como lo mismo confunde la revisión: solicitar un análisis no confirma qué subtítulo se seleccionará ni aparecerá en el resultado.
HandBrake ya ofrece reglas de selección de subtítulos en ajustes preestablecidos guardados y edición individual de trabajos. Esta guía añade una comprobación CSV limitada y sin conexión de opciones seleccionadas en tu propia copia de una exportación compatible. No repara un fallo nativo, modifica la cola, abre medios ni inicia una codificación.
Empieza con la exportación guardada correcta
Configura y guarda las reglas de selección de subtítulos de HandBrake en tu ajuste preestablecido antes de añadir un lote. Los ajustes guardan el comportamiento de selección, no una lista fija de pistas elegidas manualmente. Para trabajos ya en cola, usa Edit y vuelve a añadir el trabajo modificado; revisar cada trabajo sigue siendo un método nativo válido.
Esta receta usa una copia separada llamada queue.json de una exportación compatible de la cola de Windows para uso con CLI. El código actual de Windows genera una matriz de objetos que contienen Job, con Source, Destination y Subtitle. La copia normal de la cola de la GUI tiene otra estructura y se rechaza. La exportación CLI excluye trabajos completados, pero el informe no muestra estados ni afirma que todos los trabajos exportados estén pendientes.
Es una instantánea guardada, no un monitor en directo. Se necesita JSON UTF-8, SubtitleList como matriz y campos seleccionados de tipos compatibles. Si falta Search se acepta como no registrado; un objeto Search explícitamente null, la estructura SRT antigua o formas incompatibles se rechazan. Las exportaciones de GUI de otras plataformas y formatos futuros quedan fuera.
Crea el informe sin sobrescribir archivos
En una terminal Linux Bash con Python 3, abre la carpeta de tu copia queue.json. Copia el comando siguiente como una sola línea, conservando comillas rectas y barras inversas. Rechaza subtitles.csv si ya existe. Ejecutamos este comando exacto solo en Linux Bash, no en Windows ni macOS.
El conversor analiza y valida todas las filas antes de crear el CSV. Rechaza claves JSON duplicadas, números no estándar, tipos incorrectos en los campos seleccionados y estructuras compatibles mal formadas. No abre medios de origen ni archivos de subtítulos importados. No audita opciones desconocidas: informa de diecisiete campos seleccionados, sin validar toda la configuración de codificación.
python3 -c 'exec("import csv, json, sys\nfrom pathlib import Path\ndef pairs(items):\n d = {}\n for k, v in items:\n if k in d:\n raise ValueError('"'"'Duplicate JSON key'"'"')\n d[k] = v\n return d\ndef bad(value):\n raise ValueError('"'"'Nonstandard JSON number'"'"')\ndef obj(v):\n if not isinstance(v, dict):\n raise ValueError('"'"'Expected object'"'"')\n return v\ndef text(v):\n if v is None: return '"'"''"'"'\n if not isinstance(v, str): raise ValueError('"'"'Expected string'"'"')\n v.encode('"'"'utf-8'"'"')\n return v\ndef integer(v):\n if v is None: return '"'"''"'"'\n if type(v) is not int: raise ValueError('"'"'Expected integer'"'"')\n return str(v)\ndef flag(v):\n if v is None: return '"'"''"'"'\n if type(v) is not bool: raise ValueError('"'"'Expected Boolean'"'"')\n return '"'"'true'"'"' if v else '"'"'false'"'"'\nsrc, dst = map(Path, sys.argv[1:])\njobs = json.loads(src.read_bytes().decode('"'"'utf-8-sig'"'"'), object_pairs_hook=pairs, parse_constant=bad)\nif not isinstance(jobs, list): raise ValueError('"'"'Expected CLI queue array'"'"')\nrows = []\nfor n, wrapper in enumerate(jobs, 1):\n job = obj(obj(wrapper)['"'"'Job'"'"'])\n source, dest, sub = obj(job['"'"'Source'"'"']), obj(job['"'"'Destination'"'"']), obj(job['"'"'Subtitle'"'"'])\n title = source['"'"'Title'"'"']\n if type(title) is not int or title < 1: raise ValueError('"'"'Expected positive title index'"'"')\n base = [str(n), text(source['"'"'Path'"'"']), text(dest['"'"'File'"'"']), str(title)]\n search = obj(sub.get('"'"'Search'"'"', {}))\n rows.append(base + ['"'"'search'"'"', '"'"''"'"', '"'"''"'"', flag(search.get('"'"'Enable'"'"'))] + [flag(search.get(k)) for k in ['"'"'Burn'"'"', '"'"'Default'"'"', '"'"'Forced'"'"']] + ['"'"''"'"'] * 6)\n tracks = sub['"'"'SubtitleList'"'"']\n if not isinstance(tracks, list): raise ValueError('"'"'Expected subtitle list'"'"')\n for i, item in enumerate(tracks, 1):\n item = obj(item)\n if '"'"'SRT'"'"' in item: raise ValueError('"'"'Legacy SRT shape unsupported'"'"')\n imp = item.get('"'"'Import'"'"')\n if imp is None:\n track = item['"'"'Track'"'"']\n if type(track) is not int or track < 0: raise ValueError('"'"'Expected embedded track index'"'"')\n kind, imp = '"'"'embedded'"'"', {}\n else:\n kind, imp = '"'"'import'"'"', obj(imp)\n if not text(imp['"'"'Filename'"'"']): raise ValueError('"'"'Import filename missing'"'"')\n rows.append(base + [kind, str(i), integer(item.get('"'"'Track'"'"')), '"'"''"'"'] + [flag(item.get(k)) for k in ['"'"'Burn'"'"', '"'"'Default'"'"', '"'"'Forced'"'"']] + [integer(item.get('"'"'Offset'"'"'))] + [text(imp.get(k)) for k in ['"'"'Filename'"'"', '"'"'Language'"'"', '"'"'Codeset'"'"', '"'"'Format'"'"']] + [text(item.get('"'"'Name'"'"'))])\nheader = ['"'"'job'"'"', '"'"'source'"'"', '"'"'destination'"'"', '"'"'title'"'"', '"'"'row_kind'"'"', '"'"'entry'"'"', '"'"'track'"'"', '"'"'enable'"'"', '"'"'burn'"'"', '"'"'default'"'"', '"'"'forced'"'"', '"'"'offset_ms'"'"', '"'"'import_file'"'"', '"'"'import_language'"'"', '"'"'import_codeset'"'"', '"'"'import_format'"'"', '"'"'name'"'"']\nwith dst.open('"'"'x'"'"', encoding='"'"'utf-8-sig'"'"', newline='"'"''"'"') as f:\n w = csv.writer(f); w.writerow(header); w.writerows(rows)\nwith dst.open(encoding='"'"'utf-8-sig'"'"', newline='"'"''"'"') as f:\n if list(csv.reader(f)) != [header] + rows: raise ValueError('"'"'Readback mismatch; discard partial CSV'"'"')\nprint(len(jobs), '"'"'saved jobs and'"'"', len(rows), '"'"'report rows checked; not an encode result'"'"')\n")' "queue.json" "subtitles.csv"
Lee por separado búsqueda y pistas explícitas
Las columnas son job, source, destination, title, row_kind, entry, track, enable, burn, default, forced, offset_ms, import_file, import_language, import_codeset, import_format y name. Los números de trabajo y entrada explícita empiezan en 1 según el orden guardado; conserva trabajos repetidos. Origen, destino y nombres son cadenas analizadas, sin expandir rutas ni comprobar archivos.
Cada trabajo tiene una fila row_kind search con sus opciones Search, seguida de filas para entradas explícitas de SubtitleList. Un enable true solicita una búsqueda; no confirma una pista seleccionada. Por eso una lista explícita vacía con búsqueda activada difiere de no configurar búsqueda. Los booleanos se escriben true o false; campos seleccionados ausentes o null quedan vacíos, como valor no registrado, nunca como un false inferido. Las filas de pistas dejan enable vacío porque pertenece a la búsqueda.
Los índices de pistas integradas Track empiezan en cero: 0 es la primera pista válida, no un valor ausente. Las entradas importadas nativas usan Track -1 y un objeto Import separado; se conservan nombre de archivo, idioma, codificación y formato sin inspeccionar el archivo. Offset es un entero en milisegundos, incluidos negativos, no segundos. Opciones y desplazamientos no prueban que el archivo exista, corresponda al idioma previsto o se reproduzca bien en el contenedor de salida.
Verifica la instantánea antes de aprobar el lote
El comando relee el CSV y compara cabecera y cada campo antes de indicar números de trabajos y filas. Comprueba exactamente una fila de búsqueda por trabajo exportado y compara entradas integradas e importadas conocidas con la cola nativa. Cuenta registros CSV, no líneas físicas: un campo entre comillas puede contener saltos de línea. Una exportación vacía crea solo la cabecera. Conserva JSON original y CSV maestro; edita o filtra una copia de trabajo.
En LibreOffice Calc importa UTF-8 con comas y comillas dobles rectas como delimitador de texto. Pon las diecisiete columnas como Texto; desactiva Combinar delimitadores, Recortar espacios y Evaluar fórmulas. Activa Editar configuración de filtros si no aparece el diálogo. No ejecutamos la interfaz de Calc. Modifica trabajos en HandBrake, exporta otra instantánea y crea un informe con nombre nuevo; editar el CSV no actualiza la cola y no es un archivo para importar.
Ejecutamos 26 casos sintéticos: siete válidos cubrieron listas vacías, búsqueda sin pistas explícitas, Track 0, importación Track -1, desplazamientos negativos, campos ausentes o null, Unicode, comillas, saltos de línea y trabajos repetidos en orden; diecinueve inválidos se detuvieron antes de crear el CSV. Se rechazaron siete intentos adicionales de sobrescritura, sin cambiar bytes de entrada ni salida existente. No probamos la GUI real de HandBrake, exportación de colas ni codificación. Si falla escritura o relectura, descarta el CSV parcial y usa un nombre nuevo. Antes de confiar en un lote nocturno, verifica aparte una prueba corta de codificación y reproducción de material que puedes usar; el informe no prueba resultados de búsqueda ni subtítulos de salida.
