Add button abstraction as component, support PotPlayer
This commit is contained in:
@@ -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<string> {
|
||||
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 = `
|
||||
<i class="icon-play"></i>
|
||||
<span>IINA</span>
|
||||
`;
|
||||
|
||||
// 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 '';
|
||||
}
|
||||
}
|
||||
@@ -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); /* 轻微放大 */
|
||||
}
|
||||
/* 外观和布局 */
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user