|
![]() |
Este programa es (download) PARA LINUX y PARA WINDOWS es el mismo archivo y permite resincronizar subtítulos de peliculas. Cuando tenemos un archivo con una película, si esta ha sido cortada y si tenemos el archivo de subtítulos de la original, podremos resincronizar estos estirándolos y achícándolos como un acordeón, tan solo ubicando dos subtítulos cualquiera en toda la secuencia. Utilizando un programa auxiliar como mplayer en linux o VirtualDub en Windows, podemos detectar exactamente el frame donde debería aparecer el primer subtítulo, luego hacemos lo mismo para averiguar donde se desvanece el segundo subtítulo de referencia, preferentemente el último de la secuencia para que la presición sea mayor. Este proceso se puede repetir varias veces hasta conseguir StartFrame y StopFrame adecuados para el subtítulo 1 y el último, los valores -1 indican que será por defecto el último subtítulo o frame el seleccionado. Este programa lee tres tipos de archivos, sub srt y sub como srt, pero sólo guarda en el más confiable en sub...
Advertencia!!!!!!: Revisar siempre la consola donde se ejecuta, porque si al abrir un archivo se ve el Mensaje AUCH! OFFSET APLICADO SOLO SERVIRA SI ALGUIEN PEGO DOS ARCHIVOS DISTINTOS, en caso contrario deberán abrir el archivo con un editor como kwrite o el block de notas buscar la linea que suguiere el programa en la consola y fijarse que cerca no haya un número descabellado o que no concuerda, es decir que el tiempo 1 sea siempre menor que el tiempo 2. Corregirlo a un valor posible, mi programa no puede adivinar estos cambios porque fueron hechos a propósito por humanos, para que estos archivos no funcionen adecuadamente al descargarlos de Interner, porque es ilegal, distribuir películas en Internet, este programa fue hecho con fines educativos..
no hace falta, tan solo descargamos este archivo zip (download) con el código fuente y el programa ya compilado listo para su uso, si están interesados en reprogramarlo, pueden descomprimir el archivo jar y reconfigurarlo, la interface gráfica está programada a los apurones, así que puede ser optimizada. La interface de consola funciona con jdk1.1.8 en adelante, pero la interface gráfica sólo con jdk1.2 en adelante, es preferible usar 1.3, 1.4.2 o superior. En windows ejecutar subfixer.exe, en linux subfixer...
Supongamos que en el directorio actual se encuentra el archivo subg.jar podemos ejecutar y ver la ayuda así...
gus@gus~ >java -classpath subg.jar com.compunauta.SubFixer.GSubFixer --help
Modo de uso: java -classpath subfixer.jar com.compunauta.SubFixer.GSubFixer [-opcion valor]
Donde opción puede ser una o varias de las siguientes:
-i nombrearchivo
Archivo de entrada, por defecto STDIN (Teclado)
-o nombrearchivo
Archivo de salida,por defecto STDOUT (Pantalla)
-sf NúmeroFrame
Frame donde aparece el subtitulo a sincronizar (el 1º, por defecto)
-ef NúmeroFrame
Frame donde se desvanece el segundo (el último por defecto)
-ssz NúmeroSubtítulo
Primer subtÝtulo de referencia (el primero por defecto)
-esz NúmeroSubtítulo
Segundo y posterior al primer subtítulo de referencia (el lt.x.d.)
-sp NúmeroSubtítulo
Procesar dese este subtítulo en adelante
-ep NúmeroSubtítulo
Procesar hasta este subtítulo
|
Si no se especifica salida se la puede bajar a un archivo con el signo >nombrearchivo.sub
Este modo de uso, permite más opciones como por ejemplo corregir un subtitulado de un archivo de video al cual se le quitó una parte. Para ello se sincronizan partes diferentes pertenecientes a los fragmentos de la película en un único video, y se debe guardar no el proceso sincronizado sino una secuencia (BOTON DEL MENU).
gus@gus~ >java -classpath subg.jar com.compunauta.SubFixer.GSubFixerGui |

