diff --git a/components/PlayerButtons.ts b/components/PlayerButtons.ts
new file mode 100644
index 0000000..2b701c8
--- /dev/null
+++ b/components/PlayerButtons.ts
@@ -0,0 +1,72 @@
+/**
+ * 创建或更新一个浮动播放器按钮。
+ * @param id - 按钮元素的 ID。
+ * @param name - 按钮上显示的文本。
+ * @param iconClass - 按钮图标的 CSS 类。
+ * @param url - 按钮点击后跳转的 URL。
+ * @param topPosition - 按钮的 CSS 'top' 属性值。
+ */
+function createOrUpdateButton(id: string, name:string, iconClass: string, url: string, topPosition: string) {
+ let button = document.getElementById(id) as HTMLAnchorElement | null;
+
+ if (!button) {
+ button = document.createElement('a');
+ button.id = id;
+ button.className = 'wxt-player-button'; // 为所有播放器按钮使用一个通用类名
+ button.innerHTML = `
+
+ ${name}
+ `;
+ document.body.appendChild(button);
+
+ // 添加点击事件监听器
+ button.addEventListener('click', (e) => {
+ e.preventDefault(); // 阻止 标签的默认跳转行为
+ window.location.href = button!.href;
+ });
+ }
+
+ // 更新按钮的链接和位置
+ button.href = url;
+ button.style.top = topPosition;
+ button.style.display = 'flex'; // 确保按钮可见
+ }
+
+ /**
+ * 隐藏指定的播放器按钮。
+ * @param id - 要隐藏的按钮的 ID。
+ */
+ function hideButton(id: string) {
+ const button = document.getElementById(id);
+ if (button) {
+ button.style.display = 'none';
+ }
+ }
+
+ // 定义按钮的常量 ID
+ const IINA_BUTTON_ID = 'wxt-iina-floating-button';
+ const POTPLAYER_BUTTON_ID = 'wxt-potplayer-floating-button';
+
+ /**
+ * 添加或更新 IINA 和 PotPlayer 的浮动按钮。
+ * @param missavUUID - 用于生成播放链接的 UUID。
+ */
+ export function addOrUpdatePlayerButtons(missavUUID: string) {
+ const playlistUrl = `https://surrit.com/${missavUUID}/playlist.m3u8`;
+
+ // IINA 按钮
+ const iinaUrl = `iina://weblink?url=${playlistUrl}`;
+ createOrUpdateButton(IINA_BUTTON_ID, 'IINA', 'icon-play', iinaUrl, '100px');
+
+ // PotPlayer 按钮
+ const potplayerUrl = `potplayer://${playlistUrl}`;
+ createOrUpdateButton(POTPLAYER_BUTTON_ID, 'PotPlayer', 'icon-play', potplayerUrl, '150px');
+ }
+
+ /**
+ * 隐藏所有的播放器按钮。
+ */
+ export function hidePlayerButtons() {
+ hideButton(IINA_BUTTON_ID);
+ hideButton(POTPLAYER_BUTTON_ID);
+ }
\ No newline at end of file
diff --git a/entrypoints/content/index.ts b/entrypoints/content/index.ts
index a89b177..9bbf3b7 100644
--- a/entrypoints/content/index.ts
+++ b/entrypoints/content/index.ts
@@ -1,44 +1,44 @@
import './style.css'
+// 导入新的组件方法
+import { addOrUpdatePlayerButtons, hidePlayerButtons } from '../../components/PlayerButtons';
-// same storage key as popup
+// storage key, 与 popup 中保持一致
const STORAGE_KEY = 'feature_enabled';
export default defineContentScript({
matches: ['*://*.javdb.com/v/*'],
async main() {
- // check if the feature is enabled
+ // 检查功能是否启用
const isEnabled = await storage.getItem(`sync:${STORAGE_KEY}`) ?? true;
if (!isEnabled) {
- console.log('❌ [JavDB Helper] Feature is disabled.');
- hideFloatingButton();
+ console.log('❌ [JavDB Helper] 功能已禁用。');
+ hidePlayerButtons(); // 使用新的方法隐藏按钮
return;
}
- console.log('🚀 [JavDB Helper] Feature is enabled, running script...');
+ console.log('🚀 [JavDB Helper] 功能已启用,正在运行脚本...');
- // core logic, now includes the control of button visibility
const processPage = async () => {
- // check if the path is the one we care about
if (window.location.pathname.startsWith('/v/')) {
const videoNumber = getVideoNumber();
if (videoNumber) {
const missavUUID = await getMissavUUID(videoNumber);
if (missavUUID) {
- // if the UUID is successfully fetched, create or update the button
- addOrUpdateFloatingButton(missavUUID);
+ // 成功获取 UUID,创建或更新按钮
+ addOrUpdatePlayerButtons(missavUUID); // 使用新的方法更新按钮
} else {
- // if the UUID is not fetched, hide the button
- hideFloatingButton();
+ // 未获取到 UUID,隐藏按钮
+ hidePlayerButtons();
}
}
} else {
- // if not in the video detail page, hide the button
- hideFloatingButton();
+ // 如果不在视频详情页,隐藏按钮
+ hidePlayerButtons();
}
};
- // listen for URL path change
+ // 监听 URL 路径变化
let lastPathname = window.location.pathname;
new MutationObserver(() => {
const currentPathname = window.location.pathname;
@@ -48,122 +48,65 @@ export default defineContentScript({
}
}).observe(document.body, { childList: true, subtree: true });
- // execute once when page is loaded
+ // 页面加载时执行一次
processPage();
}
});
-// get target video number
+// 获取目标视频番号 (此函数保持不变)
function getVideoNumber(): string {
- // get target video number
- const targetElement = document.querySelector('a.button.is-white.copy-to-clipboard')
+ const targetElement = document.querySelector('a.button.is-white.copy-to-clipboard');
if (!targetElement) {
- console.log('not found target element')
- return ''
+ console.log('未找到目标元素');
+ return '';
}
-
- const targetNumber = targetElement.getAttribute('data-clipboard-text')
+ const targetNumber = targetElement.getAttribute('data-clipboard-text');
if (!targetNumber) {
- console.log('no target number')
- return ''
+ console.log('无目标番号');
+ return '';
}
-
- console.log('targetNumber', targetNumber)
- return targetNumber
+ console.log('目标番号', targetNumber);
+ return targetNumber;
}
-// get missav UUID
+// 获取 missav UUID (此函数保持不变)
async function getMissavUUID(videoNumber: string): Promise {
- const lowerTargetNumber = videoNumber.toLowerCase()
- const targetUrl = `https://missav.ws/dm1/en/${lowerTargetNumber}`
+ const lowerTargetNumber = videoNumber.toLowerCase();
+ const targetUrl = `https://missav.ws/dm1/en/${lowerTargetNumber}`;
try {
- // 'response' here is the object { success: boolean, html?: string, error?: string }
- // sent from your background script. It is NOT a Fetch API Response object.
const response = await chrome.runtime.sendMessage({
type: 'fetchMissav',
url: targetUrl
- })
+ });
if (!response.success) {
- throw new Error(response.error)
+ throw new Error(response.error);
}
- // Directly use the 'html' property which is already a string.
- // DO NOT try to call response.text().
- const parser = new DOMParser()
- const doc = parser.parseFromString(response.html, 'text/html')
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(response.html, 'text/html');
- const scripts = doc.getElementsByTagName('script')
- // console.log('scripts', scripts)
+ const scripts = doc.getElementsByTagName('script');
for (const script of scripts) {
- const content = script.textContent || ''
+ const content = script.textContent || '';
if (content.includes('thumbnail')) {
- const urlsMatch = content.match(/urls:\s*\[(.*?)\]/s)
+ const urlsMatch = content.match(/urls:\s*\[(.*?)\]/s);
if (urlsMatch) {
- const firstUrl = urlsMatch[1].split(',')[0].trim().replace(/"/g, '').replace(/\\/g, '')
- const uuidMatch = firstUrl.match(/\/([0-9a-f-]+)\/seek\//i)
+ const firstUrl = urlsMatch[1].split(',')[0].trim().replace(/"/g, '').replace(/\\/g, '');
+ const uuidMatch = firstUrl.match(/\/([0-9a-f-]+)\/seek\//i);
if (uuidMatch) {
- console.log('uuidMatch', uuidMatch[1])
- return uuidMatch[1]
+ console.log('uuidMatch', uuidMatch[1]);
+ return uuidMatch[1];
}
}
}
}
- console.warn('uuid not found')
- return ''
+ console.warn('未找到 uuid');
+ return '';
} catch (error) {
- // This is where your error was being caught.
- console.error('fetch or parse document error:', error)
- return ''
- }
-}
-
-/**
- * create a new floating button, or update the existing button's link
- * @param missavUUID - the UUID used to generate the playback link
- */
-function addOrUpdateFloatingButton(missavUUID: string) {
- const BUTTON_ID = 'wxt-iina-floating-button';
- const iinaUrl = `iina://weblink?url=https://surrit.com/${missavUUID}/playlist.m3u8`;
- console.log('Playlist URL:', iinaUrl);
-
- // check if the button already exists
- let iinaButton = document.getElementById(BUTTON_ID) as HTMLAnchorElement | null;
-
- if (!iinaButton) {
- // if the button does not exist, create it
- iinaButton = document.createElement('a');
- iinaButton.id = BUTTON_ID;
- iinaButton.className = 'wxt-iina-button'; // use class name to apply style
- iinaButton.innerHTML = `
-
- IINA
- `;
-
- // add the button to the body
- document.body.appendChild(iinaButton);
-
- // click event (since we use the a tag's href directly, we can omit this, but it's better to add it to prevent default behavior)
- iinaButton.addEventListener('click', (e) => {
- e.preventDefault();
- window.location.href = iinaButton!.href;
- });
- }
-
- // update the href property of the button and ensure it is visible
- iinaButton.href = iinaUrl;
- iinaButton.style.display = 'flex';
-}
-
-/**
- * hide the floating button
- */
-function hideFloatingButton() {
- const BUTTON_ID = 'wxt-iina-floating-button';
- const iinaButton = document.getElementById(BUTTON_ID);
- if (iinaButton) {
- iinaButton.style.display = 'none';
+ console.error('获取或解析文档时出错:', error);
+ return '';
}
}
\ No newline at end of file
diff --git a/entrypoints/content/style.css b/entrypoints/content/style.css
index 97a3c73..8616631 100644
--- a/entrypoints/content/style.css
+++ b/entrypoints/content/style.css
@@ -1,29 +1,28 @@
-.wxt-iina-button {
- /* 定位和层级 */
- position: fixed;
- top: 100px; /* 距离顶部 150px,可以按喜好调整 */
- right: 0px; /* 距离右侧 20px */
- z-index: 9999; /* 确保在页面最上层 */
-
- /* 外观和布局 */
- display: flex; /* 使用 flexbox 让图标和文字居中 */
- align-items: center;
- gap: 5px; /* 图标和文字的间距 */
- background-color: #3173dc; /* 一个不错的蓝色 */
- color: white;
- padding: 5px 10px;
- /* border-radius: 5px; 圆角 */
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
- font-size: 14px;
- font-weight: 500;
- text-decoration: none;
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); /* 添加阴影增加立体感 */
-
- /* 交互效果 */
- cursor: pointer;
- transition: transform 0.2s ease-in-out, background-color 0.2s ease;
- }
+.wxt-player-button {
+ /* 定位和层级 */
+ position: fixed;
+ /* 'top' 属性现在通过内联样式设置 */
+ right: 0px;
+ z-index: 9999;
- .wxt-iina-button:hover {
- transform: scale(1.05); /* 轻微放大 */
- }
\ No newline at end of file
+ /* 外观和布局 */
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ background-color: #3173dc;
+ color: white;
+ padding: 5px 10px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ text-decoration: none;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
+
+ /* 交互效果 */
+ cursor: pointer;
+ transition: transform 0.2s ease-in-out, background-color 0.2s ease;
+}
+
+.wxt-player-button:hover {
+ transform: scale(1.05);
+}
\ No newline at end of file