From 7e8ac5845cde8188cc974f1b467cbf988143c51a Mon Sep 17 00:00:00 2001
From: juneix <81808039+juneix@users.noreply.github.com>
Date: Sat, 16 May 2026 10:56:12 +0800
Subject: [PATCH] v1.2
---
zh/index.html | 172 ++++++++++++++++-
zh/info.html | 523 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 691 insertions(+), 4 deletions(-)
create mode 100644 zh/info.html
diff --git a/zh/index.html b/zh/index.html
index 9bc928d..cc4ccb1 100644
--- a/zh/index.html
+++ b/zh/index.html
@@ -220,6 +220,25 @@
+
+
+
+
+
+
同步进度
+
+
+
+
+
+
+
+
📱 EmbyX
v1.1
+ class="ml-2 px-2 py-0.5 bg-primary/20 text-primary text-xs font-bold rounded-full border border-primary/30">v1.2
@@ -394,7 +413,14 @@
-
🔮 播放性能
+
+ 🔮 播放性能
+
+
+
+
- 原生 HTML5 播放器,新设备体验最佳
- 老设备需服务器转码,
不如放到**上回收(没打钱 😂
@@ -651,7 +677,7 @@
// 检查当前是否有任何弹窗/面板处于打开状态
// 这种状态下不应触发自动清屏
isAnyModalOpen() {
- const modals = ['mediaInfoModal', 'deleteConfirmModal', 'profilePage', 'libraryModal'];
+ const modals = ['mediaInfoModal', 'deleteConfirmModal', 'syncConfirmModal', 'profilePage', 'libraryModal'];
return modals.some(id => {
const el = document.getElementById(id);
if (!el) return false;
@@ -772,6 +798,7 @@
'favoriteBtn', 'scaleBtn', 'muteBtn', 'fullscreenBtn', 'deleteBtn',
'mediaInfoModal', 'mediaInfoContent', 'closeMediaInfoBtn',
'deleteConfirmModal', 'confirmDeleteBtn', 'cancelDeleteBtn', 'deleteFileName',
+ 'syncConfirmModal', 'confirmSyncBtn', 'cancelSyncBtn', 'syncFileName',
'configServer', 'configUser', 'configPwd', 'configStatic', 'configAutoplay', 'configShortDrama', 'configDeleteMode'
];
ids.forEach(id => this.dom[id] = document.getElementById(id));
@@ -1042,6 +1069,18 @@
} else {
this.state.videos = data.Items;
this.state.currentIndex = 0;
+
+ // 短剧模式:尝试从本地恢复进度
+ if (this.config.shortDrama && !isAppend && this.state.currentLibraryId) {
+ const lastId = localStorage.getItem(`emby_last_id_${this.state.currentLibraryId}`);
+ if (lastId) {
+ const savedIndex = this.state.videos.findIndex(v => v.Id === lastId);
+ if (savedIndex !== -1) {
+ this.state.currentIndex = savedIndex;
+ }
+ }
+ }
+
// 保存媒体库真实总数(API 返回,不受 Limit 影响)供分页显示
this.state.totalCount = data.TotalRecordCount || 0;
@@ -1055,7 +1094,11 @@
this.renderGridView();
} else {
this.renderSlides();
- this.loadVideo(0);
+ this.loadVideo(this.state.currentIndex);
+ // 短剧模式:异步检查云端是否有更新的进度
+ if (this.config.shortDrama && !isAppend) {
+ setTimeout(() => this.checkCloudSync(), 1500);
+ }
}
}
} else if (isLoadMore || isAppend) {
@@ -1457,6 +1500,15 @@
if (!videoEl || !videoData) return;
+ // 短剧模式:记录当前本地播放进度标识
+ if (this.config.shortDrama && this.state.currentLibraryId) {
+ const now = new Date().toISOString();
+ localStorage.setItem(`emby_last_id_${this.state.currentLibraryId}`, videoData.Id);
+ localStorage.setItem(`emby_last_date_${this.state.currentLibraryId}`, now);
+ // 异步同步到云端
+ this.saveCloudSync(videoData.Id, this.state.currentLibraryId, this.state.currentLibraryType, now);
+ }
+
// renderBuffer 已赋值 src,处理未匹配时的修正
const src = this.getVideoSrc(videoData.Id, videoData);
if (videoEl.src !== src) {
@@ -1800,6 +1852,110 @@
}).catch(() => { });
}
+ // --- 续播与同步逻辑 (方案 A: DisplayPreferences) ---
+
+ async saveCloudSync(itemId, libId, libType, date) {
+ try {
+ const { server, token, userId } = this.config;
+ const url = `${server}/emby/DisplayPreferences/EmbyX-Resume-Drama?userId=${userId}&api_key=${token}`;
+
+ const prefs = {
+ Id: "EmbyX-Resume-Drama",
+ CustomPrefs: {
+ lastId: itemId,
+ libId: libId,
+ libType: libType,
+ date: date,
+ deviceName: this.config.deviceName
+ }
+ };
+
+ fetch(url, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(prefs)
+ }).catch(() => { });
+ } catch (e) { }
+ }
+
+ async checkCloudSync() {
+ if (!this.config.shortDrama) return;
+
+ try {
+ const { server, token, userId } = this.config;
+ const url = `${server}/emby/DisplayPreferences/EmbyX-Resume-Drama?userId=${userId}&api_key=${token}`;
+ const res = await fetch(url);
+ if (!res.ok) return;
+
+ const data = await res.json();
+ const cloud = data.CustomPrefs;
+ if (!cloud || !cloud.lastId || !cloud.date) return;
+
+ const localId = localStorage.getItem(`emby_last_id_${this.state.currentLibraryId}`);
+ const localDateStr = localStorage.getItem(`emby_last_date_${this.state.currentLibraryId}`) || '';
+
+ const cloudDate = new Date(cloud.date).getTime();
+ const localDate = localDateStr ? new Date(localDateStr).getTime() : 0;
+
+ // 只有云端进度更新,且 ID 不同时才提示
+ if (cloudDate > localDate && cloud.lastId !== localId) {
+ // 获取设备名称与视频名称
+ let deviceDisplay = cloud.deviceName || "其他设备";
+ let displayName = "更新的剧集";
+
+ if (cloud.libId === this.state.currentLibraryId) {
+ const item = this.state.videos.find(v => v.Id === cloud.lastId);
+ if (item) displayName = item.Name;
+ }
+
+ this.dom.syncFileName.textContent = `检测到 ${deviceDisplay} 播放至:${displayName}`;
+ this.state._pendingSyncItem = cloud;
+ this.toggleModal('syncConfirmModal', true);
+ lucide.createIcons();
+ }
+ } catch (e) {
+ console.warn('云端同步检测失败:', e);
+ }
+ }
+
+ async applyCloudSync() {
+ const cloud = this.state._pendingSyncItem;
+ if (!cloud) return;
+
+ this.toggleModal('syncConfirmModal', false);
+ this.showToast('正在同步进度...');
+
+ // 更新本地缓存,确保后续逻辑一致
+ localStorage.setItem(`emby_last_id_${cloud.libId}`, cloud.lastId);
+ localStorage.setItem(`emby_last_date_${cloud.libId}`, cloud.date);
+
+ if (cloud.libId !== this.state.currentLibraryId) {
+ // 跨库跳转
+ this.state.currentLibraryId = cloud.libId;
+ this.state.currentLibraryType = cloud.libType;
+ localStorage.setItem('emby_lib_id', cloud.libId);
+ localStorage.setItem('emby_lib_type', cloud.libType);
+
+ // 重新加载新库
+ this.state.startIndex = 0;
+ await this.fetchVideos(0, false);
+ this.showToast('✅ 已跨库同步');
+ } else {
+ // 同库跳转
+ const index = this.state.videos.findIndex(v => v.Id === cloud.lastId);
+ if (index !== -1) {
+ this.state.currentIndex = index;
+ this.renderSlides();
+ this.loadVideo(index);
+ this.showToast('✅ 进度已同步');
+ } else {
+ // 如果当前分页没找到,重刷该库
+ this.state.startIndex = 0;
+ await this.fetchVideos(0, false);
+ }
+ }
+ }
+
reportCapabilities() {
const { server, token } = this.config;
if (!server || !token) return;
@@ -2543,6 +2699,8 @@
this.dom.confirmDeleteBtn.onclick = () => this.executeDelete();
this.dom.cancelDeleteBtn.onclick = () => this.toggleModal('deleteConfirmModal', false);
+ this.dom.confirmSyncBtn.onclick = () => this.applyCloudSync();
+ this.dom.cancelSyncBtn.onclick = () => this.toggleModal('syncConfirmModal', false);
this.dom.deleteConfirmModal.onclick = (e) => {
if (e.target === e.currentTarget) {
@@ -2550,6 +2708,12 @@
}
};
+ this.dom.syncConfirmModal.onclick = (e) => {
+ if (e.target === e.currentTarget) {
+ this.toggleModal('syncConfirmModal', false);
+ }
+ };
+
document.getElementById('progressBarContainer').onclick = (e) => {
e.stopPropagation();
const rect = e.currentTarget.getBoundingClientRect();
diff --git a/zh/info.html b/zh/info.html
new file mode 100644
index 0000000..7b8d6f5
--- /dev/null
+++ b/zh/info.html
@@ -0,0 +1,523 @@
+
+
+
+
+
+
+ EmbyX - 浏览器播放能力检测
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
检测结果
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 正在获取 User-Agent...
+
+
+
+
+
+
体验评估
+
+
+
+
+
+
+
EmbyX 体验度预估
+
老古董设备,建议换机或转码。
+
+
+
+
--
+
+
+
+
+
+
视频播放与解码能力
+
+
+
+
+ | 序号 |
+ 功能特性 |
+ 支持状态 |
+ 详细说明 |
+
+
+
+
+
+ | 正在扫描媒体能力... |
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file