Problema: Tenemos un video cortado al principio y al final, o sea que el subtitulado original debería empezar tarde y se debería mostrar en una parte que no existe... Debemos correrlo y ponerlo donde se debe.
Solución: Cargamos el archivo original de subtítulo y nos fijamos donde debería aparecer el primero, es decir en que frame deberíamos ver el primer subtítulo, para ello nos valemos de un programa para ver videos como mplayer en Linux,QNX o VirtualDub en Windows,en mplayer ponemos pausa y se nos muestra en la consola asociada el frame correspondiente.




A continuación ponemos en el casillero Debajo de StartFrame el número de frame y presionamos enter o hacemos click en el botón superior.

Ahora repetimos lo mismo, es decir avanzamos el video hasta donde se debería desvanecer el último subtítulo pausamos y anotamos el último frame.


Borramos el contenido del cuadro de texto debajo del botón StopFrame y ponemos el frame anotado. presionamos enter o el botón StopFrame.

Suficiente, presionamos el botón sincronizar y luego lo podemos guardar usando el botón GUARDAR que está en el grupo de botones que estuvimos usando, no usar los de las barras de botónes con el dibujo del disquette porque hacen algo más general.

Ahora el subtitulado está sincronizado... en este ejemplo los videos primeros ya muestran el subtitulado corregido.
Problema: Tenemos una película que fue cortada en una o más partes y a su vez a la primera parte le faltan cuatro trozos intercalados.
Solución: con un único archivo de subtitulado abierto varias veces generamos un Proyecto, es decir abrimos el archivo una vez y seleccionamos el primer subtítulo que aparece en el primer fragmento, presionamos StartSub y SaveFrom, luego pensemos que necesitamos dos de referencia, así que si no usamos el primero podemos seleccionar uno posterior al primero y presionando sólo StartSub otra vez, pero SaveFrom mantendrá el número del que aparece primero, marcamos el último del fragmento con SaveTo, y el último subtítulo que aparece con EndSub. Repetimos el proceso como tantos fragmentos haya, y al culminar, ponemos la solapa correspondiente al primer fragmento y lo agregamos a la secuencia con el botón agregar al final de la ventana. Luego con el segundo, y así hasta el quinto (o cuantos haya).

