- 增加字幕记忆

- 字幕乱码bug修复
pull/52/head
okjackcaptain 4 years ago
parent 2f6f26e336
commit 215dd80884
  1. 1
      app/build.gradle
  2. 39
      app/src/main/java/com/github/tvbox/osc/subtitle/DefaultSubtitleEngine.java
  3. 4
      app/src/main/java/com/github/tvbox/osc/subtitle/SubtitleEngine.java
  4. 10
      app/src/main/java/com/github/tvbox/osc/subtitle/SubtitleLoadSuccessResult.java
  5. 80
      app/src/main/java/com/github/tvbox/osc/subtitle/SubtitleLoader.java
  6. 8
      app/src/main/java/com/github/tvbox/osc/subtitle/widget/SimpleSubtitleView.java
  7. 16
      app/src/main/java/com/github/tvbox/osc/ui/activity/PlayActivity.java
  8. 16
      app/src/main/java/com/github/tvbox/osc/ui/fragment/PlayFragment.java
  9. 3
      app/src/main/java/com/github/tvbox/osc/viewmodel/SourceViewModel.java

@ -99,4 +99,5 @@ dependencies {
implementation 'org.jsoup:jsoup:1.14.1'
implementation 'com.github.hedzr:android-file-chooser:v1.2.0-final'
implementation 'commons-io:commons-io:2.11.0'
implementation 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3'
}

