diff --git a/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java b/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java index 3efbe06a7..dabe64aef 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java +++ b/app/src/main/java/com/fongmi/android/tv/api/EpgParser.java @@ -28,7 +28,7 @@ import java.util.Set; public class EpgParser { public static boolean start(Live live) throws Exception { - if (!live.getEpg().endsWith(".xml") && !live.getEpg().endsWith(".gz")) return false; + if (!live.getEpg().contains("xml") && !live.getEpg().contains("gz")) return false; File file = Path.epg(Uri.parse(live.getEpg()).getLastPathSegment()); if (shouldDownload(file)) Download.create(live.getEpg(), file).start(); if (file.getName().endsWith(".gz")) readGzip(live, file); diff --git a/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java b/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java index bca30264f..37eff0211 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java +++ b/app/src/main/java/com/fongmi/android/tv/api/LiveParser.java @@ -10,6 +10,8 @@ import com.fongmi.android.tv.bean.ClearKey; import com.fongmi.android.tv.bean.Drm; import com.fongmi.android.tv.bean.Group; import com.fongmi.android.tv.bean.Live; +import com.fongmi.android.tv.bean.XCategory; +import com.fongmi.android.tv.bean.XStream; import com.fongmi.android.tv.utils.UrlUtil; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Json; @@ -17,6 +19,7 @@ import com.github.catvod.utils.Path; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -30,6 +33,7 @@ public class LiveParser { private static final Pattern TVG_URL = Pattern.compile(".*x-tvg-url=\"(.?|.+?)\".*"); private static final Pattern GROUP = Pattern.compile(".*group-title=\"(.?|.+?)\".*"); private static final Pattern NAME = Pattern.compile(".*,(.+?)$"); + private static final Pattern M3U = Pattern.compile("#EXTM3U|#EXTINF"); private static String extract(String line, Pattern pattern) { Matcher matcher = pattern.matcher(line.trim()); @@ -47,7 +51,8 @@ public class LiveParser { public static void text(Live live, String text) { int number = 0; if (live.getGroups().size() > 0) return; - if (text.contains("#EXTM3U")) m3u(live, text); + if (M3U.matcher(text).find()) m3u(live, text); + else if (live.isXtream()) xtream(live); else txt(live, text); for (Group group : live.getGroups()) { for (Channel channel : group.getChannel()) { @@ -102,6 +107,22 @@ public class LiveParser { } } + private static void xtream(Live live) { + List categoryList = XtreamParser.getCategoryList(live); + List streamList = XtreamParser.getStreamList(live); + Map categoryMap = new HashMap<>(); + for (XCategory category : categoryList) { + categoryMap.put(category.getCategoryId(), category.getCategoryName()); + } + for (XStream stream : streamList) { + Group group = live.find(Group.create(categoryMap.get(stream.getCategoryId()), live.isPass())); + Channel channel = group.find(Channel.create(stream.getName())); + channel.getUrls().add(XtreamParser.getTsUrl(live, stream.getStreamId())); + if (!stream.getStreamIcon().isEmpty()) channel.setLogo(stream.getStreamIcon()); + if (!stream.getEpgChannelId().isEmpty()) channel.setTvgName(stream.getEpgChannelId()); + } + } + private static void txt(Live live, String text) { Setting setting = Setting.create(); for (String line : text.split("\n")) { diff --git a/app/src/main/java/com/fongmi/android/tv/api/XtreamParser.java b/app/src/main/java/com/fongmi/android/tv/api/XtreamParser.java new file mode 100644 index 000000000..dd0c17dd5 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/api/XtreamParser.java @@ -0,0 +1,38 @@ +package com.fongmi.android.tv.api; + +import com.fongmi.android.tv.bean.Live; +import com.fongmi.android.tv.bean.XCategory; +import com.fongmi.android.tv.bean.XStream; +import com.github.catvod.net.OkHttp; + +import java.util.List; + +import okhttp3.HttpUrl; + +public class XtreamParser { + + public static HttpUrl.Builder getBuilder(Live live) { + HttpUrl url = HttpUrl.parse(live.getUrl()); + return new HttpUrl.Builder().scheme(url.scheme()).host(url.host()).port(url.port()); + } + + public static String getEpgUrl(Live live) { + return getBuilder(live).addPathSegment("xmltv.php").addQueryParameter("username", live.getUsername()).addQueryParameter("password", live.getPassword()).build().toString(); + } + + public static String getApiUrl(Live live, String action) { + return getBuilder(live).addPathSegment("player_api.php").addQueryParameter("username", live.getUsername()).addQueryParameter("password", live.getPassword()).addQueryParameter("action", action).build().toString(); + } + + public static String getTsUrl(Live live, String id) { + return getBuilder(live).addPathSegment("live").addPathSegment(live.getUsername()).addPathSegment(live.getPassword()).addPathSegment(id + ".ts").build().toString(); + } + + public static List getCategoryList(Live live) { + return XCategory.arrayFrom(OkHttp.string(getApiUrl(live, "get_live_categories"))); + } + + public static List getStreamList(Live live) { + return XStream.arrayFrom(OkHttp.string(getApiUrl(live, "get_live_streams"))); + } +} diff --git a/app/src/main/java/com/fongmi/android/tv/api/config/LiveConfig.java b/app/src/main/java/com/fongmi/android/tv/api/config/LiveConfig.java index 65374224f..d38784aeb 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/config/LiveConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/config/LiveConfig.java @@ -130,7 +130,7 @@ public class LiveConfig { } private void parseText(String text, Callback callback) { - Live live = new Live(parseName(config.getUrl()), config.getUrl()).sync(); + Live live = new Live(parseName(config.getUrl()), config.getUrl()).check().sync(); LiveParser.text(live, text); lives.add(live); setHome(live, true); @@ -185,7 +185,7 @@ public class LiveConfig { live.setApi(parseApi(live.getApi())); live.setExt(parseExt(live.getExt())); live.setJar(parseJar(live, spider)); - lives.add(live.sync()); + lives.add(live.check().sync()); } for (Live live : lives) { if (live.getName().equals(config.getHome())) { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Live.java b/app/src/main/java/com/fongmi/android/tv/bean/Live.java index 59bf32ead..9bb628440 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Live.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Live.java @@ -1,5 +1,6 @@ package com.fongmi.android.tv.bean; +import android.net.Uri; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -10,9 +11,11 @@ import androidx.room.PrimaryKey; import com.fongmi.android.tv.App; import com.fongmi.android.tv.Constant; import com.fongmi.android.tv.R; +import com.fongmi.android.tv.api.XtreamParser; import com.fongmi.android.tv.api.loader.BaseLoader; import com.fongmi.android.tv.db.AppDatabase; import com.fongmi.android.tv.gson.ExtAdapter; +import com.fongmi.android.tv.utils.UrlUtil; import com.github.catvod.crawler.Spider; import com.github.catvod.utils.Json; import com.google.common.net.HttpHeaders; @@ -76,6 +79,14 @@ public class Live { @SerializedName("referer") private String referer; + @Ignore + @SerializedName("username") + private String username; + + @Ignore + @SerializedName("password") + private String password; + @Ignore @SerializedName("type") private Integer type; @@ -148,6 +159,10 @@ public class Live { return TextUtils.isEmpty(url) ? "" : url; } + public void setUrl(String url) { + this.url = url; + } + public String getApi() { return TextUtils.isEmpty(api) ? "" : api; } @@ -200,6 +215,22 @@ public class Live { return TextUtils.isEmpty(referer) ? "" : referer; } + public String getUsername() { + return TextUtils.isEmpty(username) ? "" : username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return TextUtils.isEmpty(password) ? "" : password; + } + + public void setPassword(String password) { + this.password = password; + } + public Integer getType() { return type == null ? 0 : type; } @@ -260,6 +291,10 @@ public class Live { this.width = width; } + public boolean isXtream() { + return !getUsername().isEmpty() && !getPassword().isEmpty(); + } + public boolean isEmpty() { return getName().isEmpty(); } @@ -297,6 +332,17 @@ public class Live { return this; } + public Live check() { + Uri uri = Uri.parse(getUrl()); + boolean php = UrlUtil.path(uri).contains("get.php"); + String username = uri.getQueryParameter("username"); + String password = uri.getQueryParameter("password"); + if (php && username != null) setUsername(username); + if (php && password != null) setPassword(password); + if (isXtream() && getEpg().isEmpty()) setEpg(XtreamParser.getEpgUrl(this)); + return this; + } + public Live recent() { BaseLoader.get().setRecent(getName(), getApi(), getJar()); return this; diff --git a/app/src/main/java/com/fongmi/android/tv/bean/XCategory.java b/app/src/main/java/com/fongmi/android/tv/bean/XCategory.java new file mode 100644 index 000000000..420d5bff0 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/bean/XCategory.java @@ -0,0 +1,33 @@ +package com.fongmi.android.tv.bean; + +import android.text.TextUtils; + +import com.fongmi.android.tv.App; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; + +public class XCategory { + + @SerializedName("category_id") + private String categoryId; + @SerializedName("category_name") + private String categoryName; + + public static List arrayFrom(String str) { + Type listType = new TypeToken>() {}.getType(); + List items = App.gson().fromJson(str, listType); + return items == null ? Collections.emptyList() : items; + } + + public String getCategoryId() { + return TextUtils.isEmpty(categoryId) ? "" : categoryId; + } + + public String getCategoryName() { + return TextUtils.isEmpty(categoryName) ? "" : categoryName; + } +} diff --git a/app/src/main/java/com/fongmi/android/tv/bean/XStream.java b/app/src/main/java/com/fongmi/android/tv/bean/XStream.java new file mode 100644 index 000000000..10c6e906a --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/bean/XStream.java @@ -0,0 +1,51 @@ +package com.fongmi.android.tv.bean; + +import android.text.TextUtils; + +import com.fongmi.android.tv.App; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; + +public class XStream { + + @SerializedName("name") + private String name; + @SerializedName("stream_id") + private String streamId; + @SerializedName("stream_icon") + private String streamIcon; + @SerializedName("epg_channel_id") + private String epgChannelId; + @SerializedName("category_id") + private String categoryId; + + public static List arrayFrom(String str) { + Type listType = new TypeToken>() {}.getType(); + List items = App.gson().fromJson(str, listType); + return items == null ? Collections.emptyList() : items; + } + + public String getName() { + return TextUtils.isEmpty(name) ? "" : name; + } + + public String getStreamId() { + return TextUtils.isEmpty(streamId) ? "" : streamId; + } + + public String getStreamIcon() { + return TextUtils.isEmpty(streamIcon) ? "" : streamIcon; + } + + public String getEpgChannelId() { + return TextUtils.isEmpty(epgChannelId) ? "" : epgChannelId; + } + + public String getCategoryId() { + return TextUtils.isEmpty(categoryId) ? "" : categoryId; + } +}