Support WebDAV - part 1

This commit is contained in:
FongMi 2023-04-15 02:46:23 +08:00
parent 155066d132
commit 92c50f9780
12 changed files with 373 additions and 28 deletions

View File

@ -8,7 +8,7 @@ android {
defaultConfig {
applicationId "com.github.catvod.demo"
minSdk 17
minSdk 21
targetSdk 29
ndk { abiFilters "armeabi-v7a" }
buildConfigField("String", "CLIENT_ID", "\"${clientId}\"")
@ -36,11 +36,12 @@ android {
dependencies {
//Debug For HTTP/3
debugImplementation 'org.chromium.net:cronet-embedded:101.4951.41'
implementation 'com.github.thegrizzlylabs:sardine-android:0.8'
implementation 'com.google.net.cronet:cronet-okhttp:0.1.0'
implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10'
implementation 'androidx.annotation:annotation:1.5.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.13'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'cn.wanghaomiao:JsoupXpath:2.5.1'
implementation 'com.google.zxing:core:3.3.0'
implementation 'com.google.zxing:core:3.4.1'
implementation 'org.jsoup:jsoup:1.15.3'
}

View File

@ -11,7 +11,7 @@
-keepattributes Signature
-keepattributes *Annotation*
-dontwarn sun.misc.**
-keep class com.google.gson.**{*;}
-keep class com.google.gson.** { *; }
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
@ -20,16 +20,20 @@
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
# Cronet
-keep class org.chromium.net.** { *; }
-keep class com.google.net.cronet.** { *; }
# OkHttp
-dontwarn okhttp3.**
-keep class okio.** { *; }
-keep class okhttp3.** { *; }
# Sardine
-keep class com.thegrizzlylabs.sardineandroid.** { *; }
# Zxing
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# OkHttp
-keep class okio.**{*;}
-keep class okhttp3.**{*;}
# Cronet
-keep class org.chromium.net.**{*;}
-keep class com.google.net.cronet.**{*;}
}

View File

@ -11,9 +11,9 @@ import org.json.JSONObject;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Result {
@ -105,7 +105,7 @@ public class Result {
return this;
}
public Result header(HashMap<String, String> header) {
public Result header(Map<String, String> header) {
if (header.isEmpty()) return this;
this.header = new Gson().toJson(header);
return this;

View File

@ -47,12 +47,12 @@ public class Vod {
setVodRemarks(vodRemarks);
}
public Vod(String vodId, String vodName, String vodPic, String vodRemarks, String vodTag) {
public Vod(String vodId, String vodName, String vodPic, String vodRemarks, boolean folder) {
setVodId(vodId);
setVodName(vodName);
setVodPic(vodPic);
setVodRemarks(vodRemarks);
setVodTag(vodTag);
setVodTag(folder ? "folder" : "file");
}
public void setTypeName(String typeName) {

View File

@ -120,15 +120,11 @@ public class Item {
return Utils.getSize(getSize());
}
public String getVodTag() {
return isFolder() ? "folder" : "file";
}
public Vod getVod(String id, String pic) {
return new Vod(getVodId(id), getName(), getPic(pic), getRemark(), getVodTag());
return new Vod(getVodId(id), getName(), getPic(pic), getRemark(), isFolder());
}
public Vod getVod(Drive drive, String pic) {
return new Vod(getVodId(drive.getName()), getName(), getPic(pic), drive.getName(), getVodTag());
return new Vod(getVodId(drive.getName()), getName(), getPic(pic), drive.getName(), isFolder());
}
}

View File

@ -0,0 +1,107 @@
package com.github.catvod.bean.webdav;
import android.net.Uri;
import android.text.TextUtils;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Vod;
import com.github.catvod.utils.Utils;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import com.thegrizzlylabs.sardineandroid.DavResource;
import com.thegrizzlylabs.sardineandroid.Sardine;
import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine;
import java.util.ArrayList;
import java.util.List;
public class Drive {
@SerializedName("vodPic")
private String vodPic;
@SerializedName("drives")
private List<Drive> drives;
@SerializedName("name")
private String name;
@SerializedName("server")
private String server;
@SerializedName("user")
private String user;
@SerializedName("pass")
private String pass;
@SerializedName("path")
private String path;
@SerializedName("webdav")
private Sardine webdav;
public static Drive objectFrom(String str) {
return new Gson().fromJson(str, Drive.class);
}
public Drive(String name) {
this.name = name;
}
public List<Drive> getDrives() {
return drives == null ? new ArrayList<>() : drives;
}
public String getVodPic() {
return TextUtils.isEmpty(vodPic) ? "" : vodPic;
}
public String getName() {
return TextUtils.isEmpty(name) ? "" : name;
}
public String getServer() {
return TextUtils.isEmpty(server) ? "" : server;
}
public String getUser() {
return TextUtils.isEmpty(user) ? "" : user;
}
public String getPass() {
return TextUtils.isEmpty(pass) ? "" : pass;
}
public String getPath() {
return TextUtils.isEmpty(path) ? "" : path;
}
public void setPath(String path) {
this.path = TextUtils.isEmpty(path) ? "" : path;
}
public String getHost() {
return getServer().replace(getPath(), "");
}
public Sardine getWebdav() {
return webdav;
}
public Class toType() {
return new Class(getName(), getName(), "1");
}
public Drive init() {
webdav = new OkHttpSardine();
webdav.setCredentials(getUser(), getPass());
setPath(Uri.parse(getServer()).getPath());
return this;
}
public Vod vod(DavResource item, String vodPic) {
return new Vod(getName() + item.getPath(), item.getName(), vodPic, Utils.getSize(item.getContentLength()), item.isDirectory());
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Drive)) return false;
Drive it = (Drive) obj;
return getName().equals(it.getName());
}
}

View File

@ -0,0 +1,37 @@
package com.github.catvod.bean.webdav;
import com.thegrizzlylabs.sardineandroid.DavResource;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Sorter implements Comparator<DavResource> {
private final String type;
private final String order;
public static void sort(String type, String order, List<DavResource> items) {
Collections.sort(items, new Sorter(type, order));
}
public Sorter(String type, String order) {
this.type = type;
this.order = order;
}
@Override
public int compare(DavResource o1, DavResource o2) {
boolean asc = order.equals("asc");
switch (type) {
case "name":
return asc ? o1.getName().compareTo(o2.getName()) : o2.getName().compareTo(o1.getName());
case "size":
return asc ? Long.compare(o1.getContentLength(), o2.getContentLength()) : Long.compare(o2.getContentLength(), o1.getContentLength());
case "date":
return asc ? o1.getModified().compareTo(o2.getModified()) : o2.getModified().compareTo(o1.getModified());
default:
return -1;
}
}
}

View File

@ -5,7 +5,6 @@ import com.github.catvod.crawler.SpiderDebug;
import com.github.catvod.net.OkHttp;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Objects;
@ -13,12 +12,14 @@ public class Proxy extends Spider {
private static int port = -1;
public static Object[] proxy(Map<String, String> params) throws UnsupportedEncodingException {
public static Object[] proxy(Map<String, String> params) throws Exception {
switch (Objects.requireNonNull(params.get("do"))) {
case "ck":
return new Object[]{200, "text/plain; charset=utf-8", new ByteArrayInputStream("ok".getBytes("UTF-8"))};
case "ali":
return Ali.vod(params);
case "webdav":
return WebDAV.vod(params);
default:
return null;
}

View File

@ -0,0 +1,197 @@
package com.github.catvod.spider;
import android.content.Context;
import android.text.TextUtils;
import com.github.catvod.bean.Class;
import com.github.catvod.bean.Filter;
import com.github.catvod.bean.Result;
import com.github.catvod.bean.Sub;
import com.github.catvod.bean.Vod;
import com.github.catvod.bean.webdav.Drive;
import com.github.catvod.bean.webdav.Sorter;
import com.github.catvod.crawler.Spider;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Utils;
import com.thegrizzlylabs.sardineandroid.DavResource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class WebDAV extends Spider {
private static List<Drive> drives;
private List<String> playExt;
private List<String> allExt;
private String vodPic;
private String ext;
private List<Filter> getFilter() {
List<Filter> items = new ArrayList<>();
items.add(new Filter("type", "排序類型", Arrays.asList(new Filter.Value("預設", ""), new Filter.Value("名稱", "name"), new Filter.Value("大小", "size"), new Filter.Value("修改時間", "date"))));
items.add(new Filter("order", "排序方式", Arrays.asList(new Filter.Value("預設", ""), new Filter.Value("", "asc"), new Filter.Value("", "desc"))));
return items;
}
private void fetchRule() {
if (drives != null && !drives.isEmpty()) return;
if (ext.startsWith("http")) ext = OkHttp.string(ext);
Drive drive = Drive.objectFrom(ext);
drives = drive.getDrives();
vodPic = drive.getVodPic();
}
private String getExt(DavResource item) {
return item.getName().substring(item.getName().lastIndexOf(".") + 1);
}
private String removeExt(DavResource item) {
return item.getName().indexOf(".") > 0 ? item.getName().substring(0, item.getName().lastIndexOf(".")) : item.getName();
}
private static Drive getDrive(String name) {
return drives.get(drives.indexOf(new Drive(name)));
}
@Override
public void init(Context context, String extend) {
playExt = Arrays.asList("mp4", "mkv", "flv", "avi", "mp3", "aac", "flac", "m4a");
allExt = new ArrayList<>(Arrays.asList("ass", "ssa", "srt"));
allExt.addAll(playExt);
ext = extend;
fetchRule();
}
@Override
public String homeContent(boolean filter) throws Exception {
fetchRule();
List<Class> classes = new ArrayList<>();
LinkedHashMap<String, List<Filter>> filters = new LinkedHashMap<>();
for (Drive drive : drives) classes.add(drive.init().toType());
for (Class item : classes) filters.put(item.getTypeId(), getFilter());
return Result.string(classes, filters);
}
@Override
public String categoryContent(String tid, String pg, boolean filter, HashMap<String, String> extend) throws Exception {
String key = tid.contains("/") ? tid.substring(0, tid.indexOf("/")) : tid;
String path = tid.contains("/") ? tid.substring(tid.indexOf("/")) : "";
String order = extend.containsKey("order") ? extend.get("order") : "";
String type = extend.containsKey("type") ? extend.get("type") : "";
List<DavResource> folders = new ArrayList<>();
List<DavResource> files = new ArrayList<>();
List<Vod> list = new ArrayList<>();
Drive drive = getDrive(key);
for (DavResource item : getList(drive, path, playExt)) {
if (item.isDirectory()) folders.add(item);
else files.add(item);
}
if (!TextUtils.isEmpty(type) && !TextUtils.isEmpty(order)) {
Sorter.sort(type, order, folders);
Sorter.sort(type, order, files);
}
for (DavResource item : folders) list.add(drive.vod(item, vodPic));
for (DavResource item : files) list.add(drive.vod(item, vodPic));
return Result.get().vod(list).page().string();
}
@Override
public String detailContent(List<String> ids) throws Exception {
String id = ids.get(0);
String key = id.contains("/") ? id.substring(0, id.indexOf("/")) : id;
String name = id.substring(id.lastIndexOf("/") + 1);
String parent = id.substring(0, id.lastIndexOf("/"));
String path = parent.contains("/") ? parent.substring(parent.indexOf("/")) + "/" : "";
Drive drive = getDrive(key);
List<DavResource> parents = getList(drive, path, allExt);
List<DavResource> subs = getSubs(parents);
Sorter.sort("name", "asc", parents);
List<String> playUrls = new ArrayList<>();
for (DavResource item : parents) {
if (playExt.contains(getExt(item))) {
playUrls.add(item.getName() + "$" + drive.getName() + item.getPath() + findSubs(drive, item, subs));
}
}
Vod vod = new Vod();
vod.setVodId(id);
//TODO 資料夾名稱
vod.setVodName(name);
vod.setVodPic(vodPic);
vod.setVodPlayFrom(key);
vod.setVodPlayUrl(TextUtils.join("#", playUrls));
return Result.string(vod);
}
@Override
public String playerContent(String flag, String id, List<String> vipFlags) throws Exception {
String[] ids = id.split("~~~");
String key = ids[0].contains("/") ? ids[0].substring(0, ids[0].indexOf("/")) : ids[0];
return Result.get().url(getProxyUrl(key, ids[0])).subs(getSub(ids)).string();
}
private List<DavResource> getList(Drive drive, String path, List<String> ext) throws Exception {
path = drive.getHost() + (path.startsWith(drive.getPath()) ? path : drive.getPath() + path);
List<DavResource> items = drive.getWebdav().list(path);
items.remove(0); //Remove parent
Iterator<DavResource> iterator = items.iterator();
while (iterator.hasNext()) {
DavResource item = iterator.next();
if (!item.isDirectory() && !item.getName().contains(".")) iterator.remove();
if (!item.isDirectory() && !ext.contains(getExt(item))) iterator.remove();
}
return items;
}
private List<DavResource> getSubs(List<DavResource> items) {
List<DavResource> subs = new ArrayList<>();
for (DavResource item : items) if (Utils.isSub(getExt(item))) subs.add(item);
return subs;
}
private String findSubs(Drive drive, DavResource res, List<DavResource> items) {
StringBuilder sb = new StringBuilder();
for (DavResource item : items) if (removeExt(item).equals(removeExt(res))) sb.append("~~~").append(item.getName()).append("@@@").append(getExt(item)).append("@@@").append(drive.getName() + item.getPath());
return sb.length() > 0 ? sb.toString() : findSubs(drive, items);
}
private String findSubs(Drive drive, List<DavResource> items) {
StringBuilder sb = new StringBuilder();
for (DavResource item : items) sb.append("~~~").append(item.getName()).append("@@@").append(getExt(item)).append("@@@").append(drive.getName() + item.getPath());
return sb.toString();
}
private List<Sub> getSub(String[] ids) {
List<Sub> sub = new ArrayList<>();
for (String text : ids) {
if (!text.contains("@@@")) continue;
String[] split = text.split("@@@");
String name = split[0];
String ext = split[1];
String key = split[2].contains("/") ? split[2].substring(0, split[2].indexOf("/")) : split[2];
String url = getProxyUrl(key, split[2]);
sub.add(Sub.create().name(name).ext(ext).url(url));
}
return sub;
}
private String getProxyUrl(String key, String path) {
return Proxy.getUrl() + "?do=webdav" + "&key=" + key + "&url=" + getDrive(key).getHost() + path.replace(key, "");
}
public static Object[] vod(Map<String, String> params) throws IOException {
String key = params.get("key");
String url = params.get("url");
Drive drive = getDrive(key);
Object[] result = new Object[3];
result[0] = 200;
result[1] = "application/octet-stream";
result[2] = drive.getWebdav().get(url);
return result;
}
}

View File

@ -39,7 +39,7 @@ public class Utils {
}
public static String getSize(double size) {
if (size == 0) return "";
if (size <= 0) return "";
if (size > 1024 * 1024 * 1024 * 1024.0) {
size /= (1024 * 1024 * 1024 * 1024.0);
return String.format(Locale.getDefault(), "%.2f%s", size, "TB");

View File

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
@ -15,6 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
android.enableJetifier=true
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library

View File

@ -11,6 +11,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
rootProject.name = "CatVodSpider"