From 32cc2f93051e18c36a05f1463d104a79fdc31345 Mon Sep 17 00:00:00 2001 From: R-J Lim Date: Thu, 9 Nov 2023 20:58:04 -0800 Subject: [PATCH] UI notification when post mine action is 'none' --- common/app/src/components/App.tsx | 2 +- common/app/src/components/VideoPlayer.tsx | 18 ++- common/app/src/services/player-channel.ts | 17 ++- common/src/message.ts | 12 +- extension/src/background-page.ts | 36 +++--- extension/src/background.ts | 6 +- .../handlers/video/record-media-handler.ts | 91 +++------------ .../handlers/video/rerecord-media-handler.ts | 28 ++--- .../video/start-recording-media-handler.ts | 87 +++----------- .../video/stop-recording-media-handler.ts | 96 ++++----------- .../handlers/video/take-screenshot-handler.ts | 28 ++--- extension/src/services/binding.ts | 12 +- extension/src/services/card-publisher.ts | 109 ++++++++++++++++-- 13 files changed, 240 insertions(+), 302 deletions(-) diff --git a/common/app/src/components/App.tsx b/common/app/src/components/App.tsx index cbda59f0..5ea98f46 100755 --- a/common/app/src/components/App.tsx +++ b/common/app/src/components/App.tsx @@ -544,7 +544,7 @@ function App({ origin, logoUrl, settings, extension, fetcher, onSettingsChanged id, subtitle, surroundingSubtitles, - subtitleFileName: subtitleFile?.name, + subtitleFileName: subtitleFile?.name ?? videoFile?.name ?? '', url, image, audio, diff --git a/common/app/src/components/VideoPlayer.tsx b/common/app/src/components/VideoPlayer.tsx index 3d8007b6..b22053ce 100755 --- a/common/app/src/components/VideoPlayer.tsx +++ b/common/app/src/components/VideoPlayer.tsx @@ -836,7 +836,13 @@ export default function VideoPlayer({ switch (postMineAction) { case PostMineAction.showAnkiDialog: if (popOut) { - playerChannel.copy(subtitle, surroundingSubtitles, timestamp, PostMineAction.none); + playerChannel.copy( + subtitle, + surroundingSubtitles, + videoFileName ?? '', + timestamp, + PostMineAction.none + ); onAnkiDialogRequest( videoFileUrl, videoFileName ?? '', @@ -852,11 +858,17 @@ export default function VideoPlayer({ setResumeOnFinishedAnkiDialogRequest(true); } } else { - playerChannel.copy(subtitle, surroundingSubtitles, timestamp, PostMineAction.showAnkiDialog); + playerChannel.copy( + subtitle, + surroundingSubtitles, + videoFileName ?? '', + timestamp, + PostMineAction.showAnkiDialog + ); } break; default: - playerChannel.copy(subtitle, surroundingSubtitles, timestamp, postMineAction); + playerChannel.copy(subtitle, surroundingSubtitles, videoFileName ?? '', timestamp, postMineAction); } setLastMinedRecord({ diff --git a/common/app/src/services/player-channel.ts b/common/app/src/services/player-channel.ts index 957b8676..b14d4d2e 100755 --- a/common/app/src/services/player-channel.ts +++ b/common/app/src/services/player-channel.ts @@ -40,7 +40,7 @@ export default class PlayerChannel { private currentTimeCallbacks: ((currentTime: number) => void)[]; private audioTrackSelectedCallbacks: ((id: string) => void)[]; private closeCallbacks: (() => void)[]; - private subtitlesCallbacks: ((subtitles: SubtitleModel[]) => void)[]; + private subtitlesCallbacks: ((subtitles: SubtitleModel[], subtitleFileName: string) => void)[]; private offsetCallbacks: ((offset: number) => void)[]; private playbackRateCallbacks: ((playbackRate: number) => void)[]; private playModeCallbacks: ((playMode: PlayMode) => void)[]; @@ -125,7 +125,10 @@ export default class PlayerChannel { const subtitlesMessage = event.data as SubtitlesToVideoMessage; for (let callback of that.subtitlesCallbacks) { - callback(subtitlesMessage.value); + callback( + subtitlesMessage.value, + subtitlesMessage.names.length > 0 ? subtitlesMessage.names[0] : '' + ); } break; case 'offset': @@ -245,7 +248,7 @@ export default class PlayerChannel { return () => this._remove(callback, this.readyCallbacks); } - onSubtitles(callback: (subtitles: SubtitleModel[]) => void) { + onSubtitles(callback: (subtitles: SubtitleModel[], subtitleFileName: string) => void) { this.subtitlesCallbacks.push(callback); return () => this._remove(callback, this.subtitlesCallbacks); } @@ -368,14 +371,16 @@ export default class PlayerChannel { copy( subtitle: SubtitleModel, surroundingSubtitles: SubtitleModel[], + subtitleFileName: string, mediaTimestamp: number, postMineAction: PostMineAction ) { const message: CopyMessage = { command: 'copy', - subtitle: subtitle, - surroundingSubtitles: surroundingSubtitles, - postMineAction: postMineAction, + subtitle, + surroundingSubtitles, + subtitleFileName, + postMineAction, mediaTimestamp, }; diff --git a/common/src/message.ts b/common/src/message.ts index 7a8e3ef5..c822273b 100755 --- a/common/src/message.ts +++ b/common/src/message.ts @@ -101,7 +101,6 @@ export interface RecordMediaAndForwardSubtitleMessage extends Message, ImageCapt readonly audioPaddingEnd: number; readonly imageDelay: number; readonly playbackRate: number; - readonly ankiSettings?: AnkiSettings; readonly mediaTimestamp: number; } @@ -114,7 +113,6 @@ export interface StartRecordingMediaMessage extends Message, ImageCaptureParams readonly imageDelay: number; readonly url?: string; readonly subtitleFileName: string; - readonly ankiSettings?: AnkiSettings; } export interface StopRecordingMediaMessage extends Message, ImageCaptureParams { @@ -127,7 +125,6 @@ export interface StopRecordingMediaMessage extends Message, ImageCaptureParams { readonly videoDuration: number; readonly url?: string; readonly subtitleFileName: string; - readonly ankiSettings?: AnkiSettings; readonly subtitle?: SubtitleModel; readonly surroundingSubtitles?: SubtitleModel[]; } @@ -136,7 +133,7 @@ export interface Card { readonly id?: string; readonly subtitle: SubtitleModel; readonly surroundingSubtitles: SubtitleModel[]; - readonly subtitleFileName?: string; + readonly subtitleFileName: string; readonly url?: string; readonly image?: ImageModel; readonly audio?: AudioModel; @@ -173,7 +170,7 @@ export interface TakeScreenshotMessage extends Message { export interface TakeScreenshotFromExtensionMessage extends Message, ImageCaptureParams { readonly command: 'take-screenshot'; readonly ankiUiState?: AnkiUiSavedState; - readonly subtitleFileName?: string; + readonly subtitleFileName: string; readonly mediaTimestamp: number; } @@ -191,6 +188,11 @@ export interface CardUpdatedMessage extends Message { readonly url?: string; } +export interface CardSavedMessage extends Message { + readonly command: 'card-saved'; + readonly text: string; +} + export interface ScreenshotTakenMessage extends Message { readonly command: 'screenshot-taken'; readonly ankiUiState?: AnkiUiSavedState; diff --git a/extension/src/background-page.ts b/extension/src/background-page.ts index 8b6d204d..c923d7a6 100755 --- a/extension/src/background-page.ts +++ b/extension/src/background-page.ts @@ -74,25 +74,27 @@ window.onload = async () => { break; case 'copy': const copyMessage = request.message as CopyMessage; - settings.getSingle('miningHistoryStorageLimit').then((storageLimit) => { - return new CopyHistoryRepository(storageLimit).save({ - ...copyMessage.subtitle, - id: copyMessage.id ?? uuidv4(), - timestamp: Date.now(), - name: - copyMessage.subtitleFileName?.substring( + settings + .getSingle('miningHistoryStorageLimit') + .then((storageLimit) => { + return new CopyHistoryRepository(storageLimit).save({ + ...copyMessage.subtitle, + id: copyMessage.id ?? uuidv4(), + timestamp: Date.now(), + name: copyMessage.subtitleFileName.substring( 0, copyMessage.subtitleFileName.lastIndexOf('.') - ) ?? '', - subtitleFileName: copyMessage.subtitleFileName ?? '', - mediaTimestamp: copyMessage.mediaTimestamp, - surroundingSubtitles: copyMessage.surroundingSubtitles, - url: copyMessage.url, - audio: copyMessage.audio, - image: copyMessage.image, - }); - }); - break; + ), + subtitleFileName: copyMessage.subtitleFileName, + mediaTimestamp: copyMessage.mediaTimestamp, + surroundingSubtitles: copyMessage.surroundingSubtitles, + url: copyMessage.url, + audio: copyMessage.audio, + image: copyMessage.image, + }); + }) + .then(() => sendResponse(true)); + return true; } } }; diff --git a/extension/src/background.ts b/extension/src/background.ts index db9b214a..fb08099a 100755 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -61,14 +61,14 @@ chrome.runtime.onStartup.addListener(startListener); const tabRegistry = new TabRegistry(settings); const backgroundPageManager = new BackgroundPageManager(tabRegistry); const imageCapturer = new ImageCapturer(settings); -const cardPublisher = new CardPublisher(backgroundPageManager); +const cardPublisher = new CardPublisher(backgroundPageManager, settings); const handlers: CommandHandler[] = [ new VideoHeartbeatHandler(tabRegistry), - new RecordMediaHandler(backgroundPageManager, imageCapturer, cardPublisher), + new RecordMediaHandler(backgroundPageManager, imageCapturer, cardPublisher, settings), new RerecordMediaHandler(backgroundPageManager, cardPublisher), new StartRecordingMediaHandler(backgroundPageManager, imageCapturer, cardPublisher), - new StopRecordingMediaHandler(backgroundPageManager, imageCapturer, cardPublisher), + new StopRecordingMediaHandler(backgroundPageManager, imageCapturer, cardPublisher, settings), new TakeScreenshotHandler(imageCapturer, cardPublisher), new ToggleSubtitlesHandler(settings, tabRegistry), new SyncHandler(tabRegistry), diff --git a/extension/src/handlers/video/record-media-handler.ts b/extension/src/handlers/video/record-media-handler.ts index 7d086625..c47df48d 100755 --- a/extension/src/handlers/video/record-media-handler.ts +++ b/extension/src/handlers/video/record-media-handler.ts @@ -1,21 +1,16 @@ import ImageCapturer from '../../services/image-capturer'; -import { v4 as uuidv4 } from 'uuid'; import { AudioModel, Command, - CopyMessage, ImageModel, Message, RecordMediaAndForwardSubtitleMessage, VideoToExtensionCommand, ExtensionToVideoCommand, - ShowAnkiUiMessage, ScreenshotTakenMessage, - PostMineAction, - CardUpdatedMessage, RecordingFinishedMessage, - updateLastCard, - sourceString, + Card, + SettingsProvider, } from '@project/common'; import BackgroundPageManager from '../../services/background-page-manager'; import { CardPublisher } from '../../services/card-publisher'; @@ -24,15 +19,18 @@ export default class RecordMediaHandler { private readonly _audioRecorder: BackgroundPageManager; private readonly _imageCapturer: ImageCapturer; private readonly _cardPublisher: CardPublisher; + private readonly _settingsProvider: SettingsProvider; constructor( audioRecorder: BackgroundPageManager, imageCapturer: ImageCapturer, - cardPublisher: CardPublisher + cardPublisher: CardPublisher, + settingsProvider: SettingsProvider ) { this._audioRecorder = audioRecorder; this._imageCapturer = imageCapturer; this._cardPublisher = cardPublisher; + this._settingsProvider = settingsProvider; } get sender() { @@ -48,19 +46,18 @@ export default class RecordMediaHandler { const recordMediaCommand = command as VideoToExtensionCommand; try { - const itemId = uuidv4(); const subtitle = recordMediaCommand.message.subtitle; let audioPromise = undefined; let imagePromise = undefined; let imageModel: ImageModel | undefined = undefined; let audioModel: AudioModel | undefined = undefined; - const mp3 = recordMediaCommand.message.ankiSettings?.preferMp3 ?? false; + const preferMp3 = await this._settingsProvider.getSingle('preferMp3'); if (recordMediaCommand.message.record) { const time = (subtitle.end - subtitle.start) / recordMediaCommand.message.playbackRate + recordMediaCommand.message.audioPaddingEnd; - audioPromise = this._audioRecorder.startWithTimeout(time, mp3, { + audioPromise = this._audioRecorder.startWithTimeout(time, preferMp3, { src: recordMediaCommand.src, tabId: sender.tab?.id, }); @@ -94,7 +91,7 @@ export default class RecordMediaHandler { const audioBase64 = await audioPromise; audioModel = { base64: audioBase64, - extension: mp3 ? 'mp3' : 'webm', + extension: preferMp3 ? 'mp3' : 'webm', paddingStart: recordMediaCommand.message.audioPaddingStart, paddingEnd: recordMediaCommand.message.audioPaddingEnd, playbackRate: recordMediaCommand.message.playbackRate, @@ -109,71 +106,17 @@ export default class RecordMediaHandler { }; } - const message: CopyMessage = { - command: 'copy', - id: itemId, - url: recordMediaCommand.message.url, - subtitle: subtitle, - surroundingSubtitles: recordMediaCommand.message.surroundingSubtitles, + const card: Card = { image: imageModel, audio: audioModel, - subtitleFileName: recordMediaCommand.message.subtitleFileName, - mediaTimestamp: recordMediaCommand.message.mediaTimestamp, + ...recordMediaCommand.message, }; - this._cardPublisher.publish(message); - - if (recordMediaCommand.message.postMineAction == PostMineAction.showAnkiDialog) { - const showAnkiUiCommand: ExtensionToVideoCommand = { - sender: 'asbplayer-extension-to-video', - message: { - command: 'show-anki-ui', - id: itemId, - subtitle: message.subtitle, - surroundingSubtitles: message.surroundingSubtitles, - image: message.image, - audio: message.audio, - url: message.url, - subtitleFileName: recordMediaCommand.message.subtitleFileName, - mediaTimestamp: recordMediaCommand.message.mediaTimestamp, - }, - src: recordMediaCommand.src, - }; - - chrome.tabs.sendMessage(senderTab.id!, showAnkiUiCommand); - } else if (recordMediaCommand.message.postMineAction == PostMineAction.updateLastCard) { - if (!recordMediaCommand.message.ankiSettings) { - throw new Error('Cannot update last card because anki settings is undefined'); - } - - const cardName = await updateLastCard( - recordMediaCommand.message.ankiSettings, - subtitle, - recordMediaCommand.message.surroundingSubtitles, - audioModel, - imageModel, - sourceString( - recordMediaCommand.message.subtitleFileName, - recordMediaCommand.message.mediaTimestamp - ), - recordMediaCommand.message.url - ); - - const cardUpdatedCommand: ExtensionToVideoCommand = { - sender: 'asbplayer-extension-to-video', - message: { - command: 'card-updated', - cardName: `${cardName}`, - subtitle, - surroundingSubtitles: recordMediaCommand.message.surroundingSubtitles, - audio: audioModel, - image: imageModel, - url: recordMediaCommand.message.url, - }, - src: recordMediaCommand.src, - }; - - chrome.tabs.sendMessage(senderTab.id!, cardUpdatedCommand); - } + this._cardPublisher.publish( + card, + recordMediaCommand.message.postMineAction, + senderTab.id!, + recordMediaCommand.src + ); } finally { if (recordMediaCommand.message.record) { const recordingFinishedCommand: ExtensionToVideoCommand = { diff --git a/extension/src/handlers/video/rerecord-media-handler.ts b/extension/src/handlers/video/rerecord-media-handler.ts index 7e540cf8..8bb662aa 100755 --- a/extension/src/handlers/video/rerecord-media-handler.ts +++ b/extension/src/handlers/video/rerecord-media-handler.ts @@ -1,4 +1,3 @@ -import { v4 as uuidv4 } from 'uuid'; import { AudioModel, Command, @@ -50,19 +49,20 @@ export default class RerecordMediaHandler { playbackRate: rerecordCommand.message.playbackRate, }; - this._cardPublisher.publish({ - // Ideally we send the same ID so that asbplayer can update the existing item. - // There's a bug where asbplayer isn't properly updating the item right now, so - // let's just create a new item for now by using a new ID. - id: uuidv4(), - audio: audio, - image: rerecordCommand.message.uiState.image, - url: rerecordCommand.message.uiState.url, - subtitle: rerecordCommand.message.uiState.subtitle, - surroundingSubtitles: rerecordCommand.message.uiState.sliderContext.subtitles, - subtitleFileName: rerecordCommand.message.subtitleFileName, - mediaTimestamp: rerecordCommand.message.timestamp, - }); + this._cardPublisher.publish( + { + audio: audio, + image: rerecordCommand.message.uiState.image, + url: rerecordCommand.message.uiState.url, + subtitle: rerecordCommand.message.uiState.subtitle, + surroundingSubtitles: rerecordCommand.message.uiState.sliderContext.subtitles, + subtitleFileName: rerecordCommand.message.subtitleFileName, + mediaTimestamp: rerecordCommand.message.timestamp, + }, + undefined, + sender.tab!.id!, + rerecordCommand.src + ); const newUiState = { ...rerecordCommand.message.uiState, diff --git a/extension/src/handlers/video/start-recording-media-handler.ts b/extension/src/handlers/video/start-recording-media-handler.ts index aabbf80e..93e56658 100755 --- a/extension/src/handlers/video/start-recording-media-handler.ts +++ b/extension/src/handlers/video/start-recording-media-handler.ts @@ -1,18 +1,12 @@ import ImageCapturer from '../../services/image-capturer'; -import { v4 as uuidv4 } from 'uuid'; import { - CardUpdatedMessage, Command, ExtensionToVideoCommand, ImageModel, Message, - PostMineAction, ScreenshotTakenMessage, - ShowAnkiUiMessage, - sourceString, StartRecordingMediaMessage, SubtitleModel, - updateLastCard, VideoToExtensionCommand, } from '@project/common'; import BackgroundPageManager from '../../services/background-page-manager'; @@ -23,11 +17,7 @@ export default class StartRecordingMediaHandler { private readonly _imageCapturer: ImageCapturer; private readonly _cardPublisher: CardPublisher; - constructor( - audioRecorder: BackgroundPageManager, - imageCapturer: ImageCapturer, - cardPublisher: CardPublisher - ) { + constructor(audioRecorder: BackgroundPageManager, imageCapturer: ImageCapturer, cardPublisher: CardPublisher) { this._audioRecorder = audioRecorder; this._imageCapturer = imageCapturer; this._cardPublisher = cardPublisher; @@ -80,8 +70,6 @@ export default class StartRecordingMediaHandler { track: 0, }; - const id = uuidv4(); - let imageModel: ImageModel | undefined = undefined; if (imageBase64) { @@ -91,66 +79,19 @@ export default class StartRecordingMediaHandler { }; } - this._cardPublisher.publish({ - id: id, - subtitle: subtitle, - surroundingSubtitles: [], - image: imageModel, - url: startRecordingCommand.message.url, - subtitleFileName: startRecordingCommand.message.subtitleFileName, - mediaTimestamp: startRecordingCommand.message.mediaTimestamp, - }); - - if (startRecordingCommand.message.postMineAction === PostMineAction.showAnkiDialog) { - const showAnkiUiCommand: ExtensionToVideoCommand = { - sender: 'asbplayer-extension-to-video', - message: { - command: 'show-anki-ui', - id: id, - subtitle: subtitle, - surroundingSubtitles: [], - image: imageModel, - url: startRecordingCommand.message.url, - subtitleFileName: startRecordingCommand.message.subtitleFileName, - mediaTimestamp: startRecordingCommand.message.mediaTimestamp, - }, - src: startRecordingCommand.src, - }; - - chrome.tabs.sendMessage(sender.tab!.id!, showAnkiUiCommand); - } else if (startRecordingCommand.message.postMineAction === PostMineAction.updateLastCard) { - if (!startRecordingCommand.message.ankiSettings) { - throw new Error('Unable to update last card because anki settings is undefined'); - } - - const cardName = await updateLastCard( - startRecordingCommand.message.ankiSettings, - subtitle, - [], - undefined, - imageModel, - sourceString( - startRecordingCommand.message.subtitleFileName, - startRecordingCommand.message.mediaTimestamp - ), - startRecordingCommand.message.url - ); - - const cardUpdatedCommand: ExtensionToVideoCommand = { - sender: 'asbplayer-extension-to-video', - message: { - command: 'card-updated', - cardName: `${cardName}`, - subtitle, - surroundingSubtitles: [], - image: imageModel, - url: startRecordingCommand.message.url, - }, - src: startRecordingCommand.src, - }; - - chrome.tabs.sendMessage(sender.tab!.id!, cardUpdatedCommand); - } + this._cardPublisher.publish( + { + subtitle: subtitle, + surroundingSubtitles: [], + image: imageModel, + url: startRecordingCommand.message.url, + subtitleFileName: startRecordingCommand.message.subtitleFileName, + mediaTimestamp: startRecordingCommand.message.mediaTimestamp, + }, + startRecordingCommand.message.postMineAction, + sender.tab!.id!, + startRecordingCommand.src + ); } } } diff --git a/extension/src/handlers/video/stop-recording-media-handler.ts b/extension/src/handlers/video/stop-recording-media-handler.ts index b5200372..7649df57 100755 --- a/extension/src/handlers/video/stop-recording-media-handler.ts +++ b/extension/src/handlers/video/stop-recording-media-handler.ts @@ -1,21 +1,16 @@ import BackgroundPageManager from '../../services/background-page-manager'; import ImageCapturer from '../../services/image-capturer'; -import { v4 as uuidv4 } from 'uuid'; import { AudioModel, - CardUpdatedMessage, Command, ExtensionToVideoCommand, ImageModel, Message, mockSurroundingSubtitles, - PostMineAction, RecordingFinishedMessage, - ShowAnkiUiMessage, - sourceString, + SettingsProvider, StopRecordingMediaMessage, SubtitleModel, - updateLastCard, VideoToExtensionCommand, } from '@project/common'; import { CardPublisher } from '../../services/card-publisher'; @@ -24,15 +19,18 @@ export default class StopRecordingMediaHandler { private readonly _audioRecorder: BackgroundPageManager; private readonly _imageCapturer: ImageCapturer; private readonly _cardPublisher: CardPublisher; + private readonly _settingsProvider: SettingsProvider; constructor( audioRecorder: BackgroundPageManager, imageCapturer: ImageCapturer, - cardPublisher: CardPublisher + cardPublisher: CardPublisher, + settingsProvider: SettingsProvider ) { this._audioRecorder = audioRecorder; this._imageCapturer = imageCapturer; this._cardPublisher = cardPublisher; + this._settingsProvider = settingsProvider; } get sender() { @@ -47,7 +45,6 @@ export default class StopRecordingMediaHandler { const stopRecordingCommand = command as VideoToExtensionCommand; try { - const itemId = uuidv4(); const subtitle: SubtitleModel = stopRecordingCommand.message.subtitle ?? { text: '', start: stopRecordingCommand.message.startTimestamp, @@ -81,11 +78,11 @@ export default class StopRecordingMediaHandler { }; } - const mp3 = stopRecordingCommand.message.ankiSettings?.preferMp3 ?? false; - const audioBase64 = await this._audioRecorder.stop(mp3); + const preferMp3 = await this._settingsProvider.getSingle('preferMp3'); + const audioBase64 = await this._audioRecorder.stop(preferMp3); const audioModel: AudioModel = { base64: audioBase64, - extension: mp3 ? 'mp3' : 'webm', + extension: preferMp3 ? 'mp3' : 'webm', paddingStart: 0, paddingEnd: 0, start: stopRecordingCommand.message.startTimestamp, @@ -93,69 +90,20 @@ export default class StopRecordingMediaHandler { playbackRate: stopRecordingCommand.message.playbackRate, }; - this._cardPublisher.publish({ - id: itemId, - subtitle: subtitle, - surroundingSubtitles: surroundingSubtitles, - image: imageModel, - audio: audioModel, - url: stopRecordingCommand.message.url, - subtitleFileName: stopRecordingCommand.message.subtitleFileName, - mediaTimestamp: stopRecordingCommand.message.startTimestamp, - }); - - if (stopRecordingCommand.message.postMineAction === PostMineAction.showAnkiDialog) { - const showAnkiUiCommand: ExtensionToVideoCommand = { - sender: 'asbplayer-extension-to-video', - message: { - command: 'show-anki-ui', - id: itemId, - subtitle: subtitle, - surroundingSubtitles: surroundingSubtitles, - image: imageModel, - audio: audioModel, - url: stopRecordingCommand.message.url, - subtitleFileName: stopRecordingCommand.message.subtitleFileName, - mediaTimestamp: stopRecordingCommand.message.startTimestamp, - }, - src: stopRecordingCommand.src, - }; - - chrome.tabs.sendMessage(sender.tab!.id!, showAnkiUiCommand); - } else if (stopRecordingCommand.message.postMineAction === PostMineAction.updateLastCard) { - if (!stopRecordingCommand.message.ankiSettings) { - throw new Error('Unable to update last card because anki settings is undefined'); - } - - const cardName = await updateLastCard( - stopRecordingCommand.message.ankiSettings, - subtitle, - surroundingSubtitles, - audioModel, - imageModel, - sourceString( - stopRecordingCommand.message.subtitleFileName, - stopRecordingCommand.message.startTimestamp - ), - stopRecordingCommand.message.url - ); - - const cardUpdatedCommand: ExtensionToVideoCommand = { - sender: 'asbplayer-extension-to-video', - message: { - command: 'card-updated', - cardName: `${cardName}`, - subtitle, - surroundingSubtitles: surroundingSubtitles, - image: imageModel, - audio: audioModel, - url: stopRecordingCommand.message.url, - }, - src: stopRecordingCommand.src, - }; - - chrome.tabs.sendMessage(sender.tab!.id!, cardUpdatedCommand); - } + this._cardPublisher.publish( + { + subtitle: subtitle, + surroundingSubtitles: surroundingSubtitles, + image: imageModel, + audio: audioModel, + url: stopRecordingCommand.message.url, + subtitleFileName: stopRecordingCommand.message.subtitleFileName, + mediaTimestamp: stopRecordingCommand.message.startTimestamp, + }, + stopRecordingCommand.message.postMineAction, + sender.tab!.id!, + stopRecordingCommand.src + ); } finally { const recordingFinishedCommand: ExtensionToVideoCommand = { sender: 'asbplayer-extension-to-video', diff --git a/extension/src/handlers/video/take-screenshot-handler.ts b/extension/src/handlers/video/take-screenshot-handler.ts index 3b61e8b1..682c0bb3 100755 --- a/extension/src/handlers/video/take-screenshot-handler.ts +++ b/extension/src/handlers/video/take-screenshot-handler.ts @@ -1,5 +1,4 @@ import ImageCapturer from '../../services/image-capturer'; -import { v4 as uuidv4 } from 'uuid'; import { Command, Message, @@ -47,19 +46,20 @@ export default class TakeScreenshotHandler { base64: imageBase64, extension: 'jpeg', }; - this._cardPublisher.publish({ - // Ideally we send the same ID so that asbplayer can update the existing item. - // There's a bug where asbplayer isn't properly updating the item right now, so - // let's just create a new item for now by using a new ID. - id: uuidv4(), - audio: ankiUiState!.audio, - image: ankiUiState!.image, - url: ankiUiState!.url, - subtitle: ankiUiState!.subtitle, - surroundingSubtitles: ankiUiState!.sliderContext.subtitles, - subtitleFileName: takeScreenshotCommand.message.subtitleFileName, - mediaTimestamp: takeScreenshotCommand.message.mediaTimestamp, - }); + this._cardPublisher.publish( + { + audio: ankiUiState.audio, + image: ankiUiState.image, + url: ankiUiState!.url, + subtitle: ankiUiState!.subtitle, + surroundingSubtitles: ankiUiState!.sliderContext.subtitles, + subtitleFileName: takeScreenshotCommand.message.subtitleFileName, + mediaTimestamp: takeScreenshotCommand.message.mediaTimestamp, + }, + undefined, + senderTab.id!, + takeScreenshotCommand.src + ); } const screenshotTakenCommand: ExtensionToVideoCommand = { diff --git a/extension/src/services/binding.ts b/extension/src/services/binding.ts index 2e0d516b..c45dc3d7 100755 --- a/extension/src/services/binding.ts +++ b/extension/src/services/binding.ts @@ -2,6 +2,7 @@ import { AckMessage, AnkiUiSavedState, AutoPausePreference, + CardSavedMessage, CardUpdatedMessage, CopySubtitleMessage, cropAndResize, @@ -494,6 +495,9 @@ export default class Binding { dialogRequestedTimestamp: this.video.currentTime * 1000, }; break; + case 'card-saved': + const cardSavedMessage = request.message as CardSavedMessage; + this.subtitleController.notification('info.copiedSubtitle', { text: cardSavedMessage.text }); case 'recording-finished': this.recordingMedia = false; this.recordingMediaStartedTimestamp = undefined; @@ -698,9 +702,6 @@ export default class Binding { await this.play(); } - const ankiSettings = - postMineAction === PostMineAction.updateLastCard ? this.ankiUiController.ankiSettings : undefined; - const command: VideoToExtensionCommand = { sender: 'asbplayer-video', message: { @@ -717,7 +718,6 @@ export default class Binding { audioPaddingEnd: this.audioPaddingEnd, imageDelay: this.imageDelay, playbackRate: this.video.playbackRate, - ankiSettings: ankiSettings, ...this._imageCaptureParams, }, src: this.video.src, @@ -727,8 +727,6 @@ export default class Binding { } async _toggleRecordingMedia(postMineAction: PostMineAction) { - const ankiSettings = - postMineAction === PostMineAction.updateLastCard ? this.ankiUiController.ankiSettings : undefined; if (this.recordingMedia) { const currentTimestamp = this.video.currentTime * 1000; const command: VideoToExtensionCommand = { @@ -743,7 +741,6 @@ export default class Binding { videoDuration: this.video.duration * 1000, url: this.url, subtitleFileName: this.subtitleFileName(), - ankiSettings: ankiSettings, ...this._imageCaptureParams, ...this._surroundingSubtitlesAroundInterval(this.recordingMediaStartedTimestamp!, currentTimestamp), }, @@ -777,7 +774,6 @@ export default class Binding { url: this.url, subtitleFileName: this.subtitleFileName(), imageDelay: this.imageDelay, - ankiSettings: ankiSettings, ...this._imageCaptureParams, }, src: this.video.src, diff --git a/extension/src/services/card-publisher.ts b/extension/src/services/card-publisher.ts index 80cdf0e0..8e654409 100644 --- a/extension/src/services/card-publisher.ts +++ b/extension/src/services/card-publisher.ts @@ -1,21 +1,110 @@ -import { Card, CopyMessage, ExtensionToBackgroundPageCommand } from '@project/common'; +import { + AnkiSettings, + Card, + CardSavedMessage, + CardUpdatedMessage, + CopyMessage, + ExtensionToBackgroundPageCommand, + ExtensionToVideoCommand, + PostMineAction, + SettingsProvider, + ShowAnkiUiMessage, + ankiSettingsKeys, + humanReadableTime, + sourceString, + updateLastCard, +} from '@project/common'; import BackgroundPageManager from './background-page-manager'; +import { v4 as uuidv4 } from 'uuid'; export class CardPublisher { private readonly _backgroundPageManager: BackgroundPageManager; - constructor(backgroundPageManager: BackgroundPageManager) { + private readonly _settingsProvider: SettingsProvider; + constructor(backgroundPageManager: BackgroundPageManager, settingsProvider: SettingsProvider) { this._backgroundPageManager = backgroundPageManager; + this._settingsProvider = settingsProvider; } - async publish(card: Card) { - const backgroundPageCopyCommand: ExtensionToBackgroundPageCommand = { - sender: 'asbplayer-extension-to-background-page', - message: { ...card, command: 'copy' }, - }; - const tabId = await this._backgroundPageManager.tabId(); + async publish(card: Card, postMineAction?: PostMineAction, tabId?: number, src?: string) { + const id = uuidv4(); + const savePromise = this._saveCardToRepository(id, card); - if (tabId !== undefined) { - chrome.tabs.sendMessage(tabId, backgroundPageCopyCommand); + if (tabId === undefined || src === undefined) { + return; + } + + if (postMineAction == PostMineAction.showAnkiDialog) { + const showAnkiUiCommand: ExtensionToVideoCommand = { + sender: 'asbplayer-extension-to-video', + message: { + ...card, + id, + command: 'show-anki-ui', + }, + src, + }; + + chrome.tabs.sendMessage(tabId, showAnkiUiCommand); + } else if (postMineAction == PostMineAction.updateLastCard) { + const cardName = await updateLastCard( + (await this._settingsProvider.get(ankiSettingsKeys)) as AnkiSettings, + card.subtitle, + card.surroundingSubtitles, + card.audio, + card.image, + sourceString(card.subtitleFileName, card.mediaTimestamp), + card.url + ); + + const cardUpdatedCommand: ExtensionToVideoCommand = { + sender: 'asbplayer-extension-to-video', + message: { + command: 'card-updated', + cardName: `${cardName}`, + subtitle: card.subtitle, + surroundingSubtitles: card.surroundingSubtitles, + audio: card.audio, + image: card.image, + url: card.url, + }, + src, + }; + + chrome.tabs.sendMessage(tabId, cardUpdatedCommand); + } else if (postMineAction == PostMineAction.none) { + savePromise.then((saved: boolean) => { + if (saved) { + const cardSavedCommand: ExtensionToVideoCommand = { + sender: 'asbplayer-extension-to-video', + message: { + command: 'card-saved', + text: card.subtitle.text || humanReadableTime(card.mediaTimestamp), + }, + src: src, + }; + + chrome.tabs.sendMessage(tabId, cardSavedCommand); + } + }); + } + } + + private async _saveCardToRepository(id: string, card: Card) { + try { + const backgroundPageCopyCommand: ExtensionToBackgroundPageCommand = { + sender: 'asbplayer-extension-to-background-page', + message: { ...card, id, command: 'copy' }, + }; + const tabId = await this._backgroundPageManager.tabId(); + + if (tabId !== undefined) { + return await chrome.tabs.sendMessage(tabId, backgroundPageCopyCommand); + } + + return false; + } catch (e) { + console.error(e); + return false; } } }