Support refresh danmaku

pull/178/head
FongMi 2 years ago
parent 6dfe2d44a4
commit 3187f65c21
  1. 18
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java
  2. 2
      app/src/leanback/java/com/fongmi/android/tv/ui/dialog/ConfigDialog.java
  3. 2
      app/src/leanback/java/com/fongmi/android/tv/ui/dialog/ProxyDialog.java
  4. 2
      app/src/leanback/java/com/fongmi/android/tv/ui/dialog/UaDialog.java
  5. 17
      app/src/main/assets/index.html
  6. 12
      app/src/main/assets/js/script.js
  7. 19
      app/src/main/java/com/fongmi/android/tv/bean/Sub.java
  8. 20
      app/src/main/java/com/fongmi/android/tv/event/RefreshEvent.java
  9. 6
      app/src/main/java/com/fongmi/android/tv/event/ServerEvent.java
  10. 2
      app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java
  11. 1
      app/src/main/java/com/fongmi/android/tv/player/Players.java
  12. 12
      app/src/main/java/com/fongmi/android/tv/player/danmu/Parser.java
  13. 53
      app/src/main/java/com/fongmi/android/tv/server/process/Action.java
  14. 2
      app/src/main/java/com/fongmi/android/tv/utils/Sniffer.java
  15. 12
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java

