Compare commits

...

12 Commits

Author SHA1 Message Date
jhengazuki f2a3997f5b Optimize 5 months ago
jhengazuki 179f693de8 Clean code 5 months ago
jhengazuki f791ce1d7f Fix lock bug 5 months ago
jhengazuki 4394648760 Fix bug 5 months ago
jhengazuki bb34b20a48 Fix config save bug 5 months ago
jhengazuki 9de4a3edc0 Fix leanback style 5 months ago
jhengazuki e71f3d7e35 Downgrade okhttp 5 months ago
jhengazuki bb2251e32d Remove auto play when resume 5 months ago
jhengazuki 396494d2be Fix live drm parse 5 months ago
jhengazuki f66f86585c Optimize media3 okhttp 5 months ago
jhengazuki 161dfac28a Downgrade okhttp 5 months ago
jhengazuki b51a9576d5 Clean code 5 months ago
  1. 1
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/CastActivity.java
  2. 1
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java
  3. 1
      app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java
  4. 7
      app/src/leanback/res/values-v27/styles.xml
  5. 2
      app/src/leanback/res/values/styles.xml
  6. 6
      app/src/main/java/com/fongmi/android/tv/App.java
  7. 40
      app/src/main/java/com/fongmi/android/tv/api/LiveParser.java
  8. 9
      app/src/main/java/com/fongmi/android/tv/api/config/LiveConfig.java
  9. 11
      app/src/main/java/com/fongmi/android/tv/api/config/VodConfig.java
  10. 5
      app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java
  11. 27
      app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java
  12. 14
      app/src/main/java/com/fongmi/android/tv/player/ParseJob.java
  13. 18
      app/src/main/java/com/fongmi/android/tv/player/danmaku/DanPlayer.java
  14. 2
      app/src/main/java/com/fongmi/android/tv/player/exo/MediaSourceFactory.java
  15. 3
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/LiveActivity.java
  16. 3
      app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java
  17. 2
      build.gradle
  18. 7
      catvod/src/main/java/com/github/catvod/net/OkHttp.java

@ -518,7 +518,6 @@ public class CastActivity extends BaseActivity implements CustomKeyDownVod.Liste
@Override
protected void onResume() {
super.onResume();
if (isRedirect()) onPlay();
setRedirect(false);
}

@ -1020,7 +1020,6 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick
@Override
protected void onResume() {
super.onResume();
if (isRedirect()) onPlay();
setRedirect(false);
}

@ -1402,7 +1402,6 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List
@Override
protected void onResume() {
super.onResume();
if (isRedirect()) onPlay();
setRedirect(false);
}

@ -0,0 +1,7 @@
<resources>
<style name="Theme.App" parent="Theme.Base">
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>

@ -5,6 +5,8 @@
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primaryDark</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowAnimationStyle">@style/NoAnim</item>
<item name="bottomSheetDialogTheme">@style/BottomSheetDialog</item>
</style>

@ -27,8 +27,10 @@ import com.orhanobut.logger.PrettyFormatStrategy;
import org.greenrobot.eventbus.EventBus;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import cat.ereza.customactivityoncrash.config.CaocConfig;
@ -66,6 +68,10 @@ public class App extends Application {
return get().activity;
}
public static <T> Future<T> submit(Callable<T> task) {
return get().executor.submit(task);
}
public static void execute(Runnable runnable) {
get().executor.execute(runnable);
}