@ -32,12 +32,16 @@ import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.github.tvbox.osc.base.App;
import com.github.tvbox.osc.cache.CacheManager;
import com.github.tvbox.osc.subtitle.cache.SubtitleCache;
import com.github.tvbox.osc.subtitle.model.Subtitle;
import com.github.tvbox.osc.subtitle.model.Time;
import com.github.tvbox.osc.subtitle.model.TimedTextObject;
import com.github.tvbox.osc.util.FileUtils;
import com.github.tvbox.osc.util.MD5;
import com.github.tvbox.osc.util.SubtitleHelper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
@ -67,7 +71,6 @@ public class DefaultSubtitleEngine implements SubtitleEngine {
public DefaultSubtitleEngine() {
mCache = new SubtitleCache();
}
@Override
@ -92,12 +95,12 @@ public class DefaultSubtitleEngine implements SubtitleEngine {
}
SubtitleLoader.loadSubtitle(path, new SubtitleLoader.Callback() {
@Override
public void onSuccess(final TimedTextObject timedTextObject) {
if (timedTextObject == null) {
public void onSuccess(final SubtitleLoadSuccessResult subtitleLoadSuccessResult) {
if (subtitleLoadSuccessResult.timedTextObject == null) {
Log.d(TAG, "onSuccess: timedTextObject is null.");
return;
}
final TreeMap<Integer, Subtitle> captions = timedTextObject.captions;
final TreeMap<Integer, Subtitle> captions = subtitleLoadSuccessResult.timedTextObject.captions;
if (captions == null) {
Log.d(TAG, "onSuccess: captions is null.");
return;
@ -106,6 +109,23 @@ public class DefaultSubtitleEngine implements SubtitleEngine {
setSubtitleDelay(SubtitleHelper.getTimeDelay());
notifyPrepared();
mCache.put(path, new ArrayList<>(captions.values()));
String subtitlePath = subtitleLoadSuccessResult.subtitlePath;
if (subtitlePath.startsWith("http://") || subtitlePath.startsWith("https://")) {
String subtitleFileCacheDir = App.getInstance().getCacheDir().getAbsolutePath() + "/zimu/";
File cacheDir = new File(subtitleFileCacheDir);
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
String subtitleFile = subtitleFileCacheDir + subtitleLoadSuccessResult.fileName;
File cacheSubtitleFile = new File(subtitleFile);
boolean writeResult = FileUtils.writeSimple(subtitleLoadSuccessResult.content.getBytes(), cacheSubtitleFile);
if (writeResult) {
CacheManager.save(MD5.string2MD5(getPlaySubtitleCacheKey()), subtitleFile);
}
} else {
CacheManager.save(MD5.string2MD5(getPlaySubtitleCacheKey()), path);
}
}
@Override
@ -143,6 +163,15 @@ public class DefaultSubtitleEngine implements SubtitleEngine {
mSubtitles = thisSubtitles;
}
private static String playSubtitleCacheKey;
public void setPlaySubtitleCacheKey(String cacheKey) {
playSubtitleCacheKey = cacheKey;
}
public String getPlaySubtitleCacheKey() {
return playSubtitleCacheKey;
}
@Override
public void reset() {
stop();

@ -52,6 +52,10 @@ public interface SubtitleEngine {
*/
void setSubtitleDelay(Integer milliseconds);
void setPlaySubtitleCacheKey(String cacheKey);
String getPlaySubtitleCacheKey();
/**
* 开启字幕刷新任务
*/

@ -0,0 +1,10 @@
package com.github.tvbox.osc.subtitle;
import com.github.tvbox.osc.subtitle.model.TimedTextObject;
public class SubtitleLoadSuccessResult {
public String fileName;
public String content;
public TimedTextObject timedTextObject;
public String subtitlePath;
}

@ -25,7 +25,6 @@
package com.github.tvbox.osc.subtitle;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
@ -35,16 +34,19 @@ import com.github.tvbox.osc.subtitle.format.FormatSRT;
import com.github.tvbox.osc.subtitle.format.FormatSTL;
import com.github.tvbox.osc.subtitle.model.TimedTextObject;
import com.github.tvbox.osc.subtitle.runtime.AppTaskExecutor;
import com.github.tvbox.osc.util.FileUtils;
import com.github.tvbox.osc.util.UnicodeReader;
import com.lzy.okgo.OkGo;
import org.apache.commons.io.input.ReaderInputStream;
import org.mozilla.universalchardet.UniversalDetector;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import okhttp3.Response;
@ -77,12 +79,12 @@ public class SubtitleLoader {
@Override
public void run() {
try {
final TimedTextObject timedTextObject = loadFromRemote(remoteSubtitlePath);
final SubtitleLoadSuccessResult subtitleLoadSuccessResult = loadFromRemote(remoteSubtitlePath);
if (callback != null) {
AppTaskExecutor.mainThread().execute(new Runnable() {
@Override
public void run() {
callback.onSuccess(timedTextObject);
callback.onSuccess(subtitleLoadSuccessResult);
}
});
}
@ -109,12 +111,12 @@ public class SubtitleLoader {
@Override
public void run() {
try {
final TimedTextObject timedTextObject = loadFromLocal(localSubtitlePath);
final SubtitleLoadSuccessResult subtitleLoadSuccessResult = loadFromLocal(localSubtitlePath);
if (callback != null) {
AppTaskExecutor.mainThread().execute(new Runnable() {
@Override
public void run() {
callback.onSuccess(timedTextObject);
callback.onSuccess(subtitleLoadSuccessResult);
}
});
}
@ -135,7 +137,7 @@ public class SubtitleLoader {
});
}
public TimedTextObject loadSubtitle(String path) {
public SubtitleLoadSuccessResult loadSubtitle(String path) {
if (TextUtils.isEmpty(path)) {
return null;
}
@ -152,47 +154,75 @@ public class SubtitleLoader {
return null;
}
private static TimedTextObject loadFromRemote(final String remoteSubtitlePath)
private static SubtitleLoadSuccessResult loadFromRemote(final String remoteSubtitlePath)
throws IOException, FatalParsingException {
Log.d(TAG, "parseRemote: remoteSubtitlePath = " + remoteSubtitlePath);
String referer = "";
String from = "";
if (remoteSubtitlePath.contains("alicloud")) {
referer = "https://www.aliyundrive.com/";
from = "aliyundrive";
} else if (remoteSubtitlePath.contains("assrt.net")) {
referer = "https://secure.assrt.net/";
from = "assrt";
}
String ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36";
Response response = OkGo.<String>get(remoteSubtitlePath)
.headers("Referer", referer)
.headers("User-Agent", ua)
.execute();
String content = response.body().string();
byte[] bytes = response.body().bytes();
try {
Uri uri = Uri.parse(remoteSubtitlePath);
InputStream subtitle = new ByteArrayInputStream(content.getBytes());
UniversalDetector detector = new UniversalDetector(null);
detector.handleData(bytes, 0, bytes.length);
detector.dataEnd();
String encoding = detector.getDetectedCharset();
String content = new String(bytes, encoding);
InputStream is = new ByteArrayInputStream(content.getBytes());
String filename = "";
if (from == "aliyundrive") {
filename = uri.getQueryParameter("response-content-disposition");
filename = "zimu." + filename.substring(filename.lastIndexOf(".")+1);
} else {
filename = uri.getPath();
filename = "zimu." + filename.substring(filename.lastIndexOf(".")+1);
String contentDispostion = response.header("content-disposition", "");
String[] cd = contentDispostion.split(";");
if (cd.length > 1) {
String filenameInfo = cd[1];
filenameInfo = filenameInfo.trim();
if (filenameInfo.startsWith("filename=")) {
filename = filenameInfo.replace("filename=", "");
filename = filename.replace("\"", "");
} else if (filenameInfo.startsWith("filename*=")) {
filename = filenameInfo.substring(filenameInfo.lastIndexOf("''")+2);
}
filename = filename.trim();
}
return loadAndParse(subtitle, filename);
SubtitleLoadSuccessResult subtitleLoadSuccessResult = new SubtitleLoadSuccessResult();
subtitleLoadSuccessResult.timedTextObject = loadAndParse(is, filename);
subtitleLoadSuccessResult.fileName = filename;
subtitleLoadSuccessResult.content = content;
subtitleLoadSuccessResult.subtitlePath = remoteSubtitlePath;
return subtitleLoadSuccessResult;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static TimedTextObject loadFromLocal(final String localSubtitlePath)
private static SubtitleLoadSuccessResult loadFromLocal(final String localSubtitlePath)
throws IOException, FatalParsingException {
Log.d(TAG, "parseLocal: localSubtitlePath = " + localSubtitlePath);
File file = new File(localSubtitlePath);
return loadAndParse(new FileInputStream(file), file.getPath());
if (!file.exists()) {
return null;
}
byte[] bytes = FileUtils.readSimple(file);
UniversalDetector detector = new UniversalDetector(null);
detector.handleData(bytes, 0, bytes.length);
detector.dataEnd();
String encoding = detector.getDetectedCharset();
String content = new String(bytes, encoding);
InputStream is = new ByteArrayInputStream(content.getBytes());
String filePath = file.getPath();
SubtitleLoadSuccessResult subtitleLoadSuccessResult = new SubtitleLoadSuccessResult();
subtitleLoadSuccessResult.timedTextObject = loadAndParse(is, filePath);
String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
subtitleLoadSuccessResult.fileName = fileName;
subtitleLoadSuccessResult.subtitlePath = localSubtitlePath;
return subtitleLoadSuccessResult;
}
private static TimedTextObject loadAndParse(final InputStream is, final String filePath)
@ -201,7 +231,7 @@ public class SubtitleLoader {
String ext = fileName.substring(fileName.lastIndexOf("."));
Log.d(TAG, "parse: name = " + fileName + ", ext = " + ext);
Reader reader = new UnicodeReader(is); //处理有BOM头的utf8
InputStream newInputStream = new ReaderInputStream(reader, "UTF-8");
InputStream newInputStream = new ReaderInputStream(reader, Charset.defaultCharset());
if (".srt".equalsIgnoreCase(ext)) {
return new FormatSRT().parseFile(fileName, newInputStream);
} else if (".ass".equalsIgnoreCase(ext)) {
@ -215,7 +245,7 @@ public class SubtitleLoader {
}
public interface Callback {
void onSuccess(TimedTextObject timedTextObject);
void onSuccess(SubtitleLoadSuccessResult SubtitleLoadSuccessResult);
void onError(Exception exception);
}

@ -99,6 +99,14 @@ public class SimpleSubtitleView extends TextView
mSubtitleEngine.setSubtitleDelay(mseconds);
}
public void setPlaySubtitleCacheKey(String cacheKey) {
mSubtitleEngine.setPlaySubtitleCacheKey(cacheKey);
}
public String getPlaySubtitleCacheKey() {
return mSubtitleEngine.getPlaySubtitleCacheKey();
}
@Override
public void reset() {
mSubtitleEngine.reset();

@ -330,10 +330,17 @@ public class PlayActivity extends BaseActivity {
// 绑定MediaPlayer
mController.mSubtitleView.bindToMediaPlayer(mVideoView.getMediaPlayer());
mController.mSubtitleView.setVisibility(View.INVISIBLE);
mController.mSubtitleView.setPlaySubtitleCacheKey(subtitleCacheKey);
if (playSubtitle != null && playSubtitle .length() > 0) {
// 设置字幕
mController.mSubtitleView.setSubtitlePath(playSubtitle);
mController.mSubtitleView.setVisibility(View.VISIBLE);
} else {
String subtitlePathCache = (String)CacheManager.getCache(MD5.string2MD5(subtitleCacheKey));
if (subtitlePathCache != null && !subtitlePathCache.isEmpty()) {
mController.mSubtitleView.setSubtitlePath(subtitlePathCache);
mController.mSubtitleView.setVisibility(View.VISIBLE);
}
}
//加载字幕结束
}
@ -353,6 +360,7 @@ public class PlayActivity extends BaseActivity {
boolean parse = info.optString("parse", "1").equals("1");
boolean jx = info.optString("jx", "0").equals("1");
playSubtitle = info.optString("subt", /*"https://dash.akamaized.net/akamai/test/caption_test/ElephantsDream/ElephantsDream_en.vtt"*/"");
subtitleCacheKey = info.optString("subtKey", null);
String playUrl = info.optString("playUrl", "");
String flag = info.optString("flag");
String url = info.getString("url");
@ -546,9 +554,8 @@ public class PlayActivity extends BaseActivity {
stopParse();
if(mVideoView!=null) mVideoView.release();
String subtitleCacheKey = mVodInfo.sourceKey + "-" + mVodInfo.id + "-" + mVodInfo.playFlag + "-" + mVodInfo.playIndex + "-subt";
String progressKey = mVodInfo.sourceKey + mVodInfo.id + mVodInfo.playFlag + mVodInfo.playIndex;
//存储播放进度
Object bodyKey=CacheManager.getCache(MD5.string2MD5(progressKey));
//重新播放清除现有进度
if (reset) {CacheManager.delete(MD5.string2MD5(progressKey), 0);}
if (Thunder.play(vs.url, new Thunder.ThunderCallback() {
@ -573,12 +580,11 @@ public class PlayActivity extends BaseActivity {
mController.showParse(false);
return;
}
sourceViewModel.getPlay(sourceKey, mVodInfo.playFlag, progressKey, vs.url);
//执行重新播放后还原之前的进度
// if (reset) CacheManager.save(MD5.string2MD5(progressKey),bodyKey);
sourceViewModel.getPlay(sourceKey, mVodInfo.playFlag, progressKey, vs.url, subtitleCacheKey);
}
private String playSubtitle;
private String subtitleCacheKey;
private String progressKey;
private String parseFlag;
private String webUrl;

@ -328,10 +328,17 @@ public class PlayFragment extends BaseLazyFragment {
// 绑定MediaPlayer
mController.mSubtitleView.bindToMediaPlayer(mVideoView.getMediaPlayer());
mController.mSubtitleView.setVisibility(View.INVISIBLE);
mController.mSubtitleView.setPlaySubtitleCacheKey(subtitleCacheKey);
if (playSubtitle != null && playSubtitle .length() > 0) {
// 设置字幕
mController.mSubtitleView.setSubtitlePath(playSubtitle);
mController.mSubtitleView.setVisibility(View.VISIBLE);
} else {
String subtitlePathCache = (String)CacheManager.getCache(MD5.string2MD5(subtitleCacheKey));
if (subtitlePathCache != null && !subtitlePathCache.isEmpty()) {
mController.mSubtitleView.setSubtitlePath(subtitlePathCache);
mController.mSubtitleView.setVisibility(View.VISIBLE);
}
}
//加载字幕结束
}
@ -351,6 +358,7 @@ public class PlayFragment extends BaseLazyFragment {
boolean parse = info.optString("parse", "1").equals("1");
boolean jx = info.optString("jx", "0").equals("1");
playSubtitle = info.optString("subt", /*"https://dash.akamaized.net/akamai/test/caption_test/ElephantsDream/ElephantsDream_en.vtt"*/"");
subtitleCacheKey = info.optString("subtKey", null);
String playUrl = info.optString("playUrl", "");
String flag = info.optString("flag");
String url = info.getString("url");
@ -558,9 +566,8 @@ public class PlayFragment extends BaseLazyFragment {
stopParse();
if(mVideoView!=null) mVideoView.release();
String subtitleCacheKey = mVodInfo.sourceKey + "-" + mVodInfo.id + "-" + mVodInfo.playFlag + "-" + mVodInfo.playIndex + "-subt";
String progressKey = mVodInfo.sourceKey + mVodInfo.id + mVodInfo.playFlag + mVodInfo.playIndex;
//存储播放进度
Object bodyKey=CacheManager.getCache(MD5.string2MD5(progressKey));
//重新播放清除现有进度
if (reset) CacheManager.delete(MD5.string2MD5(progressKey), 0);
if (Thunder.play(vs.url, new Thunder.ThunderCallback() {
@ -585,12 +592,11 @@ public class PlayFragment extends BaseLazyFragment {
mController.showParse(false);
return;
}
sourceViewModel.getPlay(sourceKey, mVodInfo.playFlag, progressKey, vs.url);
//执行重新播放后还原之前的进度
// if (reset) CacheManager.save(MD5.string2MD5(progressKey),bodyKey);
sourceViewModel.getPlay(sourceKey, mVodInfo.playFlag, progressKey, vs.url, subtitleCacheKey);
}
private String playSubtitle;
private String subtitleCacheKey;
private String progressKey;
private String parseFlag;
private String webUrl;

@ -630,7 +630,7 @@ public class SourceViewModel extends ViewModel {
}
}
// playerContent
public void getPlay(String sourceKey, String playFlag, String progressKey, String url) {
public void getPlay(String sourceKey, String playFlag, String progressKey, String url, String subtitleKey) {
SourceBean sourceBean = ApiConfig.get().getSource(sourceKey);
int type = sourceBean.getType();
if (type == 3) {
@ -643,6 +643,7 @@ public class SourceViewModel extends ViewModel {
JSONObject result = new JSONObject(json);
result.put("key", url);
result.put("proKey", progressKey);
result.put("subtKey", subtitleKey);
if (!result.has("flag"))
result.put("flag", playFlag);
playResult.postValue(result);

Loading…
Cancel
Save