├── README.md └── SubTitleReader ├── .classpath ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── bin └── com │ └── ver │ └── subtitle │ ├── Main.class │ ├── PlayerSubTitleProxy.class │ ├── loader │ ├── PlayerSubTitleLoader$1.class │ ├── PlayerSubTitleLoader$LoaderSubTitleListener.class │ └── PlayerSubTitleLoader.class │ ├── model │ ├── SubTitleItem.class │ ├── TuziSubTitleInfoTreeMap$KeyEntry.class │ └── TuziSubTitleInfoTreeMap.class │ ├── parser │ ├── ISubTitleParser.class │ ├── SrtSubTitleShop.class │ ├── SubTitleParserFactory.class │ └── srt │ │ ├── InvalidSRTException.class │ │ ├── SRTException.class │ │ ├── SRTReader.class │ │ ├── SRTReaderException.class │ │ ├── SRTTimeFormat$SRTTime.class │ │ ├── SRTTimeFormat$Type.class │ │ └── SRTTimeFormat.class │ └── utils │ └── Log.class ├── libs └── CharsetDetector.jar └── src └── com └── ver └── subtitle ├── Main.java ├── PlayerSubTitleProxy.java ├── loader └── PlayerSubTitleLoader.java ├── model ├── SubTitleItem.java └── TuziSubTitleInfoTreeMap.java ├── parser ├── ISubTitleParser.java ├── SrtSubTitleShop.java ├── SubTitleParserFactory.java └── srt │ ├── InvalidSRTException.java │ ├── SRTException.java │ ├── SRTReader.java │ ├── SRTReaderException.java │ └── SRTTimeFormat.java └── utils └── Log.java /README.md: -------------------------------------------------------------------------------- 1 | # srt-reader-java 2 | Srt字幕文件的解析,支持多行解析,支持播放器是时间轴对应,读取的时间复杂度低 3 | 4 | 5 | ---- 6 | # Srt字幕文件的解析 7 | * 支持所有的文件格式的解析 8 | * 支持Srt多行字幕的解析 9 | * 加载字幕方式:通过TreeMap的数据结构去读取字幕文件,时间复杂度O(1) 10 | * 可以结合播放器,去读取每一秒对应的字幕 11 | 12 | # 包结构 13 | * Main.java 为测试的调用类 14 | * PlayerSubTitleProxy 为代理类,注册一个监听器,如果加载成功则为字幕文件正确 15 | * TuziSubTitleInfoTreeMap 为核心数据结构 16 | * SRTReader 为读取类,用于去读取字幕文件 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /SubTitleReader/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SubTitleReader/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | SubTitleReader 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /SubTitleReader/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.6 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.source=1.6 12 | -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/Main.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/Main.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/PlayerSubTitleProxy.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/PlayerSubTitleProxy.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/loader/PlayerSubTitleLoader$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/loader/PlayerSubTitleLoader$1.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/loader/PlayerSubTitleLoader$LoaderSubTitleListener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/loader/PlayerSubTitleLoader$LoaderSubTitleListener.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/loader/PlayerSubTitleLoader.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/loader/PlayerSubTitleLoader.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/model/SubTitleItem.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/model/SubTitleItem.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/model/TuziSubTitleInfoTreeMap$KeyEntry.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/model/TuziSubTitleInfoTreeMap$KeyEntry.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/model/TuziSubTitleInfoTreeMap.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/model/TuziSubTitleInfoTreeMap.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/ISubTitleParser.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/ISubTitleParser.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/SrtSubTitleShop.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/SrtSubTitleShop.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/SubTitleParserFactory.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/SubTitleParserFactory.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/InvalidSRTException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/InvalidSRTException.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTException.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTReader.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTReader.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTReaderException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTReaderException.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTTimeFormat$SRTTime.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTTimeFormat$SRTTime.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTTimeFormat$Type.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTTimeFormat$Type.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTTimeFormat.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/parser/srt/SRTTimeFormat.class -------------------------------------------------------------------------------- /SubTitleReader/bin/com/ver/subtitle/utils/Log.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/bin/com/ver/subtitle/utils/Log.class -------------------------------------------------------------------------------- /SubTitleReader/libs/CharsetDetector.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WangDingchun/srt-reader-java/138023210e136f0eaf0e5a1ba5a56817f7980ba5/SubTitleReader/libs/CharsetDetector.jar -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/Main.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle; 2 | 3 | /** 4 | * 5 | * @title 测试字幕加载类 6 | * @company 北京奔流网络信息技术有线公司 7 | * @email mail@wangdingchun.net 8 | * @author Vermouth 9 | * @version 1.0 10 | * @created 2015-2-27 下午3:16:22 11 | * @changeRecord [修改记录]
12 | */ 13 | public class Main { 14 | 15 | public static void main(String[] args) { 16 | PlayerSubTitleProxy mPlayerSubTitleProxy = new PlayerSubTitleProxy(); 17 | mPlayerSubTitleProxy.loaderSubTitle("/Users/Vermouth/Downloads/test.srt"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/PlayerSubTitleProxy.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle; 2 | 3 | import com.ver.subtitle.loader.PlayerSubTitleLoader; 4 | import com.ver.subtitle.loader.PlayerSubTitleLoader.LoaderSubTitleListener; 5 | import com.ver.subtitle.model.TuziSubTitleInfoTreeMap; 6 | import com.ver.subtitle.utils.Log; 7 | 8 | /** 9 | * 字幕的代理类 处理字幕展现和加载类的之间的逻辑 10 | * 11 | * @author Vermouth 12 | * 13 | */ 14 | public class PlayerSubTitleProxy implements LoaderSubTitleListener { 15 | private PlayerSubTitleLoader mPlayerSubTitleLoader; 16 | private TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap = null; 17 | 18 | public PlayerSubTitleProxy() { 19 | mPlayerSubTitleLoader = new PlayerSubTitleLoader(); 20 | mPlayerSubTitleLoader.setLoaderSubTitleListener(this); 21 | } 22 | 23 | public void updatCurrentSubTitle(long ms) { 24 | Log.d("zimu", formatTime((int) ms)); 25 | if (mTuziSubTitleInfoTreeMap != null) { 26 | String subtitle = mTuziSubTitleInfoTreeMap.getByMins(ms); 27 | Log.d("zimu", "当前的字幕是:"+subtitle); 28 | } 29 | } 30 | 31 | public void loaderSubTitle(String filepath) { 32 | Log.d("zimu", filepath); 33 | mPlayerSubTitleLoader.loadSubTitle(filepath); 34 | } 35 | 36 | public void clearSubTitle() { 37 | if (mTuziSubTitleInfoTreeMap != null) { 38 | mTuziSubTitleInfoTreeMap.clear(); 39 | mTuziSubTitleInfoTreeMap = null; 40 | } 41 | } 42 | 43 | @Override 44 | public void onLoadSuccess(TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap) { 45 | this.mTuziSubTitleInfoTreeMap = mTuziSubTitleInfoTreeMap; 46 | //如果加载成功了,测试读取2分钟时候的字幕 47 | updatCurrentSubTitle(1000*120); 48 | } 49 | 50 | @Override 51 | public void onLoadFail() { 52 | 53 | } 54 | 55 | @SuppressWarnings("all") 56 | public static String formatTime(int millisecond) { 57 | int totalSecond = millisecond / 1000; 58 | int minute = totalSecond / 60; 59 | int hour = minute / 60; 60 | int second = totalSecond % 60; 61 | minute %= 60; 62 | // p_txt_totaltime.setText(String.format("%02d:%02d:%02d", hour,minute, 63 | // second)); 64 | return String.format("%02d:%02d:%02d", hour, minute, second); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/loader/PlayerSubTitleLoader.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.loader; 2 | 3 | import java.io.File; 4 | 5 | import com.ver.subtitle.model.TuziSubTitleInfoTreeMap; 6 | import com.ver.subtitle.parser.SubTitleParserFactory; 7 | 8 | /** 9 | * 字幕的加载类 10 | * 11 | * @author Vermouth 12 | * 13 | */ 14 | public class PlayerSubTitleLoader { 15 | private LoaderSubTitleListener mLoaderSubTitleListener; 16 | private SubTitleParserFactory mSubTitleParserFactory; 17 | 18 | public PlayerSubTitleLoader() { 19 | mSubTitleParserFactory = new SubTitleParserFactory(); 20 | } 21 | 22 | public void setLoaderSubTitleListener(LoaderSubTitleListener mLoaderSubTitleListener) { 23 | this.mLoaderSubTitleListener = mLoaderSubTitleListener; 24 | } 25 | 26 | public interface LoaderSubTitleListener { 27 | public void onLoadSuccess(TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap); 28 | 29 | public void onLoadFail(); 30 | } 31 | 32 | public void loadSubTitle(final String filePath) { 33 | if (!"".equals(filePath) && null != (filePath)) { 34 | new Thread(new Runnable() { 35 | 36 | @Override 37 | public void run() { 38 | try { 39 | File file = new File(filePath); 40 | if (!file.exists()) { 41 | if (mLoaderSubTitleListener != null) { 42 | mLoaderSubTitleListener.onLoadFail(); 43 | } 44 | return; 45 | } 46 | final TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap = mSubTitleParserFactory.parserSubTitle("srt", file); 47 | if (mLoaderSubTitleListener != null) { 48 | mLoaderSubTitleListener.onLoadSuccess(mTuziSubTitleInfoTreeMap); 49 | } 50 | }catch (Exception e) { 51 | e.printStackTrace(); 52 | 53 | if (mLoaderSubTitleListener != null) { 54 | mLoaderSubTitleListener.onLoadFail(); 55 | } 56 | } 57 | 58 | } 59 | }).start(); 60 | } 61 | 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/model/SubTitleItem.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.model; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class SubTitleItem { 6 | private int count; 7 | private long start; 8 | private long end; 9 | private ArrayList mSubLists = new ArrayList(); 10 | 11 | public int getCount() { 12 | return count; 13 | } 14 | 15 | public void setCount(int count) { 16 | this.count = count; 17 | } 18 | 19 | public long getStart() { 20 | return start; 21 | } 22 | 23 | public void setStart(long start) { 24 | this.start = start; 25 | } 26 | 27 | public long getEnd() { 28 | return end; 29 | } 30 | 31 | public void setEnd(long end) { 32 | this.end = end; 33 | } 34 | 35 | public ArrayList getSubLists() { 36 | return mSubLists; 37 | } 38 | 39 | public void setSubLists(ArrayList mSubLists) { 40 | this.mSubLists = mSubLists; 41 | } 42 | 43 | public String getSubTitle(){ 44 | StringBuffer sb=new StringBuffer(""); 45 | for(int i=0;i mSubMap = new TreeMap(); 8 | 9 | public TuziSubTitleInfoTreeMap() { 10 | 11 | } 12 | 13 | public void insert(int index, long start, long end, ArrayList mSubTitles) { 14 | // System.out.println(index+" "+start+" "+end+" "+mSubTitles); 15 | mSubMap.put(new KeyEntry(start, end), genData(index, start, end, mSubTitles)); 16 | } 17 | 18 | public void clear() { 19 | mSubMap.clear(); 20 | } 21 | 22 | public String getByMins(long ms) { 23 | SubTitleItem data = mSubMap.get(new KeyEntry(ms, ms)); 24 | if (data == null) { 25 | return ""; 26 | } else { 27 | return data.getSubTitle(); 28 | } 29 | 30 | } 31 | 32 | private class KeyEntry implements Comparable { 33 | 34 | final long start; 35 | final long end; 36 | 37 | public KeyEntry(long start, long end) { 38 | this.start = start; 39 | this.end = end; 40 | } 41 | 42 | public int compareTo(KeyEntry t) { 43 | long t1 = start - t.start; 44 | if (t1 < 0) { 45 | return -1; 46 | } 47 | long t2 = end - t.end; 48 | if (t1 >= 0 && t2 <= 0) { 49 | return 0; 50 | } 51 | return 1; 52 | } 53 | 54 | } 55 | 56 | private static SubTitleItem genData(int index, long start, long end, ArrayList mSubTitles) { 57 | SubTitleItem data = new SubTitleItem(); 58 | data.setCount(index); 59 | data.setSubLists(mSubTitles); 60 | data.setStart(start); 61 | data.setEnd(end); 62 | return data; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/ISubTitleParser.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser; 2 | 3 | import java.io.File; 4 | import java.io.InputStream; 5 | 6 | import com.ver.subtitle.model.TuziSubTitleInfoTreeMap; 7 | 8 | public interface ISubTitleParser { 9 | public TuziSubTitleInfoTreeMap parser(InputStream is); 10 | public TuziSubTitleInfoTreeMap parser(File file); 11 | } 12 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/SrtSubTitleShop.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser; 2 | 3 | import java.io.File; 4 | import java.io.InputStream; 5 | 6 | import com.ver.subtitle.model.TuziSubTitleInfoTreeMap; 7 | import com.ver.subtitle.parser.srt.SRTReader; 8 | 9 | /** 10 | * 加载srt字幕的车间 11 | * 12 | * @author Vermouth 13 | * 14 | */ 15 | public class SrtSubTitleShop implements ISubTitleParser { 16 | @Override 17 | public TuziSubTitleInfoTreeMap parser(InputStream is) { 18 | TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap = SRTReader.read(is); 19 | return mTuziSubTitleInfoTreeMap; 20 | } 21 | 22 | @Override 23 | public TuziSubTitleInfoTreeMap parser(File file) { 24 | TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap = SRTReader.read(file); 25 | return mTuziSubTitleInfoTreeMap; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/SubTitleParserFactory.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser; 2 | 3 | import java.io.File; 4 | import java.io.InputStream; 5 | 6 | import com.ver.subtitle.model.TuziSubTitleInfoTreeMap; 7 | 8 | public class SubTitleParserFactory { 9 | private ISubTitleParser mSubTitleParser; 10 | 11 | public TuziSubTitleInfoTreeMap parserSubTitle(String type, File file) { 12 | if ("srt".equals(type)) { 13 | mSubTitleParser = new SrtSubTitleShop(); 14 | return mSubTitleParser.parser(file); 15 | } else { 16 | return null; 17 | } 18 | } 19 | 20 | public TuziSubTitleInfoTreeMap parserSubTitle(String type, InputStream is) { 21 | if ("srt".equals(type)) { 22 | mSubTitleParser = new SrtSubTitleShop(); 23 | return mSubTitleParser.parser(is); 24 | } else { 25 | return null; 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/srt/InvalidSRTException.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser.srt; 2 | 3 | /** 4 | * An exception for an invalid SRT format. 5 | * 6 | * @author fredy 7 | */ 8 | public class InvalidSRTException extends SRTException { 9 | private static final long serialVersionUID = 1L; 10 | 11 | /** 12 | * @param message the exception message 13 | * @param cause the cause 14 | */ 15 | public InvalidSRTException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | /** 20 | * @param message the exception message 21 | */ 22 | public InvalidSRTException(String message) { 23 | super(message); 24 | } 25 | 26 | /** 27 | * @param cause the cause 28 | */ 29 | public InvalidSRTException(Throwable cause) { 30 | super(cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/srt/SRTException.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser.srt; 2 | 3 | /** 4 | * Any exceptions related to SRT. 5 | * 6 | * @author fredy 7 | */ 8 | public class SRTException extends RuntimeException { 9 | private static final long serialVersionUID = 1L; 10 | 11 | /** 12 | * @param message the exception message 13 | * @param cause the cause 14 | */ 15 | public SRTException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | /** 20 | * @param message the exception message 21 | */ 22 | public SRTException(String message) { 23 | super(message); 24 | } 25 | 26 | /** 27 | * @param cause the cause 28 | */ 29 | public SRTException(Throwable cause) { 30 | super(cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/srt/SRTReader.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser.srt; 2 | 3 | import java.io.BufferedInputStream; 4 | import java.io.BufferedReader; 5 | import java.io.EOFException; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.text.ParseException; 12 | import java.util.ArrayList; 13 | import java.util.Date; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | import cn.xddai.chardet.CharsetDetector; 18 | 19 | import com.ver.subtitle.model.TuziSubTitleInfoTreeMap; 20 | import com.ver.subtitle.utils.Log; 21 | 22 | public class SRTReader { 23 | 24 | public SRTReader() { 25 | } 26 | 27 | /** 28 | * Reads an SRT file and transforming it into SRT object. 29 | * 30 | * @param srtFile 31 | * SRT file 32 | * @return the SRTInfo object 33 | * @throws InvalidSRTException 34 | * thrown when the SRT file is invalid 35 | * @throws SRTReaderException 36 | * thrown while reading SRT file 37 | */ 38 | public static TuziSubTitleInfoTreeMap read(InputStream is) throws InvalidSRTException, SRTReaderException { 39 | if (is == null) { 40 | throw new SRTReaderException("is does not null"); 41 | } 42 | TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap = new TuziSubTitleInfoTreeMap(); 43 | try { 44 | BufferedReader br = new BufferedReader(new InputStreamReader(is)); 45 | while (true) { 46 | parse(mTuziSubTitleInfoTreeMap, br); 47 | } 48 | } catch (EOFException e) { 49 | // Do nothing 50 | } catch (IOException e) { 51 | throw new SRTReaderException(e); 52 | } 53 | 54 | return mTuziSubTitleInfoTreeMap; 55 | } 56 | 57 | /** 58 | * Reads an SRT file and transforming it into SRT object. 59 | * 60 | * @param srtFile 61 | * SRT file 62 | * @return the SRTInfo object 63 | * @throws InvalidSRTException 64 | * thrown when the SRT file is invalid 65 | * @throws SRTReaderException 66 | * thrown while reading SRT file 67 | */ 68 | public static TuziSubTitleInfoTreeMap read(File srtFile) throws InvalidSRTException, SRTReaderException { 69 | if (!srtFile.exists()) { 70 | throw new SRTReaderException(srtFile.getAbsolutePath() + " does not exist"); 71 | } 72 | if (!srtFile.isFile()) { 73 | throw new SRTReaderException(srtFile.getAbsolutePath() + " is not a regular file"); 74 | } 75 | TuziSubTitleInfoTreeMap mTuziSubTitleInfoTreeMap = new TuziSubTitleInfoTreeMap(); 76 | InputStream in = null; 77 | InputStream inputStream = null; 78 | 79 | BufferedInputStream bin = null; 80 | try { 81 | in = new FileInputStream(srtFile); 82 | inputStream = new FileInputStream(srtFile); 83 | BufferedReader br = null; 84 | try { 85 | String code = codeString(in); 86 | br = new BufferedReader(new InputStreamReader(inputStream, code)); 87 | } catch (Exception e) { 88 | // TODO Auto-generated catch block 89 | e.printStackTrace(); 90 | } 91 | parse(mTuziSubTitleInfoTreeMap, br); 92 | } catch (EOFException e) { 93 | 94 | } catch (IOException e) { 95 | throw new SRTReaderException(e); 96 | } finally { 97 | if (in != null) { 98 | try { 99 | in.close(); 100 | } catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | if (inputStream != null) { 105 | try { 106 | inputStream.close(); 107 | } catch (IOException e) { 108 | e.printStackTrace(); 109 | } 110 | } 111 | 112 | if (bin != null) { 113 | try { 114 | bin.close(); 115 | } catch (IOException e) { 116 | e.printStackTrace(); 117 | } 118 | 119 | } 120 | 121 | } 122 | 123 | return mTuziSubTitleInfoTreeMap; 124 | } 125 | 126 | private static String codeString(InputStream bin) throws Exception { 127 | CharsetDetector charDect = new CharsetDetector(); 128 | String[] probableSet = charDect.detectChineseCharset(bin); 129 | for (String charset : probableSet) { 130 | Log.e("lyric", "p=" + charset); 131 | } 132 | if (probableSet != null && probableSet.length > 0) { 133 | return probableSet[0]; 134 | } 135 | 136 | return ""; 137 | } 138 | 139 | private static void parse(TuziSubTitleInfoTreeMap mTreeMap, BufferedReader br) throws IOException, EOFException { 140 | String srtResult = bufferReader2String(br); 141 | 142 | if (srtResult == null) { 143 | throw new EOFException(); 144 | } 145 | 146 | String dealSrt = dealSrt(srtResult); 147 | 148 | if (dealSrt == null) { 149 | throw new EOFException(); 150 | } 151 | 152 | Pattern pat = Pattern.compile("^\\d{1,}\\s*\\r?\\n", Pattern.MULTILINE); 153 | Matcher mat = pat.matcher(dealSrt); 154 | String parseResult = mat.replaceAll("<1>\n"); 155 | Pattern pattern = Pattern.compile("([^\\n-]*)\\s*-->\\s*([^\\n-]*)[\\r\\n]?([^<]*)", Pattern.MULTILINE); 156 | Matcher m = pattern.matcher(parseResult); 157 | int count = 0; 158 | while (m.find()) { 159 | count = count + 1; 160 | long startTimeLong = 0; 161 | long endTimeLong = 0; 162 | ArrayList subtitleLines = new ArrayList(); 163 | for (int i = 1; i <= m.groupCount(); i++) { 164 | if (i == 1) { 165 | Date startTime = null; 166 | try { 167 | startTime = SRTTimeFormat.parse("1970-01-01 " + m.group(i).replaceAll("\r", "").replaceAll("\n", "").trim().replaceAll(",", ".")); 168 | startTimeLong = startTime.getTime(); 169 | } catch (ParseException e) { 170 | throw new InvalidSRTException(m.group(i) + " has an invalid time format"); 171 | } 172 | 173 | } 174 | if (i == 2) { 175 | Date endTime = null; 176 | try { 177 | endTime = SRTTimeFormat.parse("1970-01-01 " + m.group(i).replaceAll("\r", "").replaceAll("\n", "").trim().replaceAll(",", ".")); 178 | endTimeLong = endTime.getTime(); 179 | } catch (ParseException e) { 180 | throw new InvalidSRTException(m.group(i) + " has an invalid time format"); 181 | } 182 | } 183 | 184 | if (i == 3) { 185 | String[] results = m.group(i).split("[\\r\\n]"); 186 | for (int j = 0; j < results.length; j++) { 187 | if (!"".equals(results[j].replaceAll("\r", "").replaceAll("\n", "").trim()) && (null != results[j].replaceAll("\r", "").replaceAll("\n", "").trim())) { 188 | subtitleLines.add(results[j].replaceAll("\r", "").replaceAll("\n", "").trim()); 189 | } 190 | } 191 | } 192 | } 193 | mTreeMap.insert(count, startTimeLong, endTimeLong, subtitleLines); 194 | } 195 | 196 | // 识别字幕中的,时间,文字等 197 | // String pat = "(\\r\\n)+"; 198 | // Pattern pattern = Pattern.compile(pat); 199 | // String[] models = pattern.split(dealSrt); 200 | // if (models.length > 0) { 201 | // for (int i = 0; i < models.length; i++) { 202 | // String[] zimuItems = models[i].split("[\\r\\n]"); 203 | // if (zimuItems.length >= 2) { 204 | // // 读取字幕中的时间 205 | // String timeString = zimuItems[1]; 206 | // String[] times = timeString.split(SRTTimeFormat.TIME_DELIMITER); 207 | // if (times.length != 2) { 208 | // throw new InvalidSRTException(timeString + 209 | // " needs to be seperated with " + SRTTimeFormat.TIME_DELIMITER); 210 | // } 211 | // Date startTime = null; 212 | // long startTimeLong = 0; 213 | // try { 214 | // startTime = SRTTimeFormat.parse("1970-01-01 " + 215 | // times[0].replaceAll(",", ".")); 216 | // startTimeLong = startTime.getTime(); 217 | // } catch (ParseException e) { 218 | // System.out.println(e); 219 | // throw new InvalidSRTException(times[0] + 220 | // " has an invalid time format"); 221 | // } 222 | // 223 | // Date endTime = null; 224 | // long endTimeLong = 0; 225 | // 226 | // try { 227 | // endTime = SRTTimeFormat.parse("1970-01-01 " + 228 | // times[1].replaceAll(",", ".")); 229 | // endTimeLong = endTime.getTime(); 230 | // 231 | // } catch (ParseException e) { 232 | // throw new InvalidSRTException(times[1] + 233 | // " has an invalid time format"); 234 | // } 235 | // // 读取字幕中的内容 236 | // ArrayList subtitleLines = new ArrayList(); 237 | // 238 | // if (zimuItems.length > 2) { 239 | // for (int j = 2; j < zimuItems.length; j++) 240 | // subtitleLines.add(zimuItems[j]); 241 | // } 242 | // mTreeMap.insert(i + 1, startTimeLong, endTimeLong, subtitleLines); 243 | // 244 | // } 245 | // } 246 | // } 247 | 248 | // String nString = br.readLine(); 249 | // if (nString == null) { 250 | // throw new EOFException(); 251 | // } 252 | // 253 | // int subtitleNumber = -1; 254 | // try { 255 | // subtitleNumber = Integer.parseInt(nString.replaceAll("\\D+", 256 | // "").replaceAll("\r", "").replaceAll("\n", "").trim()); 257 | // } catch (Exception e) { 258 | // throw new InvalidSRTException(nString + 259 | // " has an invalid subtitle number"); 260 | // } 261 | // 262 | // String tString = br.readLine(); 263 | // if (tString == null) { 264 | // throw new 265 | // InvalidSRTException("Start time and end time information is not present"); 266 | // } 267 | // String timeString = tString.replaceAll("\\D+", "").replaceAll("\r", 268 | // "").replaceAll("\n", "").trim(); 269 | // String[] times = timeString.split(SRTTimeFormat.TIME_DELIMITER); 270 | // if (times.length != 2) { 271 | // throw new InvalidSRTException(timeString + 272 | // " needs to be seperated with " + SRTTimeFormat.TIME_DELIMITER); 273 | // } 274 | // Date startTime = null; 275 | // long startTimeLong = 0; 276 | // try { 277 | // startTime = SRTTimeFormat.parse("1970-01-01 " + 278 | // times[0].replaceAll(",", ".")); 279 | // startTimeLong = startTime.getTime(); 280 | // } catch (ParseException e) { 281 | // System.out.println(e); 282 | // 283 | // throw new InvalidSRTException(times[0] + 284 | // " has an invalid time format"); 285 | // } 286 | // 287 | // Date endTime = null; 288 | // long endTimeLong = 0; 289 | // 290 | // try { 291 | // endTime = SRTTimeFormat.parse("1970-01-01 " + 292 | // times[1].replaceAll(",", ".")); 293 | // endTimeLong = endTime.getTime(); 294 | // 295 | // } catch (ParseException e) { 296 | // throw new InvalidSRTException(times[1] + 297 | // " has an invalid time format"); 298 | // } 299 | // 300 | // ArrayList subtitleLines = new ArrayList(); 301 | // String line; 302 | // 303 | // while ((line = br.readLine()) != null) { 304 | // if (line.trim().isEmpty()) { 305 | // break; 306 | // } 307 | // // Spanned spanned=Html.fromHtml(line); 308 | // subtitleLines.add(line.replaceAll("\\D+", "").replaceAll("\r", 309 | // "").replaceAll("\n", "").trim()); 310 | // } 311 | // 312 | // if (subtitleLines.size() == 0) { 313 | // br.readLine(); 314 | // // throw new 315 | // // InvalidSRTException("Missing subtitle text information"); 316 | // } 317 | // 318 | // mTreeMap.insert(subtitleNumber, startTimeLong, endTimeLong, 319 | // subtitleLines); 320 | 321 | } 322 | 323 | private static String bufferReader2String(BufferedReader br) throws IOException { 324 | StringBuffer out = new StringBuffer(); 325 | String line = ""; 326 | while ((line = br.readLine()) != null) { 327 | out.append(line + "\n"); 328 | } 329 | return out.toString(); 330 | } 331 | 332 | /** 333 | * 处理文件中的换行问题 334 | * 335 | * @param content 336 | * @return 337 | */ 338 | private static String dealSrt(String content) { 339 | String p = "(\\s*\\n){2,}"; 340 | Pattern pattern = Pattern.compile(p, Pattern.MULTILINE); 341 | Matcher matcher = pattern.matcher(content); 342 | String result = matcher.replaceAll("\r\n\r\n"); 343 | return result; 344 | } 345 | 346 | } 347 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/srt/SRTReaderException.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser.srt; 2 | 3 | /** 4 | * An exception while reading an SRT file. 5 | * 6 | * @author fredy 7 | */ 8 | public class SRTReaderException extends SRTException { 9 | private static final long serialVersionUID = 1L; 10 | 11 | /** 12 | * @param message the exception message 13 | * @param cause the cause 14 | */ 15 | public SRTReaderException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | /** 20 | * @param message the exception message 21 | */ 22 | public SRTReaderException(String message) { 23 | super(message); 24 | } 25 | 26 | /** 27 | * @param cause the cause 28 | */ 29 | public SRTReaderException(Throwable cause) { 30 | super(cause); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/parser/srt/SRTTimeFormat.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.parser.srt; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.TimeZone; 8 | 9 | /** 10 | * This class contains utility methods for SRT time format related stuff. 11 | * 12 | * @author fredy 13 | */ 14 | public class SRTTimeFormat { 15 | public static final String TIME_DELIMITER = " --> "; 16 | public static final String TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; 17 | public static final String HOUR_FORMAT = "HH"; 18 | public static final String MINUTE_FORMAT = "mm"; 19 | public static final String SECOND_FORMAT = "ss"; 20 | public static final String MILLISECOND_FORMAT = "SSS"; 21 | private static final SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT); 22 | 23 | public enum Type { 24 | HOUR, 25 | MINUTE, 26 | SECOND, 27 | MILLISECOND 28 | } 29 | 30 | public static class SRTTime { 31 | public final int hour; 32 | public final int minute; 33 | public final int second; 34 | public final int millisecond; 35 | 36 | 37 | public SRTTime(int hour, int minute, int second, int millisecond) { 38 | this.hour = hour; 39 | this.minute = minute; 40 | this.second = second; 41 | this.millisecond = millisecond; 42 | } 43 | } 44 | 45 | private SRTTimeFormat() { 46 | } 47 | 48 | /** 49 | * Formats the date into SRT time format. 50 | * @param date the date 51 | * @return the SRT time format 52 | */ 53 | public static String format(Date date) { 54 | return sdf.format(date); 55 | } 56 | 57 | /** 58 | * Parses the SRT time format into date. 59 | * @param srtTime the SRT time format 60 | * @return the date 61 | * @throws ParseException 62 | */ 63 | public static Date parse(String srtTime) throws ParseException { 64 | sdf.setTimeZone(TimeZone.getTimeZone("GMT0")); 65 | Date date= sdf.parse(srtTime); 66 | return date; 67 | } 68 | 69 | /** 70 | * Converts Date to SRTTime. 71 | * 72 | * @param date the date 73 | * @return the SRTTime 74 | */ 75 | public static SRTTime toSRTTime(Date date) { 76 | Calendar cal = Calendar.getInstance(); 77 | cal.setTime(date); 78 | return new SRTTime( 79 | cal.get(Calendar.HOUR), 80 | cal.get(Calendar.MINUTE), 81 | cal.get(Calendar.SECOND), 82 | cal.get(Calendar.MILLISECOND)); 83 | } 84 | 85 | /** 86 | * Converts SRTTime to Date. 87 | * 88 | * @param srtTime the SRTTime 89 | * @return the Date 90 | * @throws ParseException 91 | */ 92 | public static Date fromSRTTime(SRTTime srtTime) throws ParseException { 93 | StringBuilder timeStr = new StringBuilder(); 94 | if (srtTime.hour < 10) { 95 | timeStr.append("0"); 96 | } 97 | timeStr.append(Integer.toString(srtTime.hour)); 98 | timeStr.append(":"); 99 | if (srtTime.minute < 10) { 100 | timeStr.append("0"); 101 | } 102 | timeStr.append(Integer.toString(srtTime.minute)); 103 | timeStr.append(":"); 104 | if (srtTime.second < 10) { 105 | timeStr.append("0"); 106 | } 107 | timeStr.append(Integer.toString(srtTime.second)); 108 | timeStr.append(","); 109 | if (srtTime.second < 10) { 110 | timeStr.append("00"); 111 | } else if (srtTime.second < 100) { 112 | timeStr.append("0"); 113 | } 114 | timeStr.append(Integer.toString(srtTime.millisecond)); 115 | 116 | return SRTTimeFormat.parse(timeStr.toString()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /SubTitleReader/src/com/ver/subtitle/utils/Log.java: -------------------------------------------------------------------------------- 1 | package com.ver.subtitle.utils; 2 | 3 | public class Log { 4 | public static final boolean DEBUG = true; 5 | 6 | public static void v(String msg) { 7 | if (DEBUG) { 8 | if (msg == null) 9 | msg = "null"; 10 | System.out.println(msg); 11 | } 12 | } 13 | 14 | public static void v(String TAG, String msg) { 15 | if (DEBUG) { 16 | if (msg == null) 17 | msg = "null"; 18 | System.out.println(msg); 19 | } 20 | } 21 | 22 | public static void e(String msg) { 23 | if (DEBUG) { 24 | if (msg == null) 25 | msg = "null"; 26 | System.out.println(msg); 27 | } 28 | } 29 | 30 | public static void e(String TAG, String msg) { 31 | if (DEBUG) { 32 | if (msg == null) 33 | msg = "null"; 34 | System.out.println(msg); 35 | } 36 | } 37 | 38 | public static void e(String TAG, String msg, Throwable ex) { 39 | if (DEBUG) { 40 | if (msg == null) 41 | msg = "null"; 42 | System.out.println(msg); 43 | } 44 | } 45 | 46 | public static void i(String msg) { 47 | if (DEBUG) { 48 | if (msg == null) 49 | msg = "null"; 50 | System.out.println(msg); 51 | } 52 | } 53 | 54 | public static void i(String TAG, String msg) { 55 | if (DEBUG) { 56 | if (msg == null) 57 | msg = "null"; 58 | System.out.println(msg); 59 | } 60 | } 61 | 62 | public static void i(String TAG, String msg, Throwable ex) { 63 | if (DEBUG) { 64 | if (msg == null) 65 | msg = "null"; 66 | System.out.println(msg); 67 | } 68 | } 69 | 70 | public static void d(String msg) { 71 | if (DEBUG) { 72 | if (msg == null) 73 | msg = "null"; 74 | System.out.println(msg); 75 | } 76 | } 77 | 78 | public static void d(String TAG, String msg) { 79 | if (DEBUG) { 80 | if (msg == null) 81 | msg = "null"; 82 | System.out.println(msg); 83 | } 84 | } 85 | 86 | public static void d(String TAG, String msg, Throwable ex) { 87 | if (DEBUG) { 88 | if (msg == null) 89 | msg = "null"; 90 | System.out.println(msg); 91 | } 92 | } 93 | 94 | public static void w(String msg) { 95 | if (DEBUG) { 96 | if (msg == null) 97 | msg = "null"; 98 | System.out.println(msg); 99 | } 100 | } 101 | 102 | public static void w(String TAG, String msg) { 103 | if (DEBUG) { 104 | if (msg == null) 105 | msg = "null"; 106 | System.out.println(msg); 107 | } 108 | } 109 | 110 | public static void w(String TAG, Throwable ex) { 111 | if (DEBUG) { 112 | System.out.println(ex.toString()); 113 | } 114 | } 115 | 116 | public static void w(String TAG, String msg, Throwable ex) { 117 | if (DEBUG) { 118 | if (msg == null) 119 | msg = "null"; 120 | System.out.println(msg); 121 | } 122 | } 123 | 124 | public static int VERBOSE = 2; 125 | 126 | public static boolean isLoggable(String str, int VERBOSE) { 127 | return DEBUG; 128 | } 129 | } 130 | --------------------------------------------------------------------------------