WebDAV同步功能重大改进:修复编码问题和双向同步
主要改进: 1. 修复观看记录key中站点名称的编码问题(电视版乱码修复) 2. 实现智能合并策略,支持时间和进度比较 3. 自动修复过期时间戳,确保记录能正常显示 4. 上传和下载都使用findAllRecent(0),确保完整同步 5. 添加详细日志,方便调试定位问题 技术细节: - 新增fixHistoryKey()方法,单独修复key中的站点名称部分 - 改进合并算法,考虑时间相近、进度领先等多种情况 - 修复createTime超过60天被过滤的问题 - 统一本地和远程记录的编码处理 删除的文件: - other/sample/* - 示例配置文件 - other/image/* - 示例图片 - .vscode/settings.json - 编辑器配置
This commit is contained in:
Vendored
-3
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"java.configuration.updateBuildConfiguration": "automatic"
|
|
||||||
}
|
|
||||||
@@ -245,7 +245,24 @@ public class HomeActivity extends BaseActivity implements CustomTitleView.Listen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void getHistory(boolean renew) {
|
private void getHistory(boolean renew) {
|
||||||
List<History> items = History.get();
|
// 获取所有视频源的观看记录(最近60天)
|
||||||
|
List<History> items = History.getAll();
|
||||||
|
com.github.catvod.utils.Logger.d("HomeActivity: 获取观看记录,共 " + items.size() + " 条");
|
||||||
|
|
||||||
|
// 对比一下数据库中所有记录
|
||||||
|
List<com.fongmi.android.tv.bean.History> allInDb = com.fongmi.android.tv.db.AppDatabase.get().getHistoryDao().findAllRecent(0);
|
||||||
|
com.github.catvod.utils.Logger.d("HomeActivity: 数据库总记录数: " + allInDb.size() + " 条(包含所有时间)");
|
||||||
|
|
||||||
|
if (items.size() < allInDb.size()) {
|
||||||
|
com.github.catvod.utils.Logger.w("HomeActivity: 有 " + (allInDb.size() - items.size()) + " 条记录因为时间过滤被隐藏");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (History h : items) {
|
||||||
|
com.github.catvod.utils.Logger.d("HomeActivity: 记录 - " + h.getVodName() +
|
||||||
|
" (cid=" + h.getCid() +
|
||||||
|
", createTime=" + h.getCreateTime() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
int historyIndex = getHistoryIndex();
|
int historyIndex = getHistoryIndex();
|
||||||
int recommendIndex = getRecommendIndex();
|
int recommendIndex = getRecommendIndex();
|
||||||
boolean exist = recommendIndex - historyIndex == 2;
|
boolean exist = recommendIndex - historyIndex == 2;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
<item name="colorPrimary">@color/primary</item>
|
<item name="colorPrimary">@color/primary</item>
|
||||||
<item name="colorPrimaryDark">@color/primaryDark</item>
|
<item name="colorPrimaryDark">@color/primaryDark</item>
|
||||||
<item name="colorAccent">@color/accent</item>
|
<item name="colorAccent">@color/accent</item>
|
||||||
<item name="colorControlHighlight">@color/primary</item>
|
|
||||||
<item name="android:windowFullscreen">true</item>
|
<item name="android:windowFullscreen">true</item>
|
||||||
<item name="android:windowBackground">@null</item>
|
<item name="android:windowBackground">@null</item>
|
||||||
<item name="android:windowDisablePreview">true</item>
|
<item name="android:windowDisablePreview">true</item>
|
||||||
|
|||||||
@@ -54,7 +54,9 @@ public class App extends Application {
|
|||||||
executor = Executors.newFixedThreadPool(Constant.THREAD_POOL);
|
executor = Executors.newFixedThreadPool(Constant.THREAD_POOL);
|
||||||
handler = HandlerCompat.createAsync(Looper.getMainLooper());
|
handler = HandlerCompat.createAsync(Looper.getMainLooper());
|
||||||
time = System.currentTimeMillis();
|
time = System.currentTimeMillis();
|
||||||
gson = new Gson();
|
gson = new com.google.gson.GsonBuilder()
|
||||||
|
.disableHtmlEscaping()
|
||||||
|
.create();
|
||||||
cleanTask = this::checkCacheClean;
|
cleanTask = this::checkCacheClean;
|
||||||
syncTask = this::checkWebDAVSync;
|
syncTask = this::checkWebDAVSync;
|
||||||
appJustLaunched = true;
|
appJustLaunched = true;
|
||||||
@@ -227,18 +229,8 @@ public class App extends Application {
|
|||||||
if (manager.isConfigured()) {
|
if (manager.isConfigured()) {
|
||||||
// 应用启动时,如果已配置WebDAV,立即执行一次同步(下载远程数据)
|
// 应用启动时,如果已配置WebDAV,立即执行一次同步(下载远程数据)
|
||||||
// 这样新设备配置后,下次启动应用时就能看到其他设备的历史记录
|
// 这样新设备配置后,下次启动应用时就能看到其他设备的历史记录
|
||||||
App.execute(() -> {
|
Logger.d("App: WebDAV已配置,准备执行同步");
|
||||||
try {
|
manager.syncHistory(true); // 使用统一的同步方法,包含防重复逻辑
|
||||||
Logger.d("App: 应用启动,执行WebDAV同步");
|
|
||||||
// 先上传本地记录
|
|
||||||
manager.uploadHistory();
|
|
||||||
// 再下载远程记录并合并
|
|
||||||
manager.downloadHistory();
|
|
||||||
Logger.d("App: WebDAV同步完成");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Logger.e("App: WebDAV同步失败: " + e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 如果启用了自动同步,设置定期同步
|
// 如果启用了自动同步,设置定期同步
|
||||||
if (Setting.isWebDAVAutoSync()) {
|
if (Setting.isWebDAVAutoSync()) {
|
||||||
@@ -246,6 +238,8 @@ public class App extends Application {
|
|||||||
// 延迟执行下次同步,避免影响启动速度
|
// 延迟执行下次同步,避免影响启动速度
|
||||||
post(syncTask, interval * 60 * 1000L);
|
post(syncTask, interval * 60 * 1000L);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Logger.d("App: WebDAV未配置,跳过同步");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,10 @@ public class History {
|
|||||||
return AppDatabase.get().getHistoryDao().find(cid, System.currentTimeMillis() - Constant.HISTORY_TIME);
|
return AppDatabase.get().getHistoryDao().find(cid, System.currentTimeMillis() - Constant.HISTORY_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<History> getAll() {
|
||||||
|
return AppDatabase.get().getHistoryDao().findAllRecent(System.currentTimeMillis() - Constant.HISTORY_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
public static History find(String key) {
|
public static History find(String key) {
|
||||||
return AppDatabase.get().getHistoryDao().find(VodConfig.getCid(), key);
|
return AppDatabase.get().getHistoryDao().find(VodConfig.getCid(), key);
|
||||||
}
|
}
|
||||||
@@ -272,8 +276,15 @@ public class History {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
|
try {
|
||||||
|
com.github.catvod.utils.Logger.d("History.update: 开始更新观看记录 key=" + getKey());
|
||||||
merge(find(), false);
|
merge(find(), false);
|
||||||
save();
|
save();
|
||||||
|
com.github.catvod.utils.Logger.d("History.update: 更新成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
com.github.catvod.utils.Logger.e("History.update: 更新失败 - " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public History update(int cid) {
|
public History update(int cid) {
|
||||||
@@ -287,6 +298,7 @@ public class History {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public History save() {
|
public History save() {
|
||||||
|
com.github.catvod.utils.Logger.d("History.save: key=" + getKey() + ", vodName=" + getVodName());
|
||||||
AppDatabase.get().getHistoryDao().insertOrUpdate(this);
|
AppDatabase.get().getHistoryDao().insertOrUpdate(this);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,12 +105,37 @@ public class Site implements Parcelable {
|
|||||||
|
|
||||||
public static Site objectFrom(JsonElement element) {
|
public static Site objectFrom(JsonElement element) {
|
||||||
try {
|
try {
|
||||||
return App.gson().fromJson(element, Site.class);
|
Site site = App.gson().fromJson(element, Site.class);
|
||||||
|
// 尝试修复可能的编码问题
|
||||||
|
if (site != null && site.getKey() != null) {
|
||||||
|
site.setKey(fixEncoding(site.getKey()));
|
||||||
|
if (site.getName() != null) {
|
||||||
|
site.setName(fixEncoding(site.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return site;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return new Site();
|
return new Site();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String fixEncoding(String str) {
|
||||||
|
if (str == null || str.isEmpty()) return str;
|
||||||
|
try {
|
||||||
|
// 检查是否包含乱码字符(替换字符 U+FFFD)
|
||||||
|
if (str.indexOf('\uFFFD') >= 0) {
|
||||||
|
// 尝试用ISO-8859-1重新解码为UTF-8
|
||||||
|
byte[] bytes = str.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||||
|
String fixed = new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
com.github.catvod.utils.Logger.d("Site.fixEncoding: 修复编码 '" + str + "' -> '" + fixed + "'");
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
com.github.catvod.utils.Logger.e("Site.fixEncoding: 修复失败 - " + e.getMessage());
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
public static Site get(String key) {
|
public static Site get(String key) {
|
||||||
Site site = new Site();
|
Site site = new Site();
|
||||||
site.setKey(key);
|
site.setKey(key);
|
||||||
@@ -133,6 +158,13 @@ public class Site implements Parcelable {
|
|||||||
|
|
||||||
public void setKey(@NonNull String key) {
|
public void setKey(@NonNull String key) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
// 检查key中是否有异常字符
|
||||||
|
for (int i = 0; i < key.length(); i++) {
|
||||||
|
char c = key.charAt(i);
|
||||||
|
if (c == 0xFFFD || c < 0x20 || (c >= 0x7F && c < 0xA0)) {
|
||||||
|
com.github.catvod.utils.Logger.w("Site.setKey: 检测到异常字符 at position " + i + ": U+" + String.format("%04X", (int)c));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@@ -141,6 +173,15 @@ public class Site implements Parcelable {
|
|||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
// 检查name中是否有异常字符
|
||||||
|
if (name != null) {
|
||||||
|
for (int i = 0; i < name.length(); i++) {
|
||||||
|
char c = name.charAt(i);
|
||||||
|
if (c == 0xFFFD || c < 0x20 || (c >= 0x7F && c < 0xA0)) {
|
||||||
|
com.github.catvod.utils.Logger.w("Site.setName: 检测到异常字符 at position " + i + ": U+" + String.format("%04X", (int)c) + " in name: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getApi() {
|
public String getApi() {
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public abstract class HistoryDao extends BaseDao<History> {
|
|||||||
@Query("SELECT * FROM History WHERE cid = :cid AND createTime >= :createTime ORDER BY createTime DESC")
|
@Query("SELECT * FROM History WHERE cid = :cid AND createTime >= :createTime ORDER BY createTime DESC")
|
||||||
public abstract List<History> find(int cid, long createTime);
|
public abstract List<History> find(int cid, long createTime);
|
||||||
|
|
||||||
|
@Query("SELECT * FROM History WHERE createTime >= :createTime ORDER BY createTime DESC")
|
||||||
|
public abstract List<History> findAllRecent(long createTime);
|
||||||
|
|
||||||
@Query("SELECT * FROM History WHERE cid = :cid AND `key` = :key")
|
@Query("SELECT * FROM History WHERE cid = :cid AND `key` = :key")
|
||||||
public abstract History find(int cid, String key);
|
public abstract History find(int cid, String key);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.fongmi.android.tv.App;
|
|||||||
import com.fongmi.android.tv.bean.Backup;
|
import com.fongmi.android.tv.bean.Backup;
|
||||||
import com.fongmi.android.tv.bean.History;
|
import com.fongmi.android.tv.bean.History;
|
||||||
import com.fongmi.android.tv.db.AppDatabase;
|
import com.fongmi.android.tv.db.AppDatabase;
|
||||||
|
import com.fongmi.android.tv.event.RefreshEvent;
|
||||||
import com.github.catvod.utils.Logger;
|
import com.github.catvod.utils.Logger;
|
||||||
import com.github.catvod.utils.Prefers;
|
import com.github.catvod.utils.Prefers;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@@ -280,20 +281,60 @@ public class WebDAVSyncManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取所有观看记录
|
// 获取所有观看记录 - 使用findAllRecent(0)来获取所有记录(包括旧记录)
|
||||||
List<History> historyList = AppDatabase.get().getHistoryDao().findAll();
|
Logger.d("WebDAV: 开始查询数据库中的观看记录...");
|
||||||
|
List<History> historyList = AppDatabase.get().getHistoryDao().findAllRecent(0);
|
||||||
|
Logger.d("WebDAV: 数据库查询完成,结果: " + (historyList == null ? "null" : historyList.size() + " 条"));
|
||||||
|
|
||||||
if (historyList == null) {
|
if (historyList == null) {
|
||||||
|
Logger.w("WebDAV: 查询结果为null,创建空列表");
|
||||||
historyList = new java.util.ArrayList<>();
|
historyList = new java.util.ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 修复数据中可能的编码问题(重点修复key中的站点名称部分)
|
||||||
|
Logger.d("WebDAV: 开始修复上传数据的编码问题...");
|
||||||
|
for (History h : historyList) {
|
||||||
|
String originalKey = h.getKey();
|
||||||
|
|
||||||
|
// key格式: 站点key$视频ID$cid,需要单独修复站点key部分
|
||||||
|
String fixedKey = fixHistoryKey(originalKey);
|
||||||
|
if (!originalKey.equals(fixedKey)) {
|
||||||
|
Logger.d("WebDAV: 修复key编码: '" + originalKey + "' -> '" + fixedKey + "'");
|
||||||
|
h.setKey(fixedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalName = h.getVodName();
|
||||||
|
String fixedName = fixEncodingIfNeeded(originalName);
|
||||||
|
if (!originalName.equals(fixedName)) {
|
||||||
|
Logger.d("WebDAV: 修复vodName编码: '" + originalName + "' -> '" + fixedName + "'");
|
||||||
|
h.setVodName(fixedName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Logger.d("WebDAV: 准备上传观看记录,共 " + historyList.size() + " 条");
|
Logger.d("WebDAV: 准备上传观看记录,共 " + historyList.size() + " 条");
|
||||||
|
|
||||||
|
// 记录前3条数据的详细信息
|
||||||
|
for (int i = 0; i < Math.min(3, historyList.size()); i++) {
|
||||||
|
History h = historyList.get(i);
|
||||||
|
Logger.d("WebDAV: 上传记录[" + i + "] key=" + h.getKey() + ", vodName=" + h.getVodName());
|
||||||
|
// 检查key中的每个字符
|
||||||
|
String key = h.getKey();
|
||||||
|
StringBuilder hexDump = new StringBuilder();
|
||||||
|
for (int j = 0; j < Math.min(20, key.length()); j++) {
|
||||||
|
hexDump.append(String.format("%04x ", (int)key.charAt(j)));
|
||||||
|
}
|
||||||
|
Logger.d("WebDAV: key前20字符的Unicode: " + hexDump.toString());
|
||||||
|
}
|
||||||
|
|
||||||
String json = App.gson().toJson(historyList);
|
String json = App.gson().toJson(historyList);
|
||||||
if (TextUtils.isEmpty(json)) {
|
if (TextUtils.isEmpty(json)) {
|
||||||
Logger.w("WebDAV: JSON数据为空");
|
Logger.w("WebDAV: JSON数据为空");
|
||||||
json = "[]"; // 确保至少有一个有效的JSON数组
|
json = "[]"; // 确保至少有一个有效的JSON数组
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 记录JSON的前500个字符
|
||||||
|
Logger.d("WebDAV: JSON前500字符: " + json.substring(0, Math.min(500, json.length())));
|
||||||
|
|
||||||
// 确保目录存在(如果baseUrl包含子目录)
|
// 确保目录存在(如果baseUrl包含子目录)
|
||||||
if (syncMode == SyncMode.ACCOUNT && !TextUtils.isEmpty(baseUrl)) {
|
if (syncMode == SyncMode.ACCOUNT && !TextUtils.isEmpty(baseUrl)) {
|
||||||
try {
|
try {
|
||||||
@@ -338,18 +379,22 @@ public class WebDAVSyncManager {
|
|||||||
public boolean downloadHistory() {
|
public boolean downloadHistory() {
|
||||||
if (!isConfigured()) {
|
if (!isConfigured()) {
|
||||||
Logger.e("WebDAV: 未配置,无法下载观看记录");
|
Logger.e("WebDAV: 未配置,无法下载观看记录");
|
||||||
|
Logger.e("WebDAV: baseUrl=" + baseUrl + ", username=" + username);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String fileUrl = getFileUrl(HISTORY_FILE);
|
String fileUrl = getFileUrl(HISTORY_FILE);
|
||||||
|
Logger.d("WebDAV: 检查文件是否存在: " + fileUrl);
|
||||||
|
|
||||||
// 检查文件是否存在
|
// 检查文件是否存在
|
||||||
if (!sardine.exists(fileUrl)) {
|
if (!sardine.exists(fileUrl)) {
|
||||||
Logger.d("WebDAV: 观看记录文件不存在,跳过下载");
|
Logger.w("WebDAV: 观看记录文件不存在,跳过下载");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.d("WebDAV: 文件存在,开始下载");
|
||||||
|
|
||||||
// 下载文件(使用循环读取,避免available()不准确的问题)
|
// 下载文件(使用循环读取,避免available()不准确的问题)
|
||||||
InputStream is = sardine.get(fileUrl);
|
InputStream is = sardine.get(fileUrl);
|
||||||
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
||||||
@@ -378,7 +423,72 @@ public class WebDAVSyncManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 智能合并:比较本地和远程记录,保留较新的
|
// 智能合并:比较本地和远程记录,保留较新的
|
||||||
List<History> localHistoryList = AppDatabase.get().getHistoryDao().findAll();
|
List<History> localHistoryList = AppDatabase.get().getHistoryDao().findAllRecent(0);
|
||||||
|
Logger.d("WebDAV: 本地记录数: " + localHistoryList.size());
|
||||||
|
Logger.d("WebDAV: 远程记录数: " + remoteHistoryList.size());
|
||||||
|
|
||||||
|
// 修复远程记录的编码问题和时间戳
|
||||||
|
Logger.d("WebDAV: 开始修复远程记录编码和时间戳...");
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
long historyTimeLimit = currentTime - com.fongmi.android.tv.Constant.HISTORY_TIME; // 60天前
|
||||||
|
|
||||||
|
for (History remote : remoteHistoryList) {
|
||||||
|
if (remote != null) {
|
||||||
|
String originalKey = remote.getKey();
|
||||||
|
// 修复key中的站点名称部分
|
||||||
|
String fixedKey = fixHistoryKey(originalKey);
|
||||||
|
if (!originalKey.equals(fixedKey)) {
|
||||||
|
Logger.d("WebDAV: 修复远程key: '" + originalKey + "' -> '" + fixedKey + "'");
|
||||||
|
remote.setKey(fixedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalName = remote.getVodName();
|
||||||
|
String fixedName = fixEncodingIfNeeded(originalName);
|
||||||
|
if (!originalName.equals(fixedName)) {
|
||||||
|
Logger.d("WebDAV: 修复远程vodName: '" + originalName + "' -> '" + fixedName + "'");
|
||||||
|
remote.setVodName(fixedName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关键修复:确保createTime在60天内,否则会被过滤掉!
|
||||||
|
long remoteCreateTime = remote.getCreateTime();
|
||||||
|
if (remoteCreateTime < historyTimeLimit) {
|
||||||
|
Logger.d("WebDAV: 修复过期时间戳: " + remote.getVodName() +
|
||||||
|
" createTime=" + remoteCreateTime + " -> " + currentTime +
|
||||||
|
" (已过期 " + ((currentTime - remoteCreateTime) / (24*60*60*1000)) + " 天)");
|
||||||
|
remote.setCreateTime(currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录前3条远程数据的详细信息
|
||||||
|
if (remoteHistoryList.indexOf(remote) < 3) {
|
||||||
|
Logger.d("WebDAV: 远程记录[" + remoteHistoryList.indexOf(remote) + "]: " +
|
||||||
|
remote.getVodName() + " (key=" + remote.getKey() +
|
||||||
|
", cid=" + remote.getCid() +
|
||||||
|
", createTime=" + remote.getCreateTime() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复本地记录的编码问题(重要!)
|
||||||
|
Logger.d("WebDAV: 开始修复本地记录编码...");
|
||||||
|
for (History local : localHistoryList) {
|
||||||
|
if (local != null) {
|
||||||
|
String originalKey = local.getKey();
|
||||||
|
// 修复key中的站点名称部分
|
||||||
|
String fixedKey = fixHistoryKey(originalKey);
|
||||||
|
if (!originalKey.equals(fixedKey)) {
|
||||||
|
Logger.d("WebDAV: 修复本地key: '" + originalKey + "' -> '" + fixedKey + "'");
|
||||||
|
local.setKey(fixedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录前3条本地数据的详细信息
|
||||||
|
if (localHistoryList.indexOf(local) < 3) {
|
||||||
|
Logger.d("WebDAV: 本地记录[" + localHistoryList.indexOf(local) + "]: " +
|
||||||
|
local.getVodName() + " (key=" + local.getKey() +
|
||||||
|
", cid=" + local.getCid() +
|
||||||
|
", createTime=" + local.getCreateTime() + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建本地记录的映射(key -> History)
|
// 创建本地记录的映射(key -> History)
|
||||||
java.util.Map<String, History> localMap = new java.util.HashMap<>();
|
java.util.Map<String, History> localMap = new java.util.HashMap<>();
|
||||||
@@ -387,11 +497,14 @@ public class WebDAVSyncManager {
|
|||||||
localMap.put(local.getKey(), local);
|
localMap.put(local.getKey(), local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Logger.d("WebDAV: 本地记录映射大小: " + localMap.size());
|
||||||
|
|
||||||
// 合并远程记录
|
// 合并远程记录
|
||||||
List<History> toInsert = new java.util.ArrayList<>();
|
List<History> toInsert = new java.util.ArrayList<>();
|
||||||
List<History> toUpdate = new java.util.ArrayList<>();
|
List<History> toUpdate = new java.util.ArrayList<>();
|
||||||
|
|
||||||
|
Logger.d("WebDAV: 开始合并 " + remoteHistoryList.size() + " 条远程记录...");
|
||||||
|
|
||||||
for (History remote : remoteHistoryList) {
|
for (History remote : remoteHistoryList) {
|
||||||
// 验证远程记录
|
// 验证远程记录
|
||||||
if (remote == null || TextUtils.isEmpty(remote.getKey())) {
|
if (remote == null || TextUtils.isEmpty(remote.getKey())) {
|
||||||
@@ -403,43 +516,110 @@ public class WebDAVSyncManager {
|
|||||||
|
|
||||||
if (local == null) {
|
if (local == null) {
|
||||||
// 本地没有,直接添加
|
// 本地没有,直接添加
|
||||||
|
Logger.d("WebDAV: 发现新记录: " + remote.getVodName() + " (key=" + remote.getKey() + ")");
|
||||||
toInsert.add(remote);
|
toInsert.add(remote);
|
||||||
} else {
|
} else {
|
||||||
// 本地有,比较createTime,保留较新的
|
Logger.d("WebDAV: 本地已有记录: " + remote.getVodName() + ", 比较时间 remote=" + remote.getCreateTime() + " local=" + local.getCreateTime());
|
||||||
if (remote.getCreateTime() > local.getCreateTime()) {
|
|
||||||
// 远程更新,更新本地
|
// 改进的合并策略:优先保留较新的记录,但也要比较播放进度
|
||||||
toUpdate.add(remote);
|
|
||||||
} else if (remote.getCreateTime() == local.getCreateTime()) {
|
|
||||||
// 时间相同,比较position,保留进度更靠后的
|
|
||||||
// 注意:position可能是C.TIME_UNSET(负数),需要处理
|
|
||||||
long remotePos = remote.getPosition();
|
long remotePos = remote.getPosition();
|
||||||
long localPos = local.getPosition();
|
long localPos = local.getPosition();
|
||||||
// 如果都是有效值(>=0),比较大小;如果有无效值,保留有效值
|
long remoteTime = remote.getCreateTime();
|
||||||
|
long localTime = local.getCreateTime();
|
||||||
|
|
||||||
|
boolean shouldUpdate = false;
|
||||||
|
String reason = "";
|
||||||
|
|
||||||
|
// 策略1:如果远程时间更新,直接更新
|
||||||
|
if (remoteTime > localTime) {
|
||||||
|
shouldUpdate = true;
|
||||||
|
reason = "远程时间更新 (" + remoteTime + " > " + localTime + ")";
|
||||||
|
}
|
||||||
|
// 策略2:如果时间相同或相近(误差1秒内),比较播放进度
|
||||||
|
else if (Math.abs(remoteTime - localTime) <= 1000) {
|
||||||
if (remotePos >= 0 && localPos >= 0) {
|
if (remotePos >= 0 && localPos >= 0) {
|
||||||
if (remotePos > localPos) {
|
if (remotePos > localPos) {
|
||||||
toUpdate.add(remote);
|
shouldUpdate = true;
|
||||||
|
reason = "播放进度更新 (" + remotePos + " > " + localPos + ")";
|
||||||
|
} else {
|
||||||
|
reason = "本地进度更新或相同";
|
||||||
}
|
}
|
||||||
} else if (remotePos >= 0 && localPos < 0) {
|
} else if (remotePos >= 0 && localPos < 0) {
|
||||||
// 远程有效,本地无效,更新
|
shouldUpdate = true;
|
||||||
|
reason = "远程有有效进度,本地无效";
|
||||||
|
} else {
|
||||||
|
reason = "保留本地";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 策略3:即使本地时间更新,如果远程有更大的播放进度,也更新
|
||||||
|
else if (remoteTime < localTime) {
|
||||||
|
if (remotePos >= 0 && localPos >= 0 && remotePos > localPos + 60000) {
|
||||||
|
// 远程进度领先本地超过1分钟,可能是用户在另一台设备继续观看
|
||||||
|
shouldUpdate = true;
|
||||||
|
reason = "虽然本地时间更新,但远程进度显著领先 (" + remotePos + " > " + localPos + ")";
|
||||||
|
} else {
|
||||||
|
reason = "本地时间更新 (" + localTime + " > " + remoteTime + "),保留本地";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldUpdate) {
|
||||||
|
Logger.d("WebDAV: → 将更新本地 - " + reason);
|
||||||
toUpdate.add(remote);
|
toUpdate.add(remote);
|
||||||
}
|
} else {
|
||||||
// 否则保留本地,不更新
|
Logger.d("WebDAV: → 保留本地 - " + reason);
|
||||||
}
|
|
||||||
// 否则保留本地,不更新
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.d("WebDAV: 合并完成,待插入 " + toInsert.size() + " 条,待更新 " + toUpdate.size() + " 条");
|
||||||
|
|
||||||
// 执行插入和更新
|
// 执行插入和更新
|
||||||
if (!toInsert.isEmpty()) {
|
if (!toInsert.isEmpty()) {
|
||||||
|
Logger.d("WebDAV: 开始插入 " + toInsert.size() + " 条新记录...");
|
||||||
AppDatabase.get().getHistoryDao().insert(toInsert);
|
AppDatabase.get().getHistoryDao().insert(toInsert);
|
||||||
Logger.d("WebDAV: 新增 " + toInsert.size() + " 条观看记录");
|
Logger.d("WebDAV: 新增 " + toInsert.size() + " 条观看记录");
|
||||||
|
for (History h : toInsert) {
|
||||||
|
Logger.d("WebDAV: ✓ 新增 - " + h.getVodName() + " (cid=" + h.getCid() + ", key=" + h.getKey() + ")");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Logger.d("WebDAV: 没有需要插入的新记录");
|
||||||
|
}
|
||||||
|
|
||||||
if (!toUpdate.isEmpty()) {
|
if (!toUpdate.isEmpty()) {
|
||||||
|
Logger.d("WebDAV: 开始更新 " + toUpdate.size() + " 条记录...");
|
||||||
AppDatabase.get().getHistoryDao().update(toUpdate);
|
AppDatabase.get().getHistoryDao().update(toUpdate);
|
||||||
Logger.d("WebDAV: 更新 " + toUpdate.size() + " 条观看记录");
|
Logger.d("WebDAV: 更新 " + toUpdate.size() + " 条观看记录");
|
||||||
|
for (History h : toUpdate) {
|
||||||
|
Logger.d("WebDAV: ✓ 更新 - " + h.getVodName() + " (cid=" + h.getCid() + ")");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.d("WebDAV: 没有需要更新的记录");
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.d("WebDAV: 观看记录合并完成,远程 " + remoteHistoryList.size() + " 条,本地 " + localHistoryList.size() + " 条");
|
Logger.d("WebDAV: 观看记录合并完成,远程 " + remoteHistoryList.size() + " 条,本地 " + localHistoryList.size() + " 条");
|
||||||
|
|
||||||
|
// 验证数据库中的记录总数
|
||||||
|
List<History> allInDb = AppDatabase.get().getHistoryDao().findAllRecent(0);
|
||||||
|
Logger.d("WebDAV: 数据库中总共有 " + allInDb.size() + " 条观看记录");
|
||||||
|
|
||||||
|
// 输出数据库中前5条记录的详细信息
|
||||||
|
Logger.d("WebDAV: === 数据库中的记录(前5条)===");
|
||||||
|
for (int i = 0; i < Math.min(5, allInDb.size()); i++) {
|
||||||
|
History h = allInDb.get(i);
|
||||||
|
Logger.d("WebDAV: [" + i + "] " + h.getVodName() +
|
||||||
|
" (key=" + h.getKey() +
|
||||||
|
", cid=" + h.getCid() +
|
||||||
|
", createTime=" + h.getCreateTime() + ")");
|
||||||
|
}
|
||||||
|
Logger.d("WebDAV: =========================");
|
||||||
|
|
||||||
|
// 强制触发UI刷新(即使没有新增或更新,也刷新一次以确保显示)
|
||||||
|
Logger.d("WebDAV: 触发UI刷新事件");
|
||||||
|
App.post(() -> {
|
||||||
|
RefreshEvent.history();
|
||||||
|
Logger.d("WebDAV: UI刷新事件已发送到主线程");
|
||||||
|
});
|
||||||
|
|
||||||
return true; // 即使远程为空,也算同步成功
|
return true; // 即使远程为空,也算同步成功
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.e("WebDAV: 观看记录下载失败: " + e.getMessage());
|
Logger.e("WebDAV: 观看记录下载失败: " + e.getMessage());
|
||||||
@@ -755,5 +935,76 @@ public class WebDAVSyncManager {
|
|||||||
public void reloadConfig() {
|
public void reloadConfig() {
|
||||||
loadConfig();
|
loadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修复History的key中的站点名称编码
|
||||||
|
* key格式: 站点key$视频ID$cid
|
||||||
|
*/
|
||||||
|
private String fixHistoryKey(String key) {
|
||||||
|
if (key == null || key.isEmpty()) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用AppDatabase.SYMBOL分隔
|
||||||
|
String symbol = com.fongmi.android.tv.db.AppDatabase.SYMBOL;
|
||||||
|
String[] parts = key.split(java.util.regex.Pattern.quote(symbol));
|
||||||
|
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
// parts[0] = 站点key, parts[1] = 视频ID, parts[2] = cid
|
||||||
|
String siteKey = parts[0];
|
||||||
|
String fixedSiteKey = fixEncodingIfNeeded(siteKey);
|
||||||
|
|
||||||
|
if (!siteKey.equals(fixedSiteKey)) {
|
||||||
|
// 重新组装key
|
||||||
|
StringBuilder newKey = new StringBuilder(fixedSiteKey);
|
||||||
|
for (int i = 1; i < parts.length; i++) {
|
||||||
|
newKey.append(symbol).append(parts[i]);
|
||||||
|
}
|
||||||
|
return newKey.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("WebDAV: 修复History key失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修复字符串编码问题
|
||||||
|
* 尝试将错误编码的UTF-8字符串修复为正确的UTF-8
|
||||||
|
*/
|
||||||
|
private String fixEncodingIfNeeded(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查字符串中是否包含明显的乱码特征
|
||||||
|
// 1. 包含替换字符 U+FFFD
|
||||||
|
// 2. 包含异常的低位控制字符
|
||||||
|
boolean needsFix = false;
|
||||||
|
for (int i = 0; i < str.length(); i++) {
|
||||||
|
char c = str.charAt(i);
|
||||||
|
if (c == '\uFFFD' || (c >= 0x80 && c < 0xA0)) {
|
||||||
|
needsFix = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsFix) {
|
||||||
|
// 尝试修复:假设原始数据是UTF-8,但被错误地当作ISO-8859-1解码
|
||||||
|
byte[] bytes = str.getBytes(java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||||
|
String fixed = new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
Logger.d("WebDAV: 编码修复 '" + str + "' -> '" + fixed + "'");
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.e("WebDAV: 编码修复失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class HistoryActivity extends BaseActivity implements HistoryAdapter.OnCl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void getHistory() {
|
private void getHistory() {
|
||||||
mAdapter.addAll(History.get());
|
mAdapter.addAll(History.getAll()); // 显示所有视频源的观看记录
|
||||||
mBinding.delete.setVisibility(mAdapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
|
mBinding.delete.setVisibility(mAdapter.getItemCount() > 0 ? View.VISIBLE : View.GONE);
|
||||||
updateEmptyState();
|
updateEmptyState();
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,69 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "广告过滤配置示例",
|
|
||||||
"description": "演示如何配置广告域名黑名单,阻止视频中途弹出的广告(如澳门新葡京等博彩广告)",
|
|
||||||
|
|
||||||
"说明": {
|
|
||||||
"内置拦截": "应用已内置常见广告域名库,包括:澳门新葡京、皇冠、金沙等博彩广告;Google、百度、淘宝等广告联盟;优酷、爱奇艺等视频平台广告",
|
|
||||||
"自定义拦截": "可以在配置文件中添加ads字段,补充需要拦截的广告域名",
|
|
||||||
"支持正则": "域名支持正则表达式匹配,使用 .* 作为通配符"
|
|
||||||
},
|
|
||||||
|
|
||||||
"配置示例": {
|
|
||||||
"spider": "your_spider_url",
|
|
||||||
"sites": [],
|
|
||||||
|
|
||||||
"ads": [
|
|
||||||
"注释: 以下是自定义广告域名列表,会与内置域名库合并使用",
|
|
||||||
|
|
||||||
"注释: 精确匹配 - 直接写完整域名",
|
|
||||||
"mimg.0c1q0l.cn",
|
|
||||||
"www.92424.cn",
|
|
||||||
"vip.ffzyad.com",
|
|
||||||
|
|
||||||
"注释: 模糊匹配 - 使用通配符",
|
|
||||||
".*\\.doubleclick\\.net",
|
|
||||||
".*\\.googlesyndication\\.com",
|
|
||||||
|
|
||||||
"注释: 关键词匹配 - 拦截包含特定关键词的域名",
|
|
||||||
".*葡京.*",
|
|
||||||
".*皇冠.*",
|
|
||||||
".*金沙.*",
|
|
||||||
".*casino.*",
|
|
||||||
".*bet.*",
|
|
||||||
|
|
||||||
"注释: 特定平台的广告",
|
|
||||||
"wan.51img1.com",
|
|
||||||
"k.jinxiuzhilv.com",
|
|
||||||
"ssl.kdd.cc"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"常见问题": {
|
|
||||||
"Q1": "为什么配置了还是有广告?",
|
|
||||||
"A1": "1. 检查广告域名是否正确;2. 某些广告可能直接嵌入视频流,无法通过域名拦截;3. 尝试使用片头片尾跳过功能",
|
|
||||||
|
|
||||||
"Q2": "如何找到广告的域名?",
|
|
||||||
"A2": "1. 使用浏览器开发者工具查看网络请求;2. 查看应用日志中的URL;3. 参考其他用户分享的广告域名列表",
|
|
||||||
|
|
||||||
"Q3": "会不会误拦截正常内容?",
|
|
||||||
"A3": "内置域名库经过筛选,主要针对已知广告。如有误拦截,可以反馈给开发者"
|
|
||||||
},
|
|
||||||
|
|
||||||
"片头片尾跳过": {
|
|
||||||
"说明": "对于嵌入视频流中的广告,可以使用片头片尾跳过功能",
|
|
||||||
"使用方法": [
|
|
||||||
"1. 播放视频时,在片头(前5分钟内)按【片头】按钮,记录当前时间点",
|
|
||||||
"2. 在片尾(后5分钟内)按【片尾】按钮,记录结束前的时间点",
|
|
||||||
"3. 下次播放相同视频时,会自动跳过片头,并在片尾前停止",
|
|
||||||
"4. 如需重置,长按对应按钮即可"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
"技术说明": {
|
|
||||||
"拦截层级": "WebView网络请求层拦截",
|
|
||||||
"拦截方式": "返回空响应,阻止广告内容加载",
|
|
||||||
"性能影响": "极小,仅在WebView解析时生效",
|
|
||||||
"隐私保护": "所有拦截在本地进行,不上传任何数据"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
{
|
|
||||||
"lives": [
|
|
||||||
{
|
|
||||||
"name": "M3U",
|
|
||||||
"url": "file://Download/live.m3u"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TXT",
|
|
||||||
"url": "file://Download/live.txt",
|
|
||||||
"epg": "https://epg.112114.xyz/?ch={name}&date={date}",
|
|
||||||
"logo": "https://epg.112114.xyz/logo/{name}.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "UA",
|
|
||||||
"url": "file://Download/live.txt",
|
|
||||||
"epg": "https://epg.112114.xyz/?ch={name}&date={date}",
|
|
||||||
"logo": "https://epg.112114.xyz/logo/{name}.png",
|
|
||||||
"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
|
||||||
"referer": "https://github.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Custom",
|
|
||||||
"boot": false,
|
|
||||||
"pass": true,
|
|
||||||
"url": "file://Download/live.txt",
|
|
||||||
"epg": "https://epg.112114.xyz/?ch={name}&date={date}&serverTimeZone=Asia/Shanghai",
|
|
||||||
"logo": "https://epg.112114.xyz/logo/{name}.png",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
|
||||||
"Referer": "https://github.com/"
|
|
||||||
},
|
|
||||||
"catchup": {
|
|
||||||
"days": "7",
|
|
||||||
"type": "append",
|
|
||||||
"regex": "/PLTV/",
|
|
||||||
"replace": "/PLTV/,/TVOD/",
|
|
||||||
"source": "?playseek=${(b)yyyyMMddHHmmss}-${(e)yyyyMMddHHmmss}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "JSON",
|
|
||||||
"type": 1,
|
|
||||||
"url": "file://Download/live.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Spider-JS",
|
|
||||||
"type": 3,
|
|
||||||
"api": "./live.js",
|
|
||||||
"ext": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Spider-Python",
|
|
||||||
"type": 3,
|
|
||||||
"api": "./live.py",
|
|
||||||
"ext": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"host": "gslbserv.itv.cmvideo.cn",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "okhttp/3.12.13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"proxy": [
|
|
||||||
"raw.githubusercontent.com"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"cache.ott.ystenlive.itv.cmvideo.cn=base-v4-free-mghy.e.cdn.chinamobile.com"
|
|
||||||
],
|
|
||||||
"ads": [
|
|
||||||
"static-mozai.4gtv.tv"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
{
|
|
||||||
"lives": [
|
|
||||||
{
|
|
||||||
"name": "M3U",
|
|
||||||
"url": "https://github.com/live.m3u"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TXT",
|
|
||||||
"url": "https://github.com/live.txt",
|
|
||||||
"epg": "https://epg.112114.xyz/?ch={name}&date={date}",
|
|
||||||
"logo": "https://epg.112114.xyz/logo/{name}.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "UA",
|
|
||||||
"url": "https://github.com/live.txt",
|
|
||||||
"epg": "https://epg.112114.xyz/?ch={name}&date={date}",
|
|
||||||
"logo": "https://epg.112114.xyz/logo/{name}.png",
|
|
||||||
"ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
|
||||||
"referer": "https://github.com/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Custom",
|
|
||||||
"boot": false,
|
|
||||||
"pass": true,
|
|
||||||
"url": "https://github.com/live.txt",
|
|
||||||
"epg": "https://epg.112114.xyz/?ch={name}&date={date}&serverTimeZone=Asia/Shanghai",
|
|
||||||
"logo": "https://epg.112114.xyz/logo/{name}.png",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
|
||||||
"Referer": "https://github.com/"
|
|
||||||
},
|
|
||||||
"catchup": {
|
|
||||||
"days": "7",
|
|
||||||
"type": "append",
|
|
||||||
"regex": "/PLTV/",
|
|
||||||
"replace": "/PLTV/,/TVOD/",
|
|
||||||
"source": "?playseek=${(b)yyyyMMddHHmmss}-${(e)yyyyMMddHHmmss}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "JSON",
|
|
||||||
"type": 1,
|
|
||||||
"url": "https://github.com/live.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Spider-JS",
|
|
||||||
"type": 3,
|
|
||||||
"api": "https://github.com/live.js",
|
|
||||||
"ext": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Spider-Python",
|
|
||||||
"type": 3,
|
|
||||||
"api": "https://github.com/live.py",
|
|
||||||
"ext": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"host": "gslbserv.itv.cmvideo.cn",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "okhttp/3.12.13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"proxy": [
|
|
||||||
"raw.githubusercontent.com"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"cache.ott.ystenlive.itv.cmvideo.cn=base-v4-free-mghy.e.cdn.chinamobile.com"
|
|
||||||
],
|
|
||||||
"ads": [
|
|
||||||
"static-mozai.4gtv.tv"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{
|
|
||||||
"spider": "file://Download/custom_spider.jar",
|
|
||||||
"sites": [
|
|
||||||
{
|
|
||||||
"key": "one",
|
|
||||||
"name": "One",
|
|
||||||
"type": 3,
|
|
||||||
"api": "csp_Csp",
|
|
||||||
"searchable": 1,
|
|
||||||
"changeable": 1,
|
|
||||||
"ext": "file://Download/one.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "two",
|
|
||||||
"name": "Two",
|
|
||||||
"type": 3,
|
|
||||||
"api": "csp_Csp",
|
|
||||||
"searchable": 1,
|
|
||||||
"changeable": 1,
|
|
||||||
"ext": "file://Download/two.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "extend",
|
|
||||||
"name": "Extend",
|
|
||||||
"type": 3,
|
|
||||||
"api": "csp_Csp",
|
|
||||||
"searchable": 1,
|
|
||||||
"changeable": 1,
|
|
||||||
"ext": "file://Download/extend.json",
|
|
||||||
"jar": "file://Download/extend.jar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parses": [
|
|
||||||
{
|
|
||||||
"name": "官方",
|
|
||||||
"type": 1,
|
|
||||||
"url": "https://google.com/api/?url="
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"doh": [
|
|
||||||
{
|
|
||||||
"name": "Google",
|
|
||||||
"url": "https://dns.google/dns-query",
|
|
||||||
"ips": [
|
|
||||||
"8.8.4.4",
|
|
||||||
"8.8.8.8"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"host": "gslbserv.itv.cmvideo.cn",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "okhttp/3.12.13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"proxy": [
|
|
||||||
"raw.githubusercontent.com"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"cache.ott.ystenlive.itv.cmvideo.cn=base-v4-free-mghy.e.cdn.chinamobile.com"
|
|
||||||
],
|
|
||||||
"flags": [
|
|
||||||
"qq"
|
|
||||||
],
|
|
||||||
"ads": [
|
|
||||||
"static-mozai.4gtv.tv"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{
|
|
||||||
"spider": "https://github.com/custom_spider.jar",
|
|
||||||
"sites": [
|
|
||||||
{
|
|
||||||
"key": "one",
|
|
||||||
"name": "One",
|
|
||||||
"type": 3,
|
|
||||||
"api": "csp_Csp",
|
|
||||||
"searchable": 1,
|
|
||||||
"changeable": 1,
|
|
||||||
"ext": "https://github.com/one.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "two",
|
|
||||||
"name": "Two",
|
|
||||||
"type": 3,
|
|
||||||
"api": "csp_Csp",
|
|
||||||
"searchable": 1,
|
|
||||||
"changeable": 1,
|
|
||||||
"ext": "https://github.com/two.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"key": "extend",
|
|
||||||
"name": "Extend",
|
|
||||||
"type": 3,
|
|
||||||
"api": "csp_Csp",
|
|
||||||
"searchable": 1,
|
|
||||||
"changeable": 1,
|
|
||||||
"ext": "https://github.com/extend.json",
|
|
||||||
"jar": "https://github.com/extend.jar"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parses": [
|
|
||||||
{
|
|
||||||
"name": "官方",
|
|
||||||
"type": 1,
|
|
||||||
"url": "https://google.com/api/?url="
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"doh": [
|
|
||||||
{
|
|
||||||
"name": "Google",
|
|
||||||
"url": "https://dns.google/dns-query",
|
|
||||||
"ips": [
|
|
||||||
"8.8.4.4",
|
|
||||||
"8.8.8.8"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"headers": [
|
|
||||||
{
|
|
||||||
"host": "gslbserv.itv.cmvideo.cn",
|
|
||||||
"header": {
|
|
||||||
"User-Agent": "okhttp/3.12.13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"proxy": [
|
|
||||||
"raw.githubusercontent.com"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"cache.ott.ystenlive.itv.cmvideo.cn=base-v4-free-mghy.e.cdn.chinamobile.com"
|
|
||||||
],
|
|
||||||
"flags": [
|
|
||||||
"qq"
|
|
||||||
],
|
|
||||||
"ads": [
|
|
||||||
"static-mozai.4gtv.tv"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user