diff --git a/components/NavigationButtons.css b/components/NavigationButtons.css
new file mode 100644
index 0000000..fe0218d
--- /dev/null
+++ b/components/NavigationButtons.css
@@ -0,0 +1,22 @@
+.wxt-nav-button {
+ position: fixed;
+ right: 0px;
+ z-index: 9999;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ background-color: #28a745; /* 导航按钮使用绿色以作区分 */
+ 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-nav-button:hover {
+ transform: scale(1.05);
+}
\ No newline at end of file
diff --git a/components/NavigationButtons.ts b/components/NavigationButtons.ts
new file mode 100644
index 0000000..2ebdf35
--- /dev/null
+++ b/components/NavigationButtons.ts
@@ -0,0 +1,55 @@
+import './NavigationButtons.css';
+
+/**
+ * 创建或更新一个浮动导航按钮。
+ * 此按钮在新标签页中打开链接。
+ */
+function createOrUpdateNavButton(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-nav-button';
+ button.innerHTML = `
+
+ ${name}
+ `;
+
+ // 关键:设置在新标签页打开
+ button.target = '_blank';
+ // 安全性最佳实践
+ button.rel = 'noopener noreferrer';
+
+ document.body.appendChild(button);
+ }
+
+ button.href = url;
+ button.style.top = topPosition;
+ button.style.display = 'flex';
+}
+
+function hideButton(id: string) {
+ const button = document.getElementById(id);
+ if (button) {
+ button.style.display = 'none';
+ }
+}
+
+const MISSAV_BUTTON_ID = 'wxt-missav-nav-button';
+
+/**
+ * 添加或更新 MissAV 导航按钮。
+ * @param videoNumber - 视频番号 (例如, 'IPX-811')。
+ */
+export function addOrUpdateNavigationButtons(videoNumber: string) {
+ const missavUrl = `https://missav.ws/${videoNumber.toLowerCase()}`;
+ createOrUpdateNavButton(MISSAV_BUTTON_ID, 'MissAV', 'icon-play', missavUrl, '200px');
+}
+
+/**
+ * 隐藏所有的导航按钮。
+ */
+export function hideNavigationButtons() {
+ hideButton(MISSAV_BUTTON_ID);
+}
\ No newline at end of file
diff --git a/components/PlayerButtons.css b/components/PlayerButtons.css
new file mode 100644
index 0000000..ad5cda9
--- /dev/null
+++ b/components/PlayerButtons.css
@@ -0,0 +1,27 @@
+.wxt-player-button {
+ /* 定位和层级 */
+ position: fixed;
+ right: 0px;
+ z-index: 9999;
+
+ /* 外观和布局 */
+ 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
diff --git a/components/PlayerButtons.ts b/components/PlayerButtons.ts
index 2b701c8..afff191 100644
--- a/components/PlayerButtons.ts
+++ b/components/PlayerButtons.ts
@@ -1,72 +1,66 @@
+import './PlayerButtons.css';
+
/**
* 创建或更新一个浮动播放器按钮。
- * @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'; // 确保按钮可见
+ 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;
+ });
}
-
- /**
- * 隐藏指定的播放器按钮。
- * @param id - 要隐藏的按钮的 ID。
- */
- function hideButton(id: string) {
- const button = document.getElementById(id);
- if (button) {
- button.style.display = 'none';
- }
+
+ button.href = url;
+ button.style.top = topPosition;
+ button.style.display = 'flex';
+}
+
+/**
+ * 隐藏指定的播放器按钮。
+ */
+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';
+
+/**
+ * 添加或更新播放器按钮。
+ * @param missavUUID - 用于生成播放链接的 UUID。
+ */
+export function addOrUpdatePlayerButtons(missavUUID: string) {
+ const playlistUrl = `https://surrit.com/${missavUUID}/playlist.m3u8`;
- // 定义按钮的常量 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
+ // 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, '140px');
+}
+
+/**
+ * 隐藏所有的播放器按钮。
+ */
+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 9bbf3b7..5a05c2f 100644
--- a/entrypoints/content/index.ts
+++ b/entrypoints/content/index.ts
@@ -1,19 +1,18 @@
-import './style.css'
-// 导入新的组件方法
+// 从两个组件中导入方法
import { addOrUpdatePlayerButtons, hidePlayerButtons } from '../../components/PlayerButtons';
+import { addOrUpdateNavigationButtons, hideNavigationButtons } from '../../components/NavigationButtons';
-// storage key, 与 popup 中保持一致
const STORAGE_KEY = 'feature_enabled';
export default defineContentScript({
matches: ['*://*.javdb.com/v/*'],
async main() {
- // 检查功能是否启用
const isEnabled = await storage.getItem(`sync:${STORAGE_KEY}`) ?? true;
if (!isEnabled) {
console.log('❌ [JavDB Helper] 功能已禁用。');
- hidePlayerButtons(); // 使用新的方法隐藏按钮
+ hidePlayerButtons();
+ hideNavigationButtons(); // 同时隐藏导航按钮
return;
}
@@ -23,18 +22,22 @@ export default defineContentScript({
if (window.location.pathname.startsWith('/v/')) {
const videoNumber = getVideoNumber();
if (videoNumber) {
+ // 只要有番号,就显示导航按钮
+ addOrUpdateNavigationButtons(videoNumber);
+
+ // 异步获取 UUID 来显示播放器按钮
const missavUUID = await getMissavUUID(videoNumber);
if (missavUUID) {
- // 成功获取 UUID,创建或更新按钮
- addOrUpdatePlayerButtons(missavUUID); // 使用新的方法更新按钮
+ addOrUpdatePlayerButtons(missavUUID);
} else {
- // 未获取到 UUID,隐藏按钮
+ // 如果没有 UUID,则只隐藏播放器按钮
hidePlayerButtons();
}
}
} else {
- // 如果不在视频详情页,隐藏按钮
+ // 如果不在视频详情页,隐藏所有按钮
hidePlayerButtons();
+ hideNavigationButtons();
}
};
@@ -53,7 +56,7 @@ export default defineContentScript({
}
});
-// 获取目标视频番号 (此函数保持不变)
+// 获取目标视频番号
function getVideoNumber(): string {
const targetElement = document.querySelector('a.button.is-white.copy-to-clipboard');
if (!targetElement) {
@@ -69,7 +72,7 @@ function getVideoNumber(): string {
return targetNumber;
}
-// 获取 missav UUID (此函数保持不变)
+// 获取 missav UUID
async function getMissavUUID(videoNumber: string): Promise {
const lowerTargetNumber = videoNumber.toLowerCase();
const targetUrl = `https://missav.ws/dm1/en/${lowerTargetNumber}`;
diff --git a/entrypoints/content/style.css b/entrypoints/content/style.css
deleted file mode 100644
index 8616631..0000000
--- a/entrypoints/content/style.css
+++ /dev/null
@@ -1,28 +0,0 @@
-.wxt-player-button {
- /* 定位和层级 */
- position: fixed;
- /* 'top' 属性现在通过内联样式设置 */
- right: 0px;
- z-index: 9999;
-
- /* 外观和布局 */
- 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
diff --git a/structure.md b/structure.md
index 5b43b39..1cfd774 100644
--- a/structure.md
+++ b/structure.md
@@ -1,38 +1,38 @@
-Here's a brief summary of each of these files and directories:
+Here's a brief summary of each of these files and directories:
+
+📂 {rootDir}/
+ 📁 .output/
+ 📁 .wxt/
+ 📁 assets/
+ 📁 components/
+ 📁 composables/
+ 📁 entrypoints/
+ 📁 hooks/
+ 📁 modules/
+ 📁 public/
+ 📁 utils/
+ 📄 .env
+ 📄 .env.publish
+ 📄 app.config.ts
+ 📄 package.json
+ 📄 tsconfig.json
+ 📄 web-ext.config.ts
+ 📄 wxt.config.ts
-📂 {rootDir}/
- 📁 .output/
- 📁 .wxt/
- 📁 assets/
- 📁 components/
- 📁 composables/
- 📁 entrypoints/
- 📁 hooks/
- 📁 modules/
- 📁 public/
- 📁 utils/
- 📄 .env
- 📄 .env.publish
- 📄 app.config.ts
- 📄 package.json
- 📄 tsconfig.json
- 📄 web-ext.config.ts
- 📄 wxt.config.ts
-
-.output/: All build artifacts will go here
-.wxt/: Generated by WXT, it contains TS config
-assets/: Contains all CSS, images, and other assets that should be processed by WXT
-components/: Auto-imported by default, contains UI components
-composables/: Auto-imported by default, contains source code for your project's composable functions for Vue
-entrypoints/: Contains all the entrypoints that get bundled into your extension
-hooks/: Auto-imported by default, contains source code for your project's hooks for React and Solid
-modules/: Contains local WXT Modules for your project
-public/: Contains any files you want to copy into the output folder as-is, without being processed by WXT
-utils/: Auto-imported by default, contains generic utilities used throughout your project
-.env: Contains Environment Variables
-.env.publish: Contains Environment Variables for publishing
-app.config.ts: Contains Runtime Config
-package.json: The standard file used by your package manager
-tsconfig.json: Config telling TypeScript how to behave
-web-ext.config.ts: Configure Browser Startup
-wxt.config.ts: The main config file for WXT projects
\ No newline at end of file
+.output/: All build artifacts will go here
+.wxt/: Generated by WXT, it contains TS config
+assets/: Contains all CSS, images, and other assets that should be processed by WXT
+components/: Auto-imported by default, contains UI components
+composables/: Auto-imported by default, contains source code for your project's composable functions for Vue
+entrypoints/: Contains all the entrypoints that get bundled into your extension
+hooks/: Auto-imported by default, contains source code for your project's hooks for React and Solid
+modules/: Contains local WXT Modules for your project
+public/: Contains any files you want to copy into the output folder as-is, without being processed by WXT
+utils/: Auto-imported by default, contains generic utilities used throughout your project
+.env: Contains Environment Variables
+.env.publish: Contains Environment Variables for publishing
+app.config.ts: Contains Runtime Config
+package.json: The standard file used by your package manager
+tsconfig.json: Config telling TypeScript how to behave
+web-ext.config.ts: Configure Browser Startup
+wxt.config.ts: The main config file for WXT projects
\ No newline at end of file