Updated version to 0.5.0, enhanced support for jable.tv, optimized the display logic of navigation and player buttons, added video source selection function, and updated related documents and styles.
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
# Jav Play
|
||||
1. Call the local player to play the video in javdb.com/javlibrary.com directly.
|
||||
2. Jump from the javdb.com/javlibrary.com page to the corresponding playback page of missav
|
||||
2. Jump from the javdb.com/javlibrary.com page to the corresponding playback page of missav/jable
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
1. Download extension zip file from github release page.
|
||||
2. Open Chrome, navigate to chrome://extensions, then drag and drop the file onto the extensions page
|
||||
3. Enable or Disable feature in popup setting. (Enable by default)
|
||||
4. Choose video source from popup setting. (missav.ws by default)
|
||||
|
||||
## Supported Players
|
||||
1. [IINA](https://iina.io/) Recommend in MacOS
|
||||
@@ -14,6 +16,4 @@
|
||||
|
||||
## Players to be supported
|
||||
1. ~~[mpv](https://mpv.io/)~~ Because the mpv player is not automatically registered as a protocol handler in the operating system, it is quite troublesome to handle, and support is not currently being considered. If someone is willing to add pmv support, please submit a request.
|
||||
|
||||
## Planned Features
|
||||
1. [x] Directly jump from the javdb page to the playback page of the corresponding film on missav.
|
||||
2. ~~[mpc-be](https://github.com/Aleksoid1978/MPC-BE)~~ Same reason as mpv player.
|
||||
|
||||
@@ -4,17 +4,13 @@ import './NavigationButtons.css';
|
||||
* 创建或更新一个浮动导航按钮。
|
||||
* 此按钮在新标签页中打开链接。
|
||||
*/
|
||||
function createOrUpdateNavButton(id: string, name: string, iconClass: string, url: string, topPosition: string) {
|
||||
function createOrUpdateNavButton(id: string, name: string, iconClass: string, url: string, topPosition: string, isDisabled: boolean = false) {
|
||||
let button = document.getElementById(id) as HTMLAnchorElement | null;
|
||||
|
||||
if (!button) {
|
||||
button = document.createElement('a');
|
||||
button.id = id;
|
||||
button.className = 'wxt-nav-button';
|
||||
button.innerHTML = `
|
||||
<i class="${iconClass}"></i>
|
||||
<span>${name}</span>
|
||||
`;
|
||||
|
||||
// 关键:设置在新标签页打开
|
||||
button.target = '_blank';
|
||||
@@ -24,9 +20,47 @@ function createOrUpdateNavButton(id: string, name: string, iconClass: string, ur
|
||||
document.body.appendChild(button);
|
||||
}
|
||||
|
||||
// 无论是新创建还是更新,都要设置innerHTML
|
||||
button.innerHTML = `
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="M16 12L10 16.3301V7.66987L16 12Z" fill="currentColor" />
|
||||
</svg>
|
||||
<span>${name}</span>
|
||||
`;
|
||||
|
||||
button.href = url;
|
||||
button.style.top = topPosition;
|
||||
button.style.display = 'flex';
|
||||
|
||||
// 根据是否禁用来设置样式和行为
|
||||
if (isDisabled) {
|
||||
button.style.backgroundColor = '#6c757d'; // 灰色
|
||||
button.style.cursor = 'not-allowed';
|
||||
button.style.opacity = '0.6';
|
||||
|
||||
// 阻止点击事件
|
||||
button.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
button.style.backgroundColor = '#28a745'; // 原来的绿色
|
||||
button.style.cursor = 'pointer';
|
||||
button.style.opacity = '1';
|
||||
button.onclick = null; // 移除点击阻止
|
||||
}
|
||||
}
|
||||
|
||||
function hideButton(id: string) {
|
||||
@@ -36,20 +70,50 @@ function hideButton(id: string) {
|
||||
}
|
||||
}
|
||||
|
||||
const MISSAV_BUTTON_ID = 'wxt-missav-nav-button';
|
||||
const NAV_BUTTON_ID = 'wxt-nav-button';
|
||||
|
||||
/**
|
||||
* 添加或更新 MissAV 导航按钮。
|
||||
* @param videoNumber - 视频番号 (例如, 'IPX-811')。
|
||||
* 显示检查状态的导航按钮
|
||||
* @param videoSource - 视频源 ('missav' 或 'jable')
|
||||
*/
|
||||
export function addOrUpdateNavigationButtons(videoNumber: string) {
|
||||
const missavUrl = `https://missav.ws/${videoNumber.toLowerCase()}`;
|
||||
createOrUpdateNavButton(MISSAV_BUTTON_ID, 'MissAV', 'icon-play', missavUrl, '200px');
|
||||
export function showNavigationButtonChecking(videoSource: string = 'missav') {
|
||||
const name = videoSource === 'jable' ? 'Jable' : 'MissAV';
|
||||
createOrUpdateNavButton(NAV_BUTTON_ID, `${name} Checking...`, 'icon-play', '#', '100px', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示404状态的导航按钮
|
||||
* @param videoSource - 视频源 ('missav' 或 'jable')
|
||||
*/
|
||||
export function showNavigationButton404(videoSource: string = 'missav') {
|
||||
const name = videoSource === 'jable' ? 'Jable' : 'MissAV';
|
||||
createOrUpdateNavButton(NAV_BUTTON_ID, `${name} 404`, 'icon-play', '#', '100px', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加或更新导航按钮。
|
||||
* @param videoNumber - 视频番号 (例如, 'IPX-811')。
|
||||
* @param videoSource - 视频源 ('missav' 或 'jable')。
|
||||
*/
|
||||
export function addOrUpdateNavigationButtons(videoNumber: string, videoSource: string = 'missav') {
|
||||
let url: string;
|
||||
let name: string;
|
||||
|
||||
if (videoSource === 'jable') {
|
||||
url = `https://jable.tv/videos/${videoNumber.toLowerCase()}/`;
|
||||
name = 'Jable';
|
||||
} else {
|
||||
// default to missav
|
||||
url = `https://missav.ws/${videoNumber.toLowerCase()}`;
|
||||
name = 'MissAV';
|
||||
}
|
||||
|
||||
createOrUpdateNavButton(NAV_BUTTON_ID, name, 'icon-play', url, '100px', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏所有的导航按钮。
|
||||
*/
|
||||
export function hideNavigationButtons() {
|
||||
hideButton(MISSAV_BUTTON_ID);
|
||||
hideButton(NAV_BUTTON_ID);
|
||||
}
|
||||
@@ -3,28 +3,70 @@ import './PlayerButtons.css';
|
||||
/**
|
||||
* 创建或更新一个浮动播放器按钮。
|
||||
*/
|
||||
function createOrUpdateButton(id: string, name:string, iconClass: string, url: string, topPosition: string) {
|
||||
function createOrUpdateButton(id: string, name: string, iconClass: string, url: string, topPosition: string, isDisabled: boolean = false) {
|
||||
let button = document.getElementById(id) as HTMLAnchorElement | null;
|
||||
|
||||
if (!button) {
|
||||
button = document.createElement('a');
|
||||
button.id = id;
|
||||
button.className = 'wxt-player-button';
|
||||
button.innerHTML = `
|
||||
<i class="${iconClass}"></i>
|
||||
<span>${name}</span>
|
||||
`;
|
||||
document.body.appendChild(button);
|
||||
|
||||
button.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
window.location.href = button!.href;
|
||||
});
|
||||
// 只在按钮未禁用时添加点击事件
|
||||
if (!isDisabled) {
|
||||
button.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
window.location.href = button!.href;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 无论是新创建还是更新,都要设置innerHTML
|
||||
button.innerHTML = `
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 21C16.9706 21 21 16.9706 21 12C21 7.02944 16.9706 3 12 3C7.02944 3 3 7.02944 3 12C3 16.9706 7.02944 21 12 21ZM12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path d="M16 12L10 16.3301V7.66987L16 12Z" fill="currentColor" />
|
||||
</svg>
|
||||
<span>${name}</span>
|
||||
`;
|
||||
|
||||
button.href = url;
|
||||
button.style.top = topPosition;
|
||||
button.style.display = 'flex';
|
||||
|
||||
// 根据是否禁用来设置样式和行为
|
||||
if (isDisabled) {
|
||||
button.style.backgroundColor = '#6c757d'; // 灰色
|
||||
button.style.cursor = 'not-allowed';
|
||||
button.style.opacity = '0.6';
|
||||
|
||||
// 阻止点击事件
|
||||
button.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
button.style.backgroundColor = '#3173dc'; // 原来的蓝色
|
||||
button.style.cursor = 'pointer';
|
||||
button.style.opacity = '1';
|
||||
|
||||
// 恢复正常点击行为
|
||||
button.onclick = (e) => {
|
||||
e.preventDefault();
|
||||
window.location.href = button.href;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,19 +84,33 @@ 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`;
|
||||
|
||||
export function showPlayerButtonsChecking() {
|
||||
createOrUpdateButton(IINA_BUTTON_ID, 'IINA', 'icon-play', '#', '140px', true);
|
||||
createOrUpdateButton(POTPLAYER_BUTTON_ID, 'PotPlayer', 'icon-play', '#', '180px', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示404状态的播放器按钮
|
||||
*/
|
||||
export function showPlayerButtons404() {
|
||||
createOrUpdateButton(IINA_BUTTON_ID, 'IINA', 'icon-play', '#', '140px', true);
|
||||
createOrUpdateButton(POTPLAYER_BUTTON_ID, 'PotPlayer', 'icon-play', '#', '180px', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加或更新播放器按钮。
|
||||
* @param playlistUrl - 直接的播放链接。
|
||||
*/
|
||||
export function addOrUpdatePlayerButtons(playlistUrl: string) {
|
||||
// IINA 按钮
|
||||
const iinaUrl = `iina://weblink?url=${playlistUrl}`;
|
||||
createOrUpdateButton(IINA_BUTTON_ID, 'IINA', 'icon-play', iinaUrl, '100px');
|
||||
createOrUpdateButton(IINA_BUTTON_ID, 'IINA', 'icon-play', iinaUrl, '140px', false);
|
||||
|
||||
// PotPlayer 按钮
|
||||
const potplayerUrl = `potplayer://${playlistUrl}`;
|
||||
createOrUpdateButton(POTPLAYER_BUTTON_ID, 'PotPlayer', 'icon-play', potplayerUrl, '140px');
|
||||
createOrUpdateButton(POTPLAYER_BUTTON_ID, 'PotPlayer', 'icon-play', potplayerUrl, '180px', false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
interface FetchMissavRequest {
|
||||
type: 'fetchMissav'
|
||||
interface FetchVideoRequest {
|
||||
type: 'fetchVideo'
|
||||
url: string
|
||||
}
|
||||
|
||||
interface CheckUrlRequest {
|
||||
type: 'checkUrl'
|
||||
url: string
|
||||
}
|
||||
|
||||
type BackgroundRequest = FetchVideoRequest | CheckUrlRequest;
|
||||
|
||||
export default defineBackground({
|
||||
main() {
|
||||
chrome.runtime.onMessage.addListener((
|
||||
request: FetchMissavRequest,
|
||||
request: BackgroundRequest,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
sendResponse: (response: any) => void
|
||||
) => {
|
||||
if (request.type === 'fetchMissav') {
|
||||
console.log('fetchMissav', request.url)
|
||||
if (request.type === 'fetchVideo') {
|
||||
console.log('fetchVideo', request.url)
|
||||
fetch(request.url, {
|
||||
redirect: 'follow',
|
||||
// Add headers to mimic a browser request
|
||||
@@ -20,14 +27,12 @@ export default defineBackground({
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
console.log('response', response)
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
return response.text()
|
||||
})
|
||||
.then(html => {
|
||||
console.log('html', html)
|
||||
sendResponse({ success: true, html })
|
||||
})
|
||||
.catch(error => {
|
||||
@@ -35,6 +40,35 @@ export default defineBackground({
|
||||
})
|
||||
return true // Keep the message channel open for the async response
|
||||
}
|
||||
|
||||
if (request.type === 'checkUrl') {
|
||||
console.log('checkUrl', request.url)
|
||||
fetch(request.url, {
|
||||
method: 'HEAD', // 只检查头部,不下载完整内容
|
||||
redirect: 'follow',
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
console.log('checkUrl response', response.status)
|
||||
sendResponse({
|
||||
success: true,
|
||||
exists: response.ok,
|
||||
status: response.status
|
||||
})
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('checkUrl error', error)
|
||||
sendResponse({
|
||||
success: true,
|
||||
exists: false,
|
||||
status: 0,
|
||||
error: error.message
|
||||
})
|
||||
})
|
||||
return true // Keep the message channel open for the async response
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,8 +1,19 @@
|
||||
// 从两个组件中导入方法
|
||||
import { addOrUpdatePlayerButtons, hidePlayerButtons } from '../../components/PlayerButtons';
|
||||
import { addOrUpdateNavigationButtons, hideNavigationButtons } from '../../components/NavigationButtons';
|
||||
import {
|
||||
addOrUpdatePlayerButtons,
|
||||
hidePlayerButtons,
|
||||
showPlayerButtonsChecking,
|
||||
showPlayerButtons404
|
||||
} from '../../components/PlayerButtons';
|
||||
import {
|
||||
addOrUpdateNavigationButtons,
|
||||
hideNavigationButtons,
|
||||
showNavigationButtonChecking,
|
||||
showNavigationButton404
|
||||
} from '../../components/NavigationButtons';
|
||||
|
||||
const STORAGE_KEY = 'feature_enabled';
|
||||
const VIDEO_SOURCE_KEY = 'video_source';
|
||||
|
||||
export default defineContentScript({
|
||||
matches: ['*://*.javdb.com/v/*', '*://*.javlibrary.com/*'],
|
||||
@@ -21,16 +32,35 @@ export default defineContentScript({
|
||||
const processPage = async () => {
|
||||
const videoNumber = getVideoNumber();
|
||||
if (videoNumber) {
|
||||
// 只要有番号,就显示导航按钮
|
||||
addOrUpdateNavigationButtons(videoNumber);
|
||||
|
||||
// 异步获取 UUID 来显示播放器按钮
|
||||
const missavUUID = await getMissavUUID(videoNumber);
|
||||
if (missavUUID) {
|
||||
addOrUpdatePlayerButtons(missavUUID);
|
||||
// 获取用户选择的视频源
|
||||
const videoSource = await storage.getItem(`sync:${VIDEO_SOURCE_KEY}`) ?? 'missav';
|
||||
|
||||
// 1. 先显示检查状态
|
||||
showNavigationButtonChecking(videoSource as string);
|
||||
showPlayerButtonsChecking();
|
||||
|
||||
// 2. 检查目标页面是否存在
|
||||
const targetUrl = getTargetUrl(videoNumber, videoSource as string);
|
||||
const urlExists = await checkUrlExists(targetUrl);
|
||||
|
||||
if (!urlExists) {
|
||||
// 3.1 目标页面不存在,显示404状态
|
||||
showNavigationButton404(videoSource as string);
|
||||
showPlayerButtons404();
|
||||
console.log('目标页面不存在:', targetUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3.2 目标页面存在,显示正常的导航按钮
|
||||
addOrUpdateNavigationButtons(videoNumber, videoSource as string);
|
||||
|
||||
// 4. 异步获取播放链接来显示播放器按钮
|
||||
const playUrl = await getPlayUrl(videoNumber, videoSource as string);
|
||||
if (playUrl) {
|
||||
addOrUpdatePlayerButtons(playUrl);
|
||||
} else {
|
||||
// 如果没有 UUID,则只隐藏播放器按钮
|
||||
hidePlayerButtons();
|
||||
// 如果没有播放链接,显示404状态
|
||||
showPlayerButtons404();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -77,14 +107,54 @@ function getVideoNumber(): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 missav UUID
|
||||
async function getMissavUUID(videoNumber: string): Promise<string> {
|
||||
// 获取目标URL
|
||||
function getTargetUrl(videoNumber: string, videoSource: string): string {
|
||||
if (videoSource === 'jable') {
|
||||
return `https://jable.tv/videos/${videoNumber.toLowerCase()}/`;
|
||||
} else {
|
||||
// default to missav
|
||||
return `https://missav.ws/${videoNumber.toLowerCase()}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查URL是否存在
|
||||
async function checkUrlExists(url: string): Promise<boolean> {
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'checkUrl',
|
||||
url: url
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
console.error('检查URL时出错:', response.error);
|
||||
return false;
|
||||
}
|
||||
|
||||
return response.exists;
|
||||
} catch (error) {
|
||||
console.error('检查URL时出错:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取播放链接 (支持不同视频源)
|
||||
async function getPlayUrl(videoNumber: string, videoSource: string): Promise<string> {
|
||||
if (videoSource === 'jable') {
|
||||
return await getJablePlayUrl(videoNumber);
|
||||
} else {
|
||||
// default to missav
|
||||
return await getMissavPlayUrl(videoNumber);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 MissAV 播放链接
|
||||
async function getMissavPlayUrl(videoNumber: string): Promise<string> {
|
||||
const lowerTargetNumber = videoNumber.toLowerCase();
|
||||
const targetUrl = `https://missav.ws/dm1/en/${lowerTargetNumber}`;
|
||||
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'fetchMissav',
|
||||
type: 'fetchVideo',
|
||||
url: targetUrl
|
||||
});
|
||||
|
||||
@@ -105,16 +175,53 @@ async function getMissavUUID(videoNumber: string): Promise<string> {
|
||||
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('MissAV uuidMatch', uuidMatch[1]);
|
||||
return `https://surrit.com/${uuidMatch[1]}/playlist.m3u8`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.warn('未找到 uuid');
|
||||
console.warn('未找到 MissAV uuid');
|
||||
return '';
|
||||
} catch (error) {
|
||||
console.error('获取或解析文档时出错:', error);
|
||||
console.error('获取或解析 MissAV 文档时出错:', error);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// 获取 Jable 播放链接
|
||||
async function getJablePlayUrl(videoNumber: string): Promise<string> {
|
||||
const lowerTargetNumber = videoNumber.toLowerCase();
|
||||
const targetUrl = `https://jable.tv/videos/${lowerTargetNumber}/`;
|
||||
|
||||
try {
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'fetchVideo',
|
||||
url: targetUrl
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(response.html, 'text/html');
|
||||
|
||||
const scripts = doc.getElementsByTagName('script');
|
||||
|
||||
for (const script of scripts) {
|
||||
const content = script.textContent || '';
|
||||
// 查找 var hlsUrl 变量
|
||||
const hlsUrlMatch = content.match(/var\s+hlsUrl\s*=\s*['"](.*?)['"]/);
|
||||
if (hlsUrlMatch) {
|
||||
console.log('Jable hlsUrl', hlsUrlMatch[1]);
|
||||
return hlsUrlMatch[1];
|
||||
}
|
||||
}
|
||||
console.warn('未找到 Jable hlsUrl');
|
||||
return '';
|
||||
} catch (error) {
|
||||
console.error('获取或解析 Jable 文档时出错:', error);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,14 @@
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-row">
|
||||
<label for="video-source">Video Source</label>
|
||||
<select id="video-source" class="source-select">
|
||||
<option value="missav">missav.ws</option>
|
||||
<option value="jable">jable.tv</option>
|
||||
</select>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<hr class="separator">
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import './style.css';
|
||||
|
||||
const featureToggle = document.querySelector<HTMLInputElement>('#feature-toggle');
|
||||
const videoSourceSelect = document.querySelector<HTMLSelectElement>('#video-source');
|
||||
|
||||
// storage key, define as constant, for easy use in multiple places
|
||||
// storage keys, define as constants, for easy use in multiple places
|
||||
const STORAGE_KEY = 'feature_enabled';
|
||||
const VIDEO_SOURCE_KEY = 'video_source';
|
||||
|
||||
// 1. when popup is opened, load and set the initial state of the switch
|
||||
// 1. when popup is opened, load and set the initial state of the switch and select
|
||||
// WXT provided storage API is a wrapper of chrome.storage, usage is basically the same
|
||||
storage.getItem(`sync:${STORAGE_KEY}`).then((result) => {
|
||||
// if there is no value in the storage, we default to true
|
||||
@@ -16,8 +18,22 @@ storage.getItem(`sync:${STORAGE_KEY}`).then((result) => {
|
||||
}
|
||||
});
|
||||
|
||||
storage.getItem(`sync:${VIDEO_SOURCE_KEY}`).then((result) => {
|
||||
// default to missav if no value in storage
|
||||
const videoSource = (result as string | null) ?? 'missav';
|
||||
if (videoSourceSelect) {
|
||||
videoSourceSelect.value = videoSource;
|
||||
}
|
||||
});
|
||||
|
||||
// 2. listen for the change of the switch state, and save the new setting
|
||||
featureToggle?.addEventListener('change', () => {
|
||||
const isEnabled = featureToggle.checked;
|
||||
storage.setItem(`sync:${STORAGE_KEY}`, isEnabled);
|
||||
});
|
||||
|
||||
// 3. listen for the change of the video source selection, and save the new setting
|
||||
videoSourceSelect?.addEventListener('change', () => {
|
||||
const videoSource = videoSourceSelect.value;
|
||||
storage.setItem(`sync:${VIDEO_SOURCE_KEY}`, videoSource);
|
||||
});
|
||||
@@ -53,6 +53,11 @@ h1 {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.setting-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
footer {
|
||||
@@ -75,7 +80,24 @@ footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* --- 开关样式 (保持不变) --- */
|
||||
/* --- 下拉选择框样式 --- */
|
||||
.source-select {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.source-select:focus {
|
||||
outline: none;
|
||||
border-color: #007bff;
|
||||
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
||||
}
|
||||
|
||||
/* --- 开关样式 --- */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "jav-play",
|
||||
"description": "manifest.json description",
|
||||
"private": true,
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wxt",
|
||||
|
||||
@@ -12,8 +12,9 @@ export default defineConfig({
|
||||
// Explicitly grant permission for the background script to access this host.
|
||||
host_permissions: [
|
||||
'*://*.javdb.com/*',
|
||||
'*://*.javlibrary.com/*',
|
||||
'*://*.missav.ws/*',
|
||||
'*://*.javlibrary.com/*'
|
||||
'*://*.jable.tv/*'
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user