From cb69766bfb1b1214834e9ff1c5a060edf2c0a041 Mon Sep 17 00:00:00 2001 From: Curt Grimes Date: Sun, 14 Jun 2020 22:59:06 -0500 Subject: [PATCH] Add test Zoom integration --- app/api/routes/index.js | 1 + app/api/routes/zoom/index.js | 26 + app/lang/en-US.js | 497 ++++++++++-------- app/package-lock.json | 2 +- app/package.json | 2 +- app/pages/captioner.vue | 88 +++- app/pages/captioner/settings.vue | 143 +++-- .../captioner/settings/experiments/index.vue | 68 ++- .../captioner/settings/webhooks/index.vue | 55 +- app/pages/captioner/settings/zoom/index.vue | 84 +++ app/store/actions.js | 8 + app/store/captioner.js | 408 +++++++------- app/store/mutations.js | 13 + app/store/settingsState.js | 5 + 14 files changed, 875 insertions(+), 525 deletions(-) create mode 100644 app/api/routes/zoom/index.js create mode 100755 app/pages/captioner/settings/zoom/index.vue diff --git a/app/api/routes/index.js b/app/api/routes/index.js index 7d628a1..f9e8174 100644 --- a/app/api/routes/index.js +++ b/app/api/routes/index.js @@ -5,5 +5,6 @@ routes.use('/charges', require('./charges')); routes.use('/storage', require('./storage')); routes.use('/fonts', require('./fonts')); routes.use('/patreon', require('./patreon')); +routes.use('/zoom', require('./zoom')); module.exports = routes; diff --git a/app/api/routes/zoom/index.js b/app/api/routes/zoom/index.js new file mode 100644 index 0000000..c8ff63f --- /dev/null +++ b/app/api/routes/zoom/index.js @@ -0,0 +1,26 @@ +const zoom = require('express').Router(); +const axios = require('axios'); + +zoom.post('/api', async (req, res) => { + const { apiPath, transcript } = req.body; + if (!apiPath || !transcript) { + return res.status(400).send('Incomplete request: ' + req.body); + } + + axios + .post(apiPath, transcript, { + headers: { 'Content-Type': 'text/plain' }, + }) + .then((response) => { + // Response + console.log(transcript); + console.log(response.status); + res.sendStatus(200); + }) + .catch((e) => { + console.error(e); + res.sendStatus(500); + }); +}); + +module.exports = zoom; diff --git a/app/lang/en-US.js b/app/lang/en-US.js index 34610b8..9575d19 100644 --- a/app/lang/en-US.js +++ b/app/lang/en-US.js @@ -1,300 +1,333 @@ export default { common: { - ok: "Ok", - cancel: "Cancel", - clear: "Clear", - search: "Search...", - close: "Close", - add: "Add", - remove: "Remove", - off: "Off", - on: "On", - update: "Update", - setup: "Setup", - setUpVerb: "Set Up", - showSetup: "Show Setup", - hideSetup: "Hide Setup", - loading: "Loading", - install: "Install", - next: "Next", - done: "Done", - dismiss: "Dismiss" + ok: 'Ok', + cancel: 'Cancel', + clear: 'Clear', + search: 'Search...', + close: 'Close', + add: 'Add', + remove: 'Remove', + off: 'Off', + on: 'On', + update: 'Update', + setup: 'Setup', + setUpVerb: 'Set Up', + showSetup: 'Show Setup', + hideSetup: 'Hide Setup', + loading: 'Loading', + install: 'Install', + next: 'Next', + done: 'Done', + dismiss: 'Dismiss', }, app: { - webCaptioner: "Web Captioner", - description: "Real-time captioning for your event, speech, classroom lecture, or church service." + webCaptioner: 'Web Captioner', + description: + 'Real-time captioning for your event, speech, classroom lecture, or church service.', }, captioner: { volumeMeter: { - tooLoud: "Too loud", - tooQuiet: "Too quiet" + tooLoud: 'Too loud', + tooQuiet: 'Too quiet', }, clearTranscript: { - title: "Clear transcript?", - ok: "Clear Transcript" + title: 'Clear transcript?', + ok: 'Clear Transcript', }, saveToFile: { - title: "Save to File", - description: "Save your current transcript to a file.", - transcriptEmptyMessage: "Psst... you know you don't have anything to save yet, right?", - textFile: "Text File", - text: "Text", - wordDocument: "Word Document", - word: "Word" - } + title: 'Save to File', + description: 'Save your current transcript to a file.', + transcriptEmptyMessage: + "Psst... you know you don't have anything to save yet, right?", + textFile: 'Text File', + text: 'Text', + wordDocument: 'Word Document', + word: 'Word', + }, }, receiver: { splash: { - captionsWillBeginShortly: "Captions will begin shortly.", - connected: "Connected!", - useThisCodeToConnect: "Use this code to connect." - } + captionsWillBeginShortly: 'Captions will begin shortly.', + connected: 'Connected!', + useThisCodeToConnect: 'Use this code to connect.', + }, }, googleCast: { - cast: "Cast", - connecting: "Connecting", - castingToReceiver: "Casting to {receiverName}", - castingFailed: "Casting Failed", - unableToCast: "Unable to Cast", - pleaseTryAgain: "Please try again." + cast: 'Cast', + connecting: 'Connecting', + castingToReceiver: 'Casting to {receiverName}', + castingFailed: 'Casting Failed', + unableToCast: 'Unable to Cast', + pleaseTryAgain: 'Please try again.', }, navbar: { captioner: { - startCaptioning: "Start Captioning", - stopCaptioning: "Stop Captioning", + startCaptioning: 'Start Captioning', + stopCaptioning: 'Stop Captioning', listeningToMicrophone: 'Listening · {microphoneName}', - listening: "Listening" + listening: 'Listening', }, menu: { - about: "@:settings.about.about", - blog: "Blog", - helpCenter: "Help Center", - community: "Community", - donate: "Donate", - feedback: "Feedback", - newWindow: "New Window", - newWindowDescription: "@:settings.controls.showNewWindow", - saveToFile: "@:captioner.saveToFile.title", - settings: "Settings", - menu: "Menu", + about: '@:settings.about.about', + blog: 'Blog', + helpCenter: 'Help Center', + community: 'Community', + donate: 'Donate', + feedback: 'Feedback', + newWindow: 'New Window', + newWindowDescription: '@:settings.controls.showNewWindow', + saveToFile: '@:captioner.saveToFile.title', + settings: 'Settings', + menu: 'Menu', }, - vmixNotConnected: "vMix Not Connected" + vmixNotConnected: 'vMix Not Connected', }, settings: { - settings: "Settings", - general: "General", - integrations: "Integrations", - other: "Other", + settings: 'Settings', + general: 'General', + integrations: 'Integrations', + other: 'Other', about: { - about: "About", - learnMore: "Learn More", + about: 'About', + learnMore: 'Learn More', whatsNew: "What's New", whatsNewInWebCaptioner: "What's New in @:app.webCaptioner", - getStarted: "Get Started" + getStarted: 'Get Started', }, eventLog: { - eventLog: "Event Log", + eventLog: 'Event Log', instructions: { - i0: "Reproduce the issue you were having, and then copy this log and {sendItToMeOnFacebook}. The log includes the content of your current transcription, if any. You can leave this page and continue to use Web Captioner like normal.", - i1: "For performance reasons, logging will automatically shut off after {loggingDurationMinutes} minutes. You can still copy the log after it shuts off as long as you don't reload the page.", - sendItToMeOnFacebook: "send it to me on Facebook" + i0: + 'Reproduce the issue you were having, and then copy this log and {sendItToMeOnFacebook}. The log includes the content of your current transcription, if any. You can leave this page and continue to use Web Captioner like normal.', + i1: + "For performance reasons, logging will automatically shut off after {loggingDurationMinutes} minutes. You can still copy the log after it shuts off as long as you don't reload the page.", + sendItToMeOnFacebook: 'send it to me on Facebook', }, - copyLog: "Copy Log", - stopLogging: "Stop Logging", - restartLogging: "Restart Logging", - loggingTurnsOffIn: "Logging turns off in {timeRemainingMinutes}:{timeRemainingSeconds}", - loggingOff: "Logging Off", - eventsHidden: "{notShowingCount} events are hidden, but they'll be included when you copy the log.", - autoScroll: "Auto scroll", - events: "No events | 1 event | {count} events" + copyLog: 'Copy Log', + stopLogging: 'Stop Logging', + restartLogging: 'Restart Logging', + loggingTurnsOffIn: + 'Logging turns off in {timeRemainingMinutes}:{timeRemainingSeconds}', + loggingOff: 'Logging Off', + eventsHidden: + "{notShowingCount} events are hidden, but they'll be included when you copy the log.", + autoScroll: 'Auto scroll', + events: 'No events | 1 event | {count} events', }, experiments: { - experiments: "Experiments", - description: "Be sure to help out and give me feedback about experiments! Email me at {email} or {messageMeOnFacebook}.", - messageMeOnFacebook: "message me on Facebook", - addExperimentConfirmation: 'Do you want to add the "{experimentName}" experiment?', - addExperiment: "Add Experiment", - experimentName: "Experiment Name", - alreadyAdded: 'You\'ve already added the "{alreadyAddedExperimentName}" experiment.', - addedExperiments: "Added Experiments" + experiments: 'Experiments', + description: + 'Be sure to help out and give me feedback about experiments! Email me at {email} or {messageMeOnFacebook}.', + messageMeOnFacebook: 'message me on Facebook', + addExperimentConfirmation: + 'Do you want to add the "{experimentName}" experiment?', + addExperiment: 'Add Experiment', + experimentName: 'Experiment Name', + alreadyAdded: + 'You\'ve already added the "{alreadyAddedExperimentName}" experiment.', + addedExperiments: 'Added Experiments', }, appearance: { - appearance: "Appearance", - preview: "Preview", - text: "Text", - textColor: "Text Color", - textColorInterim: "Interim Text Color", - useRegularTextColor: "Use Regular Text Color", - interimTextColorDescription: "During captioning, words that have just been recognized may change slightly while Web Captioner determines the context of the current phrase. Those words will be this color.", - fontFamily: "Font Family", - textSize: "Text Size", - lineHeight: "Line Height", - letterSpacing: "Letter Spacing", - capitalization: "Capitalization", - uppercase: "UPPERCASE", - firstLetterOfEachWord: "First Letter Of Each Word", - properNouns: "Proper nouns and the start of sentences", - properNounsDescription: 'Separate sentences are detected only when a puncuation mark like "period" or "question mark" is literally said.', - alignment: "Alignment", - horizontalAlignment: "Horizontal Alignment", - verticalAlignment: "Vertical Alignment", - full: "Full", - left: "Left", - middle: "Middle", - right: "Right", - top: "Top", - bottom: "Bottom", - lowerThird: "Lower Third", - padding: "Padding", - background: "Background", - backgroundColor: "Background Color", - backgroundOpacity: "Background Opacity", - textShadow: "Text Shadow", - shadowColor: "Shadow Color", - opacity: "Opacity", - blur: "Blur", - xPosition: "X Position", - yPosition: "Y Position" + appearance: 'Appearance', + preview: 'Preview', + text: 'Text', + textColor: 'Text Color', + textColorInterim: 'Interim Text Color', + useRegularTextColor: 'Use Regular Text Color', + interimTextColorDescription: + 'During captioning, words that have just been recognized may change slightly while Web Captioner determines the context of the current phrase. Those words will be this color.', + fontFamily: 'Font Family', + textSize: 'Text Size', + lineHeight: 'Line Height', + letterSpacing: 'Letter Spacing', + capitalization: 'Capitalization', + uppercase: 'UPPERCASE', + firstLetterOfEachWord: 'First Letter Of Each Word', + properNouns: 'Proper nouns and the start of sentences', + properNounsDescription: + 'Separate sentences are detected only when a puncuation mark like "period" or "question mark" is literally said.', + alignment: 'Alignment', + horizontalAlignment: 'Horizontal Alignment', + verticalAlignment: 'Vertical Alignment', + full: 'Full', + left: 'Left', + middle: 'Middle', + right: 'Right', + top: 'Top', + bottom: 'Bottom', + lowerThird: 'Lower Third', + padding: 'Padding', + background: 'Background', + backgroundColor: 'Background Color', + backgroundOpacity: 'Background Opacity', + textShadow: 'Text Shadow', + shadowColor: 'Shadow Color', + opacity: 'Opacity', + blur: 'Blur', + xPosition: 'X Position', + yPosition: 'Y Position', }, censor: { - censor: "Censor", - censorProfaneLanguage: "Censor profane language.", - usEnglishOnly: "Currently available for US English only.", + censor: 'Censor', + censorProfaneLanguage: 'Censor profane language.', + usEnglishOnly: 'Currently available for US English only.', censorProfaneLanguageDescription: { - text: "What's considered profane? {seeThisList} (note: profanity ahead!) If you need to censor additional words not included in this list, {useWordReplacements}.", - seeThisList: "See this list", - useWordReplacements: "use word replacements" + text: + "What's considered profane? {seeThisList} (note: profanity ahead!) If you need to censor additional words not included in this list, {useWordReplacements}.", + seeThisList: 'See this list', + useWordReplacements: 'use word replacements', }, - replaceCensoredWordsWith: "Replace censored words with", - nothing: "nothing — just omit them", - asterisks: "asterisks" + replaceCensoredWordsWith: 'Replace censored words with', + nothing: 'nothing — just omit them', + asterisks: 'asterisks', }, controls: { - controls: "Controls", - screenLayout: "Screen Layout", - default: "Default", - defaultDescription: "Regular-sized controls", - larger: "Larger", - largerDescription: "Larger controls and additional buttons for one-click saving and clearing the transcript", - keyboardShortcuts: "Shortcuts", - then: "then", - ctrl: "Ctrl", - shift: "Shift", - toggleCaptioning: "Toggle captioning on/off", - toggleFullscreen: "Toggle fullscreen mode on/off", - showNewWindow: "Show captions in a new window", - openSettings: "Open Settings", - increaseTextSize: "Increase text size", - decreaseTextSize: "Decrease text size", + controls: 'Controls', + screenLayout: 'Screen Layout', + default: 'Default', + defaultDescription: 'Regular-sized controls', + larger: 'Larger', + largerDescription: + 'Larger controls and additional buttons for one-click saving and clearing the transcript', + keyboardShortcuts: 'Shortcuts', + then: 'then', + ctrl: 'Ctrl', + shift: 'Shift', + toggleCaptioning: 'Toggle captioning on/off', + toggleFullscreen: 'Toggle fullscreen mode on/off', + showNewWindow: 'Show captions in a new window', + openSettings: 'Open Settings', + increaseTextSize: 'Increase text size', + decreaseTextSize: 'Decrease text size', openSave: 'Open "@:captioner.saveToFile.title" dialog', - clearTranscript: "Clear transcript", - listKeyboardShortcuts: "List keyboard shortcuts" + clearTranscript: 'Clear transcript', + listKeyboardShortcuts: 'List keyboard shortcuts', }, language: { - language: "Language", - interface: "Web Captioner Interface Language", - interfaceDescription: "Select a language for the Web Captioner interface (menus, messages, and settings pages).", - wouldYouLikeYourLanguage: "Would you like your language here? {contactWebCaptionerOnFacebook} if you're interested in helping to translate Web Captioner. Thanks for all of your awesome help! {heartIcon}", - contactWebCaptionerOnFacebook: "Contact Web Captioner on Facebook", - spoken: "Spoken Language", + language: 'Language', + interface: 'Web Captioner Interface Language', + interfaceDescription: + 'Select a language for the Web Captioner interface (menus, messages, and settings pages).', + wouldYouLikeYourLanguage: + "Would you like your language here? {contactWebCaptionerOnFacebook} if you're interested in helping to translate Web Captioner. Thanks for all of your awesome help! {heartIcon}", + contactWebCaptionerOnFacebook: 'Contact Web Captioner on Facebook', + spoken: 'Spoken Language', spokenDescription: { - text: "Web Captioner will recognize speech in this language. Learn more about {supportedLanguagesAndDialects}.", - supportedLanguagesAndDialects: "supported languages and dialects" + text: + 'Web Captioner will recognize speech in this language. Learn more about {supportedLanguagesAndDialects}.', + supportedLanguagesAndDialects: 'supported languages and dialects', }, list: { - "en-US": "English (United States)", - "pt-BR": "Portuguese (Brazil)" - } + 'en-US': 'English (United States)', + 'pt-BR': 'Portuguese (Brazil)', + }, }, remoteDisplays: { - remoteDisplays: "Remote Displays" + remoteDisplays: 'Remote Displays', }, wordReplacements: { - wordReplacements: "Word Replacements", - description: "Replace or hide specific words or phrases during captioning.", - wordOrPhraseToReplace: "Word or Phrase to Replace", - wordOrPhraseToReplaceSentenceCase: "Word or phrase to replace", - wordOrPhraseToReplaceDescription: "Separate multiple words or phrases with commas. Not case sensitive.", - replaceWith: "Replace With", - replaceWithSentenceCase: "Replace with", - addAnother: "Add Another" + wordReplacements: 'Word Replacements', + description: + 'Replace or hide specific words or phrases during captioning.', + wordOrPhraseToReplace: 'Word or Phrase to Replace', + wordOrPhraseToReplaceSentenceCase: 'Word or phrase to replace', + wordOrPhraseToReplaceDescription: + 'Separate multiple words or phrases with commas. Not case sensitive.', + replaceWith: 'Replace With', + replaceWithSentenceCase: 'Replace with', + addAnother: 'Add Another', }, vmix: { - vmix: "vMix", + vmix: 'vMix', description: { - text: "{vMix} is a popular software video mixer and switcher. You can send text directly to vMix and display it in a title input. You can then use vMix's font and color controls to style captioned text. {visitTheHelpCenter} to learn more.", - visitTheHelpCenter: "Visit the Help Center" + text: + "{vMix} is a popular software video mixer and switcher. You can send text directly to vMix and display it in a title input. You can then use vMix's font and color controls to style captioned text. {visitTheHelpCenter} to learn more.", + visitTheHelpCenter: 'Visit the Help Center', }, - connectedToVmix: "Connected to vMix!", - connected: "Connected", - captionsWillAppear: "{startCaptioning} and your captions will now appear in vMix.", - startCaptioning: "Start captioning", - connectToVmix: "Connect to vMix", - sendTestMessage: "Send Test Message", - sent: "Sent!", - step1: "Step 1", - step2: "Step 2", - step3: "Step 3", - completeStep1First: "Complete step 1 first", - completeStep2First: "Complete step 2 first", - completeSteps1And2First: "Complete steps 1 and 2 first", - installChromeExtension: "Install Chrome Extension", - installChromeExtensionDescription: "The Web Captioner Connector extension for Google Chrome lets Web Captioner connect to vMix.", - addToChrome: "Add to Chrome", - extensionInstalled: "Extension Installed", - extensionNotInstalled: "Extension not installed.", - vmixWebController: "vMix Web Controller", - webControllerAddress: "vMix Web Controller Address", - webController: "Web Controller", - enableVmixWebController: "Enable the vMix Web Controller", - enableVmixWebControllerInstructions: "In vMix, go to {settingMenu}. Check the box to enable the {webController}. Specify a port number or accept the default.", - enableVmixWebControllerSettingMenu: "Settings > Web Controller", - provideAddress: "Provide the address that appears in vMix:", - example: "Example:", - cannotConnect: 'Cannot connect to vMix at "{webControllerAddress}". Make sure Web Controller is enabled in vMix and that you\'ve copied over the website address correctly. It should look something like this: http://192.168.1.1:8080', - import: "Import", - importTitleTemplate: "Import Title Template", - webCaptionerTitleTemplate: "Web Captioner Title Template", - importTitleTemplateLonger: "Import the Web Captioner Title Template into vMix", + connectedToVmix: 'Connected to vMix!', + connected: 'Connected', + captionsWillAppear: + '{startCaptioning} and your captions will now appear in vMix.', + startCaptioning: 'Start captioning', + connectToVmix: 'Connect to vMix', + sendTestMessage: 'Send Test Message', + sent: 'Sent!', + step1: 'Step 1', + step2: 'Step 2', + step3: 'Step 3', + completeStep1First: 'Complete step 1 first', + completeStep2First: 'Complete step 2 first', + completeSteps1And2First: 'Complete steps 1 and 2 first', + installChromeExtension: 'Install Chrome Extension', + installChromeExtensionDescription: + 'The Web Captioner Connector extension for Google Chrome lets Web Captioner connect to vMix.', + addToChrome: 'Add to Chrome', + extensionInstalled: 'Extension Installed', + extensionNotInstalled: 'Extension not installed.', + vmixWebController: 'vMix Web Controller', + webControllerAddress: 'vMix Web Controller Address', + webController: 'Web Controller', + enableVmixWebController: 'Enable the vMix Web Controller', + enableVmixWebControllerInstructions: + 'In vMix, go to {settingMenu}. Check the box to enable the {webController}. Specify a port number or accept the default.', + enableVmixWebControllerSettingMenu: 'Settings > Web Controller', + provideAddress: 'Provide the address that appears in vMix:', + example: 'Example:', + cannotConnect: + 'Cannot connect to vMix at "{webControllerAddress}". Make sure Web Controller is enabled in vMix and that you\'ve copied over the website address correctly. It should look something like this: http://192.168.1.1:8080', + import: 'Import', + importTitleTemplate: 'Import Title Template', + webCaptionerTitleTemplate: 'Web Captioner Title Template', + importTitleTemplateLonger: + 'Import the Web Captioner Title Template into vMix', importTitleTemplateInstructions: { - i0: "Download the {webCaptionerTitleTemplate} for vMix.", - i1: "In vMix, go to {addInputSetting}.", - i2: "In the {inputSelect} window, click {browse} in the upper right and open the Web Captioner title template you downloaded.", - i3: "The title will appear in the {recent} tab. Double-click it.", - i4: "In the Title Editor that appears, optionally customize font and text size. Close it when you are finished." + i0: 'Download the {webCaptionerTitleTemplate} for vMix.', + i1: 'In vMix, go to {addInputSetting}.', + i2: + 'In the {inputSelect} window, click {browse} in the upper right and open the Web Captioner title template you downloaded.', + i3: 'The title will appear in the {recent} tab. Double-click it.', + i4: + 'In the Title Editor that appears, optionally customize font and text size. Close it when you are finished.', }, - cantFindTemplate: "Web Captioner can connect to vMix, but it can't find the Web Captioner title template in an input.", - testAndFinishSetup: "Test and Finish Setup" + cantFindTemplate: + "Web Captioner can connect to vMix, but it can't find the Web Captioner title template in an input.", + testAndFinishSetup: 'Test and Finish Setup', }, webhooks: { - webhooks: "Webhooks" + webhooks: 'Webhooks', + }, + zoom: { + zoom: 'Zoom', }, exportRestore: { - exportRestoreSettings: "Export/Restore Settings", - restore: "Restore", - restoreDescription: "Restore settings (appearance, censor settings, word replacements, vMix settings, etc.) from a settings file you previously exported.", - restoreSettingsQuestion: "Restore settings from this file?", + exportRestoreSettings: 'Export/Restore Settings', + restore: 'Restore', + restoreDescription: + 'Restore settings (appearance, censor settings, word replacements, vMix settings, etc.) from a settings file you previously exported.', + restoreSettingsQuestion: 'Restore settings from this file?', somethingWrongWithFile: "It looks like something's wrong with that file.", - restoredSettings: "Settings Restored", - reset: "Reset", - resetDescription: "Reset all of your settings.", - resetSettingsQuestion: "Reset all your settings?", - settingsWillBeLost: "All of your current settings will be lost.", - settingsReset: "Settings Reset", - export: "Export", - exportDescription: "Your settings will be saved locally as a JSON file." - } + restoredSettings: 'Settings Restored', + reset: 'Reset', + resetDescription: 'Reset all of your settings.', + resetSettingsQuestion: 'Reset all your settings?', + settingsWillBeLost: 'All of your current settings will be lost.', + settingsReset: 'Settings Reset', + export: 'Export', + exportDescription: 'Your settings will be saved locally as a JSON file.', + }, }, incompatibleBrowser: { - incompatibleBrowser: "Incompatible Browser", + incompatibleBrowser: 'Incompatible Browser', message: { - i0: "Sorry, but currently Web Captioner only works in Google Chrome.", - i1: "You can still look around and play with settings, but in order to start captioning, you'll have to {switchToGoogleChrome}.", - i2: "If compatibility with browsers other than Chrome is important to you, {castAVoteHere}.", - switchToGoogleChrome: "switch to Google Chrome", - castAVoteHere: "cast a vote here" + i0: 'Sorry, but currently Web Captioner only works in Google Chrome.', + i1: + "You can still look around and play with settings, but in order to start captioning, you'll have to {switchToGoogleChrome}.", + i2: + 'If compatibility with browsers other than Chrome is important to you, {castAVoteHere}.', + switchToGoogleChrome: 'switch to Google Chrome', + castAVoteHere: 'cast a vote here', }, - whyJustChrome: "Why just Chrome?", - lookAroundAnyway: "Look Around Anyway" - } + whyJustChrome: 'Why just Chrome?', + lookAroundAnyway: 'Look Around Anyway', + }, }; diff --git a/app/package-lock.json b/app/package-lock.json index 0003274..2fca4e3 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "webcaptioner", - "version": "1.0.0", + "version": "2.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/app/package.json b/app/package.json index 674589a..bcc602a 100644 --- a/app/package.json +++ b/app/package.json @@ -105,4 +105,4 @@ "webpack-babel-external-helpers": "^0.1.0", "whatwg-fetch": "^2.0.4" } -} \ No newline at end of file +} diff --git a/app/pages/captioner.vue b/app/pages/captioner.vue index 5390afb..b7cf869 100755 --- a/app/pages/captioner.vue +++ b/app/pages/captioner.vue @@ -1,5 +1,9 @@ @@ -325,6 +331,82 @@ export default { } } }); + + // Zoom integration + let zoomTranscriptBuffer = []; + let zoomTranscriptCurrentlyDisplayed = []; + const zoomMaxCharactersPerLine = 100; + this.$store.subscribe((mutation, state) => { + if ( + this.$store.state.settings.integrations.zoom.on && + this.$store.state.settings.integrations.zoom.url && + [ + 'captioner/APPEND_TRANSCRIPT_STABILIZED', + 'captioner/APPEND_TRANSCRIPT_FINAL', + 'captioner/CLEAR_TRANSCRIPT', + ].includes(mutation.type) + ) { + if (mutation.type === 'captioner/APPEND_TRANSCRIPT_STABILIZED') { + zoomTranscriptBuffer.push(mutation.payload.transcript); + } else if ( + (mutation.type === 'captioner/APPEND_TRANSCRIPT_FINAL' && + mutation.payload.clearLimitedSpaceReceivers) || + mutation.type === 'captioner/CLEAR_TRANSCRIPT' + ) { + // Clear the output + zoomTranscriptBuffer = []; + } + } + }); + + setInterval(() => { + if (!zoomTranscriptBuffer.length) { + return; + } + + // Consume the buffer + zoomTranscriptCurrentlyDisplayed.push(...zoomTranscriptBuffer); + zoomTranscriptBuffer = []; + + let apiPath = new URL(this.$store.state.settings.integrations.zoom.url); + apiPath.searchParams.append( + 'seq', + this.$store.state.settings.integrations.zoom.lastSequenceNumber + ); + apiPath.searchParams.append( + 'lang', + this.$store.state.settings.locale.from || 'en-US' + ); + + const transcript = zoomTranscriptCurrentlyDisplayed.join(' '); + this.$axios.$post('/api/zoom/api', { + apiPath, + transcript, + }); + this.$store.commit('INCREMENT_ZOOM_SEQUENCE_NUMBER'); + + // If the transcript reached its max length since + // the last line break (or start of the string), + // add a new line break + const charactersSinceLastLineBreak = + transcript.length - (transcript.lastIndexOf('\n') + 1); + if (charactersSinceLastLineBreak >= zoomMaxCharactersPerLine) { + zoomTranscriptCurrentlyDisplayed.push('\n'); + } + + // Enforce two lines max by removing content before the first line break once we + // have two line breaks + if ( + zoomTranscriptCurrentlyDisplayed.filter((word) => word === '\n') + .length >= 2 + ) { + const firstLineBreakIndex = zoomTranscriptCurrentlyDisplayed.findIndex( + (word) => word === '\n' + ); + + zoomTranscriptCurrentlyDisplayed.splice(0, firstLineBreakIndex + 1); + } + }, 1000); }, watch: { socketConnected: function() { diff --git a/app/pages/captioner/settings.vue b/app/pages/captioner/settings.vue index 58d00b2..10df2cf 100755 --- a/app/pages/captioner/settings.vue +++ b/app/pages/captioner/settings.vue @@ -1,5 +1,10 @@