diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 9d49c261e..ebbd5ac76 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -12,6 +12,7 @@ import androidx.annotation.Nullable; import androidx.leanback.widget.ArrayObjectAdapter; import androidx.leanback.widget.ItemBridgeAdapter; import androidx.leanback.widget.OnChildViewHolderSelectedListener; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; @@ -20,7 +21,9 @@ import com.fongmi.android.tv.bean.Channel; import com.fongmi.android.tv.bean.Group; import com.fongmi.android.tv.databinding.ActivityLiveBinding; import com.fongmi.android.tv.event.PlayerEvent; +import com.fongmi.android.tv.model.LiveViewModel; import com.fongmi.android.tv.player.Players; +import com.fongmi.android.tv.player.source.Force; import com.fongmi.android.tv.ui.custom.CustomKeyDownLive; import com.fongmi.android.tv.ui.presenter.ChannelPresenter; import com.fongmi.android.tv.ui.presenter.GroupPresenter; @@ -40,6 +43,7 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick private ArrayObjectAdapter mChannelAdapter; private ArrayObjectAdapter mGroupAdapter; private CustomKeyDownLive mKeyDown; + private LiveViewModel mViewModel; private Runnable mRunnable; private Handler mHandler; private Players mPlayers; @@ -80,6 +84,7 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick mHandler = new Handler(Looper.getMainLooper()); mKeyDown = CustomKeyDownLive.create(this); setRecyclerView(); + setViewModel(); setVideoView(); getLive(); } @@ -101,6 +106,11 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick mBinding.channel.setAdapter(new ItemBridgeAdapter(mChannelAdapter = new ArrayObjectAdapter(new ChannelPresenter(this)))); } + private void setViewModel() { + mViewModel = new ViewModelProvider(this).get(LiveViewModel.class); + mViewModel.result.observe(this, result -> mPlayers.start(result)); + } + private void setVideoView() { getPlayerView().setPlayer(mPlayers.exo()); getPlayerView().setVisibility(View.VISIBLE); @@ -144,7 +154,7 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick } private void getUrl(Channel item) { - mPlayers.start(item); + mViewModel.getUrl(item); } private void hideUI() { @@ -293,6 +303,7 @@ public class LiveActivity extends BaseActivity implements GroupPresenter.OnClick protected void onDestroy() { super.onDestroy(); mPlayers.release(); + Force.get().destroy(); EventBus.getDefault().unregister(this); } } diff --git a/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java b/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java index 2e4b619d1..6d5f598bd 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java @@ -91,7 +91,7 @@ public class LiveConfig { public void parse(Live live) { try { - if (live.isProxy()) live = new Live(live.getChannels().get(0).getName(), live.getChannels().get(0).getUrl().split("ext=")[1]); + if (live.isProxy()) live = new Live(live.getChannels().get(0).getName(), live.getChannels().get(0).getUrls().get(0).split("ext=")[1]); if (live.getType() == 0) parse(live, getTxt(live.getUrl())); if (live.getGroups().size() > 0) lives.add(live); if (live.getName().equals(config.getHome())) setHome(live); diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Channel.java b/app/src/main/java/com/fongmi/android/tv/bean/Channel.java index 8c64f405c..b052af0de 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Channel.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Channel.java @@ -1,5 +1,6 @@ package com.fongmi.android.tv.bean; +import android.net.Uri; import android.text.TextUtils; import android.view.View; import android.widget.ImageView; @@ -33,6 +34,7 @@ public class Channel { private String ua; private boolean activated; + private String url; private int line; public static Channel objectFrom(JsonElement element) { @@ -99,6 +101,14 @@ public class Channel { this.ua = ua; } + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + public int getLine() { return line; } @@ -127,10 +137,6 @@ public class Channel { return getLine() == getUrls().size() - 1; } - public String getUrl() { - return getUrls().get(getLine()); - } - public String getLineText() { return ResUtil.getString(R.string.live_line, getLine() + 1, getUrls().size()); } @@ -150,6 +156,18 @@ public class Channel { return this; } + public String getScheme() { + return Uri.parse(getUrls().get(getLine())).getScheme().toLowerCase(); + } + + public boolean isTVBus() { + return getScheme().equals("tvbus"); + } + + public boolean isForce() { + return getScheme().startsWith("p") || getScheme().equals("mitv"); + } + public Map getHeaders() { HashMap map = new HashMap<>(); if (getUa().isEmpty()) return map; diff --git a/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java b/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java new file mode 100644 index 000000000..ea1bbce95 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java @@ -0,0 +1,52 @@ +package com.fongmi.android.tv.model; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import com.fongmi.android.tv.bean.Channel; +import com.fongmi.android.tv.player.source.Force; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class LiveViewModel extends ViewModel { + + public MutableLiveData result; + public ExecutorService executor; + + public LiveViewModel() { + this.result = new MutableLiveData<>(); + } + + public MutableLiveData getResult() { + return result; + } + + public void getUrl(Channel item) { + execute(() -> { + String url = item.getUrls().get(item.getLine()); + if (item.isForce()) item.setUrl(Force.get().fetch(url)); + else item.setUrl(url); + return item; + }); + } + + private void execute(Callable callable) { + if (executor != null) executor.shutdownNow(); + executor = Executors.newFixedThreadPool(2); + executor.execute(() -> { + try { + if (!Thread.interrupted()) result.postValue(executor.submit(callable).get(5, TimeUnit.SECONDS)); + } catch (Throwable e) { + e.printStackTrace(); + } + }); + } + + @Override + protected void onCleared() { + if (executor != null) executor.shutdownNow(); + } +} 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 968b647b2..c480a216a 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 @@ -47,7 +47,7 @@ public class ExoUtil { private static MediaSource getSource(Map headers, String url, int errorCode, List config) { SpiderDebug.log(errorCode + "," + url + "," + headers); - MediaItem.Builder builder = new MediaItem.Builder().setUri(Uri.parse(url)); + MediaItem.Builder builder = new MediaItem.Builder().setUri(Uri.parse(url.trim())); if (errorCode == PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED) builder.setMimeType(MimeTypes.APPLICATION_OCTET); else if (errorCode == PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED) builder.setMimeType(MimeTypes.APPLICATION_M3U8); if (config.size() > 0) builder.setSubtitleConfigurations(config); diff --git a/app/src/main/java/com/fongmi/android/tv/player/source/Force.java b/app/src/main/java/com/fongmi/android/tv/player/source/Force.java new file mode 100644 index 000000000..864dbacfc --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/player/source/Force.java @@ -0,0 +1,70 @@ +package com.fongmi.android.tv.player.source; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.IBinder; + +import com.fongmi.android.tv.App; +import com.fongmi.android.tv.net.OKHttp; +import com.forcetech.Port; +import com.forcetech.service.ForceService; +import com.gsoft.mitv.MainActivity; + +import okhttp3.Headers; + +public class Force { + + private boolean init; + + private static class Loader { + static volatile Force INSTANCE = new Force(); + } + + public static Force get() { + return Loader.INSTANCE; + } + + private void init() { + App.get().bindService(new Intent(App.get(), MainActivity.class), mConn, Context.BIND_AUTO_CREATE); + App.get().bindService(new Intent(App.get(), ForceService.class), mConn, Context.BIND_AUTO_CREATE); + init = true; + } + + public void destroy() { + try { + if (init) App.get().unbindService(mConn); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public String fetch(String url) { + try { + if (!init) init(); + int port = Port.get(url); + Uri uri = Uri.parse(url); + String id = uri.getLastPathSegment(); + String cmd = "http://127.0.0.1:" + port + "/cmd.xml?cmd=switch_chan&server=" + uri.getHost() + ":" + uri.getPort() + "&id=" + id; + String result = "http://127.0.0.1:" + port + "/" + id; + OKHttp.newCall(cmd, Headers.of("user-agent", "MTV")).execute(); + return result; + } catch (Exception e) { + return url; + } + } + + private final ServiceConnection mConn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + + } + + @Override + public void onServiceDisconnected(ComponentName name) { + + } + }; +} diff --git a/settings.gradle b/settings.gradle index e985e393b..8b1ed2376 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,10 +15,10 @@ dependencyResolutionManagement { } } include ':app' +include ':tvbus' include ':catvod' include ':pyramid' +include ':forcetech' rootProject.name = "TV" gradle.ext.exoplayerModulePrefix = 'exoplayer-' apply from: file("/exo/core_settings.gradle") -include ':tvbus' -include ':forcetech'