diff --git a/app/src/leanback/AndroidManifest.xml b/app/src/leanback/AndroidManifest.xml
index 8ec608c1d..e7f602b8b 100644
--- a/app/src/leanback/AndroidManifest.xml
+++ b/app/src/leanback/AndroidManifest.xml
@@ -80,5 +80,10 @@
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:screenOrientation="sensorLandscape" />
+
+
\ No newline at end of file
diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingActivity.java
index 3ff2840d2..208eb52c4 100644
--- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingActivity.java
+++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingActivity.java
@@ -109,6 +109,7 @@ public class SettingActivity extends BaseActivity implements ConfigCallback, Sit
mBinding.vodHistory.setOnClickListener(this::onVodHistory);
mBinding.version.setOnLongClickListener(this::onVersionDev);
mBinding.liveHistory.setOnClickListener(this::onLiveHistory);
+ mBinding.player.setOnLongClickListener(this::onPlayerSetting);
mBinding.wallDefault.setOnClickListener(this::setWallDefault);
mBinding.wallRefresh.setOnClickListener(this::setWallRefresh);
mBinding.quality.setOnClickListener(this::setQuality);
@@ -231,6 +232,11 @@ public class SettingActivity extends BaseActivity implements ConfigCallback, Sit
HistoryDialog.create(this).type(type = 1).show();
}
+ private boolean onPlayerSetting(View view) {
+ SettingPlayerActivity.start(this);
+ return true;
+ }
+
private void onVersion(View view) {
Updater.get().force().release().start();
}
diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingPlayerActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingPlayerActivity.java
new file mode 100644
index 000000000..fc35e628e
--- /dev/null
+++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SettingPlayerActivity.java
@@ -0,0 +1,73 @@
+package com.fongmi.android.tv.ui.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.view.View;
+
+import androidx.viewbinding.ViewBinding;
+
+import com.fongmi.android.tv.R;
+import com.fongmi.android.tv.databinding.ActivitySettingPlayerBinding;
+import com.fongmi.android.tv.impl.UaCallback;
+import com.fongmi.android.tv.player.ExoUtil;
+import com.fongmi.android.tv.ui.base.BaseActivity;
+import com.fongmi.android.tv.ui.custom.dialog.UaDialog;
+import com.fongmi.android.tv.utils.Prefers;
+import com.fongmi.android.tv.utils.ResUtil;
+
+public class SettingPlayerActivity extends BaseActivity implements UaCallback {
+
+ private ActivitySettingPlayerBinding mBinding;
+ private String[] http;
+
+ public static void start(Activity activity) {
+ activity.startActivity(new Intent(activity, SettingPlayerActivity.class));
+ }
+
+ private String getSwitch(boolean value) {
+ return getString(value ? R.string.setting_on : R.string.setting_off);
+ }
+
+ @Override
+ protected ViewBinding getBinding() {
+ return mBinding = ActivitySettingPlayerBinding.inflate(getLayoutInflater());
+ }
+
+ @Override
+ protected void initView() {
+ mBinding.uaText.setText(Prefers.getUa());
+ mBinding.tunnelText.setText(getSwitch(Prefers.isTunnel()));
+ mBinding.httpText.setText((http = ResUtil.getStringArray(R.array.select_player_http))[Prefers.getHttp()]);
+ mBinding.tunnel.setVisibility(Prefers.getPlayer() == 0 ? View.VISIBLE : View.GONE);
+ mBinding.http.setVisibility(Prefers.getPlayer() == 0 ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ protected void initEvent() {
+ mBinding.ua.setOnClickListener(this::onUa);
+ mBinding.http.setOnClickListener(this::setHttp);
+ mBinding.tunnel.setOnClickListener(this::setTunnel);
+ }
+
+ private void onUa(View view) {
+ UaDialog.create(this).show();
+ }
+
+ private void setHttp(View view) {
+ int index = Prefers.getHttp();
+ Prefers.putHttp(index = index == http.length - 1 ? 0 : ++index);
+ mBinding.httpText.setText(http[index]);
+ ExoUtil.reset();
+ }
+
+ private void setTunnel(View view) {
+ Prefers.putTunnel(!Prefers.isTunnel());
+ mBinding.tunnelText.setText(getSwitch(Prefers.isTunnel()));
+ }
+
+ @Override
+ public void setUa(String ua) {
+ mBinding.uaText.setText(ua);
+ Prefers.putUa(ua);
+ }
+}
diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java
new file mode 100644
index 000000000..33711a208
--- /dev/null
+++ b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java
@@ -0,0 +1,97 @@
+package com.fongmi.android.tv.ui.custom.dialog;
+
+import android.content.DialogInterface;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
+
+import com.fongmi.android.tv.R;
+import com.fongmi.android.tv.databinding.DialogUaBinding;
+import com.fongmi.android.tv.event.ServerEvent;
+import com.fongmi.android.tv.impl.UaCallback;
+import com.fongmi.android.tv.server.Server;
+import com.fongmi.android.tv.utils.Prefers;
+import com.fongmi.android.tv.utils.QRCode;
+import com.fongmi.android.tv.utils.ResUtil;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+public class UaDialog implements DialogInterface.OnDismissListener {
+
+ private final DialogUaBinding binding;
+ private final UaCallback callback;
+ private final AlertDialog dialog;
+
+ public static UaDialog create(FragmentActivity activity) {
+ return new UaDialog(activity);
+ }
+
+ public UaDialog(FragmentActivity activity) {
+ this.callback = (UaCallback) activity;
+ this.binding = DialogUaBinding.inflate(LayoutInflater.from(activity));
+ this.dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create();
+ }
+
+ public void show() {
+ initDialog();
+ initView();
+ initEvent();
+ }
+
+ private void initDialog() {
+ WindowManager.LayoutParams params = dialog.getWindow().getAttributes();
+ params.width = (int) (ResUtil.getScreenWidth() * 0.55f);
+ dialog.getWindow().setAttributes(params);
+ dialog.getWindow().setDimAmount(0);
+ dialog.setOnDismissListener(this);
+ dialog.show();
+ }
+
+ private void initView() {
+ String ua = Prefers.getUa();
+ String address = Server.get().getAddress();
+ binding.text.setText(ua);
+ binding.code.setImageBitmap(QRCode.getBitmap(address, 200, 0));
+ binding.text.setSelection(TextUtils.isEmpty(ua) ? 0 : ua.length());
+ binding.info.setText(ResUtil.getString(R.string.push_info, address).replace(",", "\n"));
+ }
+
+ private void initEvent() {
+ EventBus.getDefault().register(this);
+ binding.positive.setOnClickListener(this::onPositive);
+ binding.negative.setOnClickListener(this::onNegative);
+ binding.text.setOnEditorActionListener((textView, actionId, event) -> {
+ if (actionId == EditorInfo.IME_ACTION_DONE) binding.positive.performClick();
+ return true;
+ });
+ }
+
+ private void onPositive(View view) {
+ callback.setUa(binding.text.getText().toString().trim());
+ dialog.dismiss();
+ }
+
+ private void onNegative(View view) {
+ dialog.dismiss();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onServerEvent(ServerEvent event) {
+ if (event.getType() != ServerEvent.Type.API) return;
+ binding.text.setText(event.getText());
+ binding.positive.performClick();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialogInterface) {
+ EventBus.getDefault().unregister(this);
+ }
+}
diff --git a/app/src/leanback/res/layout/activity_setting_player.xml b/app/src/leanback/res/layout/activity_setting_player.xml
new file mode 100644
index 000000000..0e34b2213
--- /dev/null
+++ b/app/src/leanback/res/layout/activity_setting_player.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/leanback/res/layout/dialog_ua.xml b/app/src/leanback/res/layout/dialog_ua.xml
new file mode 100644
index 000000000..41aa73359
--- /dev/null
+++ b/app/src/leanback/res/layout/dialog_ua.xml
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/fongmi/android/tv/impl/UaCallback.java b/app/src/main/java/com/fongmi/android/tv/impl/UaCallback.java
new file mode 100644
index 000000000..66150efb3
--- /dev/null
+++ b/app/src/main/java/com/fongmi/android/tv/impl/UaCallback.java
@@ -0,0 +1,6 @@
+package com.fongmi.android.tv.impl;
+
+public interface UaCallback {
+
+ void setUa(String ua);
+}
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 7e2ac60bf..310a73666 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
@@ -12,6 +12,7 @@ import androidx.media3.database.DatabaseProvider;
import androidx.media3.database.StandaloneDatabaseProvider;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultDataSource;
+import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.datasource.cache.Cache;
import androidx.media3.datasource.cache.CacheDataSource;
@@ -39,7 +40,6 @@ import com.fongmi.android.tv.bean.Rule;
import com.fongmi.android.tv.bean.Sub;
import com.fongmi.android.tv.utils.FileUtil;
import com.fongmi.android.tv.utils.Prefers;
-import com.fongmi.android.tv.utils.Sniffer;
import com.github.catvod.net.OkHttp;
import com.google.common.net.HttpHeaders;
@@ -64,7 +64,7 @@ public class ExoUtil {
public static TrackSelector buildTrackSelector() {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(App.get());
- trackSelector.setParameters(trackSelector.buildUponParameters().setPreferredTextLanguage("zh"));
+ trackSelector.setParameters(trackSelector.buildUponParameters().setPreferredTextLanguage("zh").setTunnelingEnabled(Prefers.isTunnel()));
return trackSelector;
}
@@ -122,12 +122,11 @@ public class ExoUtil {
}
private static synchronized HttpDataSource.Factory getHttpDataSourceFactory() {
- if (httpDataSourceFactory == null) httpDataSourceFactory = new OkHttpDataSource.Factory((Call.Factory) OkHttp.client());
+ if (httpDataSourceFactory == null) httpDataSourceFactory = Prefers.getHttp() == 0 ? new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true) : new OkHttpDataSource.Factory((Call.Factory) OkHttp.client());
return httpDataSourceFactory;
}
private static synchronized DataSource.Factory getDataSourceFactory(Map headers) {
- if (!headers.containsKey(HttpHeaders.USER_AGENT)) headers.put(HttpHeaders.USER_AGENT, Sniffer.CHROME);
if (dataSourceFactory == null) dataSourceFactory = buildReadOnlyCacheDataSource(new DefaultDataSource.Factory(App.get(), getHttpDataSourceFactory()), getCache());
httpDataSourceFactory.setDefaultRequestProperties(headers);
return dataSourceFactory;
@@ -146,4 +145,13 @@ public class ExoUtil {
if (cache == null) cache = new SimpleCache(FileUtil.getCacheDir("player"), new NoOpCacheEvictor(), getDatabase());
return cache;
}
+
+ public static void reset() {
+ if (cache != null) cache.release();
+ httpDataSourceFactory = null;
+ dataSourceFactory = null;
+ extractorsFactory = null;
+ database = null;
+ cache = null;
+ }
}
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 bb03a84e9..3c45a52da 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
@@ -24,6 +24,7 @@ import com.fongmi.android.tv.utils.Prefers;
import com.fongmi.android.tv.utils.ResUtil;
import com.github.catvod.crawler.SpiderDebug;
import com.google.common.collect.ImmutableList;
+import com.google.common.net.HttpHeaders;
import java.util.Formatter;
import java.util.List;
@@ -327,8 +328,14 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
if (parseJob != null) parseJob.stop();
}
+ private Map checkHeaders(Map headers) {
+ if (Prefers.getUa().isEmpty() || headers.containsKey(HttpHeaders.USER_AGENT) || headers.containsKey(HttpHeaders.USER_AGENT.toLowerCase())) return headers;
+ headers.put(HttpHeaders.USER_AGENT, Prefers.getUa());
+ return headers;
+ }
+
private void setMediaSource(Result result) {
- SpiderDebug.log(errorCode + "," + result.getRealUrl() + "," + result.getHeaders());
+ SpiderDebug.log(errorCode + "," + result.getRealUrl() + "," + checkHeaders(result.getHeaders()));
if (isIjk()) ijkPlayer.setMediaSource(result.getRealUrl(), result.getHeaders());
if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(result, errorCode));
if (isExo()) exoPlayer.prepare();
@@ -336,7 +343,7 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
}
private void setMediaSource(Map headers, String url) {
- SpiderDebug.log(errorCode + "," + url + "," + headers);
+ SpiderDebug.log(errorCode + "," + url + "," + checkHeaders(headers));
if (isIjk()) ijkPlayer.setMediaSource(url, headers);
if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(headers, url, errorCode));
if (isExo()) exoPlayer.prepare();
diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java b/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java
index aefb53ba9..fd3b1cf93 100644
--- a/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java
+++ b/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java
@@ -203,6 +203,30 @@ public class Prefers {
put("update", update);
}
+ public static String getUa() {
+ return getString("ua", Sniffer.CHROME);
+ }
+
+ public static void putUa(String ua) {
+ put("ua", ua);
+ }
+
+ public static boolean isTunnel() {
+ return getBoolean("exo_tunnel");
+ }
+
+ public static void putTunnel(boolean tunnel) {
+ put("exo_tunnel", tunnel);
+ }
+
+ public static int getHttp() {
+ return getInt("exo_http", 1);
+ }
+
+ public static void putHttp(int http) {
+ put("exo_http", http);
+ }
+
public static float getThumbnail() {
return 0.3f * getQuality() + 0.4f;
}
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index fc3d359d5..6966a4eb0 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -61,6 +61,9 @@
直播
壁纸
播放器
+ User Agent
+ Tunnel Mode
+ Http Source
解码方式
渲染方式
缩放比例
@@ -70,6 +73,8 @@
缓存
版本
权限
+ 关
+ 开
关键字…
@@ -120,6 +125,11 @@
- IJK
+
+ - 预设
+ - OkHttp
+
+
- 低
- 中
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 5132ecb6f..deb6e3462 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -61,6 +61,9 @@
直播
壁紙
播放器
+ User Agent
+ Tunnel Mode
+ Http Source
解碼方式
渲染方式
縮放比例
@@ -70,6 +73,8 @@
暫存
版本
權限
+ 關
+ 開
關鍵字…
@@ -120,6 +125,11 @@
- IJK
+
+ - 預設
+ - OkHttp
+
+
- 低
- 中
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bc0e19450..a8efbafb6 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -61,6 +61,9 @@
Live
Wallpaper
Player
+ User Agent
+ Tunnel Mode
+ Http Source
Decode
Render
Scale
@@ -70,6 +73,8 @@
Cache
Version
Permission
+ OFF
+ ON
Keywords…
@@ -120,6 +125,11 @@
- IJK
+
+ - Default
+ - OkHttp
+
+
- Surface
- Texture
diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java
index 773302c81..fd98d4980 100644
--- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java
+++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java
@@ -14,6 +14,7 @@ import android.widget.MediaController;
import androidx.annotation.NonNull;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -187,9 +188,11 @@ public class IjkVideoView extends FrameLayout implements MediaController.MediaPl
}
private void fixUserAgent(Map headers) {
- if (!headers.containsKey(Utils.USER_AGENT)) headers.put(Utils.USER_AGENT, Utils.CHROME);
- mPlayer.setOption(format, "user_agent", headers.get(Utils.USER_AGENT));
- headers.remove(Utils.USER_AGENT);
+ for (String key : Arrays.asList(Utils.USER_AGENT, Utils.USER_AGENT.toLowerCase())) {
+ if (!headers.containsKey(key)) continue;
+ mPlayer.setOption(format, "user_agent", headers.get(key));
+ headers.remove(key);
+ }
}
private void bindSurfaceHolder(IMediaPlayer mp, IRenderView.ISurfaceHolder holder) {
diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java
index 22d7a7233..4a1ad61ec 100644
--- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java
+++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java
@@ -1,29 +1,13 @@
package tv.danmaku.ijk.media.player.ui;
import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.os.Build;
import android.util.DisplayMetrics;
public class Utils {
public static final String USER_AGENT = "User-Agent";
- public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36";
public static float dp2px(Context context, float dpValue) {
return Math.round((dpValue * context.getResources().getDisplayMetrics().densityDpi) / DisplayMetrics.DENSITY_DEFAULT);
}
-
- public static String getUserAgent(Context context) {
- String versionName;
- try {
- String packageName = context.getPackageName();
- PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
- versionName = info.versionName;
- } catch (PackageManager.NameNotFoundException e) {
- versionName = "?";
- }
- return context.getPackageName() + "/" + versionName + " (Linux;Android " + Build.VERSION.RELEASE + ") " + "IjkPlayerLib/0.8.9";
- }
}