diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Result.java b/app/src/main/java/com/fongmi/android/tv/bean/Result.java index c6063429d..1b966c4fa 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Result.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Result.java @@ -80,6 +80,8 @@ public class Result implements Parcelable { private Integer code; @SerializedName("jx") private Integer jx; + @SerializedName("drm") + private Drm drm; public static Result objectFrom(String str) { try { @@ -273,6 +275,10 @@ public class Result implements Parcelable { return jx == null ? 0 : jx; } + public Drm getDrm() { + return drm; + } + public boolean hasMsg() { return getMsg().length() > 0; } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Sub.java b/app/src/main/java/com/fongmi/android/tv/bean/Sub.java index 949fb29d1..bc296e4ae 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Sub.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Sub.java @@ -3,6 +3,7 @@ package com.fongmi.android.tv.bean; import android.net.Uri; import android.text.TextUtils; +import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.MediaItem; @@ -78,7 +79,15 @@ public class Sub { this.name = Trans.s2t(name); } - public MediaItem.SubtitleConfiguration getExo() { + public MediaItem.SubtitleConfiguration getConfig() { return new MediaItem.SubtitleConfiguration.Builder(Uri.parse(getUrl())).setLabel(getName()).setMimeType(getFormat()).setSelectionFlags(getFlag()).setLanguage(getLang()).build(); } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) return true; + if (!(obj instanceof Sub)) return false; + Sub it = (Sub) obj; + return getUrl().equals(it.getUrl()); + } } diff --git a/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java b/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java index eed8d1ca4..cbab11a1f 100644 --- a/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java +++ b/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java @@ -8,6 +8,10 @@ public class PlayerEvent { private final int state; + public static void prepare() { + EventBus.getDefault().post(new PlayerEvent(0)); + } + public static void ready() { EventBus.getDefault().post(new PlayerEvent(Player.STATE_READY)); } diff --git a/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java b/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java index efc887dba..8803364d0 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java +++ b/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java @@ -39,9 +39,7 @@ import androidx.media3.ui.CaptionStyleCompat; import com.fongmi.android.tv.App; import com.fongmi.android.tv.Setting; -import com.fongmi.android.tv.bean.Channel; import com.fongmi.android.tv.bean.Drm; -import com.fongmi.android.tv.bean.Result; import com.fongmi.android.tv.bean.Sub; import com.fongmi.android.tv.player.custom.NextRenderersFactory; import com.fongmi.android.tv.utils.Sniffer; @@ -106,7 +104,7 @@ public class ExoUtil { return MimeTypes.APPLICATION_SUBRIP; } - private static String getMimeType(String format, int errorCode) { + public static String getMimeType(String format, int errorCode) { if (format != null) return format; if (errorCode == PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED || errorCode == PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED) return MimeTypes.APPLICATION_OCTET; if (errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED || errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED) return MimeTypes.APPLICATION_M3U8; @@ -117,40 +115,31 @@ public class ExoUtil { return errorCode >= PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED && errorCode <= PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED ? 2 : errorCode > 999 ? 1 : 0; } - public static MediaSource getSource(Result result, Sub sub, int decode, int errorCode) { - return getSource(result.getHeaders(), result.getRealUrl(), result.getFormat(), result.getSubs(), sub, null, decode, errorCode); + public static MediaSource getSource(Map headers, String url, String mimeType, Drm drm, List subs, int decode) { + if (url.contains("***") && url.contains("|||")) return getConcat(headers, url, mimeType, drm, subs, decode); + return getMediaSource(headers, url, mimeType, drm, subs, decode); } - public static MediaSource getSource(Channel channel, Sub sub, int decode, int errorCode) { - return getSource(channel.getHeaders(), channel.getUrl(), channel.getFormat(), new ArrayList<>(), sub, channel.getDrm(), decode, errorCode); - } - - public static MediaSource getSource(Map headers, String url, Sub sub, int decode, int errorCode) { - return getSource(headers, url, null, new ArrayList<>(), sub, null, decode, errorCode); - } - - private static MediaSource getSource(Map headers, String url, String format, List subs, Sub sub, Drm drm, int decode, int errorCode) { - Uri uri = UrlUtil.uri(url); - if (sub != null) subs.add(sub); - String mimeType = getMimeType(format, errorCode); - if (url.contains("***") && url.contains("|||")) return getConcat(headers, url, format, subs, drm, decode, errorCode); - return new DefaultMediaSourceFactory(getDataSourceFactory(headers), getExtractorsFactory()).createMediaSource(getMediaItem(uri, mimeType, subs, drm, decode)); - } - - private static MediaSource getConcat(Map headers, String url, String format, List subs, Drm drm, int decode, int errorCode) { + private static MediaSource getConcat(Map headers, String url, String mimeType, Drm drm, List subs, int decode) { ConcatenatingMediaSource2.Builder builder = new ConcatenatingMediaSource2.Builder(); for (String split : url.split("\\*\\*\\*")) { String[] info = split.split("\\|\\|\\|"); if (info.length < 2) continue; long duration = Long.parseLong(info[1]); - builder.add(getSource(headers, info[0], format, subs, null, drm, decode, errorCode), duration); + builder.add(getMediaSource(headers, info[0], mimeType, drm, subs, decode), duration); } return builder.build(); } - private static MediaItem getMediaItem(Uri uri, String mimeType, List subs, Drm drm, int decode) { + private static MediaSource getMediaSource(Map headers, String url, String mimeType, Drm drm, List subs, int decode) { + return new DefaultMediaSourceFactory(getDataSourceFactory(headers), getExtractorsFactory()).createMediaSource(getMediaItem(UrlUtil.uri(url), mimeType, drm, subs, decode)); + } + + private static MediaItem getMediaItem(Uri uri, String mimeType, Drm drm, List subs, int decode) { + List subtitleConfigurations = new ArrayList<>(); + for (Sub sub : subs) subtitleConfigurations.add(sub.getConfig()); MediaItem.Builder builder = new MediaItem.Builder().setUri(uri); - if (!subs.isEmpty()) builder.setSubtitleConfigurations(getSubtitles(subs)); + builder.setSubtitleConfigurations(subtitleConfigurations); if (drm != null) builder.setDrmConfiguration(drm.get()); if (mimeType != null) builder.setMimeType(mimeType); builder.setAllowChunklessPreparation(decode == 1); @@ -159,12 +148,6 @@ public class ExoUtil { return builder.build(); } - private static List getSubtitles(List subs) { - List items = new ArrayList<>(); - for (Sub sub : subs) items.add(sub.getExo()); - return items; - } - private static void selectTrack(ExoPlayer player, int group, int track, List trackIndices) { if (group >= player.getCurrentTracks().getGroups().size()) return; Tracks.Group trackGroup = player.getCurrentTracks().getGroups().get(group); @@ -198,7 +181,7 @@ public class ExoUtil { private static synchronized DataSource.Factory getDataSourceFactory(Map headers) { if (dataSourceFactory == null) dataSourceFactory = buildReadOnlyCacheDataSource(new DefaultDataSource.Factory(App.get(), getHttpDataSourceFactory()), getCache()); - httpDataSourceFactory.setDefaultRequestProperties(Players.checkUa(headers)); + httpDataSourceFactory.setDefaultRequestProperties(headers); return dataSourceFactory; } diff --git a/app/src/main/java/com/fongmi/android/tv/player/Players.java b/app/src/main/java/com/fongmi/android/tv/player/Players.java index 7fe35484d..649abcdc3 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/Players.java +++ b/app/src/main/java/com/fongmi/android/tv/player/Players.java @@ -27,6 +27,7 @@ import com.fongmi.android.tv.Constant; import com.fongmi.android.tv.R; import com.fongmi.android.tv.Setting; import com.fongmi.android.tv.bean.Channel; +import com.fongmi.android.tv.bean.Drm; import com.fongmi.android.tv.bean.Result; import com.fongmi.android.tv.bean.Sub; import com.fongmi.android.tv.bean.Track; @@ -66,6 +67,8 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac private ParseJob parseJob; private Runnable runnable; private ExoPlayer player; + private List subs; + private String format; private String url; private Sub sub; @@ -75,6 +78,7 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac private int retry; public Players init(Activity activity) { + subs = new ArrayList<>(); decode = Setting.getDecode(); builder = new StringBuilder(); runnable = ErrorEvent::timeout; @@ -109,7 +113,7 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac public void setSub(Sub sub) { this.sub = sub; - setMediaSource(headers, url); + setMediaSource(headers, url, format); } public ExoPlayer get() { @@ -117,7 +121,7 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac } public Map getHeaders() { - return headers == null ? new HashMap<>() : checkUa(headers); + return headers == null ? new HashMap<>() : headers; } public String getUrl() { @@ -141,6 +145,8 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac public void clear() { headers = null; + format = null; + subs = null; url = null; } @@ -352,32 +358,45 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac parseJob = null; } + private Map checkUa(Map headers) { + if (Setting.getUa().isEmpty()) return headers; + for (Map.Entry header : headers.entrySet()) if (HttpHeaders.USER_AGENT.equalsIgnoreCase(header.getKey())) return headers; + headers.put(HttpHeaders.USER_AGENT, Setting.getUa()); + return headers; + } + + private List checkSub(List subs) { + if (sub == null || subs.contains(sub)) return subs; + subs.add(0, sub); + return subs; + } + public void setMediaSource(String url) { setMediaSource(new HashMap<>(), url); } - private void setMediaSource(Result result, int timeout) { - if (player != null) player.setMediaSource(ExoUtil.getSource(result, sub, decode, error), position); - setTimeoutCheck(result.getHeaders(), result.getRealUrl(), timeout); + private void setMediaSource(Map headers, String url) { + setMediaSource(checkUa(headers), url, null, null, checkSub(subs), Constant.TIMEOUT_PLAY); + } + + private void setMediaSource(Map headers, String url, String format) { + setMediaSource(checkUa(headers), url, format, null, checkSub(subs), Constant.TIMEOUT_PLAY); } private void setMediaSource(Channel channel, int timeout) { - if (player != null) player.setMediaSource(ExoUtil.getSource(channel, sub, decode, error)); - setTimeoutCheck(channel.getHeaders(), channel.getUrl(), timeout); + setMediaSource(checkUa(channel.getHeaders()), channel.getUrl(), channel.getFormat(), channel.getDrm(), checkSub(subs), timeout); } - private void setMediaSource(Map headers, String url) { - if (player != null) player.setMediaSource(ExoUtil.getSource(headers, url, sub, decode, error), position); - setTimeoutCheck(headers, url, Constant.TIMEOUT_PLAY); + private void setMediaSource(Result result, int timeout) { + setMediaSource(checkUa(result.getHeaders()), result.getRealUrl(), result.getFormat(), result.getDrm(), checkSub(result.getSubs()), timeout); } - private void setTimeoutCheck(Map headers, String url, int timeout) { + private void setMediaSource(Map headers, String url, String format, Drm drm, List subs, int timeout) { + if (player != null) player.setMediaSource(ExoUtil.getSource(this.headers = headers, this.url = url, ExoUtil.getMimeType(this.format = format, error), drm, this.subs = subs, decode), position); if (player != null) player.prepare(); Logger.t(TAG).d(error + "," + url); App.post(runnable, timeout); - this.headers = headers; - PlayerEvent.state(0); - this.url = url; + PlayerEvent.prepare(); } private void removeTimeoutCheck() { @@ -409,13 +428,6 @@ public class Players implements Player.Listener, AnalyticsListener, ParseCallbac return scheme.isEmpty() || "file".equals(scheme) ? !Path.exists(url) : host.isEmpty(); } - public static Map checkUa(Map headers) { - if (Setting.getUa().isEmpty()) return headers; - for (Map.Entry header : headers.entrySet()) if (HttpHeaders.USER_AGENT.equalsIgnoreCase(header.getKey())) return headers; - headers.put(HttpHeaders.USER_AGENT, Setting.getUa()); - return headers; - } - public Uri getUri() { return getUrl().startsWith("file://") || getUrl().startsWith("/") ? FileUtil.getShareUri(getUrl()) : Uri.parse(getUrl()); }