pull/142/head
FongMi 3 years ago
parent 45a5ae99f3
commit bed78e2bd8
  1. 44
      app/src/main/java/com/fongmi/android/tv/player/Players.java
  2. 43
      app/src/main/java/com/fongmi/android/tv/utils/M3U8.java
  3. 36
      app/src/mobile/java/com/fongmi/android/tv/service/PlaybackService.java

@ -1,7 +1,10 @@
package com.fongmi.android.tv.player;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
@ -24,9 +27,11 @@ import com.fongmi.android.tv.bean.Track;
import com.fongmi.android.tv.event.ErrorEvent;
import com.fongmi.android.tv.event.PlayerEvent;
import com.fongmi.android.tv.impl.ParseCallback;
import com.fongmi.android.tv.impl.SessionCallback;
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.ResUtil;
import com.github.catvod.crawler.SpiderDebug;
import com.fongmi.android.tv.utils.Utils;
import com.orhanobut.logger.Logger;
import java.util.Formatter;
import java.util.List;
@ -40,6 +45,8 @@ import tv.danmaku.ijk.media.player.ui.IjkVideoView;
public class Players implements Player.Listener, IMediaPlayer.Listener, AnalyticsListener, ParseCallback, DrawHandler.Callback {
private static final String TAG = Players.class.getSimpleName();
public static final int SYS = 0;
public static final int IJK = 1;
public static final int EXO = 2;
@ -89,6 +96,11 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
private void createSession(Activity activity) {
session = new MediaSessionCompat(activity, "TV");
session.setMediaButtonReceiver(null);
session.setCallback(SessionCallback.create(this));
session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
session.setSessionActivity(PendingIntent.getActivity(App.get(), 99, new Intent(App.get(), activity.getClass()), Utils.getPendingFlag()));
MediaControllerCompat.setMediaController(activity, session.getController());
}
public void set(PlayerView exo, IjkVideoView ijk) {
@ -409,26 +421,26 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
}
private void setMediaSource(Result result) {
SpiderDebug.log(errorCode + "," + result.getRealUrl());
if (isIjk()) ijkPlayer.setMediaSource(IjkUtil.getSource(result));
if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(result, errorCode));
if (isExo()) exoPlayer.prepare();
Logger.t(TAG).d(errorCode + "," + result.getRealUrl());
if (isIjk() && ijkPlayer != null) ijkPlayer.setMediaSource(IjkUtil.getSource(result));
if (isExo() && exoPlayer != null) exoPlayer.setMediaSource(ExoUtil.getSource(result, errorCode));
if (isExo() && exoPlayer != null) exoPlayer.prepare();
setTimeoutCheck(result.getRealUrl());
}
private void setMediaSource(Channel channel) {
SpiderDebug.log(errorCode + "," + channel.getUrl());
if (isIjk()) ijkPlayer.setMediaSource(IjkUtil.getSource(channel));
if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(channel, errorCode));
if (isExo()) exoPlayer.prepare();
Logger.t(TAG).d(errorCode + "," + channel.getUrl());
if (isIjk() && ijkPlayer != null) ijkPlayer.setMediaSource(IjkUtil.getSource(channel));
if (isExo() && exoPlayer != null) exoPlayer.setMediaSource(ExoUtil.getSource(channel, errorCode));
if (isExo() && exoPlayer != null) exoPlayer.prepare();
setTimeoutCheck(channel.getUrl());
}
private void setMediaSource(Map<String, String> headers, String url) {
SpiderDebug.log(errorCode + "," + url);
if (isIjk()) ijkPlayer.setMediaSource(IjkUtil.getSource(headers, url));
if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(headers, url, errorCode));
if (isExo()) exoPlayer.prepare();
Logger.t(TAG).d(errorCode + "," + url);
if (isIjk() && ijkPlayer != null) ijkPlayer.setMediaSource(IjkUtil.getSource(headers, url));
if (isExo() && exoPlayer != null) exoPlayer.setMediaSource(ExoUtil.getSource(headers, url, errorCode));
if (isExo() && exoPlayer != null) exoPlayer.prepare();
setTimeoutCheck(url);
}
@ -463,7 +475,8 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
}
private void setPlaybackState(int state) {
session.setPlaybackState(new PlaybackStateCompat.Builder().setState(state, getPosition(), getSpeed()).build());
long actions = PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
session.setPlaybackState(new PlaybackStateCompat.Builder().setActions(actions).setState(state, getPosition(), getSpeed()).build());
}
private boolean hasDanmu() {
@ -495,9 +508,8 @@ public class Players implements Player.Listener, IMediaPlayer.Listener, Analytic
@Override
public void onPlayerError(@NonNull PlaybackException error) {
ErrorEvent.format(ExoUtil.getRetry(errorCode = error.errorCode));
setPlaybackState(PlaybackStateCompat.STATE_ERROR);
this.errorCode = error.errorCode;
ErrorEvent.format(2);
}
@Override

@ -1,10 +1,14 @@
package com.fongmi.android.tv.utils;
import android.net.Uri;
import androidx.media3.common.util.UriUtil;
import com.github.catvod.net.OkHttp;
import com.google.common.net.HttpHeaders;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -13,16 +17,43 @@ import okhttp3.Headers;
public class M3U8 {
private static final Pattern REGEX_URI = Pattern.compile("URI=\"(.+?)\"");
private static final String TAG_DISCONTINUITY = "#EXT-X-DISCONTINUITY";
private static final String TAG_MEDIA_DURATION = "#EXTINF";
private static final String TAG_KEY = "#EXT-X-KEY";
private static final Pattern REGEX_X_DISCONTINUITY = Pattern.compile("#EXT-X-DISCONTINUITY[\\s\\S]*?(?=#EXT-X-DISCONTINUITY|$)");
private static final Pattern REGEX_MEDIA_DURATION = Pattern.compile(TAG_MEDIA_DURATION + ":([\\d\\.]+)\\b");
private static final Pattern REGEX_URI = Pattern.compile("URI=\"(.+?)\"");
public static String get(String url, Map<String, String> headers) throws Exception {
String result = OkHttp.newCall(url, getHeader(headers)).execute().body().string();
Matcher matcher = Pattern.compile("#EXT-X-STREAM-INF(.*)\\n?(.*)").matcher(result);
if (matcher.find() && matcher.groupCount() > 1) return get(UriUtil.resolve(url, matcher.group(2)), headers);
StringBuilder sb = new StringBuilder();
for (String line : result.split("\n")) sb.append(shouldResolve(line) ? resolve(url, line) : line).append("\n");
return sb.toString();
List<String> ads = Sniffer.getRegex(Uri.parse(url));
return clean(sb.toString(), ads);
}
private static String clean(String line, List<String> ads) {
boolean scan = false;
for (String ad : ads) {
if (ad.contains(TAG_DISCONTINUITY) || ad.contains(TAG_MEDIA_DURATION)) line = line.replaceAll(ad, "");
else if (isDouble(ad)) scan = true;
}
return scan ? scan(line, ads) : line;
}
private static String scan(String line, List<String> ads) {
Matcher m1 = REGEX_X_DISCONTINUITY.matcher(line);
while (m1.find()) {
String group = m1.group();
BigDecimal t = BigDecimal.ZERO;;
Matcher m2 = REGEX_MEDIA_DURATION.matcher(group);
while (m2.find()) t = t.add(new BigDecimal(m2.group(1)));
for (String ad : ads) if (t.toString().startsWith(ad)) line = line.replaceAll(group, "");
}
return line;
}
private static Headers getHeader(Map<String, String> headers) {
@ -31,6 +62,14 @@ public class M3U8 {
return builder.build();
}
private static boolean isDouble(String ad) {
try {
return Double.parseDouble(ad) > 0;
} catch (Exception e) {
return false;
}
}
private static boolean shouldResolve(String line) {
return (!line.startsWith("#") && !line.startsWith("http")) || line.startsWith(TAG_KEY);
}

@ -1,10 +1,10 @@
package com.fongmi.android.tv.service;
import android.Manifest;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.os.IBinder;
import android.support.v4.media.MediaMetadataCompat;
@ -12,7 +12,9 @@ import android.support.v4.media.MediaMetadataCompat;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.media.app.NotificationCompat.MediaStyle;
@ -27,6 +29,8 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.Objects;
public class PlaybackService extends Service {
private static Players players;
@ -41,8 +45,16 @@ public class PlaybackService extends Service {
App.get().stopService(new Intent(App.get(), PlaybackService.class));
}
private NotificationManager getManager() {
return (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
private boolean isNull() {
return Objects.isNull(players) || Objects.isNull(players.getSession());
}
private boolean nonNull() {
return Objects.nonNull(players) && Objects.nonNull(players.getSession());
}
private NotificationManagerCompat getManager() {
return NotificationManagerCompat.from(this);
}
private NotificationCompat.Action buildNotificationAction(@DrawableRes int icon, @StringRes int title, String action) {
@ -50,12 +62,12 @@ public class PlaybackService extends Service {
}
private NotificationCompat.Action getPlayPauseAction() {
if (players != null && players.isPlaying()) return buildNotificationAction(androidx.media3.ui.R.drawable.exo_icon_pause, androidx.media3.ui.R.string.exo_controls_pause_description, ActionEvent.PAUSE);
return buildNotificationAction(androidx.media3.ui.R.drawable.exo_icon_play, androidx.media3.ui.R.string.exo_controls_play_description, ActionEvent.PLAY);
if (nonNull() && players.isPlaying()) return buildNotificationAction(R.drawable.ic_notify_pause, androidx.media3.ui.R.string.exo_controls_pause_description, ActionEvent.PAUSE);
return buildNotificationAction(R.drawable.ic_notify_play, androidx.media3.ui.R.string.exo_controls_play_description, ActionEvent.PLAY);
}
private MediaMetadataCompat getMetadata() {
return players.getSession().getController().getMetadata();
return isNull() ? null : players.getSession().getController().getMetadata();
}
private String getTitle() {
@ -80,14 +92,15 @@ public class PlaybackService extends Service {
}
private void setAction(NotificationCompat.Builder builder) {
builder.addAction(buildNotificationAction(androidx.media3.ui.R.drawable.exo_icon_previous, androidx.media3.ui.R.string.exo_controls_previous_description, ActionEvent.PREV));
builder.addAction(buildNotificationAction(R.drawable.ic_notify_prev, androidx.media3.ui.R.string.exo_controls_previous_description, ActionEvent.PREV));
builder.addAction(getPlayPauseAction());
builder.addAction(buildNotificationAction(androidx.media3.ui.R.drawable.exo_icon_next, androidx.media3.ui.R.string.exo_controls_next_description, ActionEvent.NEXT));
builder.addAction(buildNotificationAction(R.drawable.ic_notify_next, androidx.media3.ui.R.string.exo_controls_next_description, ActionEvent.NEXT));
}
private void setStyle(NotificationCompat.Builder builder) {
MediaStyle style = new MediaStyle();
style.setShowCancelButton(true);
if (nonNull()) style.setMediaSession(players.getSession().getSessionToken());
style.setCancelButtonIntent(ActionReceiver.getPendingIntent(this, ActionEvent.STOP));
builder.setStyle(style);
}
@ -106,12 +119,13 @@ public class PlaybackService extends Service {
if (art != null) setLargeIcon(builder, art);
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setDeleteIntent(ActionReceiver.getPendingIntent(this, ActionEvent.STOP));
builder.setContentIntent(players.getSession().getController().getSessionActivity());
if (nonNull()) builder.setContentIntent(players.getSession().getController().getSessionActivity());
return builder.build();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onActionEvent(ActionEvent event) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) return;
if (event.isUpdate()) getManager().notify(ID, buildNotification());
}
@ -124,7 +138,7 @@ public class PlaybackService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(ID, buildNotification());
return START_STICKY;
return START_NOT_STICKY;
}
@Override

Loading…
Cancel
Save