@ -56,7 +56,6 @@ import com.fongmi.android.tv.event.ActionEvent;
import com.fongmi.android.tv.event.ErrorEvent;
import com.fongmi.android.tv.event.PlayerEvent;
import com.fongmi.android.tv.event.RefreshEvent;
import com.fongmi.android.tv.event.ServerEvent;
import com.fongmi.android.tv.impl.Callback;
import com.fongmi.android.tv.impl.SubtitleCallback;
import com.fongmi.android.tv.model.SiteViewModel;
@ -87,7 +86,6 @@ import com.fongmi.android.tv.utils.Traffic;
import com.fongmi.android.tv.utils.Util;
import com.github.bassaer.library.MDColor;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Path;
import com.github.catvod.utils.Trans;
import com.permissionx.guolindev.PermissionX;
@ -489,11 +487,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
private void checkDanmu(String danmu) {
mBinding.danmaku.release();
mBinding.danmaku.setVisibility(danmu.isEmpty() ? View.GONE : View.VISIBLE);
App.execute(() -> {
String temp = danmu;
if (temp.startsWith("http")) temp = OkHttp.string(temp);
if (temp.length() > 0) mBinding.danmaku.prepare(new Parser(temp), mDanmakuContext);
});
if (danmu.length() > 0) App.execute(() -> mBinding.danmaku.prepare(new Parser(danmu), mDanmakuContext));
}
private void setEmpty(boolean finish) {
@ -1129,16 +1123,12 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onServerEvent(ServerEvent event) {
if (event.getType() != ServerEvent.Type.API) return;
if (mPlayers.isExo()) mPlayers.setSub(Sub.from(Path.local(event.getText())));
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRefreshEvent(RefreshEvent event) {
if (event.getType() == RefreshEvent.Type.DETAIL) getDetail();
if (event.getType() == RefreshEvent.Type.PLAYER) onRefresh();
else if (event.getType() == RefreshEvent.Type.PLAYER) onRefresh();
else if (event.getType() == RefreshEvent.Type.DANMAKU) checkDanmu(event.getPath());
else if (event.getType() == RefreshEvent.Type.SUBTITLE) mPlayers.setSub(Sub.from(event.getPath()));
}
@Subscribe(threadMode = ThreadMode.MAIN)

@ -151,7 +151,7 @@ public class ConfigDialog implements DialogInterface.OnDismissListener {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onServerEvent(ServerEvent event) {
if (event.getType() != ServerEvent.Type.API) return;
if (event.getType() != ServerEvent.Type.SETTING) return;
binding.text.setText(event.getText());
binding.text.setSelection(binding.text.getText().length());
}

@ -111,7 +111,7 @@ public class ProxyDialog implements DialogInterface.OnDismissListener {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onServerEvent(ServerEvent event) {
if (event.getType() != ServerEvent.Type.API) return;
if (event.getType() != ServerEvent.Type.SETTING) return;
binding.text.setText(event.getText());
binding.positive.performClick();
}

@ -109,7 +109,7 @@ public class UaDialog implements DialogInterface.OnDismissListener {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onServerEvent(ServerEvent event) {
if (event.getType() != ServerEvent.Type.API) return;
if (event.getType() != ServerEvent.Type.SETTING) return;
binding.text.setText(event.getText());
binding.positive.performClick();
}

@ -46,7 +46,7 @@
<div class="weui-cells">
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap">
<div class="weui-cell__bd weui-flex">
<input id="push_url" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入網址..." />
<input id="push_url" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入播放網址..." />
<button onclick="push(); return false;" class="weui-cell__control weui-btn weui-btn_default weui-vcode-btn">確定</button>
</div>
</div>
@ -58,15 +58,15 @@
<div id="panel3" role="tabpanel" aria-labelledby="tab3" class="weui-tab__panel" style="display: none;">
<div class="weui-form">
<div class="weui-form__text-area">
<h2 class="weui-form__title">接口</h2>
<h2 class="weui-form__title">接口/Proxy/User-Agent</h2>
</div>
<div class="weui-form__control-area" style="margin-bottom: 0px !important;">
<div class="weui-cells__group weui-cells__group_form">
<div class="weui-cells">
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap">
<div class="weui-cell__bd weui-flex">
<input id="api_url" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入接口..." />
<button onclick="api(); return false;" class="weui-cell__control weui-btn weui-btn_default weui-vcode-btn">確定</button>
<input id="setting_text" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入..." />
<button onclick="setting(); return false;" class="weui-cell__control weui-btn weui-btn_default weui-vcode-btn">確定</button>
</div>
</div>
</div>
@ -102,7 +102,7 @@
</div>
<div id="tab3" role="tab" aria-labelledby="t3_title" aria-selected="false" aria-controls="panel3" class="weui-tabbar__item">
<img style="width: 20px; height: 20px; margin: 5px 0 5px 0;" alt="" src="images/ic_setting.svg" class="weui-tabbar__icon">
<p id="t3_title" aria-hidden="true" class="weui-tabbar__label">接口</p>
<p id="t3_title" aria-hidden="true" class="weui-tabbar__label">設定</p>
</div>
<div id="tab4" role="tab" aria-labelledby="t4_title" aria-selected="false" aria-controls="panel4" class="weui-tabbar__item">
<img style="width: 20px; height: 20px; margin: 5px 0 5px 0;" alt="" src="images/ic_local.svg" class="weui-tabbar__icon">
@ -116,15 +116,12 @@
<div role="button" class="weui-mask"></div>
<div class="weui-half-screen-dialog">
<div class="weui-half-screen-dialog__hd">
<div class="weui-half-screen-dialog__hd__main">接口設定</div>
<div class="weui-half-screen-dialog__hd__main">本地網址</div>
</div>
<div class="weui-half-screen-dialog__bd" id="fileInfo">
<div class="weui-form__control-area" style="margin-bottom: 0px !important;">
<div class="weui-cells__group weui-cells__group_form">
<div class="weui-cells">
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap">
<div class="weui-cell__hd"><label class="weui-label">本機地址</label></div>
</div>
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap">
<div class="weui-cell__bd weui-flex">
<input id="fileUrl" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" readonly />
@ -136,8 +133,8 @@
</div>
<div class="weui-half-screen-dialog__ft" style="padding-bottom: 30px;">
<div class="weui-half-screen-dialog__btn-area">
<a href="javascript:void(0)" class="weui-btn weui-btn_default" onclick="pushFile()">使用</a>
<a href="javascript:void(0)" class="weui-btn weui-btn_default" onclick="hideFileInfo()">關閉</a>
<a href="javascript:void(0)" class="weui-btn weui-btn_default" onclick="fileToApi()">使用</a>
<a href="javascript:void(0)" id="delFileBtn" class="weui-btn weui-btn_warn" onclick="delFile()">刪除</a>
</div>
</div>

@ -12,8 +12,12 @@ function push() {
doAction('push', { url: $('#push_url').val() });
}
function api() {
doAction('api', { url: $('#api_url').val() });
function setting() {
doAction('setting', { url: $('#setting_text').val() });
}
function file(path) {
doAction('file', { path: path });
}
function doAction(action, kv) {
@ -73,8 +77,8 @@ function selectFile(path, canDel) {
$("#fileInfoDialog").show();
}
function fileToApi() {
doAction('api', { url: "file:/" + current_file });
function pushFile() {
file("file:/" + current_file);
hideFileInfo();
}

@ -7,6 +7,7 @@ import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import com.fongmi.android.tv.player.ExoUtil;
import com.github.catvod.utils.Path;
import com.github.catvod.utils.Trans;
import com.google.gson.annotations.SerializedName;
@ -26,10 +27,24 @@ public class Sub {
private int flag;
public static Sub from(String path) {
return from(new File(path));
if (path.startsWith("http")) {
return http(path);
} else {
return file(Path.local(path));
}
}
public static Sub from(File file) {
private static Sub http(String url) {
Uri uri = Uri.parse(url);
Sub sub = new Sub();
sub.url = url;
sub.name = uri.getLastPathSegment();
sub.flag = C.SELECTION_FLAG_FORCED;
sub.format = ExoUtil.getMimeType(uri.getLastPathSegment());
return sub;
}
private static Sub file(File file) {
Sub sub = new Sub();
sub.name = file.getName();
sub.url = file.getAbsolutePath();

@ -5,6 +5,7 @@ import org.greenrobot.eventbus.EventBus;
public class RefreshEvent {
private final Type type;
private String path;
public static void empty() {
EventBus.getDefault().post(new RefreshEvent(Type.EMPTY));
@ -46,15 +47,32 @@ public class RefreshEvent {
EventBus.getDefault().post(new RefreshEvent(Type.PLAYER));
}
public static void subtitle(String path) {
EventBus.getDefault().post(new RefreshEvent(Type.SUBTITLE, path));
}
public static void danmaku(String path) {
EventBus.getDefault().post(new RefreshEvent(Type.DANMAKU, path));
}
private RefreshEvent(Type type) {
this.type = type;
}
public RefreshEvent(Type type, String path) {
this.type = type;
this.path = path;
}
public Type getType() {
return type;
}
public String getPath() {
return path;
}
public enum Type {
EMPTY, CONFIG, IMAGE, VIDEO, HISTORY, KEEP, SIZE, WALL, DETAIL, PLAYER
EMPTY, CONFIG, IMAGE, VIDEO, HISTORY, KEEP, SIZE, WALL, DETAIL, PLAYER, SUBTITLE, DANMAKU
}
}

@ -15,8 +15,8 @@ public class ServerEvent {
EventBus.getDefault().post(new ServerEvent(Type.PUSH, text));
}
public static void api(String text) {
EventBus.getDefault().post(new ServerEvent(Type.API, text));
public static void setting(String text) {
EventBus.getDefault().post(new ServerEvent(Type.SETTING, text));
}
private ServerEvent(Type type, String text) {
@ -33,6 +33,6 @@ public class ServerEvent {
}
public enum Type {
SEARCH, PUSH, API
SEARCH, PUSH, SETTING
}
}

@ -3,6 +3,7 @@ package com.fongmi.android.tv.player;
import android.content.Context;
import android.graphics.Color;
import android.net.Uri;
import android.text.TextUtils;
import android.view.accessibility.CaptioningManager;
import androidx.media3.common.MediaItem;
@ -108,6 +109,7 @@ public class ExoUtil {
}
public static String getMimeType(String path) {
if (TextUtils.isEmpty(path)) return "";
if (path.endsWith(".vtt")) return MimeTypes.TEXT_VTT;
if (path.endsWith(".ssa") || path.endsWith(".ass")) return MimeTypes.TEXT_SSA;
if (path.endsWith(".ttml") || path.endsWith(".xml") || path.endsWith(".dfxp")) return MimeTypes.APPLICATION_TTML;

@ -145,6 +145,7 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
public void setSub(Sub sub) {
this.sub = sub;
if (isIjk()) return;
setMediaSource(headers, url);
}

@ -4,6 +4,8 @@ import android.graphics.Color;
import android.text.TextUtils;
import com.fongmi.android.tv.bean.Danmu;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Path;
import org.json.JSONArray;
import org.json.JSONException;
@ -27,8 +29,14 @@ public class Parser extends BaseDanmakuParser {
private float scaleY;
private int index;
public Parser(String xml) {
this.danmu = Danmu.fromXml(xml);
public Parser(String path) {
this.danmu = Danmu.fromXml(getContent(path));
}
private String getContent(String path) {
if (path.startsWith("file")) return Path.read(path);
if (path.startsWith("http")) return OkHttp.string(path);
return path;
}
@Override

@ -43,8 +43,11 @@ public class Action implements Process {
case "push":
onPush(params);
break;
case "api":
onApi(params);
case "setting":
onSetting(params);
break;
case "file":
onFile(params);
break;
case "refresh":
onRefresh(params);
@ -60,27 +63,49 @@ public class Action implements Process {
}
private void onSearch(Map<String, String> params) {
String word = Objects.requireNonNullElse(params.get("word"), "");
if (word.length() > 0) ServerEvent.search(word);
String word = params.get("word");
if (TextUtils.isEmpty(word)) return;
ServerEvent.search(word);
}
private void onPush(Map<String, String> params) {
String url = Objects.requireNonNullElse(params.get("url"), "");
if (url.length() > 0) ServerEvent.push(url);
String url = params.get("url");
if (TextUtils.isEmpty(url)) return;
ServerEvent.push(url);
}
private void onSetting(Map<String, String> params) {
String text = params.get("text");
if (TextUtils.isEmpty(text)) return;
ServerEvent.setting(text);
}
private void onApi(Map<String, String> params) {
String url = Objects.requireNonNullElse(params.get("url"), "");
if (url.endsWith(".apk")) FileUtil.openFile(Path.local(url));
else if (url.length() > 0) ServerEvent.api(url);
private void onFile(Map<String, String> params) {
String path = params.get("path");
if (TextUtils.isEmpty(path)) return;
if (path.endsWith(".xml")) RefreshEvent.danmaku(path);
else if (path.endsWith(".apk")) FileUtil.openFile(Path.local(path));
else if (path.endsWith(".srt") || path.endsWith(".ssa") || path.endsWith(".ass")) RefreshEvent.subtitle(path);
else ServerEvent.setting(path);
}
private void onRefresh(Map<String, String> params) {
String type = params.get("type");
if ("detail".equals(type)) {
RefreshEvent.detail();
} else if ("player".equals(type)) {
RefreshEvent.player();
String path = params.get("path");
if (TextUtils.isEmpty(type)) return;
switch (type) {
case "detail":
RefreshEvent.detail();
break;
case "player":
RefreshEvent.player();
break;
case "subtitle":
RefreshEvent.subtitle(path);
break;
case "danmaku":
RefreshEvent.danmaku(path);
break;
}
}

@ -22,7 +22,7 @@ public class Sniffer {
private static final String TAG = Sniffer.class.getSimpleName();
public static final Pattern CLICKER = Pattern.compile("\\[a=cr:(\\{.*?\\})\\/](.*?)\\[\\/a]");
public static final String RULE = "http((?!http).){12,}?\\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)\\?.*|http((?!http).){12,}\\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)|http((?!http).)*?video/tos*";
public static final String RULE = "http((?!http).){12,}?\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)\\?.*|http((?!http).){12,}\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)|http((?!http).)*?video/tos*";
public static final List<String> THUNDER = Arrays.asList("thunder", "magnet", "ed2k");
public static String getUrl(String text) {

@ -51,6 +51,7 @@ import com.fongmi.android.tv.bean.Keep;
import com.fongmi.android.tv.bean.Parse;
import com.fongmi.android.tv.bean.Result;
import com.fongmi.android.tv.bean.Site;
import com.fongmi.android.tv.bean.Sub;
import com.fongmi.android.tv.bean.Track;
import com.fongmi.android.tv.bean.Vod;
import com.fongmi.android.tv.cast.CastVideo;
@ -94,7 +95,6 @@ import com.fongmi.android.tv.utils.Sniffer;
import com.fongmi.android.tv.utils.Traffic;
import com.fongmi.android.tv.utils.Util;
import com.github.bassaer.library.MDColor;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Trans;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.permissionx.guolindev.PermissionX;
@ -584,11 +584,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
private void checkDanmu(String danmu) {
mBinding.danmaku.release();
mBinding.danmaku.setVisibility(danmu.isEmpty() ? View.GONE : View.VISIBLE);
App.execute(() -> {
String temp = danmu;
if (temp.startsWith("http")) temp = OkHttp.string(temp);
if (temp.length() > 0) mBinding.danmaku.prepare(new Parser(temp), mDanmakuContext);
});
if (danmu.length() > 0) App.execute(() -> mBinding.danmaku.prepare(new Parser(danmu), mDanmakuContext));
}
@Override
@ -1165,7 +1161,9 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
@Subscribe(threadMode = ThreadMode.MAIN)
public void onRefreshEvent(RefreshEvent event) {
if (event.getType() == RefreshEvent.Type.DETAIL) getDetail();
if (event.getType() == RefreshEvent.Type.PLAYER) onRefresh();
else if (event.getType() == RefreshEvent.Type.PLAYER) onRefresh();
else if (event.getType() == RefreshEvent.Type.DANMAKU) checkDanmu(event.getPath());
else if (event.getType() == RefreshEvent.Type.SUBTITLE) mPlayers.setSub(Sub.from(event.getPath()));
}
@Subscribe(threadMode = ThreadMode.MAIN)

Loading…
Cancel
Save