feat: add notice for subtitle
This commit is contained in:
@@ -1598,4 +1598,31 @@ export const I18N = {
|
|||||||
en: `Default styles reference:`,
|
en: `Default styles reference:`,
|
||||||
zh_TW: `認樣式參考:`,
|
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 { sleep } from "../libs/utils.js";
|
||||||
import { createLogoSVG } from "../libs/svg.js";
|
import { createLogoSVG } from "../libs/svg.js";
|
||||||
import { randomBetween } from "../libs/utils.js";
|
import { randomBetween } from "../libs/utils.js";
|
||||||
|
import { i18n } from "../config";
|
||||||
|
|
||||||
const VIDEO_SELECT = "#container video";
|
const VIDEO_SELECT = "#container video";
|
||||||
const CONTORLS_SELECT = ".ytp-right-controls";
|
const CONTORLS_SELECT = ".ytp-right-controls";
|
||||||
@@ -25,9 +26,13 @@ class YouTubeCaptionProvider {
|
|||||||
#ytControls = null;
|
#ytControls = null;
|
||||||
#isBusy = false;
|
#isBusy = false;
|
||||||
#fromLang = "auto";
|
#fromLang = "auto";
|
||||||
|
#notificationEl = null;
|
||||||
|
#notificationTimeout = null;
|
||||||
|
#i18n = () => "";
|
||||||
|
|
||||||
constructor(setting = {}) {
|
constructor(setting = {}) {
|
||||||
this.#setting = setting;
|
this.#setting = setting;
|
||||||
|
this.#i18n = i18n(setting.uiLang || "zh");
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
@@ -45,7 +50,6 @@ class YouTubeCaptionProvider {
|
|||||||
if (this.#toggleButton) {
|
if (this.#toggleButton) {
|
||||||
this.#toggleButton.style.opacity = "0.5";
|
this.#toggleButton.style.opacity = "0.5";
|
||||||
}
|
}
|
||||||
this.#destroyManager();
|
|
||||||
this.#doubleClick();
|
this.#doubleClick();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
@@ -113,14 +117,20 @@ class YouTubeCaptionProvider {
|
|||||||
toggleButton.onclick = () => {
|
toggleButton.onclick = () => {
|
||||||
if (this.#isBusy) {
|
if (this.#isBusy) {
|
||||||
logger.info(`Youtube Provider: It's budy now...`);
|
logger.info(`Youtube Provider: It's budy now...`);
|
||||||
return;
|
this.#showNotification(this.#i18n("subtitle_data_processing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.#enabled) {
|
if (!this.#enabled) {
|
||||||
logger.info(`Youtube Provider: Feature toggled ON.`);
|
logger.info(`Youtube Provider: Feature toggled ON.`);
|
||||||
|
this.#enabled = true;
|
||||||
|
this.#toggleButton?.replaceChildren(
|
||||||
|
createLogoSVG({ isSelected: true })
|
||||||
|
);
|
||||||
this.#startManager();
|
this.#startManager();
|
||||||
} else {
|
} else {
|
||||||
logger.info(`Youtube Provider: Feature toggled OFF.`);
|
logger.info(`Youtube Provider: Feature toggled OFF.`);
|
||||||
|
this.#enabled = false;
|
||||||
|
this.#toggleButton?.replaceChildren(createLogoSVG());
|
||||||
this.#destroyManager();
|
this.#destroyManager();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -245,7 +255,7 @@ class YouTubeCaptionProvider {
|
|||||||
logger.info("Youtube Provider is busy...");
|
logger.info("Youtube Provider is busy...");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#isBusy = true; // todo: 提示用户等待中
|
this.#isBusy = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const videoId = this.#getVideoId();
|
const videoId = this.#getVideoId();
|
||||||
@@ -265,6 +275,8 @@ class YouTubeCaptionProvider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.#showNotification(this.#i18n("starting_to_process_subtitle"));
|
||||||
|
|
||||||
const captionTracks = await this.#getCaptionTracks(videoId);
|
const captionTracks = await this.#getCaptionTracks(videoId);
|
||||||
const captionTrack = this.#findCaptionTrack(captionTracks);
|
const captionTrack = this.#findCaptionTrack(captionTracks);
|
||||||
if (!captionTrack) {
|
if (!captionTrack) {
|
||||||
@@ -365,6 +377,7 @@ class YouTubeCaptionProvider {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.warn("Youtube Provider: unknow error", error);
|
logger.warn("Youtube Provider: unknow error", error);
|
||||||
|
this.#showNotification(this.#i18n("subtitle_load_failed"));
|
||||||
} finally {
|
} finally {
|
||||||
this.#isBusy = false;
|
this.#isBusy = false;
|
||||||
}
|
}
|
||||||
@@ -386,11 +399,9 @@ class YouTubeCaptionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#startManager() {
|
#startManager() {
|
||||||
if (this.#enabled || this.#managerInstance) {
|
if (this.#managerInstance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#enabled = true;
|
|
||||||
this.#toggleButton?.replaceChildren(createLogoSVG({ isSelected: true }));
|
|
||||||
|
|
||||||
const videoEl = document.querySelector(VIDEO_SELECT);
|
const videoEl = document.querySelector(VIDEO_SELECT);
|
||||||
if (!videoEl) {
|
if (!videoEl) {
|
||||||
@@ -400,8 +411,8 @@ class YouTubeCaptionProvider {
|
|||||||
|
|
||||||
const videoId = this.#getVideoId();
|
const videoId = this.#getVideoId();
|
||||||
if (!this.#subtitles?.length || this.#videoId !== videoId) {
|
if (!this.#subtitles?.length || this.#videoId !== videoId) {
|
||||||
// todo: 等待并给出用户提示
|
|
||||||
logger.info("Youtube Provider: No subtitles");
|
logger.info("Youtube Provider: No subtitles");
|
||||||
|
this.#showNotification(this.#i18n("try_get_subtitle_data"));
|
||||||
this.#doubleClick();
|
this.#doubleClick();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -416,26 +427,24 @@ class YouTubeCaptionProvider {
|
|||||||
});
|
});
|
||||||
this.#managerInstance.start();
|
this.#managerInstance.start();
|
||||||
|
|
||||||
|
this.#showNotification(this.#i18n("subtitle_load_succeed"));
|
||||||
|
|
||||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||||
ytCaption && (ytCaption.style.display = "none");
|
ytCaption && (ytCaption.style.display = "none");
|
||||||
}
|
}
|
||||||
|
|
||||||
#destroyManager() {
|
#destroyManager() {
|
||||||
if (!this.#enabled) {
|
if (!this.#managerInstance) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#enabled = false;
|
|
||||||
this.#toggleButton?.replaceChildren(createLogoSVG());
|
|
||||||
|
|
||||||
logger.info("Youtube Provider: Destroying manager...");
|
logger.info("Youtube Provider: Destroying manager...");
|
||||||
|
|
||||||
|
this.#managerInstance.destroy();
|
||||||
|
this.#managerInstance = null;
|
||||||
|
|
||||||
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
const ytCaption = document.querySelector(YT_CAPTION_SELECT);
|
||||||
ytCaption && (ytCaption.style.display = "block");
|
ytCaption && (ytCaption.style.display = "block");
|
||||||
|
|
||||||
if (this.#managerInstance) {
|
|
||||||
this.#managerInstance.destroy();
|
|
||||||
this.#managerInstance = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#formatSubtitles(flatEvents, lang) {
|
#formatSubtitles(flatEvents, lang) {
|
||||||
@@ -790,6 +799,45 @@ class YouTubeCaptionProvider {
|
|||||||
|
|
||||||
logger.info("Youtube Provider: All subtitle chunks processed.");
|
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 = (() => {
|
export const YouTubeInitializer = (() => {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export function runSubtitle({ href, setting }) {
|
|||||||
...subtitleSetting,
|
...subtitleSetting,
|
||||||
apiSetting,
|
apiSetting,
|
||||||
segApiSetting,
|
segApiSetting,
|
||||||
|
uiLang: setting.uiLang,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user