@ -12,7 +12,6 @@ import com.fongmi.android.tv.utils.UrlUtil;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Json;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
@ -112,8 +111,8 @@ public class LiveParser {
unknown.setReplace(extract(line, CATCHUP_REPLACE));
channel.setCatchup(Catchup.decide(unknown, catchup));
} else if (!line.startsWith("#") && line.contains("://")) {
String[] parts = line.split("\\|");
if (parts.length > 1) setting.headers(Arrays.copyOfRange(parts, 1, parts.length));
String[] parts = line.split("\\|", 2);
if (parts.length > 1) setting.headers(parts[1]);
channel.getUrls().add(parts[0]);
setting.copy(channel).clear();
}
@ -131,8 +130,8 @@ public class LiveParser {
if (line.contains("#genre#")) live.getGroups().add(Group.create(split[0], live.isPass()));
if (split.length > 1 && live.getGroups().isEmpty()) live.getGroups().add(Group.create());
if (split.length > 1 && split[1].contains("://")) {
String[] parts = split[1].split("\\|");
if (parts.length > 1) setting.headers(Arrays.copyOfRange(parts, 1, parts.length));
String[] parts = split[1].split("\\|", 2);
if (parts.length > 1) setting.headers(parts[1]);
Group group = live.getGroups().get(live.getGroups().size() - 1);
Channel channel = group.find(Channel.create(split[0]));
if (parts.length > 1) channel.getUrls().add(parts[0]);
@ -256,7 +255,7 @@ public class LiveParser {
private void key(String line) {
try {
key = line.split("license_key=")[1].trim();
key = line.contains("license_key=") ? line.split("license_key=")[1].trim() : line;
if (!key.startsWith("http")) convert();
} catch (Exception e) {
key = null;
@ -265,7 +264,7 @@ public class LiveParser {
private void type(String line) {
try {
type = line.split("license_type=")[1].trim();
type = line.contains("license_type=") ? line.split("license_type=")[1].trim() : line;
} catch (Exception e) {
type = null;
}
@ -274,9 +273,8 @@ public class LiveParser {
public void drmLegacy(String line) {
try {
line = line.split("drm_legacy=")[1].trim();
type = line.split("\\|")[0].trim();
key = line.split("\\|")[1].trim();
if (!key.startsWith("http")) convert();
type(line.split("\\|")[0].trim());
key(line.split("\\|")[1].trim());
} catch (Exception e) {
type = null;
key = null;
@ -294,7 +292,9 @@ public class LiveParser {
private void headers(String line) {
try {
headers(line.split("headers=")[1].trim().split("&"));
if (line.contains("headers=")) headers(line.split("headers=")[1].trim().split("&"));
else if (line.contains("|")) for (String text : line.split("\\|")) headers(text);
else headers(line.trim().split("&"));
} catch (Exception ignored) {
}
}
@ -302,23 +302,16 @@ public class LiveParser {
private void headers(String[] params) {
if (header == null) header = new HashMap<>();
for (String param : params) {
param = drmCheck(param);
if (!param.contains("=")) continue;
String[] a = param.split("=", 2);
String k = a[0].trim().replace("\"", "");
String v = a[1].trim().replace("\"", "");
header.put(k, v);
if ("drmScheme".equals(k)) type(v);
else if ("drmLicense".equals(k)) key(v);
else header.put(k, v);
}
}
private String drmCheck(String text) {
type = find(text, "(^|[?&])drmScheme=([^&]*)");
key = find(text, "(^|[?&])drmLicense=([^&]*)");
if (type == null && key == null) return text;
if (key != null && !key.startsWith("http")) convert();
return text.replaceAll("(^|[?&])drmScheme=[^&]*", "").replaceAll("(^|[?&])drmLicense=[^&]*", "").replaceAll("^[?&]+|[?&]+$", "").replaceAll("&{2,}", "&").trim();
}
private void convert() {
try {
ClearKey.objectFrom(key);
@ -327,11 +320,6 @@ public class LiveParser {
}
}
private String find(String text, String regex) {
Matcher m = Pattern.compile(regex).matcher(text);
return m.find() ? m.group(2) : null;
}
private void clear() {
ua = null;
key = null;

@ -177,6 +177,7 @@ public class LiveConfig {
try {
initLive(object);
initOther(object);
config.json(object.toString()).update();
} catch (Throwable e) {
e.printStackTrace();
} finally {
@ -317,10 +318,10 @@ public class LiveConfig {
setHome(home, false);
}
private void setHome(Live home, boolean check) {
this.home = home;
this.home.setActivated(true);
config.home(home.getName()).update();
private void setHome(Live live, boolean check) {
home = live;
home.setActivated(true);
config.home(home.getName()).save();
for (Live item : getLives()) item.setActivated(home);
if (App.activity() != null && App.activity() instanceof LiveActivity) return;
if (check) if (home.isBoot() || Setting.isBootLive()) App.post(this::bootLive);

@ -41,7 +41,6 @@ public class VodConfig {
private List<String> flags;
private List<Parse> parses;
private ExecutorService executor;
private boolean loadLive;
private static class Loader {
@ -193,7 +192,7 @@ public class VodConfig {
private void initLive(JsonObject object) {
Config temp = Config.find(config, 1).save();
boolean sync = LiveConfig.get().needSync(config.getUrl());
if (sync) LiveConfig.get().clear().config(temp).parse(object);
if (sync) LiveConfig.get().clear().config(temp.update()).parse(object);
}
private void initParse(JsonObject object) {
@ -325,9 +324,9 @@ public class VodConfig {
for (Parse item : getParses()) item.setActivated(parse);
}
public void setHome(Site home) {
this.home = home;
this.home.setActivated(true);
public void setHome(Site site) {
home = site;
home.setActivated(true);
config.home(home.getKey()).save();
for (Site item : getSites()) item.setActivated(home);
}
@ -336,6 +335,6 @@ public class VodConfig {
this.wall = wall;
boolean sync = !TextUtils.isEmpty(wall) && WallConfig.get().needSync(wall);
Config temp = Config.find(wall, config.getName(), 2).save();
if (sync) WallConfig.get().config(temp);
if (sync) WallConfig.get().config(temp.update());
}
}

@ -3,6 +3,7 @@ package com.fongmi.android.tv.model;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.Constant;
import com.fongmi.android.tv.R;
import com.fongmi.android.tv.api.EpgParser;
@ -64,7 +65,7 @@ public class LiveViewModel extends ViewModel {
this.url = new MutableLiveData<>();
this.formatTime = new ArrayList<>();
this.futures = new EnumMap<>(TaskType.class);
this.executor = Executors.newFixedThreadPool(4);
this.executor = Executors.newFixedThreadPool(2);
this.formatDate = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
this.formatTime.add(new SimpleDateFormat("yyyy-MM-ddHH:mm", Locale.getDefault()));
this.formatTime.add(new SimpleDateFormat("yyyy-MM-ddHH:mm:ss", Locale.getDefault()));
@ -141,7 +142,7 @@ public class LiveViewModel extends ViewModel {
private <T> void execute(TaskType type, Callable<T> callable) {
Future<?> oldFuture = futures.get(type);
if (oldFuture != null && !oldFuture.isDone()) oldFuture.cancel(true);
final Future<T> newFuture = executor.submit(callable);
final Future<T> newFuture = App.submit(callable);
futures.put(type, newFuture);
executor.execute(() -> {
try {

@ -43,20 +43,21 @@ import okhttp3.Response;
public class SiteViewModel extends ViewModel {
private final ExecutorService executor;
public MutableLiveData<Episode> episode;
public MutableLiveData<Result> result;
public MutableLiveData<Result> player;
public MutableLiveData<Result> search;
public MutableLiveData<Result> action;
private Future<Result> future;
public final MutableLiveData<Episode> episode;
public final MutableLiveData<Result> result;
public final MutableLiveData<Result> player;
public final MutableLiveData<Result> search;
public final MutableLiveData<Result> action;
public SiteViewModel() {
executor = Executors.newFixedThreadPool(2);
episode = new MutableLiveData<>();
result = new MutableLiveData<>();
player = new MutableLiveData<>();
search = new MutableLiveData<>();
action = new MutableLiveData<>();
executor = Executors.newSingleThreadExecutor();
}
public SiteViewModel init() {
@ -267,16 +268,16 @@ public class SiteViewModel extends ViewModel {
private Result fetchPic(Site site, Result result) throws Exception {
if (site.getType() > 2 || result.getList().isEmpty() || !result.getList().get(0).getVodPic().isEmpty()) return result;
ArrayList<String> ids = new ArrayList<>();
if (site.getCategories().isEmpty()) for (Vod item : result.getList()) ids.add(item.getVodId());
else for (Vod item : result.getList()) if (site.getCategories().contains(item.getTypeName())) ids.add(item.getVodId());
boolean empty = site.getCategories().isEmpty();
for (Vod item : result.getList()) if (empty || site.getCategories().contains(item.getTypeName())) ids.add(item.getVodId());
if (ids.isEmpty()) return result.clear();
ArrayMap<String, String> params = new ArrayMap<>();
params.put("ac", site.getType() == 0 ? "videolist" : "detail");
params.put("ids", TextUtils.join(",", ids));
Response response = OkHttp.newCall(site.getApi(), site.getHeaders(), params).execute();
result.setList(Result.fromType(site.getType(), response.body().string()).getList());
response.close();
return result;
try (Response response = OkHttp.newCall(site.getApi(), site.getHeaders(), params).execute()) {
result.setList(Result.fromType(site.getType(), response.body().string()).getList());
return result;
}
}
private void post(Site site, Result result) {
@ -287,7 +288,7 @@ public class SiteViewModel extends ViewModel {
private void execute(MutableLiveData<Result> result, Callable<Result> callable) {
if (future != null && !future.isDone()) future.cancel(true);
future = executor.submit(callable);
future = App.submit(callable);
executor.execute(() -> {
try {
Result taskResult = future.get(Constant.TIMEOUT_VOD, TimeUnit.MILLISECONDS);

@ -113,13 +113,13 @@ public class ParseJob implements ParseCallback {
}
private void jsonParse(Parse item, String webUrl, boolean error) throws Exception {
Response res = OkHttp.newCall(item.getUrl() + webUrl, Headers.of(item.getHeaders())).execute();
JsonObject object = Json.parse(res.body().string()).getAsJsonObject();
String url = Json.safeString(object, "url");
JsonObject data = object.getAsJsonObject("data");
if (url.isEmpty()) url = Json.safeString(data, "url");
checkResult(getHeader(object), url, item.getName(), error);
res.close();
try (Response res = OkHttp.newCall(item.getUrl() + webUrl, Headers.of(item.getHeaders())).execute()) {
JsonObject object = Json.parse(res.body().string()).getAsJsonObject();
String url = Json.safeString(object, "url");
JsonObject data = object.getAsJsonObject("data");
if (url.isEmpty()) url = Json.safeString(data, "url");
checkResult(getHeader(object), url, item.getName(), error);
}
}
private void jsonExtend(String webUrl) throws Throwable {

@ -9,8 +9,6 @@ import com.fongmi.android.tv.utils.ResUtil;
import com.orhanobut.logger.Logger;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import master.flame.danmaku.controller.DrawHandler;
import master.flame.danmaku.danmaku.model.BaseDanmaku;
@ -22,14 +20,12 @@ import master.flame.danmaku.ui.widget.DanmakuView;
public class DanPlayer implements DrawHandler.Callback {
private static final String TAG = DanPlayer.class.getSimpleName();
private final ExecutorService executor;
private final DanmakuContext context;
private DanmakuView view;
private Players player;
public DanPlayer() {
context = DanmakuContext.create();
executor = Executors.newCachedThreadPool();
HashMap<Integer, Integer> maxLines = new HashMap<>();
maxLines.put(BaseDanmaku.TYPE_FIX_TOP, 2);
maxLines.put(BaseDanmaku.TYPE_SCROLL_RL, 2);
@ -53,38 +49,38 @@ public class DanPlayer implements DrawHandler.Callback {
}
public void seekTo(long time) {
executor.execute(() -> {
App.execute(() -> {
if (isDanmakuPrepared()) view.seekTo(time);
if (isDanmakuPrepared()) view.hide();
});
}
public void play() {
executor.execute(() -> {
App.execute(() -> {
if (isDanmakuPrepared()) view.resume();
});
}
public void pause() {
executor.execute(() -> {
App.execute(() -> {
if (isDanmakuPrepared()) view.pause();
});
}
public void stop() {
executor.execute(() -> {
App.execute(() -> {
if (isDanmakuPrepared()) view.stop();
});
}
public void release() {
executor.execute(() -> {
App.execute(() -> {
if (isDanmakuPrepared()) view.release();
});
}
public void setDanmaku(Danmaku item) {
executor.execute(() -> {
App.execute(() -> {
view.release();
if (item.isEmpty()) return;
Logger.t(TAG).d(item.getUrl());
@ -106,7 +102,7 @@ public class DanPlayer implements DrawHandler.Callback {
App.post(() -> {
boolean playing = player.isPlaying();
long position = player.getPosition();
executor.execute(() -> {
App.execute(() -> {
if (!isDanmakuPrepared()) return;
if (playing) view.start(position);
else view.pause();

@ -98,7 +98,7 @@ public class MediaSourceFactory implements MediaSource.Factory {
}
private HttpDataSource.Factory getHttpDataSourceFactory() {
if (httpDataSourceFactory == null) httpDataSourceFactory = new OkHttpDataSource.Factory(OkHttp.client());
if (httpDataSourceFactory == null) httpDataSourceFactory = new OkHttpDataSource.Factory(OkHttp.player());
return httpDataSourceFactory;
}
}

@ -439,7 +439,7 @@ public class LiveActivity extends BaseActivity implements CustomKeyDown.Listener
private int getLockOrient() {
if (isLock()) {
return ResUtil.isLand(this) ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
return ActivityInfo.SCREEN_ORIENTATION_LOCKED;
} else if (isRotate()) {
return ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
} else {
@ -1125,7 +1125,6 @@ public class LiveActivity extends BaseActivity implements CustomKeyDown.Listener
@Override
protected void onResume() {
super.onResume();
if (isRedirect()) onPlay();
setRedirect(false);
}

@ -919,7 +919,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
private int getLockOrient() {
if (isLock()) {
return ResUtil.isLand(this) ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
return ActivityInfo.SCREEN_ORIENTATION_LOCKED;
} else if (isRotate()) {
return ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
} else if (isPort() && isAutoRotate()) {
@ -1648,7 +1648,6 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo
@Override
protected void onResume() {
super.onResume();
if (isRedirect()) onPlay();
setRedirect(false);
}

@ -12,5 +12,5 @@ project.ext {
gsonVersion = '2.13.2'
glideVersion = '5.0.5'
media3Version = '1.8.0'
okhttpVersion = '5.2.1'
okhttpVersion = '4.12.0'
}

@ -40,6 +40,7 @@ public class OkHttp {
private OkAuthenticator authenticator;
private OkProxySelector selector;
private OkHttpClient client;
private OkHttpClient player;
private OkDns dns;
private static class Loader {
@ -63,6 +64,7 @@ public class OkHttp {
public void setDoh(Doh doh) {
dns().setDoh(doh.getUrl().isEmpty() ? null : new DnsOverHttps.Builder().client(new OkHttpClient()).url(HttpUrl.get(doh.getUrl())).bootstrapDnsHosts(doh.getHosts()).build());
client = null;
player = null;
}
public static OkDns dns() {
@ -100,6 +102,11 @@ public class OkHttp {
return get().client = getBuilder().build();
}
public static OkHttpClient player() {
if (get().player != null) return get().player;
return get().player = getBuilder().build();
}
public static OkHttpClient client(long timeout) {
return client().newBuilder().connectTimeout(timeout, TimeUnit.MILLISECONDS).readTimeout(timeout, TimeUnit.MILLISECONDS).writeTimeout(timeout, TimeUnit.MILLISECONDS).build();
}

Loading…
Cancel
Save