diff --git a/app/src/main/java/com/github/catvod/bean/mqtv/Data.java b/app/src/main/java/com/github/catvod/bean/mqtv/Data.java new file mode 100644 index 00000000..3a6bfb57 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/mqtv/Data.java @@ -0,0 +1,39 @@ +package com.github.catvod.bean.mqtv; + +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.List; + +public class Data { + + @SerializedName("data") + private List data; + @SerializedName("id") + private String id; + @SerializedName("name") + private String name; + @SerializedName("stat") + private Stat stat; + + public static Data objectFrom(String str) { + return new Gson().fromJson(str, Data.class); + } + + public List getData() { + return data == null ? Collections.emptyList() : data; + } + + public String getId() { + return id == null ? "" : id; + } + + public String getName() { + return name == null ? "" : name; + } + + public Stat getStat() { + return stat == null ? new Stat() : stat; + } +} diff --git a/app/src/main/java/com/github/catvod/bean/mqtv/Stat.java b/app/src/main/java/com/github/catvod/bean/mqtv/Stat.java new file mode 100644 index 00000000..4cfa2671 --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/mqtv/Stat.java @@ -0,0 +1,16 @@ +package com.github.catvod.bean.mqtv; + +import com.google.gson.annotations.SerializedName; + +import java.util.Collections; +import java.util.List; + +public class Stat { + + @SerializedName("UserIpList") + private List userIpList; + + public List getUserIpList() { + return userIpList == null ? Collections.emptyList() : userIpList; + } +} diff --git a/app/src/main/java/com/github/catvod/bean/mqtv/User.java b/app/src/main/java/com/github/catvod/bean/mqtv/User.java new file mode 100644 index 00000000..140bfacb --- /dev/null +++ b/app/src/main/java/com/github/catvod/bean/mqtv/User.java @@ -0,0 +1,20 @@ +package com.github.catvod.bean.mqtv; + +public class User { + + private String id; + private String mac; + + public User(String id, String mac) { + this.id = id; + this.mac = mac; + } + + public String getId() { + return id; + } + + public String getMac() { + return mac; + } +} diff --git a/app/src/main/java/com/github/catvod/debug/MainActivity.java b/app/src/main/java/com/github/catvod/debug/MainActivity.java index e394b18f..741c72c6 100644 --- a/app/src/main/java/com/github/catvod/debug/MainActivity.java +++ b/app/src/main/java/com/github/catvod/debug/MainActivity.java @@ -7,13 +7,15 @@ import android.widget.Button; import com.github.catvod.R; import com.github.catvod.crawler.Spider; import com.github.catvod.spider.Init; -import com.github.catvod.spider.PTT; +import com.github.catvod.spider.MQtv; +import com.github.catvod.spider.Proxy; import com.orhanobut.logger.AndroidLogAdapter; import com.orhanobut.logger.Logger; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -32,12 +34,16 @@ public class MainActivity extends Activity { Button detailContent = findViewById(R.id.detailContent); Button playerContent = findViewById(R.id.playerContent); Button searchContent = findViewById(R.id.searchContent); + Button liveContent = findViewById(R.id.liveContent); + Button proxy = findViewById(R.id.proxy); homeContent.setOnClickListener(view -> executor.execute(this::homeContent)); homeVideoContent.setOnClickListener(view -> executor.execute(this::homeVideoContent)); categoryContent.setOnClickListener(view -> executor.execute(this::categoryContent)); detailContent.setOnClickListener(view -> executor.execute(this::detailContent)); playerContent.setOnClickListener(view -> executor.execute(this::playerContent)); searchContent.setOnClickListener(view -> executor.execute(this::searchContent)); + liveContent.setOnClickListener(view -> executor.execute(this::liveContent)); + proxy.setOnClickListener(view -> executor.execute(this::proxy)); Logger.addLogAdapter(new AndroidLogAdapter()); executor = Executors.newCachedThreadPool(); executor.execute(this::initSpider); @@ -46,7 +52,7 @@ public class MainActivity extends Activity { private void initSpider() { try { Init.init(getApplicationContext()); - spider = new PTT(); + spider = new MQtv(); spider.init(this, ""); } catch (Throwable e) { e.printStackTrace(); @@ -103,4 +109,21 @@ public class MainActivity extends Activity { e.printStackTrace(); } } + + public void liveContent() { + try { + Logger.t("liveContent").d(spider.liveContent("")); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public void proxy() { + try { + Map params = new HashMap<>(); + Logger.t("liveContent").d(Proxy.proxy(params)); + } catch (Throwable e) { + e.printStackTrace(); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/catvod/spider/MQtv.java b/app/src/main/java/com/github/catvod/spider/MQtv.java new file mode 100644 index 00000000..91adc7b7 --- /dev/null +++ b/app/src/main/java/com/github/catvod/spider/MQtv.java @@ -0,0 +1,101 @@ +package com.github.catvod.spider; + +import android.content.Context; + +import com.github.catvod.bean.mqtv.Data; +import com.github.catvod.bean.mqtv.User; +import com.github.catvod.crawler.Spider; +import com.github.catvod.crawler.SpiderDebug; +import com.github.catvod.net.OkHttp; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MQtv extends Spider { + + private static List users; + private static String ext; + + @Override + public void init(Context context, String extend) throws Exception { + users = new ArrayList<>(); + ext = extend; + } + + @Override + public String liveContent(String url) { + List data; + StringBuilder sb = new StringBuilder(); + loadUser(data = Data.objectFrom(OkHttp.string("http://" + ext + "/api/post?item=itv_traffic")).getData()); + for (Data item : data) sb.append(item.getName()).append(",").append("proxy://do=mqtv").append("&id=").append(item.getId()).append("&type=m3u8").append("\n"); + return sb.toString(); + } + + public static Object[] proxy(Map params) { + String id = params.get("id"); + String ip = params.get("ip"); + if (ip != null) ext = ip; + String token = getToken(); + String auth = authChannel(id, token); + SpiderDebug.log("id=" + id + ", token=" + token + ", auth=" + auth); + Object[] result = new Object[3]; + result[0] = 200; + result[1] = "application/vnd.apple.mpegurl"; + result[2] = new ByteArrayInputStream(getM3u8(id, token).getBytes()); + return result; + } + + private static void loadUser(List data) { + Pattern userPattern = Pattern.compile(".*?([0-9a-zA-Z]{11,}).*", Pattern.CASE_INSENSITIVE); + Pattern macPattern = Pattern.compile(".*?(([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}).*", Pattern.CASE_INSENSITIVE); + for (Data item : data) { + for (String userIp : item.getStat().getUserIpList()) { + Matcher userMatcher = userPattern.matcher(userIp); + Matcher macMatcher = macPattern.matcher(userIp); + String user = userMatcher.matches() ? userMatcher.group(1) : ""; + String mac = macMatcher.matches() ? macMatcher.group(1) : ""; + if (!user.isEmpty() && !mac.isEmpty()) users.add(new User(user, mac)); + } + } + } + + private static User choose() { + if (users == null) users = new ArrayList<>(); + if (users.isEmpty()) loadUser(Data.objectFrom(OkHttp.string("http://" + ext + "/api/post?item=itv_traffic")).getData()); + return users.get(ThreadLocalRandom.current().nextInt(users.size())); + } + + private static String getToken() { + User user = choose(); + String url = String.format(Locale.getDefault(), "http://%s/HSAndroidLogin.ecgi?ty=json&net_account=%s&mac_address1=%s&_=%d", ext, user.getId(), user.getMac(), System.currentTimeMillis()); + Pattern pattern = Pattern.compile("\"Token\"\\s*:\\s*\"(.*?)\"", Pattern.CASE_INSENSITIVE); + Matcher matcher = pattern.matcher(OkHttp.string(url)); + if (matcher.find()) return matcher.group(1); + return getToken(); + } + + private static String authChannel(String id, String token) { + String data = OkHttp.string("http://" + ext + "/ualive?cid=" + id + "&token=" + token); + Matcher matcher = Pattern.compile("\"Reason\":\"(.*?)\"", Pattern.CASE_INSENSITIVE).matcher(data); + if (matcher.find()) return matcher.group(1); + return ""; + } + + private static String getM3u8(String id, String token) { + String base = "http://" + ext.split(":")[0] + ":" + 5003 + "/"; + String m3u8 = base + id + ".m3u8?token=" + token; + String[] lines = OkHttp.string(m3u8).split("\\r?\\n"); + StringBuilder sb = new StringBuilder(); + for (String line : lines) { + if (!line.startsWith("#")) line = base + line; + sb.append(line).append("\n"); + } + return sb.toString(); + } +} diff --git a/app/src/main/java/com/github/catvod/spider/Proxy.java b/app/src/main/java/com/github/catvod/spider/Proxy.java index a241b7c9..ee1de468 100644 --- a/app/src/main/java/com/github/catvod/spider/Proxy.java +++ b/app/src/main/java/com/github/catvod/spider/Proxy.java @@ -17,6 +17,8 @@ public class Proxy { switch (params.get("do")) { case "ck": return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes(StandardCharsets.UTF_8))}; + case "mqtv": + return MQtv.proxy(params); case "bili": return Bili.proxy(params); case "webdav": diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8df9bef6..2927daa5 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -65,5 +65,13 @@ android:text="liveContent" android:textAllCaps="false" /> +