feat: add notice for subtitle
This commit is contained in:
@@ -1598,4 +1598,31 @@ export const I18N = {
|
||||
en: `Default styles reference:`,
|
||||
zh_TW: `認樣式參考:`,
|
||||
},
|
||||
subtitle_load_succeed: {
|
||||
zh: `双语字幕加载成功!`,
|
||||
en: `Bilingual subtitles loaded successfully!`,
|
||||
zh_TW: `双语字幕加载成功!`,
|
||||
},
|
||||
subtitle_load_failed: {
|
||||
zh: `双语字幕加载失败!`,
|
||||
en: `Failed to load bilingual subtitles!`,
|
||||
zh_TW: `双语字幕加载失败!`,
|
||||
},
|
||||
try_get_subtitle_data: {
|
||||
zh: `尝试获取字幕数据,请稍候...`,
|
||||
en: `Trying to get subtitle data, please wait...`,
|
||||
zh_TW: `尝试获取字幕数据,请稍候...`,
|
||||
},
|
||||
subtitle_data_processing: {
|
||||
zh: `字幕数据处理中...`,
|
||||
en: `Subtitle data processing...`,
|
||||
zh_TW: `字幕数据处理中...`,
|
||||
},
|
||||
starting_to_process_subtitle: {
|
||||
zh: `开始处理字幕数据...`,
|
||||
en: `Starting to process subtitle data...`,
|
||||
zh_TW: `开始处理字幕数据...`,
|
||||
},
|
||||
};
|
||||
|
||||
export const i18n = (lang) => (key) => I18N[key]?.[lang] || "";
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { sleep } from "../libs/utils.js";
|
||||
import { createLogoSVG } from "../libs/svg.js";
|
||||
import { randomBetween } from "../libs/utils.js";
|
||||
import { i18n } from "../config";
|
||||
|
||||
const VIDEO_SELECT = "#container video";
|
||||
const CONTORLS_SELECT = ".ytp-right-controls";
|
||||
@@ -25,9 +26,13 @@ class YouTubeCaptionProvider {
|
||||
#ytControls = null;
|
||||
#isBusy = false;
|
||||
#fromLang = "auto";
|
||||
#notificationEl = null;
|
||||
#notificationTimeout = null;
|
||||
#i18n = () => "";
|
||||
|
||||
constructor(setting = {}) {
|
||||
this.#setting = setting;
|
||||
this.#i18n = i18n(setting.uiLang || "zh");
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -45,7 +50,6 @@ class YouTubeCaptionProvider {
|
||||
if (this.#toggleButton) {
|
||||
this.#toggleButton.style.opacity = "0.5";
|
||||
}
|
||||
this.#destroyManager();
|
||||
this.#doubleClick();
|
||||
}, 1000);
|
||||
});
|
||||
@@ -113,14 +117,20 @@ class YouTubeCaptionProvider {
|
||||
toggleButton.onclick = () => {
|
||||
if (this.#isBusy) {
|
||||
logger.info(`Youtube Provider: It's budy now...`);
|
||||
return;
|
||||
this.#showNotification(this.#i18n("subtitle_data_processing"));
|
||||
}
|
||||
|
||||
if (!this.#enabled) {
|
||||
logger.info(`Youtube Provider: Feature toggled ON.`);
|
||||
this.#enabled = true;
|
||||
this.#toggleButton?.replaceChildren(
|
||||
createLogoSVG({ isSelected: true })
|
||||
);
|
||||
this.#startManager();
|
||||
} else {
|
||||
logger.info(`Youtube Provider: Feature toggled OFF.`);
|
||||
this.#enabled = false;
|
||||
this.#toggleButton?.replaceChildren(createLogoSVG());
|
||||
this.#destroyManager();
|
||||
}
|
||||
};
|
||||
@@ -245,7 +255,7 @@ class YouTubeCaptionProvider {
|
||||
logger.info("Youtube Provider is busy...");
|
||||
return;
|
||||
}
|
||||
this.#isBusy = true; // todo: 提示用户等待中
|
||||
this.#isBusy = true;
|
||||
|
||||
try {
|
||||
const videoId = this.#getVideoId();
|
||||
@@ -265,6 +275,8 @@ class YouTubeCaptionProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#showNotification(this.#i18n("starting_to_process_subtitle"));
|
||||
|
||||
const captionTracks = await this.#getCaptionTracks(videoId);
|
||||
const captionTrack = this.#findCaptionTrack(captionTracks);
|
||||
if (!captionTrack) {
|
||||
@@ -365,6 +377,7 @@ class YouTubeCaptionProvider {
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn("Youtube Provider: unknow error", error);
|
||||
this.#showNotification(this.#i18n("subtitle_load_failed"));
|
||||
} finally {
|
||||
this.#isBusy = false;
|
||||
}
|
||||
@@ -386,11 +399,9 @@ class YouTubeCaptionProvider {
|
||||
}
|
||||
|
||||
#startManager() {
|
||||
if (this.#enabled || this.#managerInstance) {
|
||||
if (this.#managerInstance) {
|
||||
return;
|
||||
}
|
||||
this.#enabled = true;
|
||||
this.#toggleButton?.replaceChildren(createLogoSVG({ isSelected: true }));
|
||||
|
||||
const videoEl = document.querySelector(VIDEO_SELECT);
|
||||
if (!videoEl) {
|
||||
@@ -400,8 +411,8 @@ class YouTubeCaptionProvider {
|
||||
|
||||
const videoId = this.#getVideoId();
|
||||
if (!this.#subtitles?.length || this.#videoId !== videoId) {
|
||||
// todo: 等待并给出用户提示
|
||||
logger.info("Youtube Provider: No subtitles");
|
||||
this.#showNotification(this.#i18n("try_get_subtitle_data"));
|
||||
this.#doubleClick();
|
||||
return;
|
||||
}
|
||||
@@ -416,26 +427,24 @@ class YouTubeCaptionProvider {
|
||||
});
|
||||
this.#managerInstance.start();
|
||||
|
||||
this.#showNotification(this.#i18n("subtitle_load_succeed"));
|
||||
|
||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||
ytCaption && (ytCaption.style.display = "none");
|
||||
}
|
||||
|
||||
#destroyManager() {
|
||||
if (!this.#enabled) {
|
||||
if (!this.#managerInstance) {
|
||||
return;
|
||||
}
|
||||
this.#enabled = false;
|
||||
this.#toggleButton?.replaceChildren(createLogoSVG());
|
||||
|
||||
logger.info("Youtube Provider: Destroying manager...");
|
||||
|
||||
this.#managerInstance.destroy();
|
||||
this.#managerInstance = null;
|
||||
|
||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||
ytCaption && (ytCaption.style.display = "block");
|
||||
|
||||
if (this.#managerInstance) {
|
||||
this.#managerInstance.destroy();
|
||||
this.#managerInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
#formatSubtitles(flatEvents, lang) {
|
||||
@@ -790,6 +799,45 @@ class YouTubeCaptionProvider {
|
||||
|
||||
logger.info("Youtube Provider: All subtitle chunks processed.");
|
||||
}
|
||||
|
||||
#createNotificationElement() {
|
||||
const notificationEl = document.createElement("div");
|
||||
notificationEl.className = "kiss-notification";
|
||||
Object.assign(notificationEl.style, {
|
||||
position: "absolute",
|
||||
top: "40%",
|
||||
left: "50%",
|
||||
transform: "translateX(-50%)",
|
||||
background: "rgba(0,0,0,0.7)",
|
||||
color: "red",
|
||||
padding: "0.5em 1em",
|
||||
borderRadius: "4px",
|
||||
zIndex: "2147483647",
|
||||
opacity: "0",
|
||||
transition: "opacity 0.3s ease-in-out",
|
||||
pointerEvents: "none",
|
||||
fontSize: "2em",
|
||||
width: "50%",
|
||||
textAlign: "center",
|
||||
});
|
||||
|
||||
const videoEl = document.querySelector(VIDEO_SELECT);
|
||||
const videoContainer = videoEl?.parentElement?.parentElement;
|
||||
if (videoContainer) {
|
||||
videoContainer.appendChild(notificationEl);
|
||||
this.#notificationEl = notificationEl;
|
||||
}
|
||||
}
|
||||
|
||||
#showNotification(message, duration = 3000) {
|
||||
if (!this.#notificationEl) this.#createNotificationElement();
|
||||
this.#notificationEl.textContent = message;
|
||||
this.#notificationEl.style.opacity = "1";
|
||||
clearTimeout(this.#notificationTimeout);
|
||||
this.#notificationTimeout = setTimeout(() => {
|
||||
this.#notificationEl.style.opacity = "0";
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
||||
export const YouTubeInitializer = (() => {
|
||||
|
||||
@@ -34,6 +34,7 @@ export function runSubtitle({ href, setting }) {
|
||||
...subtitleSetting,
|
||||
apiSetting,
|
||||
segApiSetting,
|
||||
uiLang: setting.uiLang,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user