From 35717d12f623a3533d64e54e8fceb2be94e17b88 Mon Sep 17 00:00:00 2001 From: pooky-programs Date: Sat, 22 Jun 2024 23:29:57 -0500 Subject: [PATCH 01/51] Enter confirms subtitle track selection --- .../src/ui/components/VideoDataSyncDialog.tsx | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/extension/src/ui/components/VideoDataSyncDialog.tsx b/extension/src/ui/components/VideoDataSyncDialog.tsx index a6dfed84f..ad08c8318 100644 --- a/extension/src/ui/components/VideoDataSyncDialog.tsx +++ b/extension/src/ui/components/VideoDataSyncDialog.tsx @@ -15,7 +15,7 @@ import makeStyles from '@material-ui/styles/makeStyles'; import Switch from '@material-ui/core/Switch'; import LabelWithHoverEffect from '@project/common/components/LabelWithHoverEffect'; import { ConfirmedVideoDataSubtitleTrack, VideoDataSubtitleTrack } from '@project/common'; -import React, { useEffect, useState } from 'react'; +import React, { useRef, useEffect, useState, FormEvent } from 'react'; import { useTranslation } from 'react-i18next'; const createClasses = makeStyles((theme) => ({ @@ -82,6 +82,8 @@ export default function VideoDataSyncDialog({ const [shouldRememberTrackChoices, setShouldRememberTrackChoices] = React.useState(false); const trimmedName = name.trim(); const classes = createClasses(); + const [isDropdownActive, setIsDropdownActive] = useState(false); + const okButtonRef = useRef(null); useEffect(() => { if (open) { @@ -130,7 +132,23 @@ export default function VideoDataSyncDialog({ }); }, [suggestedName, selectedSubtitles, subtitles]); - function handleOkButtonClick() { + useEffect(() => { + function handleKeyDown(event: KeyboardEvent) { + if (!isDropdownActive && okButtonRef.current && (event.key === 'Enter' || event.key === 'NumpadEnter')) { + event.preventDefault(); + event.stopImmediatePropagation(); + okButtonRef.current.click(); + } + } + if (open) { + document.addEventListener('keydown', handleKeyDown); + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + } + }, [open, isDropdownActive]); + + function handleSubmit() { const selectedSubtitleTracks: ConfirmedVideoDataSubtitleTrack[] = allSelectedSubtitleTracks(); onConfirm(selectedSubtitleTracks, shouldRememberTrackChoices); } @@ -186,6 +204,8 @@ export default function VideoDataSyncDialog({ return newSelectedSubtitles; }) } + onFocus={() => setIsDropdownActive(true)} + onBlur={() => setIsDropdownActive(false)} > {subtitles.map((subtitle) => ( @@ -264,7 +284,7 @@ export default function VideoDataSyncDialog({ - From c691714288f79542dc86c1a31b4cce4f28e3d92d Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Mon, 24 Jun 2024 06:57:22 -0700 Subject: [PATCH 02/51] Fix re-record playing audio at the same time --- common/components/AudioField.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/components/AudioField.tsx b/common/components/AudioField.tsx index 77f548e74..3eec22224 100644 --- a/common/components/AudioField.tsx +++ b/common/components/AudioField.tsx @@ -57,7 +57,14 @@ export default function AudioField({ audioClip, onPlayAudio, onRerecord }: Props audioActionElement = ( - + { + e.stopPropagation(); + onRerecord(); + }} + edge="end" + > From 2062c94fe7e6c210b7957dc6e3ddba25842aacf1 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Mon, 24 Jun 2024 07:00:20 -0700 Subject: [PATCH 03/51] Extension version 1.3.2 --- extension/src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/src/manifest.json b/extension/src/manifest.json index 5012bd5d8..8ffe00868 100755 --- a/extension/src/manifest.json +++ b/extension/src/manifest.json @@ -1,7 +1,7 @@ { "name": "asbplayer: Language-learning with subtitles", "description": "__MSG_extensionDescription__", - "version": "1.3.1", + "version": "1.3.2", "manifest_version": 3, "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxmdAa3ymqAjLms43ympXqtyuJnC2bSYh70+5ZZmtyx/MsnGhTEdfbqtsp3BKxHbv0rPd49+Joacm1Shik5/mCppZ0h4I4ISMm983X01H6p/hfAzQYAcnvw/ZQNHAv1QgY9JiuyTBirCDoYB50Fxol/kI/0EviYXuX83KoYpjB0VGP/ssY9ocT//fQUbRmeLDJnciry8y6MduWXHzseOP99axQIjeVsNTE30L4fRN+ppX3aOkG/RFJNx0eI02qbLul3qw5dUuBK5GgMbYftwjHnDoOegnZYFr1sxRO1zsgmxdp/6du75RiDPRJOkPCz2GTrw4CX2FCywbDZlqaIpwqQIDAQAB", "default_locale": "en", From f14bd91fa85f1fb65cae6bda604613b0e7ae82fd Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Mon, 24 Jun 2024 22:47:21 -0700 Subject: [PATCH 04/51] Release Firefox extension 1.3.2 --- client/public/firefox-extension-updates.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/public/firefox-extension-updates.json b/client/public/firefox-extension-updates.json index a6196479f..157bb7a19 100644 --- a/client/public/firefox-extension-updates.json +++ b/client/public/firefox-extension-updates.json @@ -5,6 +5,10 @@ { "version": "1.3.1", "update_link": "https://github.com/killergerbah/asbplayer/releases/download/v1.3.1/asbplayer-extension-1.3.1-firefox.xpi" + }, + { + "version": "1.3.2", + "update_link": "https://github.com/killergerbah/asbplayer/releases/download/v1.3.2/asbplayer-extension-1.3.2-firefox.xpi" } ] } From 01cf6d0051f3182de8ab5678eba1cdd9725f0f67 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sat, 29 Jun 2024 08:28:26 -0700 Subject: [PATCH 05/51] Fix unext video name detection --- extension/src/pages/unext-page.ts | 99 +++++++++++++++++-------------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/extension/src/pages/unext-page.ts b/extension/src/pages/unext-page.ts index 1c36aea1e..887b4e48e 100644 --- a/extension/src/pages/unext-page.ts +++ b/extension/src/pages/unext-page.ts @@ -1,55 +1,68 @@ -import { poll } from './util'; +setTimeout(() => { + let baseName: string | undefined; -document.addEventListener('asbplayer-get-synced-data', async () => { - let basename = ''; + const originalParse = JSON.parse; + JSON.parse = function () { + // @ts-ignore + const value = originalParse.apply(this, arguments); - await poll(() => { - const titleNodes = document.querySelectorAll('div[class^="Header__TitleContainer"]'); - const segments: string[] = []; - const nodes: Node[] = []; + if (typeof value?.data?.webfront_title_stage?.titleName === 'string') { + baseName = value.data.webfront_title_stage.titleName; - for (let i = 0; i < titleNodes.length; ++i) { - nodes.push(titleNodes[i]); - } - - while (nodes.length > 0) { - const node = nodes.shift(); + if (typeof value.data.webfront_title_stage.episode?.id === 'string') { + const episodeId = value.data.webfront_title_stage.episode.id; - if (node === undefined) { - break; - } + if ( + typeof value.data.webfront_title_titleEpisodes?.episodes === 'object' && + Array.isArray(value.data.webfront_title_titleEpisodes.episodes) + ) { + for (const obj of value.data.webfront_title_titleEpisodes.episodes) { + if (obj.id === episodeId) { + if (typeof obj.displayNo === 'string') { + baseName = `${baseName} ${obj.displayNo}`; + } - if (node.childNodes.length === 0) { - const textContent = node.textContent; + if (typeof obj.episodeName === 'string') { + baseName = `${baseName} ${obj.episodeName}`; + } - if (textContent !== null) { - segments.push(textContent); + break; + } + } } - - continue; - } - - for (let i = 0; i < node.childNodes.length; ++i) { - const childNode = node.childNodes[i]; - nodes.push(childNode); } } - if (segments.length > 0) { - basename = segments.join(' '); - return true; - } + return value; + }; + + document.addEventListener( + 'asbplayer-get-synced-data', + () => { + if (!baseName) { + document.dispatchEvent( + new CustomEvent('asbplayer-synced-data', { + detail: { + error: '', + basename: '', + subtitles: [], + }, + }) + ); + return; + } - return false; - }); - - document.dispatchEvent( - new CustomEvent('asbplayer-synced-data', { - detail: { - error: '', - basename: basename, - subtitles: [], - }, - }) + document.dispatchEvent( + new CustomEvent('asbplayer-synced-data', { + detail: { + error: '', + basename: baseName, + subtitles: [], + }, + }) + ); + baseName = undefined; + }, + false ); -}); +}, 0); From be4c1d570d8917237c65e6ad44a59b23c1aa8a18 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sat, 29 Jun 2024 08:35:02 -0700 Subject: [PATCH 06/51] Prevent lone surrogates in file names from breaking AnkiConnect --- common/anki/anki.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/anki/anki.ts b/common/anki/anki.ts index 88c2d0859..b6022efe5 100755 --- a/common/anki/anki.ts +++ b/common/anki/anki.ts @@ -5,6 +5,12 @@ import { AnkiSettings } from '@project/common/settings'; import sanitize from 'sanitize-filename'; import { extractText, sourceString } from '@project/common/util'; +declare global { + interface String { + toWellFormed?: () => string; + } +} + const ankiQuerySpecialCharacters = ['"', '*', '_', '\\', ':']; const alphaNumericCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; @@ -384,6 +390,10 @@ export class Anki { } private _sanitizeFileName(name: string) { + if (typeof name.toWellFormed === 'function') { + name = name.toWellFormed(); + } + return sanitize(name, { replacement: '_' }); } From 125003cbe3a614382465aee336f2f8311cbe59ab Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Wed, 3 Jul 2024 22:53:27 -0700 Subject: [PATCH 07/51] Update loc strings --- client/public/extension.json | 2 +- common/locales/ja.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/public/extension.json b/client/public/extension.json index 084230a7c..6ef55574a 100644 --- a/client/public/extension.json +++ b/client/public/extension.json @@ -17,7 +17,7 @@ { "code": "ja", "url": "/locales/ja.json", - "version": 7 + "version": 8 }, { "code": "de", diff --git a/common/locales/ja.json b/common/locales/ja.json index 32a670d16..d27018e6a 100644 --- a/common/locales/ja.json +++ b/common/locales/ja.json @@ -244,9 +244,9 @@ "settings": { "activeProfile": "設定プロファイル", "defaultProfile": "デフォルト", - "newProfile": "新しい設定プロフィールを作成", - "profileName": "設定プロフィール名", - "enterProfileName": "設定プロフィール名を入力", + "newProfile": "新しい設定プロファイルを作成", + "profileName": "設定プロファイル名", + "enterProfileName": "設定プロファイル名を入力", "profileLimitReached": "設定プロファイルの上限数に達しました", "addCustomCss": "カスタムCSSを追加", "addCustomField": "カスタムフィールドを追加", From c5abd4b0e0789f1181b55ec0d862924e0b3ee03f Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Wed, 3 Jul 2024 23:00:21 -0700 Subject: [PATCH 08/51] Update extension references in app to latest version --- client/public/extension.json | 4 ++-- common/app/components/App.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/public/extension.json b/client/public/extension.json index 6ef55574a..f23d234ab 100644 --- a/client/public/extension.json +++ b/client/public/extension.json @@ -1,7 +1,7 @@ { "latest": { - "version": "1.2.0", - "url": "https://github.com/killergerbah/asbplayer/releases/tag/v1.2.0" + "version": "1.3.2", + "url": "https://github.com/killergerbah/asbplayer/releases/tag/v1.3.2" }, "languages": [ { diff --git a/common/app/components/App.tsx b/common/app/components/App.tsx index 71cb7fb0a..c077bcaff 100755 --- a/common/app/components/App.tsx +++ b/common/app/components/App.tsx @@ -53,7 +53,7 @@ import { useAnki } from '../hooks/use-anki'; import { usePlaybackPreferences } from '../hooks/use-playback-preferences'; import { MiningContext } from '../services/mining-context'; -const latestExtensionVersion = '1.2.0'; +const latestExtensionVersion = '1.3.2'; const extensionUrl = 'https://chromewebstore.google.com/detail/asbplayer-language-learni/hkledmpjpaehamkiehglnbelcpdflcab'; const mp3WorkerFactory = () => new Worker(new URL('../../audio-clip/mp3-encoder-worker.ts', import.meta.url)); From 9dac83f214621d085eb4b10e5b3b1b0091cc4055 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Wed, 3 Jul 2024 23:36:20 -0700 Subject: [PATCH 09/51] Fix extension loc not updating on Chrome --- extension/src/services/extension-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/src/services/extension-config.ts b/extension/src/services/extension-config.ts index d7e48dac9..eb9ef6f9f 100644 --- a/extension/src/services/extension-config.ts +++ b/extension/src/services/extension-config.ts @@ -20,7 +20,7 @@ const settings = new SettingsProvider(new ExtensionSettingsStorage()); // As of this writing, session storage is not accessible to content scripts on Firefox so we use local storage instead. // Since unlike session storage, local storage is not automatically cleared, we clear it manually once a day. -const storage = isFirefoxBuild ? chrome.storage.session : chrome.storage.local; +const storage = isFirefoxBuild ? chrome.storage.local : chrome.storage.session; const firefoxTtl = 3600 * 24 * 1000; // 1 day export const fetchExtensionConfig = async (noCache = false): Promise => { From bae2032928e8ba534b1288f70d51f9bf39c64256 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Thu, 18 Jul 2024 22:07:55 -0700 Subject: [PATCH 10/51] Disable scroll-wheel to change subtitle position offset - It is no longer really useful since there is now a setting that controls offset for the webapp's video player --- common/app/components/VideoPlayer.tsx | 32 --------------------------- 1 file changed, 32 deletions(-) diff --git a/common/app/components/VideoPlayer.tsx b/common/app/components/VideoPlayer.tsx index 1207c9a9d..e5df0a5b8 100755 --- a/common/app/components/VideoPlayer.tsx +++ b/common/app/components/VideoPlayer.tsx @@ -1208,38 +1208,6 @@ export default function VideoPlayer({ [onSettingsChanged] ); - useEffect(() => { - const onWheel = (event: WheelEvent) => { - if (!displaySubtitles || !showSubtitlesRef.current?.length) { - return; - } - - if (Math.abs(event.deltaY) < 10) { - return; - } - - let shouldIncreaseOffset: boolean; - - switch (subtitleAlignment) { - case 'bottom': - shouldIncreaseOffset = event.deltaY > 0; - break; - case 'top': - shouldIncreaseOffset = event.deltaY < 0; - break; - } - - setSubtitlePositionOffset((offset) => { - const newOffset = shouldIncreaseOffset ? --offset : ++offset; - onSettingsChanged({ subtitlePositionOffset: newOffset }); - return newOffset; - }); - }; - - window.addEventListener('wheel', onWheel); - return () => window.removeEventListener('wheel', onWheel); - }, [subtitleAlignment, displaySubtitles, playbackPreferences, onSettingsChanged]); - const handleClick = useCallback(() => { if (playing()) { playerChannel.pause(); From 0300caf4607368eca83632826666fe0dbcd8c3d1 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sat, 20 Jul 2024 10:05:39 -0700 Subject: [PATCH 11/51] Subtitles width percentage setting - Allow subtitles to take up more width on the screen. Especially helps with mobile UX. --- common/app/components/SettingsDialog.tsx | 1 + common/app/components/VideoPlayer.tsx | 14 +- common/app/services/chrome-extension.ts | 5 + common/app/services/video-channel.ts | 2 + common/components/SettingsForm.tsx | 144 +++++++++++++----- common/components/SubtitleTextImage.tsx | 2 +- common/locales/de.json | 1 + common/locales/en.json | 1 + common/locales/es.json | 1 + common/locales/ja.json | 1 + common/locales/pl.json | 1 + common/locales/pt_BR.json | 1 + common/locales/ru.json | 1 + common/locales/zh_CN.json | 1 + common/settings/settings-import-export.ts | 3 + common/settings/settings-provider.ts | 1 + common/settings/settings.ts | 4 + .../mobile-video-overlay-controller.ts | 1 + .../src/controllers/subtitle-controller.ts | 12 +- extension/src/manifest.json | 2 +- extension/src/services/binding.ts | 1 + extension/src/services/element-overlay.ts | 28 +++- extension/src/ui/components/Popup.tsx | 1 + extension/src/ui/components/SettingsUi.tsx | 1 + 24 files changed, 178 insertions(+), 52 deletions(-) diff --git a/common/app/components/SettingsDialog.tsx b/common/app/components/SettingsDialog.tsx index f4d591ee0..b30f3ecf5 100755 --- a/common/app/components/SettingsDialog.tsx +++ b/common/app/components/SettingsDialog.tsx @@ -81,6 +81,7 @@ export default function SettingsDialog({ extensionSupportsSidePanel={extension.supportsSidePanel} extensionSupportsOrderableAnkiFields={extension.supportsOrderableAnkiFields} extensionSupportsTrackSpecificSettings={extension.supportsTrackSpecificSettings} + extensionSupportsSubtitlesWidthSetting={extension.supportsSubtitlesWidthSetting} insideApp chromeKeyBinds={extension.extensionCommands} onOpenChromeExtensionShortcuts={extension.openShortcuts} diff --git a/common/app/components/VideoPlayer.tsx b/common/app/components/VideoPlayer.tsx index e5df0a5b8..e34a25745 100755 --- a/common/app/components/VideoPlayer.tsx +++ b/common/app/components/VideoPlayer.tsx @@ -152,9 +152,8 @@ const showingSubtitleHtml = ( (imageBasedSubtitleScaleFactor * (videoRef.current?.width ?? window.screen.availWidth)) / subtitle.textImage.screen.width; const width = imageScale * subtitle.textImage.image.width; - return ` -
+
subtitle {displaySubtitles && (
{showSubtitles.map((subtitle, index) => { diff --git a/common/app/services/chrome-extension.ts b/common/app/services/chrome-extension.ts index 0bdc6c76b..6b5b55b7a 100755 --- a/common/app/services/chrome-extension.ts +++ b/common/app/services/chrome-extension.ts @@ -112,6 +112,11 @@ export default class ChromeExtension { window.addEventListener('message', this.windowEventListener); } + + get supportsSubtitlesWidthSetting() { + return this.installed && gte(this.version, '1.4.0'); + } + get supportsOrderableAnkiFields() { return this.installed && gte(this.version, '1.3.0'); } diff --git a/common/app/services/video-channel.ts b/common/app/services/video-channel.ts index 5b66f3a25..7ec28b290 100755 --- a/common/app/services/video-channel.ts +++ b/common/app/services/video-channel.ts @@ -412,6 +412,7 @@ export default class VideoChannel { subtitleAlignment, subtitleTracksV2, subtitlePositionOffset, + subtitlesWidth, } = settings; const message: SubtitleSettingsToVideoMessage = { command: 'subtitleSettings', @@ -432,6 +433,7 @@ export default class VideoChannel { subtitleAlignment, subtitleTracksV2, subtitlePositionOffset, + subtitlesWidth, }, }; this.protocol.postMessage(message); diff --git a/common/components/SettingsForm.tsx b/common/components/SettingsForm.tsx index 6fdffb625..cd6e4d39e 100644 --- a/common/components/SettingsForm.tsx +++ b/common/components/SettingsForm.tsx @@ -4,8 +4,10 @@ import { makeStyles, useTheme } from '@material-ui/core/styles'; import AddIcon from '@material-ui/icons/Add'; import LockIcon from '@material-ui/icons/Lock'; import Box from '@material-ui/core/Box'; +import ClearIcon from '@material-ui/icons/Clear'; import EditIcon from '@material-ui/icons/Edit'; import InfoIcon from '@material-ui/icons/Info'; +import UndoIcon from '@material-ui/icons/Undo'; import FormControl from '@material-ui/core/FormControl'; import FormGroup from '@material-ui/core/FormGroup'; import FormLabel from '@material-ui/core/FormLabel'; @@ -40,7 +42,7 @@ import { textSubtitleSettingsAreDirty, textSubtitleSettingsForTrack, } from '@project/common/settings'; -import { computeStyles, download, isNumeric } from '@project/common/util'; +import { download, isNumeric } from '@project/common/util'; import { CustomStyle, validateSettings } from '@project/common/settings'; import { useOutsideClickListener } from '@project/common/hooks'; import TagsTextField from './TagsTextField'; @@ -66,7 +68,6 @@ import { WebSocketClient } from '../web-socket-client/web-socket-client'; import { isFirefox } from '@project/common/browser-detection'; import SubtitleAppearanceTrackSelector from './SubtitleAppearanceTrackSelector'; import SubtitlePreview from './SubtitlePreview'; -import UndoIcon from '@material-ui/icons/Undo'; interface StylesProps { smallScreen: boolean; @@ -635,6 +636,7 @@ interface Props { extensionSupportsSidePanel: boolean; extensionSupportsOrderableAnkiFields: boolean; extensionSupportsTrackSpecificSettings: boolean; + extensionSupportsSubtitlesWidthSetting: boolean; insideApp?: boolean; settings: AsbplayerSettings; scrollToId?: string; @@ -661,6 +663,7 @@ export default function SettingsForm({ extensionSupportsSidePanel, extensionSupportsOrderableAnkiFields, extensionSupportsTrackSpecificSettings, + extensionSupportsSubtitlesWidthSetting, insideApp, scrollToId, chromeKeyBinds, @@ -786,6 +789,7 @@ export default function SettingsForm({ subtitlePreview, subtitlePositionOffset, subtitleAlignment, + subtitlesWidth, audioPaddingStart, audioPaddingEnd, maxImageWidth, @@ -1836,6 +1840,25 @@ export default function SettingsForm({
)} + + {subtitleBlur !== undefined && ( + + { + handleSubtitleTextSettingChanged('subtitleBlur', e.target.checked); + }} + /> + } + label={t('settings.subtitleBlur')} + labelPlacement="start" + className={classes.switchLabel} + /> + + )} + {subtitleCustomStyles !== undefined && ( <> {subtitleCustomStyles.map((customStyle, index) => { @@ -1873,48 +1896,12 @@ export default function SettingsForm({ )} - {subtitleBlur !== undefined && ( - - { - handleSubtitleTextSettingChanged('subtitleBlur', e.target.checked); - }} - /> - } - label={t('settings.subtitleBlur')} - labelPlacement="start" - className={classes.switchLabel} - /> - - )} {selectedSubtitleAppearanceTrack === undefined && ( <>
- - handleSettingChanged( - 'imageBasedSubtitleScaleFactor', - Number(event.target.value) - ) - } - /> -
-
- {t('settings.subtitleAlignment')} + + {t('settings.subtitleAlignment')} +
+ {(!extensionInstalled || extensionSupportsSubtitlesWidthSetting) && ( +
+ { + const numberValue = Number(e.target.value); + + if ( + !Number.isNaN(numberValue) && + numberValue >= 0 && + numberValue <= 100 + ) { + handleSettingChanged('subtitlesWidth', numberValue); + } + }} + InputProps={{ + endAdornment: ( + <> + {subtitlesWidth === -1 && ( + + + handleSettingChanged('subtitlesWidth', 100) + } + > + + + + )} + {subtitlesWidth !== -1 && ( + <> + % + + + handleSettingChanged('subtitlesWidth', -1) + } + > + + + + + )} + + ), + }} + /> +
+ )} +
+ + handleSettingChanged( + 'imageBasedSubtitleScaleFactor', + Number(event.target.value) + ) + } + /> +
)} diff --git a/common/components/SubtitleTextImage.tsx b/common/components/SubtitleTextImage.tsx index f53ab4d01..65fc2873e 100755 --- a/common/components/SubtitleTextImage.tsx +++ b/common/components/SubtitleTextImage.tsx @@ -16,7 +16,7 @@ export default function SubtitleTextImage({ subtitle, availableWidth, scale }: P const width = imageScale * subtitle.textImage.image.width; return ( -
+
subtitle
); diff --git a/common/locales/de.json b/common/locales/de.json index 30cc3134a..79fcbe91c 100644 --- a/common/locales/de.json +++ b/common/locales/de.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "Untertitelfilter Ersetzung", "subtitleSize": "Untertitelgröße", "subtitleThickness": "Subtitle Font Thickness", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "Surrounding Subtitles Count Radius", "surroundingSubtitlesTimeRadius": "Surrounding Subtitles Time Radius", "tags": "Schlagwörter", diff --git a/common/locales/en.json b/common/locales/en.json index 423434712..69fdd5b9c 100644 --- a/common/locales/en.json +++ b/common/locales/en.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "Subtitle regex filter text replacement", "subtitleSize": "Subtitle Size", "subtitleThickness": "Subtitle Font Thickness", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "Surrounding Subtitles Count Radius", "surroundingSubtitlesTimeRadius": "Surrounding Subtitles Time Radius", "tags": "Tags", diff --git a/common/locales/es.json b/common/locales/es.json index f658fc88a..c8785cdd6 100644 --- a/common/locales/es.json +++ b/common/locales/es.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "Texto de reemplazo para el filtro regex", "subtitleSize": "Tamaño de los subtítulos", "subtitleThickness": "Grosor de la Fuente de los Subtítulos", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "Radio de Cantidad de Subtítulos Circundantes", "surroundingSubtitlesTimeRadius": "Radio Temporal para Subtítulos Circundantes", "tags": "Etiquetas", diff --git a/common/locales/ja.json b/common/locales/ja.json index d27018e6a..b03aa3549 100644 --- a/common/locales/ja.json +++ b/common/locales/ja.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "字幕の正規表現フィルタの置き換えテキスト", "subtitleSize": "字幕のサイズ", "subtitleThickness": "字幕のフォントの太さ", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "前後に表示される字幕の最大数", "surroundingSubtitlesTimeRadius": "前後の字幕が表示される範囲(時間)", "tags": "タグ", diff --git a/common/locales/pl.json b/common/locales/pl.json index 065a23a4c..b41e1efd8 100644 --- a/common/locales/pl.json +++ b/common/locales/pl.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "Zamiana tekstu z filtru Regex napisów", "subtitleSize": "Wielkość napisów", "subtitleThickness": "Grubość czcionki napisów", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "Limit liczby napisów otaczających", "surroundingSubtitlesTimeRadius": "Limit czasu napisów otaczających", "tags": "Tagi", diff --git a/common/locales/pt_BR.json b/common/locales/pt_BR.json index a093cea79..85c56eeea 100644 --- a/common/locales/pt_BR.json +++ b/common/locales/pt_BR.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "Substituição de texto do filtro regex da legenda", "subtitleSize": "Tamanho da legenda", "subtitleThickness": "Expessura da fonte da legenda", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "Raio de contagem de legendas ao redor", "surroundingSubtitlesTimeRadius": "Raio de tempo das legendas ao redor", "tags": "Tags", diff --git a/common/locales/ru.json b/common/locales/ru.json index e0c605190..0d236c5f6 100644 --- a/common/locales/ru.json +++ b/common/locales/ru.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "Замена текста для фильтра регулярных выражений для субтитров", "subtitleSize": "Размер субтитров", "subtitleThickness": "Толщина шрифта субтитров", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "Радиус количества окружающих субтитров", "surroundingSubtitlesTimeRadius": "Временной радиус окружающих субтитров", "tags": "Теги", diff --git a/common/locales/zh_CN.json b/common/locales/zh_CN.json index d8f87db15..8cf0a054c 100644 --- a/common/locales/zh_CN.json +++ b/common/locales/zh_CN.json @@ -323,6 +323,7 @@ "subtitleRegexFilterTextReplacement": "字幕正则筛选器文本替换", "subtitleSize": "字幕大小", "subtitleThickness": "字幕字体厚度", + "subtitlesWidth": "Subtitles Width", "surroundingSubtitlesCountRadius": "环绕字幕计数半径", "surroundingSubtitlesTimeRadius": "环绕字幕时间半径", "tags": "标签", diff --git a/common/settings/settings-import-export.ts b/common/settings/settings-import-export.ts index 05d89b8d8..cc14c72e9 100644 --- a/common/settings/settings-import-export.ts +++ b/common/settings/settings-import-export.ts @@ -332,6 +332,9 @@ const settingsSchema = { }, }, }, + subtitlesWidth: { + type: 'number', + }, streamingAppUrl: { type: 'string', }, diff --git a/common/settings/settings-provider.ts b/common/settings/settings-provider.ts index 47cdbcecd..da94a150d 100755 --- a/common/settings/settings-provider.ts +++ b/common/settings/settings-provider.ts @@ -62,6 +62,7 @@ export const defaultSettings: AsbplayerSettings = { subtitlePositionOffset: 75, subtitleAlignment: 'bottom', subtitleTracksV2: [], + subtitlesWidth: 100, audioPaddingStart: 0, audioPaddingEnd: 500, maxImageWidth: 0, diff --git a/common/settings/settings.ts b/common/settings/settings.ts index 0a2a19335..850885d3b 100755 --- a/common/settings/settings.ts +++ b/common/settings/settings.ts @@ -133,6 +133,7 @@ const subtitleSettingsKeysObject: { [key in keyof SubtitleSettings]: boolean } = subtitlePositionOffset: true, subtitleAlignment: true, subtitleTracksV2: true, + subtitlesWidth: true, }; export const subtitleSettingsKeys: (keyof SubtitleSettings)[] = Object.keys( @@ -172,6 +173,9 @@ export interface SubtitleSettings extends TextSubtitleSettings { // We don't configure track 0 here to avoid having to migrate old settings into this new data structure. // Track 0 continues to be configured from the top-level settings object. readonly subtitleTracksV2: TextSubtitleSettings[]; + + // Percentage of containing video width; -1 means 'auto' + readonly subtitlesWidth: number; } export interface KeyBind { diff --git a/extension/src/controllers/mobile-video-overlay-controller.ts b/extension/src/controllers/mobile-video-overlay-controller.ts index 1d28f4567..3d9e42404 100644 --- a/extension/src/controllers/mobile-video-overlay-controller.ts +++ b/extension/src/controllers/mobile-video-overlay-controller.ts @@ -43,6 +43,7 @@ export class MobileVideoOverlayController { fullscreenContentClassName: 'asbplayer-mobile-video-overlay', offsetAnchor, contentPositionOffset: 8, + contentWidthPercentage: -1, }); } diff --git a/extension/src/controllers/subtitle-controller.ts b/extension/src/controllers/subtitle-controller.ts index 9189ef14a..217cb9e10 100755 --- a/extension/src/controllers/subtitle-controller.ts +++ b/extension/src/controllers/subtitle-controller.ts @@ -131,6 +131,10 @@ export default class SubtitleController { this.subtitlesElementOverlay.contentPositionOffset = value; } + set subtitlesWidth(value: number) { + this.subtitlesElementOverlay.contentWidthPercentage = value; + } + setSubtitleSettings(newSubtitleSettings: SubtitleSettings) { const styles = this._computeStyles(newSubtitleSettings); const classes = this._computeClasses(newSubtitleSettings); @@ -201,7 +205,9 @@ export default class SubtitleController { this.notificationElementOverlay.dispose(); const { subtitlesElementOverlay, notificationElementOverlay } = this._overlays(alignment, preCacheDom); subtitlesElementOverlay.contentPositionOffset = this.subtitlesElementOverlay.contentPositionOffset; + subtitlesElementOverlay.contentWidthPercentage = this.subtitlesElementOverlay.contentWidthPercentage; notificationElementOverlay.contentPositionOffset = this.notificationElementOverlay.contentPositionOffset; + notificationElementOverlay.contentWidthPercentage = this.notificationElementOverlay.contentWidthPercentage; this.subtitlesElementOverlay = subtitlesElementOverlay; this.notificationElementOverlay = notificationElementOverlay; @@ -239,6 +245,7 @@ export default class SubtitleController { fullscreenContainerClassName: 'asbplayer-subtitles-container-bottom', fullscreenContentClassName: 'asbplayer-fullscreen-subtitles', offsetAnchor: OffsetAnchor.bottom, + contentWidthPercentage: -1, }; notificationOverlayParams = { targetElement: this.video, @@ -247,6 +254,7 @@ export default class SubtitleController { fullscreenContainerClassName: 'asbplayer-notification-container-top', fullscreenContentClassName: 'asbplayer-notification', offsetAnchor: OffsetAnchor.top, + contentWidthPercentage: -1, }; break; } @@ -258,6 +266,7 @@ export default class SubtitleController { fullscreenContainerClassName: 'asbplayer-subtitles-container-top', fullscreenContentClassName: 'asbplayer-fullscreen-subtitles', offsetAnchor: OffsetAnchor.top, + contentWidthPercentage: -1, }; notificationOverlayParams = { targetElement: this.video, @@ -266,6 +275,7 @@ export default class SubtitleController { fullscreenContainerClassName: 'asbplayer-notification-container-bottom', fullscreenContentClassName: 'asbplayer-notification', offsetAnchor: OffsetAnchor.bottom, + contentWidthPercentage: -1, }; break; } @@ -380,7 +390,7 @@ export default class SubtitleController { const width = imageScale * subtitle.textImage.image.width; return ` -
+
subtitle { extensionSupportsSidePanel={!isFirefoxBuild} extensionSupportsOrderableAnkiFields extensionSupportsTrackSpecificSettings + extensionSupportsSubtitlesWidthSetting chromeKeyBinds={commands} onOpenChromeExtensionShortcuts={handleOpenExtensionShortcuts} onSettingsChanged={onSettingsChanged} From 039ec3257add897e16ff5afe9547b51f6ba1dda1 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sat, 20 Jul 2024 10:24:20 -0700 Subject: [PATCH 12/51] Pull loc --- common/locales/ja.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/locales/ja.json b/common/locales/ja.json index b03aa3549..22b3b31c3 100644 --- a/common/locales/ja.json +++ b/common/locales/ja.json @@ -323,7 +323,7 @@ "subtitleRegexFilterTextReplacement": "字幕の正規表現フィルタの置き換えテキスト", "subtitleSize": "字幕のサイズ", "subtitleThickness": "字幕のフォントの太さ", - "subtitlesWidth": "Subtitles Width", + "subtitlesWidth": "字幕の幅", "surroundingSubtitlesCountRadius": "前後に表示される字幕の最大数", "surroundingSubtitlesTimeRadius": "前後の字幕が表示される範囲(時間)", "tags": "タグ", From 3cf4b587edfeec1abd3c7863680f7ff003337c27 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sat, 20 Jul 2024 10:28:08 -0700 Subject: [PATCH 13/51] Fix test compilation --- common/settings/settings-provider.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/common/settings/settings-provider.test.ts b/common/settings/settings-provider.test.ts index 25d5c1208..9350b7d0d 100644 --- a/common/settings/settings-provider.test.ts +++ b/common/settings/settings-provider.test.ts @@ -170,6 +170,7 @@ const subtitleSettings = { imageBasedSubtitleScaleFactor: 1, subtitlePositionOffset: 70, subtitleAlignment: 'top' as SubtitleAlignment, + subtitlesWidth: 100, subtitleTracksV2: [ { subtitleSize: 36, From c83dacb4db6d74d3c171f66fe38217ac4ebe34a9 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sat, 20 Jul 2024 21:55:14 -0700 Subject: [PATCH 14/51] Fix subtitle container position logic when video is far down page - E.g. in the case of Amazon Prime when scrolling down the page and then opening a video. --- extension/src/services/element-overlay.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/src/services/element-overlay.ts b/extension/src/services/element-overlay.ts index 449717f87..22cec7040 100755 --- a/extension/src/services/element-overlay.ts +++ b/extension/src/services/element-overlay.ts @@ -330,7 +330,7 @@ export class CachingElementOverlay implements ElementOverlay { const clampedY = Math.max(rect.top + window.scrollY, 0); if (this.offsetAnchor === OffsetAnchor.bottom) { - const clampedHeight = Math.min(clampedY + rect.height, window.innerHeight); + const clampedHeight = Math.min(clampedY + rect.height, window.innerHeight + window.scrollY); container.style.top = clampedHeight - this.contentPositionOffset + 'px'; container.style.bottom = ''; } else { @@ -510,7 +510,7 @@ export class DefaultElementOverlay implements ElementOverlay { const clampedY = Math.max(rect.top + window.scrollY, 0); if (this.offsetAnchor === OffsetAnchor.bottom) { - const clampedHeight = Math.min(clampedY + rect.height, window.innerHeight); + const clampedHeight = Math.min(clampedY + rect.height, window.innerHeight + window.scrollY); container.style.top = clampedHeight - this.contentPositionOffset + 'px'; container.style.bottom = ''; } else { From 5ca84168bd37f2547c6bbe8f8cbbed4c7da515c6 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Sun, 21 Jul 2024 10:59:52 -0700 Subject: [PATCH 15/51] Fix overlay blocking too much of the video on small screens --- .../mobile-video-overlay-controller.ts | 21 +++++++++++++++---- .../src/ui/components/MobileVideoOverlay.tsx | 18 ++++++++++++---- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/extension/src/controllers/mobile-video-overlay-controller.ts b/extension/src/controllers/mobile-video-overlay-controller.ts index 3d9e42404..da1add619 100644 --- a/extension/src/controllers/mobile-video-overlay-controller.ts +++ b/extension/src/controllers/mobile-video-overlay-controller.ts @@ -6,12 +6,14 @@ import { VideoToExtensionCommand, } from '@project/common'; import Binding from '../services/binding'; -import { CachingElementOverlay, ElementOverlay, OffsetAnchor } from '../services/element-overlay'; +import { CachingElementOverlay, OffsetAnchor } from '../services/element-overlay'; import { adjacentSubtitle } from '@project/common/key-binder'; +const smallScreenVideoHeighThreshold = 300; + export class MobileVideoOverlayController { private readonly _context: Binding; - private _overlay: ElementOverlay; + private _overlay: CachingElementOverlay; private _pauseListener?: () => void; private _playListener?: () => void; private _seekedListener?: () => void; @@ -24,6 +26,7 @@ export class MobileVideoOverlayController { sendResponse: (response?: any) => void ) => void; private _bound = false; + private _smallScreen = false; constructor(context: Binding, offsetAnchor: OffsetAnchor) { this._context = context; @@ -187,15 +190,25 @@ export class MobileVideoOverlayController { private _doShow() { const anchor = this._overlay.offsetAnchor === OffsetAnchor.bottom ? 'bottom' : 'top'; + const smallScreen = this._context.video.getBoundingClientRect().height < smallScreenVideoHeighThreshold; + const height = smallScreen ? 64 : 108; + const tooltips = !smallScreen; + + if (smallScreen !== this._smallScreen) { + this._overlay.uncacheHtml(); + this._smallScreen = smallScreen; + } + this._overlay.setHtml([ { key: 'ui', html: () => - `