Merge remote-tracking branch 'origin/main'

# Conflicts:
#	build.gradle
#	gradle/wrapper/gradle-wrapper.properties
This commit is contained in:
chengshengru 2025-10-05 11:46:19 +08:00
commit c45f58cf10
52 changed files with 1048 additions and 3140 deletions

View File

@ -6,12 +6,18 @@ plugins {
android { android {
namespace 'com.github.catvod' namespace 'com.github.catvod'
compileSdk 35 compileSdk {
version = release(36)
}
defaultConfig { defaultConfig {
applicationId "com.github.catvod.demo" applicationId "com.github.catvod.demo"
minSdk 21 minSdk 21
targetSdk 35 targetSdk 36
}
buildFeatures {
viewBinding true
} }
buildTypes { buildTypes {
@ -28,8 +34,8 @@ android {
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_11 sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_17
} }
configurations.configureEach { configurations.configureEach {
@ -42,10 +48,9 @@ android {
dependencies { dependencies {
implementation 'com.squareup.okhttp3:okhttp:' + okhttpVersion implementation 'com.squareup.okhttp3:okhttp:' + okhttpVersion
implementation 'com.github.thegrizzlylabs:sardine-android:0.9' implementation 'com.github.thegrizzlylabs:sardine-android:0.9'
implementation 'wang.harlon.quickjs:wrapper-android:2.4.5' implementation 'wang.harlon.quickjs:wrapper-android:3.2.3'
implementation 'com.google.code.gson:gson:2.11.0' implementation 'com.google.code.gson:gson:2.13.2'
implementation 'cn.wanghaomiao:JsoupXpath:2.5.1'
implementation 'com.hierynomus:smbj:0.14.0' implementation 'com.hierynomus:smbj:0.14.0'
implementation 'com.orhanobut:logger:2.2.0' implementation 'com.orhanobut:logger:2.2.0'
implementation 'org.jsoup:jsoup:1.18.3' implementation 'org.jsoup:jsoup:1.21.2'
} }

View File

@ -14,7 +14,7 @@
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity <activity
android:name=".debug.MainActivity" android:name=".MainActivity"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -0,0 +1,142 @@
package com.github.catvod;
import android.app.Activity;
import android.os.Bundle;
import com.github.catvod.crawler.Spider;
import com.github.catvod.databinding.ActivityMainBinding;
import com.github.catvod.spider.Init;
import com.github.catvod.spider.PTT;
import com.github.catvod.spider.Proxy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import com.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends Activity {
private ActivityMainBinding binding;
private ExecutorService executor;
private Spider spider;
private Gson gson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gson = new GsonBuilder().setPrettyPrinting().create();
Logger.addLogAdapter(new AndroidLogAdapter());
executor = Executors.newCachedThreadPool();
executor.execute(this::initSpider);
spider = new PTT();
initView();
initEvent();
}
private void initView() {
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
private void initEvent() {
binding.home.setOnClickListener(view -> executor.execute(this::homeContent));
binding.homeVideo.setOnClickListener(view -> executor.execute(this::homeVideoContent));
binding.category.setOnClickListener(view -> executor.execute(this::categoryContent));
binding.detail.setOnClickListener(view -> executor.execute(this::detailContent));
binding.player.setOnClickListener(view -> executor.execute(this::playerContent));
binding.search.setOnClickListener(view -> executor.execute(this::searchContent));
binding.live.setOnClickListener(view -> executor.execute(this::liveContent));
binding.proxy.setOnClickListener(view -> executor.execute(this::proxy));
}
private void initSpider() {
try {
Init.init(getApplicationContext());
spider.init(this, "");
} catch (Throwable e) {
e.printStackTrace();
}
}
public void homeContent() {
try {
String result = gson.toJson(JsonParser.parseString(spider.homeContent(true)));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void homeVideoContent() {
try {
String result = gson.toJson(JsonParser.parseString(spider.homeVideoContent()));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void categoryContent() {
try {
HashMap<String, String> extend = new HashMap<>();
extend.put("c", "19");
extend.put("year", "2024");
String result = gson.toJson(JsonParser.parseString(spider.categoryContent("3", "2", true, extend)));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void detailContent() {
try {
String result = gson.toJson(JsonParser.parseString(spider.detailContent(List.of("78702"))));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void playerContent() {
try {
String result = gson.toJson(JsonParser.parseString(spider.playerContent("", "382044/1/78", new ArrayList<>())));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void searchContent() {
try {
String result = gson.toJson(JsonParser.parseString(spider.searchContent("我的人间烟火", false)));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void liveContent() {
try {
String result = gson.toJson(JsonParser.parseString(spider.liveContent("")));
Init.post(() -> binding.result.setText(result));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void proxy() {
try {
Map<String, String> params = new HashMap<>();
Logger.t("liveContent").d(Proxy.proxy(params));
} catch (Throwable e) {
e.printStackTrace();
}
}
}

View File

@ -111,6 +111,10 @@ public class Item {
} }
public String getVodId(String id) { public String getVodId(String id) {
return id + "/" + getName();
}
public String getVodPath(String id) {
return id + getPath() + "/" + getName(); return id + getPath() + "/" + getName();
} }
@ -127,6 +131,6 @@ public class Item {
} }
public Vod getVod(Drive drive) { public Vod getVod(Drive drive) {
return new Vod(getVodId(drive.getName()), getName(), getPic(), drive.getName(), isFolder()); return new Vod(getVodPath(drive.getName()), getName(), getPic(), drive.getName(), isFolder());
} }
} }

View File

@ -6,13 +6,16 @@ import com.github.catvod.bean.Vod;
import com.github.catvod.utils.Util; import com.github.catvod.utils.Util;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
public class Data { public class Data {
@SerializedName(value = "jump_id", alternate = "id") @SerializedName("jump_id")
private String jumpId; private String jumpId;
@SerializedName("id")
private String id;
@SerializedName(value = "thumbnail", alternate = "path") @SerializedName(value = "thumbnail", alternate = "path")
private String thumbnail; private String thumbnail;
@SerializedName("title") @SerializedName("title")
@ -24,24 +27,30 @@ public class Data {
@SerializedName("playlist") @SerializedName("playlist")
private Value playlist; private Value playlist;
@SerializedName("year") @SerializedName("year")
private Value year; private String year;
@SerializedName("area") @SerializedName("area")
private Value area; private String area;
@SerializedName("types") @SerializedName("types")
private List<Value> types; private List<Value> types;
@SerializedName("actors") @SerializedName("actors")
private List<Value> actors; private List<Value> actors;
@SerializedName("directors") @SerializedName("directors")
private List<Value> directors; private List<Value> directors;
@SerializedName("btbo_downlist") @SerializedName("source_list_source")
private List<BtboDown> btboDownlist; private List<SourceListSource> source;
@SerializedName("dataList")
private List<Data> dataList;
public String getJumpId() { public String getJumpId() {
return TextUtils.isEmpty(jumpId) ? "" : jumpId; return TextUtils.isEmpty(jumpId) ? "" : jumpId;
} }
public String getThumbnail() { public String getId() {
return TextUtils.isEmpty(thumbnail) ? "" : thumbnail + "@Referer=www.jianpianapp.com@User-Agent=jianpian-version362"; return TextUtils.isEmpty(id) ? "" : id;
}
public String getThumbnail(String imgDomain) {
return TextUtils.isEmpty(thumbnail) ? "" : "http://" + imgDomain + thumbnail;
} }
public String getTitle() { public String getTitle() {
@ -61,11 +70,11 @@ public class Data {
} }
public String getYear() { public String getYear() {
return year == null ? "" : year.getTitle(); return year == null ? "" : year;
} }
public String getArea() { public String getArea() {
return area == null ? "" : area.getTitle(); return area == null ? "" : area;
} }
public String getTypes() { public String getTypes() {
@ -80,12 +89,20 @@ public class Data {
return directors == null ? "" : getValues(directors, true); return directors == null ? "" : getValues(directors, true);
} }
public List<BtboDown> getBtboDownlist() { public List<SourceListSource> getSource() {
return btboDownlist == null ? Collections.emptyList() : btboDownlist; return source == null ? Collections.emptyList() : source;
} }
public Vod vod() { public List<Data> getDataList() {
return new Vod(getJumpId(), getTitle(), getThumbnail(), getMask()); return dataList == null ? Collections.emptyList() : dataList;
}
public Vod homeVod(String imgDomain) {
return new Vod(getJumpId(), getTitle(), getThumbnail(imgDomain));
}
public Vod vod(String imgDomain) {
return new Vod(getId(), getTitle(), getThumbnail(imgDomain), getMask());
} }
public String getValues(List<Value> items, boolean link) { public String getValues(List<Value> items, boolean link) {
@ -94,12 +111,6 @@ public class Data {
return Util.substring(sb.toString()); return Util.substring(sb.toString());
} }
public String getPlayUrl() {
StringBuilder sb = new StringBuilder();
for (BtboDown value : getBtboDownlist()) sb.append(value.getVal()).append("#");
return Util.substring(sb.toString());
}
public static class Value { public static class Value {
@SerializedName(value = "title", alternate = "name") @SerializedName(value = "title", alternate = "name")
@ -118,13 +129,51 @@ public class Data {
} }
} }
public static class BtboDown { public static class SourceListSource {
@SerializedName("val") @SerializedName("name")
private String val; private String name;
@SerializedName("source_list")
private List<SourceList> list;
public String getVal() { public String getName() {
return TextUtils.isEmpty(val) ? "" : val.replaceAll("ftp", "tvbox-xg:ftp"); return TextUtils.isEmpty(name) ? "" : name;
}
public List<SourceList> getList() {
return list == null ? Collections.emptyList() : list;
} }
} }
}
public static class SourceList {
@SerializedName("source_name")
private String name;
@SerializedName("url")
private String url;
public String getName() {
return TextUtils.isEmpty(name) ? "" : name;
}
public String getUrl() {
return TextUtils.isEmpty(url) ? "" : url.replaceAll("ftp", "tvbox-xg:ftp");
}
}
public String getVodFrom() {
List<String> items = new ArrayList<>();
for (SourceListSource source : getSource()) items.add(source.getName());
return TextUtils.join("$$$", items);
}
public String getVodUrl() {
List<String> items = new ArrayList<>();
for (SourceListSource source : getSource()) {
List<String> urls = new ArrayList<>();
for (SourceList item : source.getList()) urls.add(item.getName() + "$" + item.getUrl());
items.add(TextUtils.join("#", urls));
}
return TextUtils.join("$$$", items);
}
}

View File

@ -15,4 +15,4 @@ public class Detail {
public Data getData() { public Data getData() {
return data == null ? new Data() : data; return data == null ? new Data() : data;
} }
} }

View File

@ -18,4 +18,4 @@ public class Resp {
public List<Data> getData() { public List<Data> getData() {
return data == null ? Collections.emptyList() : data; return data == null ? Collections.emptyList() : data;
} }
} }

View File

@ -0,0 +1,52 @@
package com.github.catvod.bean.jianpian;
import android.text.TextUtils;
import com.github.catvod.bean.Vod;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
public class Search {
@SerializedName("data")
private List<Search> data;
@SerializedName("id")
private String id;
@SerializedName(value = "thumbnail", alternate = "path")
private String thumbnail;
@SerializedName("title")
private String title;
@SerializedName("mask")
private String mask;
public static Search objectFrom(String str) {
return new Gson().fromJson(str, Search.class);
}
public String getId() {
return TextUtils.isEmpty(id) ? "" : id;
}
public String getThumbnail(String imgDomain) {
return TextUtils.isEmpty(thumbnail) ? "" : "http://" + imgDomain + thumbnail;
}
public String getTitle() {
return TextUtils.isEmpty(title) ? "" : title;
}
public String getMask() {
return TextUtils.isEmpty(mask) ? "" : mask;
}
public Vod vod(String imgDomain) {
return new Vod(getId(), getTitle(), getThumbnail(imgDomain), getMask());
}
public List<Search> getData() {
return data == null ? Collections.emptyList() : data;
}
}

View File

@ -0,0 +1,124 @@
package com.github.catvod.bean.mqitv;
import android.net.Uri;
import androidx.annotation.Nullable;
import com.github.catvod.net.OkHttp;
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.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Config {
@SerializedName("name")
private String name;
@SerializedName("url")
private String url;
private List<User> users;
private List<Data> data;
private Uri uri;
public static List<Config> arrayFrom(String str) {
Type listType = new TypeToken<List<Config>>() {}.getType();
return new Gson().fromJson(str, listType);
}
public Config(String url) {
this.url = url;
}
public String getName() {
return name == null ? "" : name;
}
public String getUrl() {
return url == null ? "" : url;
}
public List<User> getUsers() {
return users = users == null ? new ArrayList<>() : users;
}
public List<Data> getData() {
return data = data == null ? Data.objectFrom(OkHttp.string(getApi(), 3000)).getData() : data;
}
public Uri getUri() {
return uri = uri == null ? Uri.parse(getUrl()) : uri;
}
public String getApi() {
return getUrl() + "/api/post?item=itv_traffic";
}
public String getPlayUrl(String port, String playing) {
return "http://" + getUri().getHost() + ":" + port + "/" + playing.replace(":/", "");
}
public void loadUser() {
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 : getData()) {
for (String userIp : item.getStat().getUserIpList()) {
if (getUsers().size() >= 5) continue;
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()) {
User u = new User(user, mac).getToken(getUrl());
if (!u.getToken().isEmpty()) getUsers().add(u);
}
}
}
}
public User getUser() {
if (getUsers().isEmpty()) loadUser();
return getUsers().isEmpty() ? new User("", "") : getUsers().get(ThreadLocalRandom.current().nextInt(getUsers().size()));
}
public String getAuth(String id, String token) {
String data = OkHttp.string(getUrl() + "/ualive?cid=" + id + "&token=" + token);
Matcher matcher = Pattern.compile("\"Reason\":\"(.*?)\"", Pattern.CASE_INSENSITIVE).matcher(data);
if (matcher.find()) return matcher.group(1);
return "";
}
public String getM3U8(String id, String token, String port) {
String base = "http://" + getUri().getHost() + ":" + port + "/";
String m3u8 = OkHttp.string(base + id + ".m3u8?token=" + token);
if (m3u8.isEmpty() || m3u8.contains("\"Reason\"")) return "";
String[] lines = m3u8.split("\\r?\\n");
StringBuilder sb = new StringBuilder();
for (String line : lines) {
if (!line.startsWith("#") && !line.startsWith("http")) line = base + line;
sb.append(line).append("\n");
}
return sb.toString();
}
public void clear() {
this.data.clear();
this.users.clear();
this.data = null;
this.users = null;
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof Config)) return false;
Config it = (Config) obj;
return getUrl().equals(it.getUrl());
}
}

View File

@ -0,0 +1,56 @@
package com.github.catvod.bean.mqitv;
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> data;
@SerializedName("id")
private String id;
@SerializedName("name")
private String name;
@SerializedName("playing")
private String playing;
@SerializedName("port")
private String port;
@SerializedName("stat")
private Stat stat;
public static Data objectFrom(String str) {
try {
Data data = new Gson().fromJson(str, Data.class);
return data == null ? new Data() : data;
} catch (Exception e) {
return new Data();
}
}
public List<Data> getData() {
return data == null ? Collections.emptyList() : data;
}
public String getId() {
return id == null ? "" : id;
}
public String getName() {
return name == null ? "" : name;
}
public String getPort() {
return port == null ? "" : port;
}
public String getPlaying() {
return playing == null ? "" : playing;
}
public Stat getStat() {
return stat == null ? new Stat() : stat;
}
}

View File

@ -0,0 +1,16 @@
package com.github.catvod.bean.mqitv;
import com.google.gson.annotations.SerializedName;
import java.util.Collections;
import java.util.List;
public class Stat {
@SerializedName("UserIpList")
private List<String> userIpList;
public List<String> getUserIpList() {
return userIpList == null ? Collections.emptyList() : userIpList;
}
}

View File

@ -0,0 +1,39 @@
package com.github.catvod.bean.mqitv;
import com.github.catvod.net.OkHttp;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class User {
private String id;
private String mac;
private String token;
public User(String id, String mac) {
this.id = id;
this.mac = mac;
}
public String getId() {
return id;
}
public String getMac() {
return mac;
}
public String getToken() {
return token == null ? "" : token;
}
public User getToken(String url) {
String result = OkHttp.string(String.format(Locale.getDefault(), "%s/HSAndroidLogin.ecgi?ty=json&net_account=%s&mac_address1=%s&_=%d", url, getId(), getMac(), System.currentTimeMillis()));
Pattern pattern = Pattern.compile("\"Token\"\\s*:\\s*\"(.*?)\"", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(result);
token = matcher.find() ? matcher.group(1) : "";
return this;
}
}

View File

@ -1,695 +0,0 @@
package com.github.catvod.bean.xpath;
import com.github.catvod.crawler.SpiderDebug;
import org.json.JSONObject;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Rule {
/**
* user-agent
*/
private String ua;
/**
* 取得分類和首頁推薦的Url
*/
private String homeUrl;
/**
* 分類節點 xpath
*/
private String cateNode;
/**
* 分類節點名 xpath
*/
private String cateName;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern cateNameR;
/**
* 分類節點 id xpath
*/
private String cateId;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern cateIdR;
/**
* 手動指定分類如果有則不從 homeUrl 中獲取分類
*/
private final LinkedHashMap<String, String> cateManual = new LinkedHashMap<>();
/**
* 篩選
*/
private JSONObject filter;
/**
* 更新推薦影片節點 xpath
*/
private String homeVodNode;
/**
* 更新推薦影片名稱 xpath
*/
private String homeVodName;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern homeVodNameR;
/**
* 更新推薦影片 id xpath
*/
private String homeVodId;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern homeVodIdR;
/**
* 更新推薦影片圖片 xpath
*/
private String homeVodImg;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern homeVodImgR;
/**
* 更新推薦影片簡介 xpath
*/
private String homeVodMark;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern homeVodMarkR;
/**
* 分類頁地址
*/
private String cateUrl;
/**
* 分類頁影片節點 xpath
*/
private String cateVodNode;
/**
* 分類頁影片名稱 xpath
*/
private String cateVodName;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern cateVodNameR;
/**
* 分類頁影片影片id xpath
*/
private String cateVodId;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern cateVodIdR;
/**
* 分類頁影片影片圖片 xpath
*/
private String cateVodImg;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern cateVodImgR;
/**
* 分類頁影片影片簡介 xpath
*/
private String cateVodMark;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern cateVodMarkR;
/**
* 詳情頁面
*/
private String dtUrl;
/**
* 詳情節點 xpath
*/
private String dtNode;
/**
* 詳情影片 xpath
*/
private String dtName;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtNameR;
/**
* 詳情影片圖片 xpath
*/
private String dtImg;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtImgR;
/**
* 詳情影片分類 xpath
*/
private String dtCate;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtCateR;
/**
* 詳情影片年份 xpath
*/
private String dtYear;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtYearR;
/**
* 詳情影片地區 xpath
*/
private String dtArea;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtAreaR;
/**
* 詳情影片簡介 xpath
*/
private String dtMark;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtMarkR;
/**
* 詳情演員 xpath
*/
private String dtActor;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtActorR;
/**
* 詳情導演 xpath
*/
private String dtDirector;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtDirectorR;
/**
* 詳情說明 xpath
*/
private String dtDesc;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern dtDescR;
/**
* 詳情播放來源節點
*/
private String dtFromNode;
/**
* 詳情播放來源名稱 xpath
*/
private String dtFromName;
/**
* 詳情
*/
private Pattern dtFromNameR;
/**
* 詳情播放地址列表節點 xpath
*/
private String dtUrlNode;
/**
* 詳情播放地址節點 xpath
*/
private String dtUrlSubNode;
/**
* 詳情播放地址id xpath
*/
private String dtUrlId;
/**
* 詳情
*/
private Pattern dtUrlIdR;
/**
* 詳情播放地址名稱 xpath
*/
private String dtUrlName;
/**
* 詳情
*/
private Pattern dtUrlNameR;
/**
* 播放頁面url
*/
private String playUrl;
/**
* 播放解析調用ua
*/
private String playUa;
/**
* 播放解析調用referer
*/
private String playReferer;
/**
* 搜尋頁地址
*/
private String searchUrl;
/**
* 搜尋頁影片節點 xpath
*/
private String scVodNode;
/**
* 搜尋頁影片名稱 xpath
*/
private String scVodName;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern scVodNameR;
/**
* 搜尋頁影片id xpath
*/
private String scVodId;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern scVodIdR;
/**
* 搜尋頁影片圖片 xpath
*/
private String scVodImg;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern scVodImgR;
/**
* 搜尋頁影片簡介 xpath
*/
private String scVodMark;
/**
* 正則對取到的數據進行二次處理
*/
private Pattern scVodMarkR;
private static Pattern getPattern(JSONObject json, String key) {
String v = json.optString(key).trim();
if (v.isEmpty())
return null;
else {
try {
return Pattern.compile(v);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
return null;
}
private static String doReplaceRegex(Pattern pattern, String src) {
if (pattern == null)
return src;
try {
Matcher matcher = pattern.matcher(src);
if (matcher.find()) {
return matcher.group(1).trim();
}
} catch (Exception e) {
SpiderDebug.log(e);
}
return src;
}
public static Rule fromJson(String json) {
try {
JSONObject jsonObj = new JSONObject(json);
Rule rule = new Rule();
rule.ua = jsonObj.optString("ua");
rule.homeUrl = jsonObj.optString("homeUrl").trim();
rule.cateNode = jsonObj.optString("cateNode").trim();
rule.cateName = jsonObj.optString("cateName").trim();
rule.cateNameR = getPattern(jsonObj, "cateNameR");
rule.cateId = jsonObj.optString("cateId").trim();
rule.cateIdR = getPattern(jsonObj, "cateIdR");
JSONObject navs = jsonObj.optJSONObject("cateManual");
if (navs != null) {
Iterator<String> keys = navs.keys();
while (keys.hasNext()) {
String name = keys.next();
rule.cateManual.put(name.trim(), navs.getString(name).trim());
}
}
rule.filter = jsonObj.optJSONObject("filter");
rule.homeVodNode = jsonObj.optString("homeVodNode").trim();
rule.homeVodName = jsonObj.optString("homeVodName").trim();
rule.homeVodNameR = getPattern(jsonObj, "homeVodNameR");
rule.homeVodId = jsonObj.optString("homeVodId").trim();
rule.homeVodIdR = getPattern(jsonObj, "homeVodIdR");
rule.homeVodImg = jsonObj.optString("homeVodImg").trim();
rule.homeVodImgR = getPattern(jsonObj, "homeVodImgR");
rule.homeVodMark = jsonObj.optString("homeVodMark").trim();
rule.homeVodMarkR = getPattern(jsonObj, "homeVodMarkR");
rule.cateUrl = jsonObj.optString("cateUrl").trim();
rule.cateVodNode = jsonObj.optString("cateVodNode").trim();
rule.cateVodName = jsonObj.optString("cateVodName").trim();
rule.cateVodNameR = getPattern(jsonObj, "cateVodNameR");
rule.cateVodId = jsonObj.optString("cateVodId").trim();
rule.cateVodIdR = getPattern(jsonObj, "cateVodIdR");
rule.cateVodImg = jsonObj.optString("cateVodImg").trim();
rule.cateVodImgR = getPattern(jsonObj, "cateVodImgR");
rule.cateVodMark = jsonObj.optString("cateVodMark").trim();
rule.cateVodMarkR = getPattern(jsonObj, "cateVodMarkR");
rule.dtUrl = jsonObj.optString("dtUrl");
rule.dtNode = jsonObj.optString("dtNode");
rule.dtName = jsonObj.optString("dtName");
rule.dtNameR = getPattern(jsonObj, "dtNameR");
rule.dtImg = jsonObj.optString("dtImg");
rule.dtImgR = getPattern(jsonObj, "dtImgR");
rule.dtCate = jsonObj.optString("dtCate");
rule.dtCateR = getPattern(jsonObj, "dtCateR");
rule.dtYear = jsonObj.optString("dtYear");
rule.dtYearR = getPattern(jsonObj, "dtYearR");
rule.dtArea = jsonObj.optString("dtArea");
rule.dtAreaR = getPattern(jsonObj, "dtAreaR");
rule.dtMark = jsonObj.optString("dtMark");
rule.dtMarkR = getPattern(jsonObj, "dtMarkR");
rule.dtActor = jsonObj.optString("dtActor");
rule.dtActorR = getPattern(jsonObj, "dtActorR");
rule.dtDirector = jsonObj.optString("dtDirector");
rule.dtDirectorR = getPattern(jsonObj, "dtDirectorR");
rule.dtDesc = jsonObj.optString("dtDesc");
rule.dtDescR = getPattern(jsonObj, "dtDescR");
rule.dtFromNode = jsonObj.optString("dtFromNode");
rule.dtFromName = jsonObj.optString("dtFromName");
rule.dtFromNameR = getPattern(jsonObj, "dtFromNameR");
rule.dtUrlNode = jsonObj.optString("dtUrlNode");
rule.dtUrlSubNode = jsonObj.optString("dtUrlSubNode");
rule.dtUrlId = jsonObj.optString("dtUrlId");
rule.dtUrlIdR = getPattern(jsonObj, "dtUrlIdR");
rule.dtUrlName = jsonObj.optString("dtUrlName");
rule.dtUrlNameR = getPattern(jsonObj, "dtUrlNameR");
rule.playUrl = jsonObj.optString("playUrl");
rule.playUa = jsonObj.optString("playUa");
rule.playReferer = jsonObj.optString("playReferer");
rule.searchUrl = jsonObj.optString("searchUrl");
rule.scVodNode = jsonObj.optString("scVodNode").trim();
rule.scVodName = jsonObj.optString("scVodName").trim();
rule.scVodNameR = getPattern(jsonObj, "scVodNameR");
rule.scVodId = jsonObj.optString("scVodId").trim();
rule.scVodIdR = getPattern(jsonObj, "scVodIdR");
rule.scVodImg = jsonObj.optString("scVodImg").trim();
rule.scVodImgR = getPattern(jsonObj, "scVodImgR");
rule.scVodMark = jsonObj.optString("scVodMark").trim();
rule.scVodMarkR = getPattern(jsonObj, "scVodMarkR");
return rule;
} catch (Exception e) {
SpiderDebug.log(e);
}
return null;
}
public String getUa() {
return ua;
}
public String getHomeUrl() {
return homeUrl;
}
public String getCateNode() {
return cateNode;
}
public String getCateName() {
return cateName;
}
public String getCateNameR(String src) {
return doReplaceRegex(cateNameR, src);
}
public String getCateId() {
return cateId;
}
public String getCateIdR(String src) {
return doReplaceRegex(cateIdR, src);
}
public LinkedHashMap<String, String> getCateManual() {
return cateManual;
}
public JSONObject getFilter() {
return filter;
}
public String getHomeVodNode() {
return homeVodNode;
}
public String getHomeVodName() {
return homeVodName;
}
public String getHomeVodNameR(String src) {
return doReplaceRegex(homeVodNameR, src);
}
public String getHomeVodId() {
return homeVodId;
}
public String getHomeVodIdR(String src) {
return doReplaceRegex(homeVodIdR, src);
}
public String getHomeVodImg() {
return homeVodImg;
}
public String getHomeVodImgR(String src) {
return doReplaceRegex(homeVodImgR, src);
}
public String getHomeVodMark() {
return homeVodMark;
}
public String getHomeVodMarkR(String src) {
return doReplaceRegex(homeVodMarkR, src);
}
public String getCateUrl() {
return cateUrl;
}
public String getCateVodNode() {
return cateVodNode;
}
public String getCateVodName() {
return cateVodName;
}
public String getCateVodNameR(String src) {
return doReplaceRegex(cateVodNameR, src);
}
public String getCateVodId() {
return cateVodId;
}
public String getCateVodIdR(String src) {
return doReplaceRegex(cateVodIdR, src);
}
public String getCateVodImg() {
return cateVodImg;
}
public String getCateVodImgR(String src) {
return doReplaceRegex(cateVodImgR, src);
}
public String getCateVodMark() {
return cateVodMark;
}
public String getCateVodMarkR(String src) {
return doReplaceRegex(cateVodNameR, src);
}
public String getDetailUrl() {
return dtUrl;
}
public String getDetailNode() {
return dtNode;
}
public String getDetailName() {
return dtName;
}
public String getDetailNameR(String src) {
return doReplaceRegex(dtNameR, src);
}
public String getDetailImg() {
return dtImg;
}
public String getDetailImgR(String src) {
return doReplaceRegex(dtImgR, src);
}
public String getDetailCate() {
return dtCate;
}
public String getDetailCateR(String src) {
return doReplaceRegex(dtCateR, src);
}
public String getDetailYear() {
return dtYear;
}
public String getDetailYearR(String src) {
return doReplaceRegex(dtYearR, src);
}
public String getDetailArea() {
return dtArea;
}
public String getDetailAreaR(String src) {
return doReplaceRegex(dtAreaR, src);
}
public String getDetailMark() {
return dtMark;
}
public String getDetailMarkR(String src) {
return doReplaceRegex(dtMarkR, src);
}
public String getDetailActor() {
return dtActor;
}
public String getDetailActorR(String src) {
return doReplaceRegex(dtActorR, src);
}
public String getDetailDirector() {
return dtDirector;
}
public String getDetailDirectorR(String src) {
return doReplaceRegex(dtDirectorR, src);
}
public String getDetailDesc() {
return dtDesc;
}
public String getDetailDescR(String src) {
return doReplaceRegex(dtDescR, src);
}
public String getDetailFromNode() {
return dtFromNode;
}
public String getDetailFromName() {
return dtFromName;
}
public String getDetailFromNameR(String src) {
return doReplaceRegex(dtFromNameR, src);
}
public String getDetailUrlNode() {
return dtUrlNode;
}
public String getDetailUrlSubNode() {
return dtUrlSubNode;
}
public String getDetailUrlId() {
return dtUrlId;
}
public String getDetailUrlIdR(String src) {
return doReplaceRegex(dtUrlIdR, src);
}
public String getDetailUrlName() {
return dtUrlName;
}
public String getDetailUrlNameR(String src) {
return doReplaceRegex(dtUrlNameR, src);
}
public String getPlayUrl() {
return playUrl;
}
public String getPlayUa() {
return playUa;
}
public String getPlayReferer() {
return playReferer;
}
public String getSearchUrl() {
return searchUrl;
}
public String getSearchVodNode() {
return scVodNode;
}
public String getSearchVodName() {
return scVodName;
}
public String getSearchVodNameR(String src) {
return doReplaceRegex(scVodNameR, src);
}
public String getSearchVodId() {
return scVodId;
}
public String getSearchVodIdR(String src) {
return doReplaceRegex(scVodIdR, src);
}
public String getSearchVodImg() {
return scVodImg;
}
public String getSearchVodImgR(String src) {
return doReplaceRegex(scVodImgR, src);
}
public String getSearchVodMark() {
return scVodMark;
}
public String getSearchVodMarkR(String src) {
return doReplaceRegex(scVodMarkR, src);
}
}

View File

@ -1,106 +0,0 @@
package com.github.catvod.debug;
import android.app.Activity;
import android.os.Bundle;
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.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.Logger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends Activity {
private ExecutorService executor;
private Spider spider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button homeContent = findViewById(R.id.homeContent);
Button homeVideoContent = findViewById(R.id.homeVideoContent);
Button categoryContent = findViewById(R.id.categoryContent);
Button detailContent = findViewById(R.id.detailContent);
Button playerContent = findViewById(R.id.playerContent);
Button searchContent = findViewById(R.id.searchContent);
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));
Logger.addLogAdapter(new AndroidLogAdapter());
executor = Executors.newCachedThreadPool();
executor.execute(this::initSpider);
}
private void initSpider() {
try {
Init.init(getApplicationContext());
spider = new PTT();
spider.init(this, "");
} catch (Throwable e) {
e.printStackTrace();
}
}
public void homeContent() {
try {
Logger.t("homeContent").d(spider.homeContent(true));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void homeVideoContent() {
try {
Logger.t("homeVideoContent").d(spider.homeVideoContent());
} catch (Throwable e) {
e.printStackTrace();
}
}
public void categoryContent() {
try {
HashMap<String, String> extend = new HashMap<>();
extend.put("c", "19");
extend.put("year", "2024");
Logger.t("categoryContent").d(spider.categoryContent("3", "2", true, extend));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void detailContent() {
try {
Logger.t("detailContent").d(spider.detailContent(Arrays.asList("78702")));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void playerContent() {
try {
Logger.t("playerContent").d(spider.playerContent("", "382044/1/78", new ArrayList<>()));
} catch (Throwable e) {
e.printStackTrace();
}
}
public void searchContent() {
try {
Logger.t("searchContent").d(spider.searchContent("我的人间烟火", false));
} catch (Throwable e) {
e.printStackTrace();
}
}
}

View File

@ -1,7 +1,7 @@
package com.github.catvod.js; package com.github.catvod.js;
import com.github.catvod.js.utils.Parser;
import com.github.catvod.js.utils.JSUtil; import com.github.catvod.js.utils.JSUtil;
import com.github.catvod.js.utils.Parser;
import com.whl.quickjs.wrapper.JSArray; import com.whl.quickjs.wrapper.JSArray;
import com.whl.quickjs.wrapper.JSMethod; import com.whl.quickjs.wrapper.JSMethod;
import com.whl.quickjs.wrapper.QuickJSContext; import com.whl.quickjs.wrapper.QuickJSContext;

View File

@ -24,6 +24,8 @@ import okhttp3.Response;
public class OkHttp { public class OkHttp {
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(15);
public static final String POST = "POST"; public static final String POST = "POST";
public static final String GET = "GET"; public static final String GET = "GET";
@ -37,28 +39,28 @@ public class OkHttp {
return Loader.INSTANCE; return Loader.INSTANCE;
} }
public static Response newCall(Request request) throws IOException {
return client().newCall(request).execute();
}
public static Response newCall(String url) throws IOException { public static Response newCall(String url) throws IOException {
return client().newCall(new Request.Builder().url(url).build()).execute(); return client().newCall(new Request.Builder().url(url).build()).execute();
} }
public static Response newCall(String url, Map<String, String> header) throws IOException {
return client().newCall(new Request.Builder().url(url).headers(Headers.of(header)).build()).execute();
}
public static String string(String url) { public static String string(String url) {
return string(url, null); return string(url, null);
} }
public static String string(String url, long timeout) {
return string(url, null, null, timeout);
}
public static String string(String url, Map<String, String> header) { public static String string(String url, Map<String, String> header) {
return string(url, null, header); return string(url, null, header);
} }
public static String string(String url, Map<String, String> params, Map<String, String> header) { public static String string(String url, Map<String, String> params, Map<String, String> header) {
return url.startsWith("http") ? new OkRequest(GET, url, params, header).execute(client()).getBody() : ""; return new OkRequest(GET, url, params, header).execute(client()).getBody();
}
public static String string(String url, Map<String, String> params, Map<String, String> header, long timeout) {
return new OkRequest(GET, url, params, header).execute(client(timeout)).getBody();
} }
public static String post(String url, Map<String, String> params) { public static String post(String url, Map<String, String> params) {
@ -94,7 +96,11 @@ public class OkHttp {
} }
private static OkHttpClient.Builder getBuilder() { private static OkHttpClient.Builder getBuilder() {
return new OkHttpClient.Builder().dns(safeDns()).connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS).hostnameVerifier((hostname, session) -> true).sslSocketFactory(getSSLContext().getSocketFactory(), trustAllCertificates()); return new OkHttpClient.Builder().dns(safeDns()).connectTimeout(TIMEOUT, TimeUnit.MILLISECONDS).readTimeout(TIMEOUT, TimeUnit.MILLISECONDS).writeTimeout(TIMEOUT, TimeUnit.MILLISECONDS).hostnameVerifier((hostname, session) -> true).sslSocketFactory(getSSLContext().getSocketFactory(), trustAllCertificates());
}
private static OkHttpClient client(long timeout) {
return client().newBuilder().connectTimeout(timeout, TimeUnit.MILLISECONDS).readTimeout(timeout, TimeUnit.MILLISECONDS).writeTimeout(timeout, TimeUnit.MILLISECONDS).build();
} }
private static OkHttpClient client() { private static OkHttpClient client() {

View File

@ -2,6 +2,7 @@ package com.github.catvod.net;
import android.text.TextUtils; import android.text.TextUtils;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.utils.Util; import com.github.catvod.utils.Util;
import java.io.IOException; import java.io.IOException;
@ -37,10 +38,10 @@ class OkRequest {
this.method = method; this.method = method;
this.params = params; this.params = params;
this.header = header; this.header = header;
getInstance(); this.buildRequest();
} }
private void getInstance() { private void buildRequest() {
Request.Builder builder = new Request.Builder(); Request.Builder builder = new Request.Builder();
if (method.equals(OkHttp.GET) && params != null) setParams(); if (method.equals(OkHttp.GET) && params != null) setParams();
if (method.equals(OkHttp.POST)) builder.post(getRequestBody()); if (method.equals(OkHttp.POST)) builder.post(getRequestBody());
@ -62,10 +63,10 @@ class OkRequest {
} }
public OkResult execute(OkHttpClient client) { public OkResult execute(OkHttpClient client) {
try { try (Response res = client.newCall(request).execute()) {
Response response = client.newCall(request).execute(); return new OkResult(res.code(), res.body().string(), res.headers().toMultimap());
return new OkResult(response.code(), response.body().string(), response.headers().toMultimap());
} catch (IOException e) { } catch (IOException e) {
SpiderDebug.log(e);
return new OkResult(); return new OkResult();
} }
} }

View File

@ -18,9 +18,6 @@ import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Util; import com.github.catvod.utils.Util;
import org.json.JSONObject; import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -270,29 +267,6 @@ public class AList extends Spider {
@Override @Override
public List<Vod> call() { public List<Vod> call() {
List<Vod> alist = alist();
return !alist.isEmpty() ? alist : xiaoya();
}
private List<Vod> xiaoya() {
List<Vod> list = new ArrayList<>();
Document doc = Jsoup.parse(OkHttp.string(drive.searchApi(keyword)));
for (Element a : doc.select("ul > a")) {
String[] splits = a.text().split("#");
if (!splits[0].contains("/")) continue;
int index = splits[0].lastIndexOf("/");
boolean file = Util.isMedia(splits[0]);
Item item = new Item();
item.setType(file ? 0 : 1);
item.setThumb(splits.length > 3 ? splits[4] : "");
item.setPath("/" + splits[0].substring(0, index));
item.setName(splits[0].substring(index + 1));
list.add(item.getVod(drive));
}
return list;
}
private List<Vod> alist() {
try { try {
List<Vod> list = new ArrayList<>(); List<Vod> list = new ArrayList<>();
String response = post(drive, drive.searchApi(), drive.params(keyword)); String response = post(drive, drive.searchApi(), drive.params(keyword));

View File

@ -198,7 +198,7 @@ public class Bili extends Spider {
String dan = "https://api.bilibili.com/x/v1/dm/list.so?oid=".concat(cid); String dan = "https://api.bilibili.com/x/v1/dm/list.so?oid=".concat(cid);
for (int i = 0; i < acceptDesc.length; i++) { for (int i = 0; i < acceptDesc.length; i++) {
url.add(acceptDesc[i]); url.add(acceptDesc[i]);
url.add(Proxy.getUrl() + "?do=bili" + "&aid=" + aid + "&cid=" + cid + "&qn=" + acceptQuality[i] + "&type=mpd"); url.add("proxy://do=bili" + "&aid=" + aid + "&cid=" + cid + "&qn=" + acceptQuality[i] + "&type=mpd");
} }
return Result.get().url(url).danmaku(Arrays.asList(Danmaku.create().name("B站").url(dan))).dash().header(getHeader()).string(); return Result.get().url(url).danmaku(Arrays.asList(Danmaku.create().name("B站").url(dan))).dash().header(getHeader()).string();
} }

View File

@ -1,138 +0,0 @@
package com.github.catvod.spider;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Filter;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.crawler.Spider;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Util;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
public class Hanime extends Spider {
private static final String siteUrl = "https://hanime1.me";
private HashMap<String, String> getHeaders() {
HashMap<String, String> headers = new HashMap<>();
headers.put("User-Agent", Util.CHROME);
return headers;
}
private Filter getFilter(String name, String key, List<String> texts) {
List<Filter.Value> values = new ArrayList<>();
if (!key.equals("by")) values.add(new Filter.Value("全部", ""));
for (String text : texts) {
if (text.isEmpty()) continue;
values.add(new Filter.Value(text));
}
return new Filter(key, name, values);
}
@Override
public String homeContent(boolean filter) throws Exception {
List<Vod> list = new ArrayList<>();
List<Class> classes = new ArrayList<>();
LinkedHashMap<String, List<Filter>> filters = new LinkedHashMap<>();
Document doc1 = Jsoup.parse(OkHttp.string(siteUrl.concat("/search?genre=裏番"), getHeaders()));
List<String> sorts = doc1.select("div.hentai-sort-options-wrapper").eachText();
List<String> years = doc1.getElementById("year").select("option").eachAttr("value");
Document doc2 = Jsoup.parse(OkHttp.string(siteUrl, getHeaders()));
for (Element element : doc2.select("a.nav-item")) {
String text = element.text();
if (text.equals("新番預告") || text.equals("H漫畫")) continue;
classes.add(new Class(text));
List<Filter> array = new ArrayList<>();
array.add(getFilter("排序", "by", sorts));
array.add(getFilter("年份", "year", years));
filters.put(text, array);
}
for (Element element : doc2.select("a")) {
if (element.attr("href").contains("watch")) {
String pic = element.select("div > img").attr("src");
String url = element.attr("href");
String name = element.select("div > div").text();
String id = url.split("=")[1];
if (name.contains("smart_display") || name.isEmpty()) continue;
list.add(new Vod(id, name, pic));
}
}
return Result.string(classes, list, filters);
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
List<Vod> list = new ArrayList<>();
if (extend.get("by") == null) extend.put("by", "最新上市");
if (extend.get("year") == null) extend.put("year", "");
String target = siteUrl.concat("/search?genre=").concat(tid).concat("&page=").concat(pg).concat("&sort=").concat(extend.get("by")).concat("&year=").concat(extend.get("year"));
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
for (Element element : doc.select("div.col-xs-6")) {
String pic = element.select("img").get(1).attr("src");
String url = element.select("a.overlay").attr("href");
String name = element.select("div.card-mobile-title").text();
String id = url.split("=")[1];
list.add(new Vod(id, name, pic));
}
if (list.isEmpty()) {
for (Element element : doc.select("a")) {
if (element.attr("href").contains("watch")) {
String pic = element.select("div > img").attr("src");
String url = element.attr("href");
String name = element.select("div > div").text();
String id = url.split("=")[1];
if (name.contains("smart_display")) continue;
list.add(new Vod(id, name, pic));
}
}
}
return Result.string(list);
}
@Override
public String detailContent(List<String> ids) throws Exception {
Document doc = Jsoup.parse(OkHttp.string(siteUrl.concat("/watch?v=").concat(ids.get(0)), getHeaders()));
String name = doc.getElementById("shareBtn-title").text();
JSONObject json = new JSONObject(doc.select("script[type=application/ld+json]").html().trim());
String content = json.optString("description");
String pic = json.optJSONArray("thumbnailUrl").optString(0);
String url = json.optString("contentUrl");;
Vod vod = new Vod();
vod.setVodId(ids.get(0));
vod.setVodPic(pic);
vod.setVodName(name);
vod.setVodContent(content);
vod.setVodPlayFrom("Hanime1");
vod.setVodPlayUrl("播放$" + url);
return Result.string(vod);
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
List<Vod> list = new ArrayList<>();
String target = siteUrl.concat("/search?query=").concat(key).concat("&genre=&sort=&year=&month=&duration=");
Document doc = Jsoup.parse(OkHttp.string(target, getHeaders()));
for (Element element : doc.select("div.col-xs-6")) {
String pic = element.select("img").get(1).attr("src");
String url = element.select("a.overlay").attr("href");
String name = element.select("div.card-mobile-title").text();
String id = url.split("=")[1];
list.add(new Vod(id, name, pic));
}
return Result.string(list);
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
return Result.get().url(id).header(getHeaders()).string();
}
}

View File

@ -1,18 +1,17 @@
package com.github.catvod.spider; package com.github.catvod.spider;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.app.Application; import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.os.Bundle;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.github.catvod.crawler.SpiderDebug; import com.github.catvod.crawler.SpiderDebug;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -20,6 +19,7 @@ public class Init {
private final ExecutorService executor; private final ExecutorService executor;
private final Handler handler; private final Handler handler;
private Activity activity;
private Application app; private Application app;
private static class Loader { private static class Loader {
@ -39,52 +39,68 @@ public class Init {
return get().app; return get().app;
} }
public static Activity activity() {
return get().activity;
}
private void setActivity(Activity activity) {
this.activity = activity;
}
public static void init(Context context) { public static void init(Context context) {
get().app = ((Application) context); get().app = ((Application) context);
SpiderDebug.log("自定義爬蟲代碼載入成功!"); SpiderDebug.log("自定義爬蟲代碼載入成功!");
registerActivityLifecycleCallbacks();
Proxy.init();
} }
public static void execute(Runnable runnable) { public static void execute(Runnable runnable) {
get().executor.execute(runnable); get().executor.execute(runnable);
} }
public static void run(Runnable runnable) { public static void post(Runnable runnable) {
get().handler.post(runnable); get().handler.post(runnable);
} }
public static void run(Runnable runnable, int delay) { public static void post(Runnable runnable, int delay) {
get().handler.postDelayed(runnable, delay); get().handler.postDelayed(runnable, delay);
} }
public static void checkPermission() { private static void registerActivityLifecycleCallbacks() {
try { get().app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
Activity activity = Init.getActivity(); @Override
if (activity == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return; public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) return; if (activity != activity()) get().setActivity(activity);
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 9999);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Activity getActivity() throws Exception {
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
activitiesField.setAccessible(true);
Map<?, ?> activities = (Map<?, ?>) activitiesField.get(activityThread);
for (Object activityRecord : activities.values()) {
Class<?> activityRecordClass = activityRecord.getClass();
Field pausedField = activityRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Activity activity = (Activity) activityField.get(activityRecord);
SpiderDebug.log(activity.getComponentName().getClassName());
return activity;
} }
}
return null; @Override
public void onActivityStarted(@NonNull Activity activity) {
if (activity != activity()) get().setActivity(activity);
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
if (activity != activity()) get().setActivity(activity);
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
if (activity == activity()) get().setActivity(null);
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
if (activity == activity()) get().setActivity(null);
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
if (activity == activity()) get().setActivity(null);
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
});
} }
} }

View File

@ -1,114 +1,138 @@
package com.github.catvod.spider; package com.github.catvod.spider;
import android.content.Context; import android.content.Context;
import com.github.catvod.bean.Class; import com.github.catvod.bean.Class;
import com.github.catvod.bean.Result; import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod; import com.github.catvod.bean.Vod;
import com.github.catvod.bean.jianpian.Data; import com.github.catvod.bean.jianpian.Data;
import com.github.catvod.bean.jianpian.Detail; import com.github.catvod.bean.jianpian.Detail;
import com.github.catvod.bean.jianpian.Resp; import com.github.catvod.bean.jianpian.Resp;
import com.github.catvod.crawler.Spider; import com.github.catvod.bean.jianpian.Search;
import com.github.catvod.net.OkHttp; import com.github.catvod.crawler.Spider;
import com.github.catvod.utils.Json; import com.github.catvod.net.OkHttp;
import com.google.gson.Gson;
import java.net.URLEncoder; import com.google.gson.JsonObject;
import java.util.ArrayList; import com.google.gson.JsonParser;
import java.util.Arrays;
import java.util.HashMap; import java.net.URLEncoder;
import java.util.List; import java.util.ArrayList;
import java.util.Map; import java.util.Arrays;
import java.util.HashMap;
/** import java.util.List;
* Qile import java.util.Map;
*/
public class Jianpian extends Spider { /**
* Qile
private final String siteUrl = "http://api2.rinhome.com"; */
private String extend; public class Jianpian extends Spider {
private Map<String, String> getHeader() { private String siteUrl = "https://ev5356.970xw.com";
Map<String, String> headers = new HashMap<>(); private String imgDomain;
headers.put("User-Agent", "jianpian-android/360"); private String extend;
headers.put("JPAUTH", "y261ow7kF2dtzlxh1GS9EB8nbTxNmaK/QQIAjctlKiEv");
return headers; private Map<String, String> getHeader() {
} Map<String, String> headers = new HashMap<>();
headers.put("User-Agent", "Mozilla/5.0 (Linux; Android 9; V2196A Build/PQ3A.190705.08211809; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/91.0.4472.114 Mobile Safari/537.36;webank/h5face;webank/1.0;netType:NETWORK_WIFI;appVersion:416;packageName:com.jp3.xg3");
@Override headers.put("Referer", siteUrl);
public void init(Context context, String extend) throws Exception { return headers;
this.extend = extend; }
}
@Override
@Override public void init(Context context, String extend) throws Exception {
public String homeContent(boolean filter) throws Exception { this.extend = extend;
List<Class> classes = new ArrayList<>(); JsonObject domains = new Gson().fromJson(OkHttp.string("https://dns.alidns.com/resolve?name=swrdsfeiujo25sw.cc&type=TXT"), JsonObject.class);
List<String> typeIds = Arrays.asList("0", "1", "2", "3", "4"); String parts = domains.getAsJsonArray("Answer").get(0).getAsJsonObject().get("data").getAsString();
List<String> typeNames = Arrays.asList("全部", "电影", "电视剧", "动漫", "综艺"); parts = parts.replace("\"", "");
for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i))); String[] domain = parts.split(",");
return Result.string(classes, Json.parse(OkHttp.string(extend))); for (String d : domain) {
} siteUrl = "https://wangerniu." + d;
String json = OkHttp.string(siteUrl + "/api/appAuthConfig");
@Override if (!json.isEmpty()) {
public String homeVideoContent() { JsonObject root = new Gson().fromJson(json, JsonObject.class);
List<Vod> list = new ArrayList<>(); imgDomain = root.getAsJsonObject("data").get("imgDomain").getAsString();
String url = siteUrl + "/api/slide/list?code=unknown9039b6856c3a3306&pos_id=888&channel=wandoujia"; break;
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader())); }
for (Data data : resp.getData()) list.add(data.vod()); }
return Result.string(list); }
}
@Override
@Override public String homeContent(boolean filter) throws Exception {
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception { List<Class> classes = new ArrayList<>();
if (tid.endsWith("/{pg}")) return searchContent(tid.split("/")[0], pg); List<String> typeIds = Arrays.asList("1", "2", "3", "4", "50", "99");
List<Vod> list = new ArrayList<>(); List<String> typeNames = Arrays.asList("電影", "電視劇", "動漫", "綜藝", "紀錄片", "Netflix");
HashMap<String, String> ext = new HashMap<>(); for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
if (extend != null && extend.size() > 0) ext.putAll(extend); return Result.string(classes, JsonParser.parseString(OkHttp.string(extend)));
String cateId = ext.get("cateId") == null ? tid : ext.get("cateId"); }
String area = ext.get("area") == null ? "0" : ext.get("area");
String year = ext.get("year") == null ? "0" : ext.get("year"); @Override
String by = ext.get("by") == null ? "hot" : ext.get("by"); public String homeVideoContent() {
String url = siteUrl + String.format("/api/crumb/list?area=%s&category_id=%s&page=%s&type=0&limit=24&sort=%s&year=%s", area, cateId, pg, by, year); List<Vod> list = new ArrayList<>();
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader())); String url = siteUrl + "/api/slide/list?pos_id=88";
for (Data data : resp.getData()) list.add(data.vod()); Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
return Result.string(list); for (Data data : resp.getData()) list.add(data.homeVod(imgDomain));
} return Result.string(list);
}
@Override
public String detailContent(List<String> ids) throws Exception { @Override
String url = siteUrl + "/api/node/detail?channel=wandoujia&token=&id=" + ids.get(0); public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
Data data = Detail.objectFrom(OkHttp.string(url, getHeader())).getData(); if (tid.endsWith("/{pg}")) return searchContent(tid.split("/")[0], pg);
Vod vod = data.vod(); if (tid.equals("50") || tid.equals("99") || tid.equals("111")) {
vod.setVodPlayFrom("Jianpian"); List<Vod> list = new ArrayList<>();
vod.setVodYear(data.getYear()); String url = siteUrl + String.format("/api/dyTag/list?category_id=%s&page=%s", tid, pg);
vod.setVodArea(data.getArea()); Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
vod.setTypeName(data.getTypes()); for (Data data : resp.getData()) for (Data dataList : data.getDataList()) list.add(dataList.vod(imgDomain));
vod.setVodActor(data.getActors()); return Result.get().page().vod(list).string();
vod.setVodPlayUrl(data.getPlayUrl()); } else {
vod.setVodDirector(data.getDirectors()); List<Vod> list = new ArrayList<>();
vod.setVodContent(data.getDescription()); HashMap<String, String> ext = new HashMap<>();
return Result.string(vod); if (extend != null && !extend.isEmpty()) ext.putAll(extend);
} String area = ext.get("area") == null ? "0" : ext.get("area");
String year = ext.get("year") == null ? "0" : ext.get("year");
@Override String by = ext.get("by") == null ? "updata" : ext.get("by");
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception { String url = siteUrl + String.format("/api/crumb/list?fcate_pid=%s&area=%s&year=%s&type=0&sort=%s&page=%s&category_id=", tid, area, year, by, pg);
return Result.get().url(id).header(getHeader()).string(); Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader()));
} for (Data data : resp.getData()) list.add(data.vod(imgDomain));
return Result.string(list);
@Override }
public String searchContent(String key, boolean quick) throws Exception { }
return searchContent(key, "1");
} @Override
public String detailContent(List<String> ids) throws Exception {
@Override String url = siteUrl + "/api/video/detailv2?id=" + ids.get(0);
public String searchContent(String key, boolean quick, String pg) throws Exception { Data data = Detail.objectFrom(OkHttp.string(url, getHeader())).getData();
return searchContent(key, pg); Vod vod = data.vod(imgDomain);
} vod.setVodPlayFrom(data.getVodFrom());
vod.setVodYear(data.getYear());
public String searchContent(String key, String pg) throws Exception { vod.setVodArea(data.getArea());
List<Vod> list = new ArrayList<>(); vod.setTypeName(data.getTypes());
String url = siteUrl + "/api/video/search?page=" + pg + "&key=" + URLEncoder.encode(key); vod.setVodActor(data.getActors());
Resp resp = Resp.objectFrom(OkHttp.string(url, getHeader())); vod.setVodPlayUrl(data.getVodUrl());
for (Data data : resp.getData()) list.add(data.vod()); vod.setVodDirector(data.getDirectors());
return Result.string(list); vod.setVodContent(data.getDescription());
} return Result.string(vod);
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
return Result.get().url(id).header(getHeader()).string();
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
return searchContent(key, "1");
}
@Override
public String searchContent(String key, boolean quick, String pg) throws Exception {
return searchContent(key, pg);
}
public String searchContent(String key, String pg) throws Exception {
List<Vod> list = new ArrayList<>();
String url = siteUrl + String.format("/api/v2/search/videoV2?key=%s&category_id=88&page=%s&pageSize=20", URLEncoder.encode(key), pg);
Search search = Search.objectFrom(OkHttp.string(url, getHeader()));
for (Search data : search.getData()) list.add(data.vod(imgDomain));
return Result.string(list);
}
} }

View File

@ -104,7 +104,7 @@ public class Local extends Spider {
Vod vod = new Vod(); Vod vod = new Vod();
vod.setVodId(file.getAbsolutePath()); vod.setVodId(file.getAbsolutePath());
vod.setVodName(file.getName()); vod.setVodName(file.getName());
vod.setVodPic(file.isFile() ? Proxy.getUrl() + "?do=local&path=" + Base64.encodeToString(file.getAbsolutePath().getBytes(), Base64.DEFAULT | Base64.URL_SAFE) : Image.FOLDER); vod.setVodPic(file.isFile() ? "proxy://do=local&path=" + Base64.encodeToString(file.getAbsolutePath().getBytes(), Base64.DEFAULT | Base64.URL_SAFE) : Image.FOLDER);
vod.setVodRemarks(format.format(file.lastModified())); vod.setVodRemarks(format.format(file.lastModified()));
vod.setVodTag(file.isDirectory() ? "folder" : "file"); vod.setVodTag(file.isDirectory() ? "folder" : "file");
return vod; return vod;

View File

@ -0,0 +1,89 @@
package com.github.catvod.spider;
import android.content.Context;
import com.github.catvod.bean.mqitv.Config;
import com.github.catvod.bean.mqitv.Data;
import com.github.catvod.crawler.Spider;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MQiTV extends Spider {
private static List<Config> configs;
public static List<Config> getConfigs() {
return configs = configs == null ? new ArrayList<>() : configs;
}
@Override
public void init(Context context, String extend) throws Exception {
configs = Config.arrayFrom(extend);
}
@Override
public String liveContent(String url) throws Exception {
StringBuilder sb = new StringBuilder();
for (Config config : getConfigs()) {
if (config.getData().isEmpty()) continue;
sb.append(config.getName()).append(",#genre#").append("\n");
boolean hasPort = config.getUri().getPort() != -1;
for (Data item : config.getData()) {
String port = hasPort ? item.getPort() : "5003";
String proxy = "proxy://do=mqitv&id=" + item.getId() + "&ip=" + config.getUrl() + "&playing=" + item.getPlaying() + "&port=" + port + "&type=m3u8";
sb.append(item.getName()).append(",").append(proxy).append("\n");
}
}
return sb.toString();
}
private static Config getConfig(String ip) {
Config config = new Config(ip);
int index = getConfigs().indexOf(config);
if (index != -1) return getConfigs().get(index);
else getConfigs().add(config);
return config;
}
public static Object[] proxy(Map<String, String> params) {
String ip = params.get("ip");
String port = params.get("port");
String playing = params.get("playing");
if (port == null) port = "5003";
Config config = getConfig(ip);
String token = config.getUser().getToken();
if (token.isEmpty()) {
return get302(config.getPlayUrl(port, playing));
} else {
String id = params.get("id");
String auth = config.getAuth(id, token);
if (!"OK".equals(auth)) config.clear();
if (!"OK".equals(auth)) return proxy(params);
String m3u8 = config.getM3U8(id, token, port);
return m3u8.isEmpty() ? get302(config.getPlayUrl(port, playing)) : get200(m3u8);
}
}
private static Object[] get302(String location) {
Map<String, String> header = new HashMap<>();
header.put("Location", location);
Object[] result = new Object[4];
result[0] = 302;
result[1] = "text/plain";
result[2] = new ByteArrayInputStream("302 Found".getBytes());
result[3] = header;
return result;
}
private static Object[] get200(String m3u8) {
Object[] result = new Object[3];
result[0] = 200;
result[1] = "application/vnd.apple.mpegurl";
result[2] = new ByteArrayInputStream(m3u8.getBytes());
return result;
}
}

View File

@ -42,7 +42,6 @@ public class Market extends Spider {
public void init(Context context, String extend) throws Exception { public void init(Context context, String extend) throws Exception {
if (extend.startsWith("http")) extend = OkHttp.string(extend); if (extend.startsWith("http")) extend = OkHttp.string(extend);
datas = Data.arrayFrom(extend); datas = Data.arrayFrom(extend);
Init.checkPermission();
} }
@Override @Override
@ -63,7 +62,7 @@ public class Market extends Spider {
try { try {
if (isBusy()) return ""; if (isBusy()) return "";
setBusy(true); setBusy(true);
Init.run(this::setDialog, 500); Init.post(this::setDialog, 500);
Response response = OkHttp.newCall(action); Response response = OkHttp.newCall(action);
File file = Path.create(new File(Path.download(), Uri.parse(action).getLastPathSegment())); File file = Path.create(new File(Path.download(), Uri.parse(action).getLastPathSegment()));
download(file, response.body().byteStream(), Double.parseDouble(response.header("Content-Length", "1"))); download(file, response.body().byteStream(), Double.parseDouble(response.header("Content-Length", "1")));
@ -71,6 +70,7 @@ public class Market extends Spider {
if (file.getName().endsWith(".apk")) FileUtil.openFile(file); if (file.getName().endsWith(".apk")) FileUtil.openFile(file);
else Result.notify("下載完成"); else Result.notify("下載完成");
checkCopy(action); checkCopy(action);
response.close();
dismiss(); dismiss();
return ""; return "";
} catch (Exception e) { } catch (Exception e) {
@ -80,8 +80,7 @@ public class Market extends Spider {
} }
private void download(File file, InputStream is, double length) throws Exception { private void download(File file, InputStream is, double length) throws Exception {
FileOutputStream os = new FileOutputStream(file); try (BufferedInputStream input = new BufferedInputStream(is); FileOutputStream os = new FileOutputStream(file)) {
try (BufferedInputStream input = new BufferedInputStream(is)) {
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int readBytes; int readBytes;
long totalBytes = 0; long totalBytes = 0;
@ -104,9 +103,9 @@ public class Market extends Spider {
} }
private void setDialog() { private void setDialog() {
Init.run(() -> { Init.post(() -> {
try { try {
dialog = new ProgressDialog(Init.getActivity()); dialog = new ProgressDialog(Init.activity());
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setCancelable(false); dialog.setCancelable(false);
if (isBusy()) dialog.show(); if (isBusy()) dialog.show();
@ -117,7 +116,7 @@ public class Market extends Spider {
} }
private void dismiss() { private void dismiss() {
Init.run(() -> { Init.post(() -> {
try { try {
setBusy(false); setBusy(false);
if (dialog != null) dialog.dismiss(); if (dialog != null) dialog.dismiss();
@ -128,7 +127,7 @@ public class Market extends Spider {
} }
private void setProgress(int value) { private void setProgress(int value) {
Init.run(() -> { Init.post(() -> {
try { try {
if (dialog != null) dialog.setProgress(value); if (dialog != null) dialog.setProgress(value);
} catch (Exception e) { } catch (Exception e) {

View File

@ -1,20 +1,24 @@
package com.github.catvod.spider; package com.github.catvod.spider;
import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug; import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp; import com.github.catvod.net.OkHttp;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
public class Proxy extends Spider { public class Proxy {
private static int port = -1; private static Method method;
private static int port;
public static Object[] proxy(Map<String, String> params) throws Exception { public static Object[] proxy(Map<String, String> params) throws Exception {
switch (params.get("do")) { switch (params.get("do")) {
case "ck": case "ck":
return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes("UTF-8"))}; return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes(StandardCharsets.UTF_8))};
case "mqitv":
return MQiTV.proxy(params);
case "bili": case "bili":
return Bili.proxy(params); return Bili.proxy(params);
case "webdav": case "webdav":
@ -26,27 +30,41 @@ public class Proxy extends Spider {
} }
} }
static void adjustPort() { public static void init() {
if (Proxy.port > 0) return; try {
int port = 9978; Class<?> clz = Class.forName("com.github.catvod.Proxy");
while (port < 10000) { port = (int) clz.getMethod("getPort").invoke(null);
String resp = OkHttp.string("http://127.0.0.1:" + port + "/proxy?do=ck", null); method = clz.getMethod("getUrl", boolean.class);
if (resp.equals("ok")) { SpiderDebug.log("本地代理端口:" + port);
SpiderDebug.log("Found local server port " + port); } catch (Throwable e) {
Proxy.port = port; findPort();
break;
}
port++;
} }
} }
public static int getPort() { public static int getPort() {
adjustPort();
return port; return port;
} }
public static String getUrl() { public static String getUrl() {
adjustPort(); return getUrl(true);
return "http://127.0.0.1:" + port + "/proxy"; }
public static String getUrl(boolean local) {
try {
return (String) method.invoke(null, local);
} catch (Throwable e) {
return "http://127.0.0.1:" + port + "/proxy";
}
}
private static void findPort() {
if (port > 0) return;
for (int p = 8964; p < 9999; p++) {
if ("ok".equals(OkHttp.string("http://127.0.0.1:" + p + "/proxy?do=ck", null))) {
SpiderDebug.log("本地代理端口:" + p);
port = p;
break;
}
}
} }
} }

View File

@ -38,7 +38,7 @@ public class Push extends Spider {
vod.setVodId(url); vod.setVodId(url);
vod.setVodPic(Image.PUSH); vod.setVodPic(Image.PUSH);
vod.setTypeName("FongMi"); vod.setTypeName("FongMi");
vod.setVodName(url.startsWith("file://") ? new File(url).getName() : url); vod.setVodName(url.startsWith("file://") ? new File(url).getName() : "");
if (url.contains("://") && url.contains("#")) url = url.replace("#", "***"); if (url.contains("://") && url.contains("#")) url = url.replace("#", "***");
if (Util.isThunder(url)) { if (Util.isThunder(url)) {
vod.setVodPlayUrl(url); vod.setVodPlayUrl(url);

View File

@ -174,7 +174,7 @@ public class WebDAV extends Spider {
} }
private String getProxyUrl(String url) { private String getProxyUrl(String url) {
return Proxy.getUrl() + "?do=webdav&url=" + url; return "proxy://do=webdav&url=" + url;
} }
public static Object[] vod(Map<String, String> params) throws IOException { public static Object[] vod(Map<String, String> params) throws IOException {

View File

@ -1,345 +0,0 @@
package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Vod;
import com.github.catvod.bean.xpath.Rule;
import com.github.catvod.crawler.Spider;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Util;
import org.json.JSONArray;
import org.json.JSONObject;
import org.seimicrawler.xpath.JXDocument;
import org.seimicrawler.xpath.JXNode;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
public class XPath extends Spider {
private HashMap<String, String> getHeaders() {
HashMap<String, String> headers = new HashMap<>();
headers.put("User-Agent", rule.getUa().isEmpty() ? Util.CHROME : rule.getUa());
return headers;
}
@Override
public void init(Context context, String extend) {
this.ext = extend;
}
@Override
public String homeContent(boolean filter) {
fetchRule();
List<Vod> list = new ArrayList<>();
List<Class> classes = new ArrayList<>();
if (!rule.getCateManual().isEmpty()) {
Set<String> keys = rule.getCateManual().keySet();
for (String k : keys) {
classes.add(new Class(rule.getCateManual().get(k), k));
}
}
String webUrl = rule.getHomeUrl();
JXDocument doc = JXDocument.create(fetch(webUrl));
if (rule.getCateManual().isEmpty()) {
List<JXNode> navNodes = doc.selN(rule.getCateNode());
for (int i = 0; i < navNodes.size(); i++) {
String name = navNodes.get(i).selOne(rule.getCateName()).asString().trim();
name = rule.getCateNameR(name);
String id = navNodes.get(i).selOne(rule.getCateId()).asString().trim();
id = rule.getCateIdR(id);
classes.add(new Class(id, name));
}
}
if (!rule.getHomeVodNode().isEmpty()) {
List<JXNode> vodNodes = doc.selN(rule.getHomeVodNode());
for (int i = 0; i < vodNodes.size(); i++) {
String name = vodNodes.get(i).selOne(rule.getHomeVodName()).asString().trim();
name = rule.getHomeVodNameR(name);
String id = vodNodes.get(i).selOne(rule.getHomeVodId()).asString().trim();
id = rule.getHomeVodIdR(id);
String pic = vodNodes.get(i).selOne(rule.getHomeVodImg()).asString().trim();
pic = rule.getHomeVodImgR(pic);
pic = Util.fixUrl(webUrl, pic);
String mark = "";
if (!rule.getHomeVodMark().isEmpty()) {
try {
mark = vodNodes.get(i).selOne(rule.getHomeVodMark()).asString().trim();
mark = rule.getHomeVodMarkR(mark);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
list.add(new Vod(id, name, pic, mark));
}
}
return Result.string(classes, list, rule.getFilter());
}
protected String categoryUrl(String tid, String pg, boolean filter, HashMap<String, String> extend) {
return rule.getCateUrl().replace("{cateId}", tid).replace("{catePg}", pg);
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) {
fetchRule();
List<Vod> list = new ArrayList<>();
String webUrl = categoryUrl(tid, pg, filter, extend);
JXDocument doc = JXDocument.create(fetch(webUrl));
List<JXNode> vodNodes = doc.selN(rule.getCateVodNode());
for (int i = 0; i < vodNodes.size(); i++) {
String name = vodNodes.get(i).selOne(rule.getCateVodName()).asString().trim();
name = rule.getCateVodNameR(name);
String id = vodNodes.get(i).selOne(rule.getCateVodId()).asString().trim();
id = rule.getCateVodIdR(id);
String pic = vodNodes.get(i).selOne(rule.getCateVodImg()).asString().trim();
pic = rule.getCateVodImgR(pic);
pic = Util.fixUrl(webUrl, pic);
String mark = "";
if (!rule.getCateVodMark().isEmpty()) {
try {
mark = vodNodes.get(i).selOne(rule.getCateVodMark()).asString().trim();
mark = rule.getCateVodMarkR(mark);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
list.add(new Vod(id, name, pic, mark));
}
return Result.string(list);
}
@Override
public String detailContent(List<String> ids) {
fetchRule();
String webUrl = rule.getDetailUrl().replace("{vid}", ids.get(0));
String webContent = fetch(webUrl);
JXDocument doc = JXDocument.create(webContent);
JXNode vodNode = doc.selNOne(rule.getDetailNode());
String cover = "", title = "", desc = "", category = "", area = "", year = "", remark = "", director = "", actor = "";
title = vodNode.selOne(rule.getDetailName()).asString().trim();
title = rule.getDetailNameR(title);
cover = vodNode.selOne(rule.getDetailImg()).asString().trim();
cover = rule.getDetailImgR(cover);
cover = Util.fixUrl(webUrl, cover);
if (!rule.getDetailCate().isEmpty()) {
try {
category = vodNode.selOne(rule.getDetailCate()).asString().trim();
category = rule.getDetailCateR(category);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (!rule.getDetailYear().isEmpty()) {
try {
year = vodNode.selOne(rule.getDetailYear()).asString().trim();
year = rule.getDetailYearR(year);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (!rule.getDetailArea().isEmpty()) {
try {
area = vodNode.selOne(rule.getDetailArea()).asString().trim();
area = rule.getDetailAreaR(area);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (!rule.getDetailMark().isEmpty()) {
try {
remark = vodNode.selOne(rule.getDetailMark()).asString().trim();
remark = rule.getDetailMarkR(remark);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (!rule.getDetailActor().isEmpty()) {
try {
actor = vodNode.selOne(rule.getDetailActor()).asString().trim();
actor = rule.getDetailActorR(actor);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (!rule.getDetailDirector().isEmpty()) {
try {
director = vodNode.selOne(rule.getDetailDirector()).asString().trim();
director = rule.getDetailDirectorR(director);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (!rule.getDetailDesc().isEmpty()) {
try {
desc = vodNode.selOne(rule.getDetailDesc()).asString().trim();
desc = rule.getDetailDescR(desc);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
Vod vod = new Vod();
vod.setVodId(ids.get(0));
vod.setVodName(title);
vod.setVodPic(cover);
vod.setTypeName(category);
vod.setVodYear(year);
vod.setVodArea(area);
vod.setVodRemarks(remark);
vod.setVodActor(actor);
vod.setVodDirector(director);
vod.setVodContent(desc);
ArrayList<String> playFrom = new ArrayList<>();
List<JXNode> fromNodes = doc.selN(rule.getDetailFromNode());
for (int i = 0; i < fromNodes.size(); i++) {
String name = fromNodes.get(i).selOne(rule.getDetailFromName()).asString().trim();
name = rule.getDetailFromNameR(name);
playFrom.add(name);
}
ArrayList<String> playList = new ArrayList<>();
List<JXNode> urlListNodes = doc.selN(rule.getDetailUrlNode());
for (int i = 0; i < urlListNodes.size(); i++) {
List<JXNode> urlNodes = urlListNodes.get(i).sel(rule.getDetailUrlSubNode());
List<String> vodItems = new ArrayList<>();
for (int j = 0; j < urlNodes.size(); j++) {
String name = urlNodes.get(j).selOne(rule.getDetailUrlName()).asString().trim();
name = rule.getDetailUrlNameR(name);
String id = urlNodes.get(j).selOne(rule.getDetailUrlId()).asString().trim();
id = rule.getDetailUrlIdR(id);
vodItems.add(name + "$" + id);
}
// 排除播放列表為空的播放源
if (vodItems.isEmpty() && playFrom.size() > i) {
playFrom.set(i, "");
}
playList.add(TextUtils.join("#", vodItems));
}
// 排除播放列表為空的播放源
for (int i = playFrom.size() - 1; i >= 0; i--) {
if (playFrom.get(i).isEmpty()) playFrom.remove(i);
}
for (int i = playList.size() - 1; i >= 0; i--) {
if (playList.get(i).isEmpty()) playList.remove(i);
}
for (int i = playList.size() - 1; i >= 0; i--) {
if (i >= playFrom.size()) playList.remove(i);
}
vod.setVodPlayFrom(TextUtils.join("$$$", playFrom));
vod.setVodPlayUrl(TextUtils.join("$$$", playList));
return Result.string(vod);
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) {
fetchRule();
String webUrl = rule.getPlayUrl().isEmpty() ? id : rule.getPlayUrl().replace("{playUrl}", id);
SpiderDebug.log(webUrl);
HashMap<String, String> headers = new HashMap<>();
if (!rule.getPlayUa().isEmpty()) headers.put("User-Agent", rule.getPlayUa());
if (!rule.getPlayReferer().isEmpty()) headers.put("Referer", rule.getPlayReferer());
return Result.get().parse().url(webUrl).header(headers).string();
}
@Override
public String searchContent(String key, boolean quick) throws Exception {
fetchRule();
if (rule.getSearchUrl().isEmpty()) return "";
String webUrl = rule.getSearchUrl().replace("{wd}", URLEncoder.encode(key));
String webContent = fetch(webUrl);
List<Vod> list = new ArrayList<>();
if (rule.getSearchVodNode().startsWith("json:")) {
String[] node = rule.getSearchVodNode().substring(5).split(">");
JSONObject data = new JSONObject(webContent);
for (int i = 0; i < node.length; i++) {
if (i == node.length - 1) {
JSONArray vodArray = data.getJSONArray(node[i]);
for (int j = 0; j < vodArray.length(); j++) {
JSONObject vod = vodArray.getJSONObject(j);
String name = vod.optString(rule.getSearchVodName()).trim();
name = rule.getSearchVodNameR(name);
String id = vod.optString(rule.getSearchVodId()).trim();
id = rule.getSearchVodIdR(id);
String pic = vod.optString(rule.getSearchVodImg()).trim();
pic = rule.getSearchVodImgR(pic);
pic = Util.fixUrl(webUrl, pic);
String mark = vod.optString(rule.getSearchVodMark()).trim();
mark = rule.getSearchVodMarkR(mark);
list.add(new Vod(id, name, pic, mark));
}
} else {
data = data.getJSONObject(node[i]);
}
}
} else {
JXDocument doc = JXDocument.create(webContent);
List<JXNode> vodNodes = doc.selN(rule.getSearchVodNode());
for (int i = 0; i < vodNodes.size(); i++) {
String name = vodNodes.get(i).selOne(rule.getSearchVodName()).asString().trim();
name = rule.getSearchVodNameR(name);
String id = vodNodes.get(i).selOne(rule.getSearchVodId()).asString().trim();
id = rule.getSearchVodIdR(id);
String pic = vodNodes.get(i).selOne(rule.getSearchVodImg()).asString().trim();
pic = rule.getSearchVodImgR(pic);
pic = Util.fixUrl(webUrl, pic);
String mark = "";
if (!rule.getCateVodMark().isEmpty()) {
try {
mark = vodNodes.get(i).selOne(rule.getSearchVodMark()).asString().trim();
mark = rule.getSearchVodMarkR(mark);
} catch (Exception e) {
SpiderDebug.log(e);
}
}
list.add(new Vod(id, name, pic, mark));
}
}
return Result.string(list);
}
@Override
public boolean manualVideoCheck() {
return false;
}
@Override
public boolean isVideoFormat(String url) {
return Util.isVideoFormat(url);
}
protected String ext = null;
protected Rule rule = null;
protected void fetchRule() {
if (rule == null) {
if (ext != null) {
if (ext.startsWith("http")) {
String json = OkHttp.string(ext, null);
rule = Rule.fromJson(json);
loadRuleExt(json);
} else {
rule = Rule.fromJson(ext);
loadRuleExt(ext);
}
}
}
}
protected void loadRuleExt(String json) {
}
protected String fetch(String webUrl) {
SpiderDebug.log(webUrl);
return OkHttp.string(webUrl, getHeaders());
}
}

View File

@ -1,36 +0,0 @@
package com.github.catvod.spider;
import android.text.TextUtils;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XPathFilter extends XPath {
@Override
protected void loadRuleExt(String json) {
super.loadRuleExt(json);
}
@Override
protected String categoryUrl(String tid, String pg, boolean filter, HashMap<String, String> extend) {
String cateUrl = rule.getCateUrl();
if (filter && extend != null && !extend.isEmpty()) {
for (String key : extend.keySet()) {
String value = extend.get(key);
if (!TextUtils.isEmpty(value)) {
cateUrl = cateUrl.replace("{" + key + "}", URLEncoder.encode(value));
}
}
}
cateUrl = cateUrl.replace("{cateId}", tid).replace("{catePg}", pg);
Matcher m = Pattern.compile("\\{(.*?)\\}").matcher(cateUrl);
while (m.find()) {
String n = m.group(0).replace("{", "").replace("}", "");
cateUrl = cateUrl.replace(m.group(0), "").replace("/" + n + "/", "");
}
return cateUrl;
}
}

View File

@ -1,196 +0,0 @@
package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import android.util.Base64;
import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.utils.Util;
import com.google.gson.Gson;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XPathMac extends XPath {
// 嘗試分析直連
private boolean decodePlayUrl;
// 嘗試匹配官源標識以調用應用配置中的解析列表
private boolean decodeVipFlag;
// 播放器配置js
private String playerConfigJs = "";
// 播放器配置js取值正則
private String playerConfigJsRegex = "[\\W|\\S|.]*?MacPlayerConfig.player_list[\\W|\\S|.]*?=([\\W|\\S|.]*?),MacPlayerConfig.downer_list";
// 站點里播放源對應的真實官源
private final HashMap<String, String> show2VipFlag = new HashMap<>();
/**
* mac cms 直連和官源調用應用內播放列表支持
*
* @param context
* @param extend
*/
public void init(Context context, String extend) {
super.init(context, extend);
}
@Override
protected void loadRuleExt(String json) {
try {
JSONObject jsonObj = new JSONObject(json);
decodePlayUrl = jsonObj.optBoolean("dcPlayUrl", false);
decodeVipFlag = jsonObj.optBoolean("dcVipFlag", false);
JSONObject dcShow2Vip = jsonObj.optJSONObject("dcShow2Vip");
if (dcShow2Vip != null) {
Iterator<String> keys = dcShow2Vip.keys();
while (keys.hasNext()) {
String name = keys.next();
show2VipFlag.put(name.trim(), dcShow2Vip.getString(name).trim());
}
}
playerConfigJs = jsonObj.optString("pCfgJs").trim();
playerConfigJsRegex = jsonObj.optString("pCfgJsR", playerConfigJsRegex).trim();
} catch (JSONException e) {
SpiderDebug.log(e);
}
}
@Override
public String homeContent(boolean filter) {
String result = super.homeContent(filter);
if (!result.isEmpty() && !playerConfigJs.isEmpty()) { // 嘗試通過playerConfigJs獲取展示和flag匹配關系
String webContent = fetch(playerConfigJs);
Matcher matcher = Pattern.compile(playerConfigJsRegex).matcher(webContent);
if (matcher.find()) {
try {
JSONObject jsonObject = new JSONObject(matcher.group(1));
Iterator<String> keys = jsonObject.keys();
while (keys.hasNext()) {
String key = keys.next();
JSONObject keyObj = jsonObject.optJSONObject(key);
if (keyObj == null) continue;
String show = keyObj.optString("show").trim();
if (show.isEmpty()) continue;
show2VipFlag.put(show, key);
}
} catch (Exception e) {
SpiderDebug.log(e);
}
}
}
return result;
}
@Override
public String detailContent(List<String> ids) {
String result = super.detailContent(ids);
if (decodeVipFlag && !result.isEmpty()) {
try {
JSONObject jsonObject = new JSONObject(result);
String[] playFrom = jsonObject.optJSONArray("list").getJSONObject(0).optString("vod_play_from").split("\\$\\$\\$");
if (playFrom.length > 0) {
for (int i = 0; i < playFrom.length; i++) {
if (show2VipFlag.containsKey(playFrom[i])) {
playFrom[i] = show2VipFlag.get(playFrom[i]);
}
}
jsonObject.optJSONArray("list").getJSONObject(0).put("vod_play_from", TextUtils.join("$$$", playFrom));
result = jsonObject.toString();
}
} catch (Throwable th) {
SpiderDebug.log(th);
}
}
return result;
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) {
fetchRule();
String webUrl = rule.getPlayUrl().isEmpty() ? id : rule.getPlayUrl().replace("{playUrl}", id);
String videoUrl = null;
// 嘗試分析直連
if (decodePlayUrl) {
try {
Document doc = Jsoup.parse(fetch(webUrl));
Elements allScript = doc.select("script");
for (int i = 0; i < allScript.size(); i++) {
String scContent = allScript.get(i).html().trim();
if (scContent.startsWith("var player_")) {
int start = scContent.indexOf('{');
int end = scContent.lastIndexOf('}') + 1;
String json = scContent.substring(start, end);
JSONObject player = new JSONObject(json);
String videoUrlTmp = player.getString("url");
if (player.has("encrypt")) {
int encrypt = player.getInt("encrypt");
if (encrypt == 1) {
videoUrlTmp = URLDecoder.decode(videoUrlTmp);
} else if (encrypt == 2) {
videoUrlTmp = new String(Base64.decode(videoUrlTmp, Base64.DEFAULT));
videoUrlTmp = URLDecoder.decode(videoUrlTmp);
}
}
videoUrl = videoUrlTmp;
break;
}
}
} catch (Exception e) {
SpiderDebug.log(e);
}
}
if (videoUrl != null) {
// 適配2.0.6的調用應用內解析列表的支持, 需要配合直連分析和匹配官源解析一起使用參考cjt影視和極品直連
if (decodeVipFlag && Util.isVip(videoUrl)) { // 使用jx:1
try {
JSONObject result = new JSONObject();
result.put("parse", 1);
result.put("jx", "1");
result.put("url", videoUrl);
return result.toString();
} catch (Exception e) {
SpiderDebug.log(e);
}
} else if (decodeVipFlag && vipFlags.contains(flag)) { // 是否使用應用內解析列表解析官源
try {
JSONObject result = new JSONObject();
result.put("parse", 1);
result.put("playUrl", "");
result.put("url", videoUrl);
result.put("header", "");
return result.toString();
} catch (Exception e) {
SpiderDebug.log(e);
}
}
// 如果是視頻直連 直接返回免解
else if (isVideoFormat(videoUrl)) {
try {
JSONObject result = new JSONObject();
result.put("parse", 0);
result.put("playUrl", "");
result.put("url", videoUrl);
HashMap<String, String> headers = new HashMap<>();
if (!rule.getPlayUa().isEmpty()) headers.put("User-Agent", rule.getPlayUa());
if (!rule.getPlayReferer().isEmpty()) headers.put("Referer", rule.getPlayReferer());
result.put("header", new Gson().toJson(headers));
return result.toString();
} catch (Exception e) {
SpiderDebug.log(e);
}
}
}
// 上述都失敗了就按默認模式走
return super.playerContent(flag, id, vipFlags);
}
}

View File

@ -1,31 +0,0 @@
package com.github.catvod.spider;
import android.text.TextUtils;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class XPathMacFilter extends XPathMac {
@Override
protected String categoryUrl(String tid, String pg, boolean filter, HashMap<String, String> extend) {
String cateUrl = rule.getCateUrl();
if (filter && extend != null && !extend.isEmpty()) {
for (String key : extend.keySet()) {
String value = extend.get(key);
if (!TextUtils.isEmpty(value)) {
cateUrl = cateUrl.replace("{" + key + "}", URLEncoder.encode(value));
}
}
}
cateUrl = cateUrl.replace("{cateId}", tid).replace("{catePg}", pg);
Matcher m = Pattern.compile("\\{(.*?)\\}").matcher(cateUrl);
while (m.find()) {
String n = m.group(0).replace("{", "").replace("}", "");
cateUrl = cateUrl.replace(m.group(0), "").replace("/" + n + "/", "");
}
return cateUrl;
}
}

View File

@ -53,9 +53,7 @@ public class YHDM extends Spider {
List<Class> classes = new ArrayList<>(); List<Class> classes = new ArrayList<>();
List<String> typeIds = Arrays.asList("guochandongman", "ribendongman", "dongmandianying", "omeidongman"); List<String> typeIds = Arrays.asList("guochandongman", "ribendongman", "dongmandianying", "omeidongman");
List<String> typeNames = Arrays.asList("国产动漫", "日本动漫", "动漫电影", "欧美动漫"); List<String> typeNames = Arrays.asList("国产动漫", "日本动漫", "动漫电影", "欧美动漫");
for (int i = 0; i < typeIds.size(); i++) for (int i = 0; i < typeIds.size(); i++) classes.add(new Class(typeIds.get(i), typeNames.get(i)));
classes.add(new Class(typeIds.get(i), typeNames.get(i)));
Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader())); Document doc = Jsoup.parse(OkHttp.string(siteUrl, getHeader()));
List<Vod> list = new ArrayList<>(); List<Vod> list = new ArrayList<>();
for (Element li : doc.select(".stui-vodlist.clearfix .myui-vodlist__box")) { for (Element li : doc.select(".stui-vodlist.clearfix .myui-vodlist__box")) {

View File

@ -12,13 +12,14 @@ import android.content.res.XmlResourceParser;
import android.database.Cursor; import android.database.Cursor;
import android.database.MatrixCursor; import android.database.MatrixCursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.text.TextUtils; import android.text.TextUtils;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import java.io.File; import java.io.File;
@ -57,16 +58,16 @@ public class FileProvider extends ContentProvider {
synchronized (sCache) { synchronized (sCache) {
sCache.remove(authority); sCache.remove(authority);
} }
mStrategy = getPathStrategy(context, authority, 0); mStrategy = getPathStrategy(context, authority);
} }
public static Uri getUriForFile(Context context, String authority, File file) { public static Uri getUriForFile(Context context, String authority, File file) {
final PathStrategy strategy = getPathStrategy(context, authority, 0); final PathStrategy strategy = getPathStrategy(context, authority);
return strategy.getUriForFile(file); return strategy.getUriForFile(file);
} }
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
final File file = mStrategy.getFileForUri(uri); final File file = mStrategy.getFileForUri(uri);
String displayName = uri.getQueryParameter(DISPLAYNAME_FIELD); String displayName = uri.getQueryParameter(DISPLAYNAME_FIELD);
if (projection == null) { if (projection == null) {
@ -92,7 +93,7 @@ public class FileProvider extends ContentProvider {
} }
@Override @Override
public String getType(Uri uri) { public String getType(@NonNull Uri uri) {
final File file = mStrategy.getFileForUri(uri); final File file = mStrategy.getFileForUri(uri);
final int lastDot = file.getName().lastIndexOf('.'); final int lastDot = file.getName().lastIndexOf('.');
if (lastDot >= 0) { if (lastDot >= 0) {
@ -106,38 +107,36 @@ public class FileProvider extends ContentProvider {
} }
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(@NonNull Uri uri, ContentValues values) {
throw new UnsupportedOperationException("No external inserts"); throw new UnsupportedOperationException("No external inserts");
} }
@Override @Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("No external updates"); throw new UnsupportedOperationException("No external updates");
} }
@Override @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
final File file = mStrategy.getFileForUri(uri); final File file = mStrategy.getFileForUri(uri);
return file.delete() ? 1 : 0; return file.delete() ? 1 : 0;
} }
@Override @Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
final File file = mStrategy.getFileForUri(uri); final File file = mStrategy.getFileForUri(uri);
final int fileMode = modeToMode(mode); final int fileMode = modeToMode(mode);
return ParcelFileDescriptor.open(file, fileMode); return ParcelFileDescriptor.open(file, fileMode);
} }
private static PathStrategy getPathStrategy(Context context, String authority, int resourceId) { private static PathStrategy getPathStrategy(Context context, String authority) {
PathStrategy strat; PathStrategy strat;
synchronized (sCache) { synchronized (sCache) {
strat = sCache.get(authority); strat = sCache.get(authority);
if (strat == null) { if (strat == null) {
try { try {
strat = parsePathStrategy(context, authority, resourceId); strat = parsePathStrategy(context, authority);
} catch (IOException e) { } catch (IOException | XmlPullParserException e) {
throw new IllegalArgumentException("Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
} catch (XmlPullParserException e) {
throw new IllegalArgumentException("Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e); throw new IllegalArgumentException("Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
} }
sCache.put(authority, strat); sCache.put(authority, strat);
@ -146,14 +145,10 @@ public class FileProvider extends ContentProvider {
return strat; return strat;
} }
static XmlResourceParser getFileProviderPathsMetaData(Context context, String authority, ProviderInfo info, int resourceId) { static XmlResourceParser getFileProviderPathsMetaData(Context context, String authority, ProviderInfo info) {
if (info == null) { if (info == null) {
throw new IllegalArgumentException("Couldn't find meta-data for provider with authority " + authority); throw new IllegalArgumentException("Couldn't find meta-data for provider with authority " + authority);
} }
if (info.metaData == null && resourceId != 0) {
info.metaData = new Bundle(1);
info.metaData.putInt(META_DATA_FILE_PROVIDER_PATHS, resourceId);
}
final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS); final XmlResourceParser in = info.loadXmlMetaData(context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
if (in == null) { if (in == null) {
throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data"); throw new IllegalArgumentException("Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
@ -161,10 +156,10 @@ public class FileProvider extends ContentProvider {
return in; return in;
} }
private static PathStrategy parsePathStrategy(Context context, String authority, int resourceId) throws IOException, XmlPullParserException { private static PathStrategy parsePathStrategy(Context context, String authority) throws IOException, XmlPullParserException {
final SimplePathStrategy strat = new SimplePathStrategy(authority); final SimplePathStrategy strat = new SimplePathStrategy(authority);
final ProviderInfo info = context.getPackageManager().resolveContentProvider(authority, PackageManager.GET_META_DATA); final ProviderInfo info = context.getPackageManager().resolveContentProvider(authority, PackageManager.GET_META_DATA);
final XmlResourceParser in = getFileProviderPathsMetaData(context, authority, info, resourceId); final XmlResourceParser in = getFileProviderPathsMetaData(context, authority, info);
int type; int type;
while ((type = in.next()) != END_DOCUMENT) { while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) { if (type == START_TAG) {

View File

@ -8,9 +8,7 @@ import android.text.TextUtils;
import com.github.catvod.spider.Init; import com.github.catvod.spider.Init;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.net.URLConnection; import java.net.URLConnection;
import java.security.MessageDigest;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
@ -39,22 +37,6 @@ public class FileUtil {
} }
} }
public static String md5(File file) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
FileInputStream fis = new FileInputStream(file);
byte[] bytes = new byte[4096];
int count;
while ((count = fis.read(bytes)) != -1) digest.update(bytes, 0, count);
fis.close();
StringBuilder sb = new StringBuilder();
for (byte b : digest.digest()) sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
return sb.toString();
} catch (Exception e) {
return "";
}
}
private static Uri getShareUri(File file) { private static Uri getShareUri(File file) {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.N ? Uri.fromFile(file) : FileProvider.getUriForFile(Init.context(), Init.context().getPackageName() + ".provider", file); return Build.VERSION.SDK_INT < Build.VERSION_CODES.N ? Uri.fromFile(file) : FileProvider.getUriForFile(Init.context(), Init.context().getPackageName() + ".provider", file);
} }

View File

@ -18,7 +18,7 @@ public class Notify {
} }
public static void show(String text) { public static void show(String text) {
Init.run(() -> get().makeText(text)); Init.post(() -> get().makeText(text));
} }
private void makeText(String message) { private void makeText(String message) {

View File

@ -10,6 +10,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -63,7 +64,7 @@ public class Path {
byte[] data = new byte[is.available()]; byte[] data = new byte[is.available()];
is.read(data); is.read(data);
is.close(); is.close();
return new String(data, "UTF-8"); return new String(data, StandardCharsets.UTF_8);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return ""; return "";

View File

@ -1,17 +0,0 @@
package com.github.catvod.utils;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import com.github.catvod.spider.Init;
public class ResUtil {
private static DisplayMetrics getDisplayMetrics() {
return Init.context().getResources().getDisplayMetrics();
}
public static int dp2px(int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getDisplayMetrics());
}
}

View File

@ -3,35 +3,21 @@ package com.github.catvod.utils;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.github.catvod.spider.Init; import com.github.catvod.spider.Init;
import java.text.DecimalFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public class Util { public class Util {
public static final Pattern RULE = Pattern.compile("http((?!http).){12,}?\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)\\?.*|http((?!http).){12,}\\.(m3u8|mp4|mkv|flv|mp3|m4a|aac)|http((?!http).)*?video/tos*");
public static final Pattern THUNDER = Pattern.compile("(magnet|thunder|ed2k):.*"); public static final Pattern THUNDER = Pattern.compile("(magnet|thunder|ed2k):.*");
public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"; public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36";
public static final List<String> MEDIA = Arrays.asList("mp4", "mkv", "mov", "wav", "wma", "wmv", "flv", "avi", "iso", "mpg", "ts", "mp3", "aac", "flac", "m4a", "ape", "ogg"); public static final List<String> MEDIA = Arrays.asList("mp4", "mkv", "mov", "wav", "wma", "wmv", "flv", "avi", "iso", "mpg", "ts", "mp3", "aac", "flac", "m4a", "ape", "ogg");
public static final List<String> SUB = Arrays.asList("srt", "ass", "ssa", "vtt"); public static final List<String> SUB = Arrays.asList("srt", "ass", "ssa", "vtt");
public static boolean isVip(String url) {
List<String> hosts = Arrays.asList("iqiyi.com", "v.qq.com", "youku.com", "le.com", "tudou.com", "mgtv.com", "sohu.com", "acfun.cn", "bilibili.com", "baofeng.com", "pptv.com");
for (String host : hosts) if (url.contains(host)) return true;
return false;
}
public static boolean isThunder(String url) { public static boolean isThunder(String url) {
return THUNDER.matcher(url).find() || isTorrent(url); return THUNDER.matcher(url).find() || isTorrent(url);
} }
@ -40,11 +26,6 @@ public class Util {
return !url.startsWith("magnet") && url.split(";")[0].endsWith(".torrent"); return !url.startsWith("magnet") && url.split(";")[0].endsWith(".torrent");
} }
public static boolean isVideoFormat(String url) {
if (url.contains("url=http") || url.contains(".js") || url.contains(".css") || url.contains(".html")) return false;
return RULE.matcher(url).find();
}
public static boolean isSub(String text) { public static boolean isSub(String text) {
return SUB.contains(getExt(text).toLowerCase()); return SUB.contains(getExt(text).toLowerCase());
} }
@ -59,31 +40,9 @@ public class Util {
public static String getSize(double size) { public static String getSize(double size) {
if (size <= 0) return ""; if (size <= 0) return "";
if (size > 1024 * 1024 * 1024 * 1024.0) { String[] units = new String[]{"bytes", "KB", "MB", "GB", "TB"};
size /= (1024 * 1024 * 1024 * 1024.0); int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
return String.format(Locale.getDefault(), "%.2f%s", size, "TB"); return new DecimalFormat("#,##0.#").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
} else if (size > 1024 * 1024 * 1024.0) {
size /= (1024 * 1024 * 1024.0);
return String.format(Locale.getDefault(), "%.2f%s", size, "GB");
} else if (size > 1024 * 1024.0) {
size /= (1024 * 1024.0);
return String.format(Locale.getDefault(), "%.2f%s", size, "MB");
} else {
size /= 1024.0;
return String.format(Locale.getDefault(), "%.2f%s", size, "KB");
}
}
public static String fixUrl(String base, String src) {
if (src.startsWith("//")) {
Uri parse = Uri.parse(base);
return parse.getScheme() + ":" + src;
} else if (!src.contains("://")) {
Uri parse = Uri.parse(base);
return parse.getScheme() + "://" + parse.getHost() + src;
} else {
return src;
}
} }
public static String removeExt(String text) { public static String removeExt(String text) {
@ -118,46 +77,4 @@ public class Util {
manager.setPrimaryClip(ClipData.newPlainText("fongmi", text)); manager.setPrimaryClip(ClipData.newPlainText("fongmi", text));
Notify.show("已複製 " + text); Notify.show("已複製 " + text);
} }
public static void loadUrl(WebView webView, String script) {
loadUrl(webView, script, null);
}
public static void loadUrl(WebView webView, String script, ValueCallback<String> callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(script, callback);
} else {
webView.loadUrl("javascript:" + script);
}
}
public static void addView(View view, ViewGroup.LayoutParams params) {
try {
ViewGroup group = Init.getActivity().getWindow().getDecorView().findViewById(android.R.id.content);
group.addView(view, params);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void removeView(View view) {
try {
ViewGroup group = Init.getActivity().getWindow().getDecorView().findViewById(android.R.id.content);
group.removeView(view);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void loadWebView(String url, WebViewClient client) {
Init.run(() -> {
WebView webView = new WebView(Init.context());
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setJavaScriptEnabled(true);
addView(webView, new ViewGroup.LayoutParams(0, 0));
webView.setWebViewClient(client);
webView.loadUrl(url);
});
}
} }

View File

@ -1,69 +1,95 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"> android:orientation="vertical">
<LinearLayout <HorizontalScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:orientation="vertical" android:fillViewport="true"
android:padding="16dp"> android:padding="16dp">
<Button <LinearLayout
android:id="@+id/homeContent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="homeContent" android:orientation="horizontal">
android:textAllCaps="false" />
<Button <Button
android:id="@+id/homeVideoContent" android:id="@+id/home"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="home"
android:textAllCaps="false" />
<Button
android:id="@+id/homeVideo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="homeVideo"
android:textAllCaps="false" />
<Button
android:id="@+id/category"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="category"
android:textAllCaps="false" />
<Button
android:id="@+id/detail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="detail"
android:textAllCaps="false" />
<Button
android:id="@+id/player"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="player"
android:textAllCaps="false" />
<Button
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="search"
android:textAllCaps="false" />
<Button
android:id="@+id/live"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="live"
android:textAllCaps="false" />
<Button
android:id="@+id/proxy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="proxy"
android:textAllCaps="false" />
</LinearLayout>
</HorizontalScrollView>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#000000" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TextView
android:id="@+id/result"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:padding="16dp"
android:text="homeVideoContent" android:textColor="#000000"
android:textAllCaps="false" /> android:textSize="14sp" />
<Button </ScrollView>
android:id="@+id/categoryContent" </LinearLayout>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="categoryContent"
android:textAllCaps="false" />
<Button
android:id="@+id/detailContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="detailContent"
android:textAllCaps="false" />
<Button
android:id="@+id/playerContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="playerContent"
android:textAllCaps="false" />
<Button
android:id="@+id/searchContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="searchContent"
android:textAllCaps="false" />
<Button
android:id="@+id/liveContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="liveContent"
android:textAllCaps="false" />
</LinearLayout>
</ScrollView>

View File

@ -1,8 +1,8 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id 'ru.cleverpumpkin.proguard-dictionaries-generator' version '1.0.8' apply false id 'ru.cleverpumpkin.proguard-dictionaries-generator' version '1.0.8' apply false
id 'com.android.application' version '8.8.0-alpha05' apply false id 'com.android.application' version '8.13.0' apply false
id 'com.android.library' version '8.8.0-alpha05' apply false id 'com.android.library' version '8.13.0' apply false
} }
tasks.register('clean', Delete) { tasks.register('clean', Delete) {
@ -10,5 +10,5 @@ tasks.register('clean', Delete) {
} }
project.ext { project.ext {
okhttpVersion = '5.0.0-alpha.14' okhttpVersion = '5.1.0'
} }

View File

@ -1,6 +1,6 @@
#Wed Mar 29 12:54:35 CST 2023 #Wed Mar 29 12:54:35 CST 2023
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-bin.zip distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.14.3-bin.zip
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

Binary file not shown.

View File

@ -1 +1 @@
fd389e3446f23e76f9112a2097d81884 bd8c878db83953d74efc2cb0723f68ac

View File

@ -1,41 +1,17 @@
[ [
{ {
"name": "小雅", "name": "Tangsan",
"server": "http://alist.xiaoya.pro" "server": "http://192.168.0.1",
},
{
"name": "觸光",
"server": "https://pan.ichuguang.com"
},
{
"name": "一只魚",
"server": "https://vtok.pp.ua/"
},
{
"name": "七米藍",
"server": "https://al.chirmyram.com"
},
{
"name": "神族九帝",
"server": "https://alist.shenzjd.com"
},
{
"name": "梓澪",
"server": "https://zi0.cc"
},
{
"name": "範本",
"server": "https://one.fongmi.com",
"search": true, "search": true,
"hidden": false, "hidden": false,
"login": { "login": {
"username": "fongmi", "username": "1234",
"password": "fongmi" "password": "1234"
}, },
"params": [ "params": [
{ {
"path": "/安齋拉拉", "path": "/test",
"pass": "18181818" "pass": "1234"
} }
] ]
} }

View File

@ -1,13 +1,11 @@
{ {
"spider": "./jar/custom_spider.jar", "spider": "../jar/custom_spider.jar",
"wallpaper": "http://饭太硬.top/深色壁纸/api.php",
"lives": [ "lives": [
{ {
"name": "XtreamCode", "name": "XtreamCode",
"api": "csp_XtreamCode", "api": "csp_XtreamCode",
"url": "http://iptv.icsnleb.com:25461/player_api.php?username=12&password=12", "url": "http://iptv.icsnleb.com:25461/player_api.php?username=12&password=12",
"epg": "http://iptv.icsnleb.com:25461/xmltv.php?username=12&password=12", "epg": "http://iptv.icsnleb.com:25461/xmltv.php?username=12&password=12",
"type": 3,
"ext": { "ext": {
"live": true, "live": true,
"vod": true, "vod": true,
@ -16,29 +14,43 @@
"ts" "ts"
] ]
} }
},
{
"name": "MQiTV",
"api": "csp_MQiTV",
"ext": [
{
"name": "Hotel A",
"url": "https://127.0.0.1:4433"
},
{
"name": "Hotel B",
"url": "http://127.0.0.1"
}
]
} }
], ],
"sites": [ "sites": [
{ {
"key": "本地", "key": "local",
"name": "本地", "name": "Local",
"type": 3, "type": 3,
"api": "csp_Local", "api": "csp_Local",
"searchable": 0, "searchable": 0,
"changeable": 0 "changeable": 0
}, },
{ {
"key": "商店", "key": "market",
"name": "商店", "name": "Market",
"type": 3, "type": 3,
"api": "csp_Market", "api": "csp_Market",
"searchable": 0, "searchable": 0,
"changeable": 0, "changeable": 0,
"ext": "./json/market.json" "ext": "./market.json"
}, },
{ {
"key": "push_agent", "key": "push_agent",
"name": "推送", "name": "Push",
"type": 3, "type": 3,
"api": "csp_Push", "api": "csp_Push",
"searchable": 0, "searchable": 0,
@ -64,68 +76,11 @@
"2606:4700:4700::1111", "2606:4700:4700::1111",
"2606:4700:4700::1001" "2606:4700:4700::1001"
] ]
},
{
"name": "AdGuard",
"url": "https://dns.adguard.com/dns-query",
"ips": [
"94.140.14.140",
"94.140.14.141"
]
},
{
"name": "DNSWatch",
"url": "https://resolver2.dns.watch/dns-query",
"ips": [
"84.200.69.80",
"84.200.70.40"
]
},
{
"name": "Quad9",
"url": "https://dns.quad9.net/dns-quer",
"ips": [
"9.9.9.9",
"149.112.112.112"
]
} }
], ],
"proxy": [
"raw.githubusercontent.com",
"googlevideo.com",
"googleapis.com",
"youtube.com"
],
"rules": [ "rules": [
{ {
"name": "火山嗅探", "name": "sniffer",
"hosts": [
"huoshan.com"
],
"regex": [
"item_id="
]
},
{
"name": "抖音嗅探",
"hosts": [
"douyin.com"
],
"regex": [
"is_play_url="
]
},
{
"name": "農民嗅探",
"hosts": [
"toutiaovod.com"
],
"regex": [
"video/tos/cn"
]
},
{
"name": "七新嗅探",
"hosts": [ "hosts": [
"api.52wyb.com" "api.52wyb.com"
], ],
@ -134,7 +89,7 @@
] ]
}, },
{ {
"name": "毛驢點擊", "name": "click",
"hosts": [ "hosts": [
"www.maolvys.com" "www.maolvys.com"
], ],

View File

@ -1,794 +0,0 @@
{
"hot_gaia": [
{
"key": "sort",
"name": "排序",
"value": [
{
"n": "热度",
"v": "recommend"
},
{
"n": "最新",
"v": "time"
},
{
"n": "评分",
"v": "rank"
}
]
},
{
"key": "area",
"name": "地区",
"value": [
{
"n": "全部",
"v": "全部"
},
{
"n": "华语",
"v": "华语"
},
{
"n": "欧美",
"v": "欧美"
},
{
"n": "韩国",
"v": "韩国"
},
{
"n": "日本",
"v": "日本"
}
]
}
],
"tv_hot": [
{
"key": "type",
"name": "分类",
"value": [
{
"n": "综合",
"v": "tv_hot"
},
{
"n": "国产剧",
"v": "tv_domestic"
},
{
"n": "欧美剧",
"v": "tv_american"
},
{
"n": "日剧",
"v": "tv_japanese"
},
{
"n": "韩剧",
"v": "tv_korean"
},
{
"n": "动画",
"v": "tv_animation"
}
]
}
],
"show_hot": [
{
"key": "type",
"name": "分类",
"value": [
{
"n": "综合",
"v": "show_hot"
},
{
"n": "国内",
"v": "show_domestic"
},
{
"n": "国外",
"v": "show_foreign"
}
]
}
],
"movie": [
{
"key": "类型",
"name": "类型",
"value": [
{
"n": "全部类型",
"v": ""
},
{
"n": "喜剧",
"v": "喜剧"
},
{
"n": "爱情",
"v": "爱情"
},
{
"n": "动作",
"v": "动作"
},
{
"n": "科幻",
"v": "科幻"
},
{
"n": "动画",
"v": "动画"
},
{
"n": "悬疑",
"v": "悬疑"
},
{
"n": "犯罪",
"v": "犯罪"
},
{
"n": "惊悚",
"v": "惊悚"
},
{
"n": "冒险",
"v": "冒险"
},
{
"n": "音乐",
"v": "音乐"
},
{
"n": "历史",
"v": "历史"
},
{
"n": "奇幻",
"v": "奇幻"
},
{
"n": "恐怖",
"v": "恐怖"
},
{
"n": "战争",
"v": "战争"
},
{
"n": "传记",
"v": "传记"
},
{
"n": "歌舞",
"v": "歌舞"
},
{
"n": "武侠",
"v": "武侠"
},
{
"n": "情色",
"v": "情色"
},
{
"n": "灾难",
"v": "灾难"
},
{
"n": "西部",
"v": "西部"
},
{
"n": "纪录片",
"v": "纪录片"
},
{
"n": "短片",
"v": "短片"
}
]
},
{
"key": "地区",
"name": "地区",
"value": [
{
"n": "全部地区",
"v": ""
},
{
"n": "华语",
"v": "华语"
},
{
"n": "欧美",
"v": "欧美"
},
{
"n": "韩国",
"v": "韩国"
},
{
"n": "日本",
"v": "日本"
},
{
"n": "中国大陆",
"v": "中国大陆"
},
{
"n": "美国",
"v": "美国"
},
{
"n": "中国香港",
"v": "中国香港"
},
{
"n": "中国台湾",
"v": "中国台湾"
},
{
"n": "英国",
"v": "英国"
},
{
"n": "法国",
"v": "法国"
},
{
"n": "德国",
"v": "德国"
},
{
"n": "意大利",
"v": "意大利"
},
{
"n": "西班牙",
"v": "西班牙"
},
{
"n": "印度",
"v": "印度"
},
{
"n": "泰国",
"v": "泰国"
},
{
"n": "俄罗斯",
"v": "俄罗斯"
},
{
"n": "加拿大",
"v": "加拿大"
},
{
"n": "澳大利亚",
"v": "澳大利亚"
},
{
"n": "爱尔兰",
"v": "爱尔兰"
},
{
"n": "瑞典",
"v": "瑞典"
},
{
"n": "巴西",
"v": "巴西"
},
{
"n": "丹麦",
"v": "丹麦"
}
]
},
{
"key": "sort",
"name": "排序",
"value": [
{
"n": "近期热度",
"v": "T"
},
{
"n": "首映时间",
"v": "R"
},
{
"n": "高分优先",
"v": "S"
}
]
},
{
"key": "年代",
"name": "年代",
"value": [
{
"n": "全部年代",
"v": ""
},
{
"n": "2023",
"v": "2023"
},
{
"n": "2022",
"v": "2022"
},
{
"n": "2021",
"v": "2021"
},
{
"n": "2020",
"v": "2020"
},
{
"n": "2019",
"v": "2019"
},
{
"n": "2010年代",
"v": "2010年代"
},
{
"n": "2000年代",
"v": "2000年代"
},
{
"n": "90年代",
"v": "90年代"
},
{
"n": "80年代",
"v": "80年代"
},
{
"n": "70年代",
"v": "70年代"
},
{
"n": "60年代",
"v": "60年代"
},
{
"n": "更早",
"v": "更早"
}
]
}
],
"tv": [
{
"key": "类型",
"name": "类型",
"value": [
{
"n": "不限",
"v": ""
},
{
"n": "电视剧",
"v": "电视剧"
},
{
"n": "综艺",
"v": "综艺"
}
]
},
{
"key": "电视剧形式",
"name": "电视剧形式",
"value": [
{
"n": "不限",
"v": ""
},
{
"n": "喜剧",
"v": "喜剧"
},
{
"n": "爱情",
"v": "爱情"
},
{
"n": "悬疑",
"v": "悬疑"
},
{
"n": "动画",
"v": "动画"
},
{
"n": "武侠",
"v": "武侠"
},
{
"n": "古装",
"v": "古装"
},
{
"n": "家庭",
"v": "家庭"
},
{
"n": "犯罪",
"v": "犯罪"
},
{
"n": "科幻",
"v": "科幻"
},
{
"n": "恐怖",
"v": "恐怖"
},
{
"n": "历史",
"v": "历史"
},
{
"n": "战争",
"v": "战争"
},
{
"n": "动作",
"v": "动作"
},
{
"n": "冒险",
"v": "冒险"
},
{
"n": "传记",
"v": "传记"
},
{
"n": "剧情",
"v": "剧情"
},
{
"n": "奇幻",
"v": "奇幻"
},
{
"n": "惊悚",
"v": "惊悚"
},
{
"n": "灾难",
"v": "灾难"
},
{
"n": "歌舞",
"v": "歌舞"
},
{
"n": "音乐",
"v": "音乐"
}
]
},
{
"key": "综艺形式",
"name": "综艺形式",
"value": [
{
"n": "不限",
"v": ""
},
{
"n": "真人秀",
"v": "真人秀"
},
{
"n": "脱口秀",
"v": "脱口秀"
},
{
"n": "音乐",
"v": "音乐"
},
{
"n": "歌舞",
"v": "歌舞"
}
]
},
{
"key": "地区",
"name": "地区",
"value": [
{
"n": "全部地区",
"v": ""
},
{
"n": "华语",
"v": "华语"
},
{
"n": "欧美",
"v": "欧美"
},
{
"n": "国外",
"v": "国外"
},
{
"n": "韩国",
"v": "韩国"
},
{
"n": "日本",
"v": "日本"
},
{
"n": "中国大陆",
"v": "中国大陆"
},
{
"n": "中国香港",
"v": "中国香港"
},
{
"n": "美国",
"v": "美国"
},
{
"n": "英国",
"v": "英国"
},
{
"n": "泰国",
"v": "泰国"
},
{
"n": "中国台湾",
"v": "中国台湾"
},
{
"n": "意大利",
"v": "意大利"
},
{
"n": "法国",
"v": "法国"
},
{
"n": "德国",
"v": "德国"
},
{
"n": "西班牙",
"v": "西班牙"
},
{
"n": "俄罗斯",
"v": "俄罗斯"
},
{
"n": "瑞典",
"v": "瑞典"
},
{
"n": "巴西",
"v": "巴西"
},
{
"n": "丹麦",
"v": "丹麦"
},
{
"n": "印度",
"v": "印度"
},
{
"n": "加拿大",
"v": "加拿大"
},
{
"n": "爱尔兰",
"v": "爱尔兰"
},
{
"n": "澳大利亚",
"v": "澳大利亚"
}
]
},
{
"key": "sort",
"name": "排序",
"value": [
{
"n": "近期热度",
"v": "T"
},
{
"n": "首播时间",
"v": "R"
},
{
"n": "高分优先",
"v": "S"
}
]
},
{
"key": "年代",
"name": "年代",
"value": [
{
"n": "全部",
"v": ""
},
{
"n": "2023",
"v": "2023"
},
{
"n": "2022",
"v": "2022"
},
{
"n": "2021",
"v": "2021"
},
{
"n": "2020",
"v": "2020"
},
{
"n": "2019",
"v": "2019"
},
{
"n": "2010年代",
"v": "2010年代"
},
{
"n": "2000年代",
"v": "2000年代"
},
{
"n": "90年代",
"v": "90年代"
},
{
"n": "80年代",
"v": "80年代"
},
{
"n": "70年代",
"v": "70年代"
},
{
"n": "60年代",
"v": "60年代"
},
{
"n": "更早",
"v": "更早"
}
]
},
{
"key": "平台",
"name": "平台",
"value": [
{
"n": "全部",
"v": ""
},
{
"n": "腾讯视频",
"v": "腾讯视频"
},
{
"n": "爱奇艺",
"v": "爱奇艺"
},
{
"n": "优酷",
"v": "优酷"
},
{
"n": "湖南卫视",
"v": "湖南卫视"
},
{
"n": "Netflix",
"v": "Netflix"
},
{
"n": "HBO",
"v": "HBO"
},
{
"n": "BBC",
"v": "BBC"
},
{
"n": "NHK",
"v": "NHK"
},
{
"n": "CBS",
"v": "CBS"
},
{
"n": "NBC",
"v": "NBC"
},
{
"n": "tvN",
"v": "tvN"
}
]
}
],
"rank_list_movie": [
{
"key": "榜单",
"name": "榜单",
"value": [
{
"n": "实时热门电影",
"v": "movie_real_time_hotest"
},
{
"n": "一周口碑电影榜",
"v": "movie_weekly_best"
},
{
"n": "豆瓣电影Top250",
"v": "movie_top250"
}
]
}
],
"rank_list_tv": [
{
"key": "榜单",
"name": "榜单",
"value": [
{
"n": "实时热门电视",
"v": "tv_real_time_hotest"
},
{
"n": "华语口碑剧集榜",
"v": "tv_chinese_best_weekly"
},
{
"n": "全球口碑剧集榜",
"v": "tv_global_best_weekly"
},
{
"n": "国内口碑综艺榜",
"v": "show_chinese_best_weekly"
},
{
"n": "国外口碑综艺榜",
"v": "show_global_best_weekly"
}
]
}
]
}

View File

@ -1,108 +1,9 @@
{ {
"0": [
{
"key": "area",
"name": "地區",
"value": [
{
"n": "全部",
"v": "0"
},
{
"n": "国产",
"v": "1"
},
{
"n": "中国香港",
"v": "3"
},
{
"n": "中国台湾",
"v": "6"
},
{
"n": "美国",
"v": "5"
},
{
"n": "韩国",
"v": "18"
},
{
"n": "日本",
"v": "2"
}
]
},
{
"key": "year",
"name": "年份",
"value": [
{
"n": "全部",
"v": "0"
},
{
"n": "2024",
"v": "119"
},
{
"n": "2023",
"v": "153"
},
{
"n": "2022",
"v": "101"
},
{
"n": "2021",
"v": "118"
},
{
"n": "2020",
"v": "16"
},
{
"n": "2019",
"v": "7"
},
{
"n": "2018",
"v": "2"
},
{
"n": "2017",
"v": "3"
},
{
"n": "2016",
"v": "22"
}
]
},
{
"key": "by",
"name": "排序",
"value": [
{
"n": "热门",
"v": "hot"
},
{
"n": "更新",
"v": "updata"
},
{
"n": "评分",
"v": "rating"
}
]
}
],
"1": [ "1": [
{ {
"key": "area", "key": "area",
"name": "地區", "name": "地區",
"init": "1",
"value": [ "value": [
{ {
"n": "全部", "n": "全部",
@ -142,6 +43,10 @@
"n": "全部", "n": "全部",
"v": "0" "v": "0"
}, },
{
"n": "2025",
"v": "107"
},
{ {
"n": "2024", "n": "2024",
"v": "119" "v": "119"
@ -183,6 +88,7 @@
{ {
"key": "by", "key": "by",
"name": "排序", "name": "排序",
"init": "updata",
"value": [ "value": [
{ {
"n": "热门", "n": "热门",
@ -203,6 +109,7 @@
{ {
"key": "area", "key": "area",
"name": "地區", "name": "地區",
"init": "1",
"value": [ "value": [
{ {
"n": "全部", "n": "全部",
@ -242,6 +149,10 @@
"n": "全部", "n": "全部",
"v": "0" "v": "0"
}, },
{
"n": "2025",
"v": "107"
},
{ {
"n": "2024", "n": "2024",
"v": "119" "v": "119"
@ -283,6 +194,7 @@
{ {
"key": "by", "key": "by",
"name": "排序", "name": "排序",
"init": "updata",
"value": [ "value": [
{ {
"n": "热门", "n": "热门",
@ -303,6 +215,7 @@
{ {
"key": "area", "key": "area",
"name": "地區", "name": "地區",
"init": "1",
"value": [ "value": [
{ {
"n": "全部", "n": "全部",
@ -342,6 +255,10 @@
"n": "全部", "n": "全部",
"v": "0" "v": "0"
}, },
{
"n": "2025",
"v": "107"
},
{ {
"n": "2024", "n": "2024",
"v": "119" "v": "119"
@ -383,6 +300,7 @@
{ {
"key": "by", "key": "by",
"name": "排序", "name": "排序",
"init": "updata",
"value": [ "value": [
{ {
"n": "热门", "n": "热门",
@ -403,6 +321,7 @@
{ {
"key": "area", "key": "area",
"name": "地區", "name": "地區",
"init": "1",
"value": [ "value": [
{ {
"n": "全部", "n": "全部",
@ -442,6 +361,10 @@
"n": "全部", "n": "全部",
"v": "0" "v": "0"
}, },
{
"n": "2025",
"v": "107"
},
{ {
"n": "2024", "n": "2024",
"v": "119" "v": "119"
@ -483,6 +406,7 @@
{ {
"key": "by", "key": "by",
"name": "排序", "name": "排序",
"init": "updata",
"value": [ "value": [
{ {
"n": "热门", "n": "热门",

View File

@ -1,126 +1,15 @@
[ [
{ {
"name": "推薦", "name": "APP",
"list": [ "list": [
{ {
"url": "https://i.imgs.ovh/2023/10/19/2mfxN.jpeg", "name": "電視版",
"icon": "https://i.imgs.ovh/2023/10/19/2mfxN.jpeg" "url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/leanback-armeabi_v7a.apk",
}
]
},
{
"name": "正式版",
"list": [
{
"name": "電視-java",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/leanback-java-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v7a" "version": "v7a"
}, },
{ {
"name": "電視-java", "name": "行動版",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/leanback-java-arm64_v8a.apk", "url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/mobile-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v8a"
},
{
"name": "電視-py",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/leanback-python-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v7a"
},
{
"name": "電視-py",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/leanback-python-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v8a"
},
{
"name": "手機-java",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/mobile-java-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v7a"
},
{
"name": "手機-java",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/mobile-java-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v8a"
},
{
"name": "手機-py",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/mobile-python-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v7a"
},
{
"name": "手機-py",
"url": "https://github.com/FongMi/Release/raw/fongmi/apk/release/mobile-python-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v8a"
}
]
},
{
"name": "內測版",
"list": [
{
"name": "電視-java",
"url": "https://fm.caioa.link/main/apk/dev/leanback-java-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v7a"
},
{
"name": "電視-java",
"url": "https://fm.caioa.link/main/apk/dev/leanback-java-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v8a"
},
{
"name": "電視-py",
"url": "https://fm.caioa.link/main/apk/dev/leanback-python-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v7a"
},
{
"name": "電視-py",
"url": "https://fm.caioa.link/main/apk/dev/leanback-python-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v8a"
},
{
"name": "手機-java",
"url": "https://fm.caioa.link/main/apk/dev/mobile-java-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v7a"
},
{
"name": "手機-java",
"url": "https://fm.caioa.link/main/apk/dev/mobile-java-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v8a"
},
{
"name": "手機-py",
"url": "https://fm.caioa.link/main/apk/dev/mobile-python-armeabi_v7a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v7a"
},
{
"name": "手機-py",
"url": "https://fm.caioa.link/main/apk/dev/mobile-python-arm64_v8a.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8lVK.png",
"version": "v8a"
}
]
},
{
"name": "4.x版",
"list": [
{
"name": "電視",
"url": "https://fm.caioa.link/main/apk/kitkat/leanback.apk",
"icon": "https://i.imgs.ovh/2023/10/17/r8nk2.png",
"version": "v7a" "version": "v7a"
} }
] ]

View File

@ -1,5 +0,0 @@
{
"msg": "FongMi 天下第一",
"date": "20230604000000",
"duration": 10
}

View File

@ -1,6 +1,6 @@
[ [
{ {
"name": "Tangsan", "name": "Tangsan",
"server": "smb://192.168.0.2/Users" "server": "smb://192.168.0.1/Users"
} }
] ]

View File

@ -1,14 +1,8 @@
[ [
{ {
"name": "七米藍", "name": "Tangsan",
"server": "https://al.chirmyram.com/dav", "server": "http://192.168.0.1/dav",
"user": "alist", "user": "1234",
"pass": "alist" "pass": "1234"
},
{
"name": "影視庫",
"server": "https://esir.eu.org/dav",
"user": "alist",
"pass": "alist"
} }
] ]