diff --git a/app/src/main/java/com/github/catvod/bean/live/Channel.java b/app/src/main/java/com/github/catvod/bean/live/Channel.java new file mode 100644 index 00000000..12062ec1 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/live/Channel.java @@ -0,0 +1,94 @@ +package com.github.catvod.bean.live; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +public class Channel { + + @SerializedName("urls") + private List urls; + @SerializedName("tvgName") + private String tvgName; + @SerializedName("number") + private String number; + @SerializedName("logo") + private String logo; + @SerializedName("name") + private String name; + @SerializedName("epg") + private String epg; + @SerializedName("ua") + private String ua; + @SerializedName("referer") + private String referer; + + public static Channel objectFrom(JsonElement element) { + return new Gson().fromJson(element, Channel.class); + } + + public static Channel create(String name) { + return new Channel(name); + } + + public Channel(String name) { + this.name = name; + } + + public String getNumber() { + return TextUtils.isEmpty(number) ? "" : number; + } + + public String getName() { + return TextUtils.isEmpty(name) ? "" : name; + } + + public List getUrls() { + return urls = urls == null ? new ArrayList<>() : urls; + } + + public void setUrls(List urls) { + this.urls = urls; + } + + public void setTvgName(String tvgName) { + this.tvgName = tvgName; + } + + public void setNumber(String number) { + this.number = number; + } + + public void setLogo(String logo) { + this.logo = logo; + } + + public void setName(String name) { + this.name = name; + } + + public void setEpg(String epg) { + this.epg = epg; + } + + public void setUa(String ua) { + this.ua = ua; + } + + public void setReferer(String referer) { + this.referer = referer; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Channel)) return false; + Channel it = (Channel) obj; + return getName().equals(it.getName()) || (!getNumber().isEmpty() && getNumber().equals(it.getNumber())); + } +} diff --git a/app/src/main/java/com/github/catvod/bean/live/Group.java b/app/src/main/java/com/github/catvod/bean/live/Group.java new file mode 100644 index 00000000..5dbf7414 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/live/Group.java @@ -0,0 +1,69 @@ +package com.github.catvod.bean.live; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Group { + + @SerializedName("channel") + private List channel; + @SerializedName("name") + private String name; + + public static List arrayFrom(String str) { + Type listType = new TypeToken>() {}.getType(); + List items = new Gson().fromJson(str, listType); + return items == null ? Collections.emptyList() : items; + } + + public static Group find(List items, Group item) { + for (Group group : items) if (group.getName().equals(item.getName())) return group; + items.add(item); + return item; + } + + public static Group create(String name) { + return new Group(name); + } + + public Group(String name) { + this.name = name; + if (name.contains("_")) setName(name.split("_")[0]); + } + + public List getChannel() { + return channel = channel == null ? new ArrayList<>() : channel; + } + + public String getName() { + return TextUtils.isEmpty(name) ? "" : name; + } + + public void setName(String name) { + this.name = name; + } + + public Channel find(Channel channel) { + int index = getChannel().indexOf(channel); + if (index != -1) return getChannel().get(index); + getChannel().add(channel); + return channel; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (this == obj) return true; + if (!(obj instanceof Group)) return false; + Group it = (Group) obj; + return getName().equals(it.getName()) && getChannel().size() == it.getChannel().size(); + } +} diff --git a/app/src/main/java/com/github/catvod/bean/xc/Config.java b/app/src/main/java/com/github/catvod/bean/xc/Config.java new file mode 100644 index 00000000..6abd48d9 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/xc/Config.java @@ -0,0 +1,50 @@ +package com.github.catvod.bean.xc; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.Arrays; +import java.util.List; + +public class Config { + + @SerializedName("name") + private String name; + @SerializedName("pass") + private String pass; + @SerializedName("vod") + private boolean vod; + @SerializedName("formats") + private List formats; + + private String url; + + public static Config objectFrom(String str) { + Config item = new Gson().fromJson(str, Config.class); + return item == null ? new Config() : item; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + public String getName() { + return name; + } + + public String getPass() { + return pass; + } + + public boolean isVod() { + return vod; + } + + public List getFormats() { + return formats == null ? Arrays.asList("ts") : formats; + } +} diff --git a/app/src/main/java/com/github/catvod/bean/xc/XCategory.java b/app/src/main/java/com/github/catvod/bean/xc/XCategory.java new file mode 100644 index 00000000..1db2b8a6 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/xc/XCategory.java @@ -0,0 +1,33 @@ +package com.github.catvod.bean.xc; + +import android.text.TextUtils; + +import com.google.gson.Gson; +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 = new 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/github/catvod/bean/xc/XStream.java b/app/src/main/java/com/github/catvod/bean/xc/XStream.java new file mode 100644 index 00000000..c1e4affb --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/xc/XStream.java @@ -0,0 +1,72 @@ +package com.github.catvod.bean.xc; + +import android.text.TextUtils; + +import com.github.catvod.spider.XtreamCode; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class XStream { + + @SerializedName("name") + private String name; + @SerializedName("stream_id") + private String streamId; + @SerializedName("stream_type") + private String streamType; + @SerializedName("stream_icon") + private String streamIcon; + @SerializedName("epg_channel_id") + private String epgChannelId; + @SerializedName("category_id") + private String categoryId; + @SerializedName("container_extension") + private String containerExtension; + + public static List arrayFrom(String str) { + Type listType = new TypeToken>() {}.getType(); + List items = new 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 getStreamType() { + return TextUtils.isEmpty(streamType) ? "" : streamType; + } + + public String getStreamIcon() { + return TextUtils.isEmpty(streamIcon) ? "" : streamIcon; + } + + public String getEpgChannelId() { + return TextUtils.isEmpty(epgChannelId) ? "" : epgChannelId; + } + + public String getCategoryId() { + return TextUtils.isEmpty(categoryId) ? "" : categoryId; + } + + public String getContainerExtension() { + return TextUtils.isEmpty(containerExtension) ? "" : containerExtension; + } + + public List getPlayUrl(Config config) { + List urls = new ArrayList<>(); + if (!getContainerExtension().isEmpty()) urls.add(XtreamCode.getBuilder(config).addPathSegment(getStreamType()).addPathSegment(config.getName()).addPathSegment(config.getPass()).addPathSegment(getStreamId() + "." + getContainerExtension()).build().toString()); + else for (String format : config.getFormats()) urls.add(XtreamCode.getBuilder(config).addPathSegment(getStreamType()).addPathSegment(config.getName()).addPathSegment(config.getPass()).addPathSegment(getStreamId() + "." + format + "$" + format.toUpperCase()).build().toString()); + return urls; + } +} diff --git a/app/src/main/java/com/github/catvod/spider/Feiyang.java b/app/src/main/java/com/github/catvod/spider/Feiyang.java deleted file mode 100644 index 77f0c733..00000000 --- a/app/src/main/java/com/github/catvod/spider/Feiyang.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.github.catvod.spider; - -import android.content.Context; -import android.os.SystemClock; - -import com.github.catvod.crawler.Spider; -import com.github.catvod.net.OkHttp; -import com.github.catvod.utils.FileUtil; -import com.github.catvod.utils.Path; -import com.github.catvod.utils.Shell; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; - -public class Feiyang extends Spider { - - private static final String URL = "http://127.0.0.1:35345/"; - private static final String AIO = "allinone-linux-musl"; - private static final String SHELL = "run-allinone.sh"; - private static final File f_shell = new File(Path.files(), SHELL); - private static final File f_aio = new File(Path.files(), AIO); - private static Thread thread; - - @Override - public void init(Context context, String extend) throws Exception { - createShell(); - checkExtend(extend); - findAIO(Path.tv().listFiles()); - findAIO(Path.download().listFiles()); - } - - @Override - public String liveContent(String url) { - int retry = 0; - while ((OkHttp.string(URL)).isEmpty() && retry++ < 20) SystemClock.sleep(250); - return OkHttp.string(url.startsWith("http") ? url : URL + url); - } - - private void createShell() { - File cache = Path.cache("allinone.cache"); - String cachePath = cache.getAbsolutePath(); - String shellPath = f_shell.getAbsolutePath(); - String aioPath = f_aio.getAbsolutePath(); - String script = "if [ -e " + shellPath + ".kill ]; then\n" + "sh " + shellPath + ".kill;\n" + "killall -9 " + AIO + ";\n" + "sleep 1;\n" + "rm -f " + shellPath + ".kill;\n" + "fi\n" + "echo \"kill -9 $$\" >> " + shellPath + ".kill;\n" + "while(true)\n" + "do\n" + "TMPDIR=" + cachePath + " " + aioPath + " -ipv6=true " + " -port 35345 " + " >/dev/null 2>&1\n" + "pidlist=$(ps -ef|grep " + SHELL + "|grep -v grep|awk '{print $2}')\n" + "selfpid=$$\n" + "for pid in $pidlist\n" + "do\n" + "if [[ $pid != \"$selfpid\" ]] then\n" + "kill -9 $pid\n" + "fi\n" + "done\n" + "killall -9 " + AIO + "\n" + "rm -rf \"" + cachePath + "\"/*\n" + "sleep 5;\n" + "done"; - Path.write(f_shell, script); - } - - private void checkExtend(String extend) { - if (!extend.contains(";md5;")) return; - String[] texts = extend.split(";md5;"); - String url = texts[0].trim(); - String md5 = texts[1].trim(); - if (md5.startsWith("http")) md5 = OkHttp.string(md5).trim(); - if (FileUtil.md5(f_aio).equals(md5)) return; - try { - File file = Path.create(new File(Path.download(), AIO)); - download(file, OkHttp.newCall(url).body().byteStream()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void download(File file, InputStream is) throws Exception { - FileOutputStream os = new FileOutputStream(file); - try (BufferedInputStream input = new BufferedInputStream(is)) { - byte[] buffer = new byte[4096]; - int readBytes; - while ((readBytes = input.read(buffer)) != -1) { - os.write(buffer, 0, readBytes); - } - } - } - - private void findAIO(File[] files) { - if (files == null || thread != null) return; - for (File f : files) { - if (f.getName().equals("allinone") || f.getName().startsWith("allinone-linux")) { - Path.move(f, f_aio); - } - } - if (f_aio.exists()) { - thread = new Thread(() -> Shell.exec("nohup " + f_shell.getAbsolutePath() + " &")); - thread.start(); - } - } - - @Override - public void destroy() { - try { - Shell.exec("killall -9 " + AIO); - if (thread != null) thread.interrupt(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - thread = null; - } - } -} diff --git a/app/src/main/java/com/github/catvod/spider/XtreamCode.java b/app/src/main/java/com/github/catvod/spider/XtreamCode.java new file mode 100644 index 00000000..64a36967 --- /dev/null +++ b/app/src/main/java/com/github/catvod/spider/XtreamCode.java @@ -0,0 +1,88 @@ +package com.github.catvod.spider; + +import android.content.Context; + +import com.github.catvod.bean.live.Channel; +import com.github.catvod.bean.live.Group; +import com.github.catvod.bean.xc.Config; +import com.github.catvod.bean.xc.XCategory; +import com.github.catvod.bean.xc.XStream; +import com.github.catvod.crawler.Spider; +import com.github.catvod.net.OkHttp; +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import okhttp3.HttpUrl; + +public class XtreamCode extends Spider { + + private List groups; + private Config config; + + @Override + public void init(Context context, String extend) throws Exception { + config = Config.objectFrom(extend); + groups = new ArrayList<>(); + } + + @Override + public String liveContent(String url) { + config.setUrl(url); + List categoryList = getCategoryList(config); + List streamList = getStreamList(config); + Map categoryMap = new HashMap<>(); + for (XCategory category : categoryList) { + categoryMap.put(category.getCategoryId(), category.getCategoryName()); + } + for (XStream stream : streamList) { + if (!categoryMap.containsKey(stream.getCategoryId())) continue; + Group group = Group.find(groups, Group.create(categoryMap.get(stream.getCategoryId()))); + Channel channel = group.find(Channel.create(stream.getName())); + if (!stream.getStreamIcon().isEmpty()) channel.setLogo(stream.getStreamIcon()); + if (!stream.getEpgChannelId().isEmpty()) channel.setTvgName(stream.getEpgChannelId()); + channel.getUrls().addAll(stream.getPlayUrl(config)); + } + return new Gson().toJson(groups); + } + + public static HttpUrl.Builder getBuilder(Config config) { + HttpUrl url = HttpUrl.parse(config.getUrl()); + return new HttpUrl.Builder().scheme(url.scheme()).host(url.host()).port(url.port()); + } + + private String getApiUrl(Config config, String action) { + return getBuilder(config).addPathSegment("player_api.php").addQueryParameter("username", config.getName()).addQueryParameter("password", config.getPass()).addQueryParameter("action", action).build().toString(); + } + + private List getLiveCategoryList(Config config) { + return XCategory.arrayFrom(OkHttp.string(getApiUrl(config, "get_live_categories"))); + } + + private List getLiveStreamList(Config config) { + return XStream.arrayFrom(OkHttp.string(getApiUrl(config, "get_live_streams"))); + } + + private List getVodCategoryList(Config config) { + return XCategory.arrayFrom(OkHttp.string(getApiUrl(config, "get_vod_categories"))); + } + + private List getVodStreamList(Config config) { + return XStream.arrayFrom(OkHttp.string(getApiUrl(config, "get_vod_streams"))); + } + + private List getCategoryList(Config config) { + List categoryList = getLiveCategoryList(config); + if (config.isVod()) categoryList.addAll(getVodCategoryList(config)); + return categoryList; + } + + private List getStreamList(Config config) { + List streamList = getLiveStreamList(config); + if (config.isVod()) streamList.addAll(getVodStreamList(config)); + return streamList; + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4f9aeb89..8df9bef6 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -57,5 +57,13 @@ android:text="searchContent" android:textAllCaps="false" /> +