Paso seguido, podemos guardar el proyecto para el caso de que no haya salido bien el sincronismo en uno de los fragmentos porque erramos algún número, y para tener un nuevo archivo de subtítulados, guardamos la secuencia con el botón de guardar secuencia de la barra de botónes superior. Existen más combinaciones que pueden experimentarlas ustedes.
No es problema, por defecto se reconocen los saltos de frames automáticamente, así que si usan la consola, cat s1.sub s2.sub|java -classpath subg.jar com.compunauta.SubFixer.GSubFixer >sfull.sub o pegan ambos con kwrite o block de notas y lo abren con el editor, esta vez la advertencia de AUCH OFFSET APLICADO, será correcta y no un error.
Sólo mostraré el código de la clase GSubFixer, porque el resto lo pueden ver descomprimiendo el paquete subg.jar
package com.compunauta.SubFixer;
/**
* Title: Subtitle fixer
* Description: Fix Unproportional subtitles gus_est_prgms@yahoo.com, gustavo@compunauta.com
* Copyright: Copyright (c) 2002
* Company: www.compunauta.com
* @author Gustavo Guillermo Software
* @version 1.0
*/
import java.util.Vector;
import java.util.Properties;
import java.io.*;
import com.compunauta.GusParser.REQManipulation;
public class GSubFixer implements java.io.Serializable{
public Vector SubTitles;
public int StartFrame;
public int StopFrame;
public Properties Options;
public int StartTitle;
public int StopTitle;
public int StartParse;
public int EndParse;
public GSubFixer() {
}
public static void main(String[] args){
PrintStream o;
BufferedReader i;
GSubFixer GS=new GSubFixer();
GS.Options=GSubFixer.getPropertiesFromArgs(args);
try{
if (GS.Options.getProperty("sf")!=null) {GS.StartFrame=Integer.parseInt(GS.Options.getProperty("sf"));}
else{GS.StartFrame=0;}
if (GS.Options.getProperty("ef")!=null) {GS.StopFrame=Integer.parseInt(GS.Options.getProperty("ef"));}
else{GS.StopFrame=-1;}
if (GS.Options.getProperty("sp")!=null) {GS.StartParse=Integer.parseInt(GS.Options.getProperty("sp"));}
else{GS.StartParse=0;}
if (GS.Options.getProperty("ep")!=null) {GS.EndParse=Integer.parseInt(GS.Options.getProperty("ep"));}
else{GS.EndParse=-1;}
if (GS.Options.getProperty("i")!=null) {i=new BufferedReader(new InputStreamReader(new FileInputStream(GS.Options.getProperty("i"))));}
else{i=new BufferedReader(new InputStreamReader(System.in));}
if (GS.Options.getProperty("o")!=null) {o=new PrintStream(new FileOutputStream(GS.Options.getProperty("o")));}
else{o=System.out;}
if (GS.Options.getProperty("ssz")!=null) {GS.StartTitle=Integer.parseInt(GS.Options.getProperty("ssz"))-1;}
else{GS.StartTitle=0;}
if (GS.Options.getProperty("esz")!=null) {GS.StopTitle=Integer.parseInt(GS.Options.getProperty("esz"))-1;}
else{GS.StopTitle=-1;}
GS.loadSub(i);
GS.sub2Ratio();
GS.dumpSub(o);
}catch (Exception e){e.printStackTrace();usage(); System.exit(1);}
}//end main
public static void usage(){
System.err.println("Modo de uso: java -classpath subfixer.jar com.compunauta.SubFixer.GSubFixer [-opcion valor]");
System.err.println("Donde opción puede ser una o varias de las siguientes:");
System.err.println("-i nombrearchivo");
System.err.println("\t\tArchivo de entrada, por defecto STDIN (Teclado)");
System.err.println("-o nombrearchivo");
System.err.println("\t\tArchivo de salida,por defecto STDOUT (Pantalla)");
System.err.println("-sf NúmeroFrame");
System.err.println("\t\tFrame donde aparece el subtitulo a sincronizar (el 1º, por defecto)");
System.err.println("-ef NúmeroFrame");
System.err.println("\t\tFrame donde se desvanece el segundo (el último por defecto)");
System.err.println("-ssz NúmeroSubtítulo");
System.err.println("\t\tPrimer subtítulo de referencia (el primero por defecto)");
System.err.println("-esz NúmeroSubtítulo");
System.err.println("\t\tSegundo y posterior al primer subtítulo de referencia (el últ.x.d.)");
System.err.println("-sp NúmeroSubtítulo");
System.err.println("\t\tProcesar dese este subtítulo en adelante");
System.err.println("-ep NúmeroSubtítulo");
System.err.println("\t\tProcesar hasta este subtítulo");
}//end usage
public boolean dumpSub (PrintStream out)throws IOException{
if(this.EndParse==-1){this.EndParse=this.SubTitles.size();}
Object[] Sub;
for(int i=this.StartParse;i<this.EndParse;i++){
Sub=(Object[])this.SubTitles.elementAt(i);
out.println("{"+((Integer)Sub[0]).intValue()+"}{"+((Integer)Sub[1]).intValue()+"}"+(String)Sub[2]);
}//end for
return true;
}//end print
public boolean sub2Ratio(){
if (this.StopTitle==-1){this.StopTitle=this.SubTitles.size()-1;}
int staSub=((Integer)((Object[])this.SubTitles.elementAt(this.StartTitle))[0]).intValue();
int stoSub=((Integer)((Object[])this.SubTitles.elementAt(this.StopTitle))[1]).intValue();;
if (this.StopFrame==-1){this.StopFrame=stoSub;}
System.err.println("El primer Subtítulo aparece en el frame "+staSub+" y el último permanece hasta el frame "+stoSub);
System.err.println("El rango Visible es ["+staSub+"-"+stoSub+"] "+(stoSub-staSub)+" frames en total");
System.err.println("Se reprogramarán entre["+this.StartFrame+"-"+this.StopFrame+"] "+(this.StopFrame-this.StartFrame)+" frames en total");
System.err.println("Reprogramando Frames a proporciones...");
double coef;
double ORange=(stoSub-staSub);
double PRange=(this.StopFrame-this.StartFrame);
int ST=this.SubTitles.size();
Object[] Sub;
double Orig;
for(int i=0;i<ST;i++){
Sub=(Object[])this.SubTitles.elementAt(i);
for(int j=0; j<2;j++){
Orig=((Integer)Sub[j]).intValue();
Sub[j]=new Integer((int)((Orig-staSub)/ORange*PRange)+this.StartFrame);
// System.err.println("old: "+(int)Orig+" New: "+((Integer)Sub[j]).intValue());
}//end for
}//end for
return true;
}//end sub2Ratio
public boolean loadSub(BufferedReader in)throws IOException{
System.err.println("Leyendo títulos");
//speed up this subroutine is not important due to time is irrelevant...
this.SubTitles=new Vector();
String Line;
Integer staSub;
Integer stoSub;
String Temp=null;
int max=0;
int acumtime;
int offset=0;
Object[] SubTitleObj=null;
REQManipulation Helper=new REQManipulation();
int format=-1;
int phase=0;
boolean dformat=true;
while((Line=in.readLine())!=null){
if (dformat){
Helper.setText(Line);
if (Line.indexOf("{")==0){System.err.println("Sub - Format Detected...!"); format=0;dformat=false;}
if (Helper.onlyNum()&&!Line.trim().equals("")){System.err.println("Srt - Format Detected... Parsing to Sub Format!");format=1;dformat=false;}
if (Helper.haveString(":")&&Helper.haveString(",")&&Helper.haveString(".")){System.err.println("Sub Like Srt - Format Detected... Parsing to Sub Format!");format=2;dformat=false;}
}//end format detection
//SUB format load...
if (format==0){
Helper.setText(Line);
Helper.drop("{");
staSub=new Integer(Helper.REQ("}"));
Helper.drop("{");
stoSub=new Integer(Helper.REQ("}"));
max=Math.max(stoSub.intValue(),max);
// System.err.println("i:"+staSub+" f:"+stoSub+" max:"+max);
if(max>stoSub.intValue()){
System.err.println("i:"+staSub+" f:"+stoSub+" max:"+max);
System.err.println("Auch: offset aplicado!!! revisar si no es correcto, cerca del frame "+stoSub.intValue());
offset=offset+max;max=0;}
if(offset!=0){
staSub=new Integer(staSub.intValue()+offset);
stoSub=new Integer(stoSub.intValue()+offset);
}//end offset
if(staSub.intValue()>=stoSub.intValue()){System.err.println("Precaución, un subtítulo tiene tiempos invertidos cerca de: "+Line);}
SubTitleObj=new Object[3];
SubTitleObj[0]=staSub;
SubTitleObj[1]=stoSub;
SubTitleObj[2]=Helper.Text;
this.SubTitles.addElement(SubTitleObj);
}//end sub format recognition
//SRT format load
NOTHING:{
if (format==1){
// System.err.println("Phase["+phase+"] "+Line);
if (phase==0){phase=1;Temp="";break NOTHING;}
if (phase==1){
SubTitleObj=new Object[3];
phase=2;
Helper.setText(Line);
acumtime=Integer.parseInt(Helper.REQ(":"))*60*60*1000+Integer.parseInt(Helper.REQ(":"))*60*1000+Integer.parseInt(Helper.REQ(","))*1000+Integer.parseInt(Helper.REQ(" --> "));
staSub=new Integer(acumtime);
acumtime=Integer.parseInt(Helper.REQ(":"))*60*60*1000+Integer.parseInt(Helper.REQ(":"))*60*1000+Integer.parseInt(Helper.REQ(","))*1000+Integer.parseInt(Helper.REQ(" --> "));
stoSub=new Integer(acumtime);
max=Math.max(stoSub.intValue(),max);
if(max>stoSub.intValue()){
System.err.println("i:"+staSub+" f:"+stoSub+" max:"+max);
System.err.println("Auch: offset aplicado!!! revisar si no es correcto, cerca de la línea:"+Line);
offset=offset+max;max=0;}
if(offset!=0){
staSub=new Integer(staSub.intValue()+offset);
stoSub=new Integer(stoSub.intValue()+offset);
}//end offset
if(staSub.intValue()>=stoSub.intValue()){System.err.println("Precaución, un subtítulo tiene tiempos invertidos cerca de: "+Line);}
SubTitleObj[0]=staSub;
SubTitleObj[1]=stoSub;
break NOTHING;
}//end phase 1
if (phase==2){
if(Line.trim().equals("")){
phase=0;
SubTitleObj[2]=Temp;
this.SubTitles.addElement(SubTitleObj);
}else{if(Temp.equals(""))Temp=Temp+Line; else Temp=Temp+"|"+Line;}
break NOTHING;
}//end phase
}//end
}//end sub srt format load;
//SUB Like SRT format load
SUBLIKESRT:{
if (format==2){
// System.err.println("Phase["+phase+"] "+Line);
if (phase==0){
if (Line.trim().equals("")){break SUBLIKESRT;}
SubTitleObj=new Object[3];
phase=1;
Helper.setText(Line);
acumtime=Integer.parseInt(Helper.REQ(":"))*60*60*100+Integer.parseInt(Helper.REQ(":"))*60*100+Integer.parseInt(Helper.REQ("."))*100+Integer.parseInt(Helper.REQ(","));
staSub=new Integer(acumtime);
acumtime=Integer.parseInt(Helper.REQ(":"))*60*60*100+Integer.parseInt(Helper.REQ(":"))*60*100+Integer.parseInt(Helper.REQ("."))*100+Integer.parseInt(Helper.REQ(","));
stoSub=new Integer(acumtime);
max=Math.max(stoSub.intValue(),max);
if(max>stoSub.intValue()){
System.err.println("i:"+staSub+" f:"+stoSub+" max:"+max);
System.err.println("Auch: offset aplicado!!! revisar si no es correcto, cerca de la línea:"+Line);
offset=offset+max;max=0;}
if(offset!=0){
staSub=new Integer(staSub.intValue()+offset);
stoSub=new Integer(stoSub.intValue()+offset);
}//end offset
if(staSub.intValue()>=stoSub.intValue()){System.err.println("Precaución, un subtítulo tiene tiempos invertidos cerca de: "+Line);}
SubTitleObj[0]=staSub;
SubTitleObj[1]=stoSub;
break SUBLIKESRT;
}//end phase 1
if (phase==1){
phase=0;
Helper.setText(Line);
Helper.repall("[br]","|");
SubTitleObj[2]=Helper.Text;
this.SubTitles.addElement(SubTitleObj);
break SUBLIKESRT;
}//end phase
}//end
}//end sub srt format load;
}//end read while
System.err.println("Hay "+this.SubTitles.size()+" Subtitulados");
return true;
}//end loadSub
public static Properties getPropertiesFromArgs(String[] args){
Properties p=new Properties();
String Key;
String Value;
for (int i=0;i<args.length;i++){
if (args[i].equals("--help")){usage();System.exit(0);}
if (args[i].indexOf("-")==0){
Key=args[i].substring(1);
i++;
Value="";
if(i<args.length){Value=args[i];p.put(Key,Value);}
}//end if
}//end for
return p;
}//end getprop.
}//end class