77// @name:ru [VOT] - Закадровый перевод видео
88// @name:zh [VOT] - 画外音视频翻译
99// @namespace vot
10- // @version 1.11.2.4
10+ // @version 1.11.2.5
1111// @author Toil, SashaXser, MrSoczekXD, mynovelhost, sodapng
1212// @description A small extension that adds a Yandex Browser video translation to other browsers
1313// @description:de Eine kleine Erweiterung, die eine Voice-over-Übersetzung von Videos aus dem Yandex-Browser zu anderen Browsern hinzufügt
@@ -12172,7 +12172,7 @@ String.raw`\b(?:-1|0):[a-f0-9]{64}\b`
1217212172 }
1217312173 }
1217412174 }
12175- async getVideoData() {
12175+ async getVideoData({ allowLanguageDetection = false } = {} ) {
1217612176 const {
1217712177 duration,
1217812178 url,
@@ -12196,31 +12196,32 @@ String.raw`\b(?:-1|0):[a-f0-9]{64}\b`
1219612196 const normalizedPossibleLanguage = normalizeToRequestLang(possibleLanguage);
1219712197 let detectedLanguage = "auto";
1219812198 if (!isStream) {
12199- detectedLanguage = userOverrideLanguage ?? normalizedPossibleLanguage ?? cachedDetectedLanguage ?? "auto";
12200- if (!userOverrideLanguage && !normalizedPossibleLanguage && !cachedDetectedLanguage) {
12201- const text = buildDetectText(title, description);
12202- if (text.length >= MIN_DETECT_TEXT_LENGTH) {
12203- const language = await this.detectLanguageSingleFlight(videoId, text);
12204- if (language) {
12205- detectedLanguage = language;
12206- this.setDetectedLanguageCache(videoId, language);
12199+ detectedLanguage = userOverrideLanguage ?? cachedDetectedLanguage ?? "auto";
12200+ if (allowLanguageDetection && !userOverrideLanguage) {
12201+ const hostDetectedLanguage = resolveHostDetectedLanguage(
12202+ this.videoHandler.site.host
12203+ );
12204+ if (hostDetectedLanguage) {
12205+ detectedLanguage = hostDetectedLanguage;
12206+ this.setDetectedLanguageCache(videoId, hostDetectedLanguage);
12207+ } else if (normalizedPossibleLanguage) {
12208+ detectedLanguage = normalizedPossibleLanguage;
12209+ this.setDetectedLanguageCache(videoId, normalizedPossibleLanguage);
12210+ } else if (!cachedDetectedLanguage) {
12211+ const text = buildDetectText(title, description);
12212+ if (text.length >= MIN_DETECT_TEXT_LENGTH) {
12213+ const language = await this.detectLanguageSingleFlight(videoId, text);
12214+ if (language) {
12215+ detectedLanguage = language;
12216+ this.setDetectedLanguageCache(videoId, language);
12217+ }
12218+ } else if (text) {
12219+ debug.log(
12220+ `Skipping language detection: text too short (${text.length} < ${MIN_DETECT_TEXT_LENGTH})`
12221+ );
1220712222 }
12208- } else if (text) {
12209- debug.log(
12210- `Skipping language detection: text too short (${text.length} < ${MIN_DETECT_TEXT_LENGTH})`
12211- );
1221212223 }
1221312224 }
12214- if (!userOverrideLanguage && normalizedPossibleLanguage) {
12215- this.setDetectedLanguageCache(videoId, normalizedPossibleLanguage);
12216- }
12217- const hostDetectedLanguage = resolveHostDetectedLanguage(
12218- this.videoHandler.site.host
12219- );
12220- if (hostDetectedLanguage && !userOverrideLanguage) {
12221- detectedLanguage = hostDetectedLanguage;
12222- this.setDetectedLanguageCache(videoId, hostDetectedLanguage);
12223- }
1222412225 }
1222512226 const videoData = {
1222612227 translationHelp,
@@ -12237,7 +12238,7 @@ videoId,
1223712238 description,
1223812239 downloadTitle: localizedTitle ?? title ?? videoId
1223912240 };
12240- if (sharedLanguageState.lastLoggedDetectedLanguage !== detectedLanguage) {
12241+ if (detectedLanguage !== "auto" && sharedLanguageState.lastLoggedDetectedLanguage !== detectedLanguage) {
1224112242 console.log("[VOT] Detected language:", detectedLanguage);
1224212243 sharedLanguageState.lastLoggedDetectedLanguage = detectedLanguage;
1224312244 }
@@ -12313,7 +12314,7 @@ syncVideoVolumeSlider() {
1231312314 const normalizedFrom = normalizeToRequestLang(from) ?? "auto";
1231412315 const langPairLogKey = `${normalizedFrom}->${to}`;
1231512316 const sharedLanguageState = getSharedLanguageState(videoData.videoId);
12316- if (sharedLanguageState.lastLoggedLangPair !== langPairLogKey) {
12317+ if (normalizedFrom !== "auto" && sharedLanguageState.lastLoggedLangPair !== langPairLogKey) {
1231712318 console.log(`[VOT] Set translation from ${normalizedFrom} to ${to}`);
1231812319 sharedLanguageState.lastLoggedLangPair = langPairLogKey;
1231912320 }
@@ -12701,7 +12702,6 @@ showed = false;
1270112702 offsetX;
1270212703 offsetY;
1270312704 _hidden;
12704- autoLayout;
1270512705 pageWidth;
1270612706 pageHeight;
1270712707 globalOffsetX;
@@ -12728,7 +12728,6 @@ tooltipId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.ran
1272812728 offset = 4,
1272912729 maxWidth = void 0,
1273012730 hidden = false,
12731- autoLayout = true,
1273212731 backgroundColor = void 0,
1273312732 borderRadius = void 0,
1273412733 bordered = true,
@@ -12748,7 +12747,6 @@ tooltipId = typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.ran
1274812747 this.offsetY = offset.y;
1274912748 }
1275012749 this._hidden = hidden;
12751- this.autoLayout = autoLayout;
1275212750 this.trigger = Tooltip.validateTrigger(trigger) ? trigger : "hover";
1275312751 this.position = Tooltip.validatePos(position2) ? position2 : "top";
1275412752 this.preferredPosition = this.position;
@@ -12968,15 +12966,15 @@ updateMount({
1296812966 if (!this.container) {
1296912967 return this;
1297012968 }
12971- const { top, left } = this.calcPos(this.autoLayout, this. preferredPosition);
12969+ const { top, left } = this.calcPos(this.preferredPosition);
1297212970 const availableWidth = Math.max(0, this.pageWidth - this.offsetX * 2);
1297312971 const maxWidth = clamp$2(this.maxWidth ?? availableWidth, 0, availableWidth);
1297412972 this.container.style.transform = `translate(${left}px, ${top}px)`;
1297512973 this.container.dataset.position = this.position;
1297612974 this.container.style.maxWidth = `${maxWidth}px`;
1297712975 return this;
1297812976 }
12979- calcPos(autoLayout = true, position2 = this.preferredPosition) {
12977+ calcPos(position2 = this.preferredPosition) {
1298012978 if (!this.container) {
1298112979 return { top: 0, left: 0 };
1298212980 }
@@ -12995,89 +12993,38 @@ updateMount({
1299512993 const right = anchorRight - this.globalOffsetX;
1299612994 const top = anchorTop - this.globalOffsetY;
1299712995 const bottom = anchorBottom - this.globalOffsetY;
12998- let resolvedPosition = position2;
12999- if (autoLayout) {
13000- switch (position2) {
13001- case "top": {
13002- const pTop = clamp$2(top - height - this.offsetY, 0, this.pageHeight);
13003- if (pTop + this.offsetY < height) {
13004- resolvedPosition = "bottom";
13005- }
13006- break;
13007- }
13008- case "right": {
13009- const pLeft = clamp$2(right + this.offsetX, 0, this.pageWidth - width);
13010- if (pLeft + width > this.pageWidth - this.offsetX) {
13011- resolvedPosition = "left";
13012- }
13013- break;
13014- }
13015- case "bottom": {
13016- const pTop = clamp$2(
13017- bottom + this.offsetY,
13018- 0,
13019- this.pageHeight - height
13020- );
13021- if (pTop + height > this.pageHeight - this.offsetY) {
13022- resolvedPosition = "top";
13023- }
13024- break;
13025- }
13026- case "left": {
13027- const pLeft = Math.max(0, left - width - this.offsetX);
13028- if (pLeft + width > left - this.offsetX) {
13029- resolvedPosition = "right";
13030- }
13031- break;
13032- }
13033- }
13034- }
1303512996 let coords;
13036- switch (resolvedPosition) {
12997+ const centeredLeft = left - width / 2 + anchorWidth / 2;
12998+ const centeredTop = top + (anchorHeight - height) / 2;
12999+ switch (position2) {
1303713000 case "top":
1303813001 coords = {
13039- top: clamp$2(top - height - this.offsetY, 0, this.pageHeight),
13040- left: clamp$2(
13041- left - width / 2 + anchorWidth / 2,
13042- this.offsetX,
13043- this.pageWidth - width - this.offsetX
13044- )
13002+ top: top - height - this.offsetY,
13003+ left: centeredLeft
1304513004 };
1304613005 break;
1304713006 case "right":
1304813007 coords = {
13049- top: clamp$2(
13050- top + (anchorHeight - height) / 2,
13051- this.offsetY,
13052- this.pageHeight - height - this.offsetY
13053- ),
13054- left: clamp$2(right + this.offsetX, 0, this.pageWidth - width)
13008+ top: centeredTop,
13009+ left: right + this.offsetX
1305513010 };
1305613011 break;
1305713012 case "bottom":
1305813013 coords = {
13059- top: clamp$2(bottom + this.offsetY, 0, this.pageHeight - height),
13060- left: clamp$2(
13061- left - width / 2 + anchorWidth / 2,
13062- this.offsetX,
13063- this.pageWidth - width - this.offsetX
13064- )
13014+ top: bottom + this.offsetY,
13015+ left: centeredLeft
1306513016 };
1306613017 break;
1306713018 case "left":
1306813019 coords = {
13069- top: clamp$2(
13070- top + (anchorHeight - height) / 2,
13071- this.offsetY,
13072- this.pageHeight - height - this.offsetY
13073- ),
13074- left: Math.max(0, left - width - this.offsetX)
13020+ top: centeredTop,
13021+ left: left - width - this.offsetX
1307513022 };
1307613023 break;
1307713024 default:
1307813025 coords = { top: 0, left: 0 };
1307913026 }
13080- this.position = resolvedPosition ;
13027+ this.position = position2 ;
1308113028 return coords;
1308213029 }
1308313030 destroy(instant = false) {
@@ -16788,8 +16735,6 @@ updateMount(nextMount) {
1678816735 target: this.votButton.translateButton,
1678916736 content: localizationProvider.get("translateVideo"),
1679016737 position: this.votButton.tooltipPos,
16791-
16792- autoLayout: false,
1679316738 hidden: direction === "row",
1679416739 bordered: false,
1679516740 parentElement: this.votOverlayPortal,
@@ -21484,7 +21429,7 @@ votSettingsView;
2148421429 }).addEventListener("change:autoTranslate", async (checked) => {
2148521430 const videoHandler = this.videoHandler;
2148621431 if (checked && videoHandler && !videoHandler.hasActiveSource()) {
21487- await this.handleTranslationBtnClick();
21432+ await this.handleTranslationBtnClick({ isAutoTrigger: true } );
2148821433 }
2148921434 }).addEventListener("change:autoSubtitles", async (checked) => {
2149021435 if (!checked || !this.videoHandler?.videoData?.videoId) {
@@ -21741,7 +21686,7 @@ votSettingsView;
2174121686 }
2174221687 return this;
2174321688 }
21744- async handleTranslationBtnClick() {
21689+ async handleTranslationBtnClick(options = {} ) {
2174521690 if (!this.votOverlayView?.isInitialized()) {
2174621691 throw new Error("[VOT] OverlayView isn't initialized");
2174721692 }
@@ -21780,6 +21725,10 @@ votSettingsView;
2178021725 this.transformBtn("none", localizationProvider.get("translateVideo"));
2178121726 return this;
2178221727 }
21728+ if (options.isAutoTrigger && err instanceof VOTLocalizedError && err.unlocalizedMessage === "VOTDisableFromYourLang") {
21729+ this.transformBtn("none", localizationProvider.get("translateVideo"));
21730+ return this;
21731+ }
2178321732 console.error("[VOT]", err);
2178421733 if (!(err instanceof Error)) {
2178521734 this.transformBtn("error", String(err));
@@ -21791,20 +21740,18 @@ votSettingsView;
2179121740 return this;
2179221741 }
2179321742 async getVideoDataForTranslation(videoHandler) {
21743+ videoHandler.videoData = await videoHandler.getVideoData({
21744+ allowLanguageDetection: true
21745+ });
2179421746 if (!videoHandler.videoData?.videoId) {
2179521747 throw new VOTLocalizedError("VOTNoVideoIDFound");
2179621748 }
21797- if (this.shouldRefreshVideoDataBeforeTranslation(videoHandler)) {
21798- videoHandler.videoData = await videoHandler.getVideoData();
21799- }
21800- if (!videoHandler.videoData?.videoId) {
21801- throw new VOTLocalizedError("VOTNoVideoIDFound");
21802- }
21749+ videoHandler.setSelectMenuValues(
21750+ videoHandler.videoData.detectedLanguage,
21751+ videoHandler.videoData.responseLanguage
21752+ );
2180321753 return videoHandler.videoData;
2180421754 }
21805- shouldRefreshVideoDataBeforeTranslation(videoHandler) {
21806- return videoHandler.site.host === "vk" && videoHandler.site.additionalData === "clips" || videoHandler.site.host === "douyin";
21807- }
2180821755 isAbortError(error2) {
2180921756 return error2 instanceof Error && error2.name === "AbortError";
2181021757 }
@@ -23117,7 +23064,9 @@ tag: `VOTtranslationFailed_${videoId || "unknown"}`,
2311723064 `[VOT] Audio track language changed to ${currentLanguage}, triggering auto-translation`
2311823065 );
2311923066 try {
23120- await self.uiManager.handleTranslationBtnClick();
23067+ await self.uiManager.handleTranslationBtnClick({
23068+ isAutoTrigger: true
23069+ });
2312123070 } catch (error2) {
2312223071 debug.log(
2312323072 "[VOT] Failed to trigger auto-translation on audio track change:",
@@ -25723,7 +25672,7 @@ getEventContainer() {
2572325672 }
2572425673async runAutoTranslate() {
2572525674 await this.videoManager.videoValidator();
25726- await this.uiManager.handleTranslationBtnClick();
25675+ await this.uiManager.handleTranslationBtnClick({ isAutoTrigger: true } );
2572725676 }
2572825677getAudioContext() {
2572925678 if (this.audioContext) return this.audioContext;
@@ -25943,8 +25892,8 @@ syncVolumeWrapper(fromType, newVolume) {
2594325892 this.setVideoVolume(nextVideo / 100);
2594425893 }
2594525894 }
25946- getVideoData() {
25947- return this.videoManager.getVideoData();
25895+ getVideoData(options ) {
25896+ return this.videoManager.getVideoData(options );
2594825897 }
2594925898videoValidator() {
2595025899 return this.videoManager.videoValidator();
0 commit comments