diff --git a/.github/workflows/functions-log.yaml b/.github/workflows/functions-log.yaml index c050c87a92..1496581ad2 100644 --- a/.github/workflows/functions-log.yaml +++ b/.github/workflows/functions-log.yaml @@ -2,16 +2,16 @@ name: functions-log on: push: branches: - - main + - main paths: - - 'functions/log/**' + - "functions/log/**" pull_request: paths: - - 'functions/log/**' + - "functions/log/**" pull_request_target: types: [labeled] schedule: - - cron: '0 0 * * 0' + - cron: "0 0 * * 0" jobs: test: strategy: @@ -19,55 +19,55 @@ jobs: # Each package in this list will be tested independently. # New packages must be manually added to this list. package: - - 'functions/log' - - 'functions/log/helloWorld' - - 'functions/log/processEntry' + - "functions/log" + - "functions/log/helloWorld" + - "functions/log/processEntry" if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} runs-on: ubuntu-latest timeout-minutes: 60 permissions: - contents: 'write' - pull-requests: 'write' - id-token: 'write' + contents: "write" + pull-requests: "write" + id-token: "write" steps: - - uses: actions/checkout@v3 - with: - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - uses: google-github-actions/auth@v0.8.0 - with: - workload_identity_provider: 'projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' - service_account: 'kokoro-system-test@long-door-651.iam.gserviceaccount.com' - create_credentials_file: 'true' - access_token_lifetime: 600s - - uses: actions/setup-node@v3 - with: - node-version: 14 - - run: npm install - working-directory: ${{ matrix.package }} - - run: npm test -- --reporter xunit --reporter-option output=sponge_log.xml --reporter-option suiteName="${{ matrix.package }}" - working-directory: ${{ matrix.package }} - env: - MOCHA_REPORTER: xunit - - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - await github.rest.issues.removeLabel({ - name: 'actions:force-run', - owner: 'GoogleCloudPlatform', - repo: 'nodejs-docs-samples', - issue_number: context.payload.pull_request.number - }); - } catch (e) { - if (!e.message.includes('Label does not exist')) { - throw e; + - uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + - uses: google-github-actions/auth@v0.8.0 + with: + workload_identity_provider: "projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider" + service_account: "kokoro-system-test@long-door-651.iam.gserviceaccount.com" + create_credentials_file: "true" + access_token_lifetime: 600s + - uses: actions/setup-node@v3 + with: + node-version: 14 + - run: npm install + working-directory: ${{ matrix.package }} + - run: npm test -- --reporter xunit --reporter-option output=sponge_log.xml --reporter-option suiteName="${{ matrix.package }}" + working-directory: ${{ matrix.package }} + env: + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } } - } - - if: ${{ github.event_name == 'schedule' && always() }} - run: | - curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L - chmod +x ./flakybot - ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + - if: ${{ github.event_name == 'schedule' && always() }} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/functions-tips.yaml b/.github/workflows/functions-tips.yaml index fb6390f696..dcd3e7a021 100644 --- a/.github/workflows/functions-tips.yaml +++ b/.github/workflows/functions-tips.yaml @@ -2,16 +2,16 @@ name: functions-tips on: push: branches: - - main + - main paths: - - 'functions/tips/**' + - "functions/tips/**" pull_request: paths: - - 'functions/tips/**' + - "functions/tips/**" pull_request_target: types: [labeled] schedule: - - cron: '0 0 * * 0' + - cron: "0 0 * * 0" jobs: test: strategy: @@ -19,58 +19,58 @@ jobs: # Each package in this list will be tested independently. # New packages must be manually added to this list. package: - - 'functions/tips/avoidInfiniteRetries' - - 'functions/tips/connectionPools' + - "functions/tips/avoidInfiniteRetries" + - "functions/tips/connectionPools" # - 'functions/tips/gcpApiCall' # TODO(muncus): reenable once tests are suitable for CI/CD. - - 'functions/tips/lazyGlobals' - - 'functions/tips/retry' - - 'functions/tips/scopeDemo' + - "functions/tips/lazyGlobals" + - "functions/tips/retry" + - "functions/tips/scopeDemo" if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} runs-on: ubuntu-latest timeout-minutes: 60 permissions: - contents: 'write' - pull-requests: 'write' - id-token: 'write' + contents: "write" + pull-requests: "write" + id-token: "write" steps: - - uses: actions/checkout@v3 - with: - ref: ${{github.event.pull_request.head.ref}} - repository: ${{github.event.pull_request.head.repo.full_name}} - - uses: google-github-actions/auth@v0.8.0 - with: - workload_identity_provider: 'projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' - service_account: 'kokoro-system-test@long-door-651.iam.gserviceaccount.com' - create_credentials_file: 'true' - access_token_lifetime: 600s - - uses: actions/setup-node@v3 - with: - node-version: 14 - - run: npm install - working-directory: ${{ matrix.package }} - - run: npm test -- --reporter xunit --reporter-option output=sponge_log.xml --reporter-option suiteName="${{ matrix.package }}" - working-directory: ${{ matrix.package }} - env: - MOCHA_REPORTER: xunit - - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - await github.rest.issues.removeLabel({ - name: 'actions:force-run', - owner: 'GoogleCloudPlatform', - repo: 'nodejs-docs-samples', - issue_number: context.payload.pull_request.number - }); - } catch (e) { - if (!e.message.includes('Label does not exist')) { - throw e; + - uses: actions/checkout@v3 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + - uses: google-github-actions/auth@v0.8.0 + with: + workload_identity_provider: "projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider" + service_account: "kokoro-system-test@long-door-651.iam.gserviceaccount.com" + create_credentials_file: "true" + access_token_lifetime: 600s + - uses: actions/setup-node@v3 + with: + node-version: 14 + - run: npm install + working-directory: ${{ matrix.package }} + - run: npm test -- --reporter xunit --reporter-option output=sponge_log.xml --reporter-option suiteName="${{ matrix.package }}" + working-directory: ${{ matrix.package }} + env: + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } } - } - - if: ${{ github.event_name == 'schedule' && always() }} - run: | - curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L - chmod +x ./flakybot - ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} + - if: ${{ github.event_name == 'schedule' && always() }} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/mediatranslation.yaml b/.github/workflows/mediatranslation.yaml new file mode 100644 index 0000000000..e28d024cb2 --- /dev/null +++ b/.github/workflows/mediatranslation.yaml @@ -0,0 +1,67 @@ +name: media-translation +on: + push: + branches: + - main + paths: + - "mediatranslation/**" + pull_request: + paths: + - "mediatranslation/**" + pull_request_target: + types: [labeled] + schedule: + - cron: "0 0 * * 0" +jobs: + test: + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: "write" + pull-requests: "write" + id-token: "write" + steps: + - uses: actions/checkout@v3.1.0 + with: + ref: ${{github.event.pull_request.head.ref}} + repository: ${{github.event.pull_request.head.repo.full_name}} + - uses: "google-github-actions/auth@v0.8.3" + with: + workload_identity_provider: "projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider" + service_account: "kokoro-system-test@long-door-651.iam.gserviceaccount.com" + create_credentials_file: "true" + access_token_lifetime: 600s + - uses: actions/setup-node@v3.5.1 + with: + node-version: 16 + - run: npm install + working-directory: mediatranslation + - run: npm test + working-directory: mediatranslation + env: + MOCHA_REPORTER_SUITENAME: mediatranslation + MOCHA_REPORTER_OUTPUT: mediatranslation_sponge_log.xml + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } + } + - if: ${{ github.event_name == 'schedule'}} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/workflows.json b/.github/workflows/workflows.json index 149b84bbb7..5fc1908212 100644 --- a/.github/workflows/workflows.json +++ b/.github/workflows/workflows.json @@ -40,6 +40,7 @@ "healthcare/dicom", "healthcare/fhir", "healthcare/hl7v2", + "mediatranslation", "monitoring/opencensus", "monitoring/prometheus", "datacatalog/cloud-client", diff --git a/CODEOWNERS b/CODEOWNERS index fba3bb4a41..71527b6db8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -17,6 +17,13 @@ cloud-tasks @GoogleCloudPlatform/torus-dpe @GoogleCloudPlatform/nodejs-samples-r workflows @GoogleCloudPlatform/torus-dpe @GoogleCloudPlatform/nodejs-samples-reviewers eventarc @GoogleCloudPlatform/torus-dpe @GoogleCloudPlatform/nodejs-samples-reviewers +# Data & AI +<<<<<<< HEAD +mediatranslation @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +======= +media-translation @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +>>>>>>> b3c8b087 (update CODEOWNERS) + # Other functions samples functions/scheduleinstance @askmeegs @GoogleCloudPlatform/nodejs-samples-reviewers functions/speech-to-speech @ricalo @GoogleCloudPlatform/nodejs-samples-reviewers diff --git a/mediatranslation/README.md b/mediatranslation/README.md new file mode 100644 index 0000000000..81483f1fdb --- /dev/null +++ b/mediatranslation/README.md @@ -0,0 +1,84 @@ +Google Cloud Platform logo + +# [Cloud Media Translation: Node.js Samples](https://github.com/GoogleCloudPlatform/nodejs-docs-samples) + +[![Open in Cloud Shell][shell_img]][shell_link] + + + +## Table of Contents + +- [Cloud Media Translation: Node.js Samples](#cloud-mediatranslation-nodejs-samples) + - [Table of Contents](#table-of-contents) + - [Before you begin](#before-you-begin) + - [Samples](#samples) + - [Quickstart](#quickstart) + - [Translate_from_file](#translate_from_file) + - [Translate_from_mic](#translate_from_mic) + +## Before you begin + +Before running the samples, make sure you've followed the steps outlined in +[Using the client library](https://github.com/googleapis/nodejs-media-translation#using-the-client-library). + +`npm install` + +`cd ..` + +## Samples + + + +### Quickstart + +View the [source code](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/mediatranslation/quickstart.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&page=editor&open_in_editor=mediatranslation/quickstart.js,mediatranslation/README.md) + +__Usage:__ + + +`node quickstart.js` + + +----- + + + + +### Translate_from_file + +View the [source code](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/mediatranslation/translate_from_file.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&page=editor&open_in_editor=mediatranslation/translate_from_file.js,mediatranslation/README.md) + +__Usage:__ + + +`node translate_from_file.js` + + +----- + + + + +### Translate_from_mic + +View the [source code](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/mediatranslation/translate_from_mic.js). + +[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&page=editor&open_in_editor=mediatranslation/translate_from_mic.js,mediatranslation/README.md) + +__Usage:__ + + +`node translate_from_mic.js` + + + + + + +[shell_img]: https://gstatic.com/cloudssh/images/open-btn.png +[shell_link]: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/nodejs-docs-samples&page=editor&open_in_editor=mediatranslation/README.md +[product-docs]: https://cloud.google.com/translate/media/docs/ diff --git a/mediatranslation/package.json b/mediatranslation/package.json new file mode 100644 index 0000000000..b183cd11d6 --- /dev/null +++ b/mediatranslation/package.json @@ -0,0 +1,24 @@ +{ + "name": "mediatranslation-samples", + "private": true, + "license": "Apache-2.0", + "author": "Google LLC", + "engines": { + "node": ">=12.0.0" + }, + "files": [ + "*.js" + ], + "scripts": { + "test": "c8 mocha --timeout 600000 test/*.js" + }, + "dependencies": { + "@google-cloud/media-translation": "^3.0.4", + "node-record-lpcm16": "1.0.1" + }, + "devDependencies": { + "c8": "^7.0.0", + "chai": "^4.2.0", + "mocha": "^8.0.0" + } +} diff --git a/mediatranslation/quickstart.js b/mediatranslation/quickstart.js new file mode 100644 index 0000000000..c72cb846fd --- /dev/null +++ b/mediatranslation/quickstart.js @@ -0,0 +1,101 @@ +// Copyright 2020, Google LLC. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Translate text from an audio file. + * @param {string} filename local path to + * @param {string} encoding the encoding of the audio rate, e.g. Linear16 + * @param {string} sourceLanguage language translating from, as BCP-47 code + * @param {string} targetLanguage languate translating to, as BCP-47 code + */ +function main(filename, encoding, sourceLanguage, targetLanguage) { + // [START mediatranslation_quickstart] + const fs = require('fs'); + + // Imports the Cloud Media Translation client library + const { + SpeechTranslationServiceClient, + } = require('@google-cloud/media-translation'); + + // Creates a client + const client = new SpeechTranslationServiceClient(); + + async function quickstart() { + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const filename = 'Local path to audio file, e.g. /path/to/audio.raw'; + // const encoding = 'Encoding of the audio file, e.g. LINEAR16'; + // const sourceLanguage = 'BCP-47 source language code, e.g. en-US'; + // const targetLanguage = 'BCP-47 target language code, e.g. es-ES'; + + const config = { + audioConfig: { + audioEncoding: encoding, + sourceLanguageCode: sourceLanguage, + targetLanguageCode: targetLanguage, + }, + }; + + // First request needs to have only a streaming config, no data. + const initialRequest = { + streamingConfig: config, + audioContent: null, + }; + + const readStream = fs.createReadStream(filename, { + highWaterMark: 4096, + encoding: 'base64', + }); + + const chunks = []; + readStream + .on('data', chunk => { + const request = { + streamingConfig: config, + audioContent: chunk.toString(), + }; + chunks.push(request); + }) + .on('close', () => { + // Config-only request should be first in stream of requests + stream.write(initialRequest); + for (let i = 0; i < chunks.length; i++) { + stream.write(chunks[i]); + } + stream.end(); + }); + + const stream = client.streamingTranslateSpeech().on('data', response => { + const {result} = response; + if (result.textTranslationResult.isFinal) { + console.log( + `\nFinal translation: ${result.textTranslationResult.translation}` + ); + console.log(`Final recognition result: ${result.recognitionResult}`); + } else { + console.log( + `\nPartial translation: ${result.textTranslationResult.translation}` + ); + console.log(`Partial recognition result: ${result.recognitionResult}`); + } + }); + + // [END mediatranslation_quickstart] + } + quickstart(); +} + +main(...process.argv.slice(2)); diff --git a/mediatranslation/resources/audio.raw b/mediatranslation/resources/audio.raw new file mode 100644 index 0000000000..5ebf79d3c9 Binary files /dev/null and b/mediatranslation/resources/audio.raw differ diff --git a/mediatranslation/test/quickstart.test.js b/mediatranslation/test/quickstart.test.js new file mode 100644 index 0000000000..c031c44129 --- /dev/null +++ b/mediatranslation/test/quickstart.test.js @@ -0,0 +1,31 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const execSync = require('child_process').execSync; +const cmd = 'node quickstart.js'; + +const filePath = path.join(__dirname, '..', 'resources/audio.raw'); +const exec = cmd => execSync(cmd, {encoding: 'utf-8'}); + +describe('Quickstart', () => { + it('should translate from a streamed file', async () => { + const stdout = exec(`${cmd} ${filePath} linear16 en-US es-ES`); + assert.include(stdout, 'Partial translation'); + }); +}); diff --git a/mediatranslation/test/translate_from_file.test.js b/mediatranslation/test/translate_from_file.test.js new file mode 100644 index 0000000000..193b3b6555 --- /dev/null +++ b/mediatranslation/test/translate_from_file.test.js @@ -0,0 +1,31 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const execSync = require('child_process').execSync; +const cmd = 'node translate_from_file.js'; + +const filePath = path.join(__dirname, '..', 'resources/audio.raw'); +const exec = cmd => execSync(cmd, {encoding: 'utf-8'}); + +describe('MediaTranslation', () => { + it('should translate from a streamed file', async () => { + const stdout = exec(`${cmd} ${filePath} linear16 en-US es-ES`); + assert.include(stdout, 'Partial translation'); + }); +}); diff --git a/mediatranslation/translate_from_file.js b/mediatranslation/translate_from_file.js new file mode 100644 index 0000000000..4fa6b718b1 --- /dev/null +++ b/mediatranslation/translate_from_file.js @@ -0,0 +1,102 @@ +// Copyright 2020, Google LLC. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +/** + * Translate text from an audio file. + * @param {string} filename local path to + * @param {string} encoding the encoding of the audio rate, e.g. Linear16 + * @param {string} sourceLanguage language translating from, as BCP-47 code + * @param {string} targetLanguage languate translating to, as BCP-47 code + */ +function main(filename, encoding, sourceLanguage, targetLanguage) { + // [START mediatranslation_translate_from_file] + const fs = require('fs'); + + // Imports the CLoud Media Translation client library + const { + SpeechTranslationServiceClient, + } = require('@google-cloud/media-translation'); + + // Creates a client + const client = new SpeechTranslationServiceClient(); + + async function translate_from_file() { + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const filename = 'Local path to audio file, e.g. /path/to/audio.raw'; + // const encoding = 'Encoding of the audio file, e.g. LINEAR16'; + // const sourceLanguage = 'BCP-47 source language code, e.g. en-US'; + // const targetLanguage = 'BCP-47 target language code, e.g. es-ES'; + + const config = { + audioConfig: { + audioEncoding: encoding, + sourceLanguageCode: sourceLanguage, + targetLanguageCode: targetLanguage, + }, + single_utterance: true, + }; + + // First request needs to have only a streaming config, no data. + const initialRequest = { + streamingConfig: config, + audioContent: null, + }; + + const readStream = fs.createReadStream(filename, { + highWaterMark: 4096, + encoding: 'base64', + }); + + const chunks = []; + readStream + .on('data', chunk => { + const request = { + streamingConfig: config, + audioContent: chunk.toString(), + }; + chunks.push(request); + }) + .on('close', () => { + // Config-only request should be first in stream of requests + stream.write(initialRequest); + for (let i = 0; i < chunks.length; i++) { + stream.write(chunks[i]); + } + stream.end(); + }); + + const stream = client.streamingTranslateSpeech().on('data', response => { + const {result} = response; + if (result.textTranslationResult.isFinal) { + console.log( + `\nFinal translation: ${result.textTranslationResult.translation}` + ); + console.log(`Final recognition result: ${result.recognitionResult}`); + } else { + console.log( + `\nPartial translation: ${result.textTranslationResult.translation}` + ); + console.log(`Partial recognition result: ${result.recognitionResult}`); + } + }); + + // [END mediatranslation_translate_from_file] + } + translate_from_file(); +} + +main(...process.argv.slice(2)); diff --git a/mediatranslation/translate_from_mic.js b/mediatranslation/translate_from_mic.js new file mode 100644 index 0000000000..37d18f578e --- /dev/null +++ b/mediatranslation/translate_from_mic.js @@ -0,0 +1,155 @@ +// Copyright 2020, Google LLC. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +'use strict'; + +/** + * This application demonstrates how to perform basic translation operations + * with the Google Cloud Media Translation API. + * + * Note: Correct microphone settings is required: check enclosed link, and make + * sure the following conditions are met: + * 1. SoX must be installed and available in your $PATH- it can be found here: + * http://sox.sourceforge.net/ + * 2. Microphone must be working + * 3. Encoding, sampleRateHertz, and # of channels must match header of audio file you're + * recording to. + * 4. Get Node-Record-lpcm16 https://www.npmjs.com/package/node-record-lpcm16 + * More Info: https://cloud.google.com/speech-to-text/docs/streaming-recognize + */ + +/** + * Translates audio streaming from a microphone + * @param {string} encoding the audio encoding codec + * @param {string} sampleRateHertz the sampling rate of the audio stream + * @param {string} sourceLanguageCode the language to translate from + * @param {string} targetLanguageCode the language to translate to + */ +function main(encoding, sampleRateHertz, sourceLanguage, targetLanguage) { + sampleRateHertz = Number(sampleRateHertz); + + // [START mediatranslation_translate_from_mic] + + // Allow user input from terminal + const readline = require('readline'); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + function doTranslationLoop() { + rl.question("Press any key to translate or 'q' to quit: ", answer => { + if (answer.toLowerCase() === 'q') { + rl.close(); + } else { + translateFromMicrophone(); + } + }); + } + + // Node-Record-lpcm16 + const recorder = require('node-record-lpcm16'); + + // Imports the Cloud Media Translation client library + const { + SpeechTranslationServiceClient, + } = require('@google-cloud/media-translation'); + + // Creates a client + const client = new SpeechTranslationServiceClient(); + + function translateFromMicrophone() { + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + //const encoding = 'linear16'; + //const sampleRateHertz = 16000; + //const sourceLanguage = 'Language to translate from, as BCP-47 locale'; + //const targetLanguage = 'Language to translate to, as BCP-47 locale'; + console.log('Begin speaking ...'); + + const config = { + audioConfig: { + audioEncoding: encoding, + sourceLanguageCode: sourceLanguage, + targetLanguageCode: targetLanguage, + }, + singleUtterance: true, + }; + + // First request needs to have only a streaming config, no data. + const initialRequest = { + streamingConfig: config, + audioContent: null, + }; + + let currentTranslation = ''; + let currentRecognition = ''; + // Create a recognize stream + const stream = client + .streamingTranslateSpeech() + .on('error', e => { + if (e.code && e.code === 4) { + console.log('Streaming translation reached its deadline.'); + } else { + console.log(e); + } + }) + .on('data', response => { + const {result, speechEventType} = response; + if (speechEventType === 'END_OF_SINGLE_UTTERANCE') { + console.log(`\nFinal translation: ${currentTranslation}`); + console.log(`Final recognition result: ${currentRecognition}`); + + stream.destroy(); + recording.stop(); + } else { + currentTranslation = result.textTranslationResult.translation; + currentRecognition = result.recognitionResult; + console.log(`\nPartial translation: ${currentTranslation}`); + console.log(`Partial recognition result: ${currentRecognition}`); + } + }); + + let isFirst = true; + // Start recording and send microphone input to the Media Translation API + const recording = recorder.record({ + sampleRateHertz: sampleRateHertz, + threshold: 0, //silence threshold + recordProgram: 'rec', + silence: '5.0', //seconds of silence before ending + }); + recording + .stream() + .on('data', chunk => { + if (isFirst) { + stream.write(initialRequest); + isFirst = false; + } + const request = { + streamingConfig: config, + audioContent: chunk.toString('base64'), + }; + if (!stream.destroyed) { + stream.write(request); + } + }) + .on('close', () => { + doTranslationLoop(); + }); + } + + doTranslationLoop(); + // [END mediatranslation_translate_from_mic] +} +main(...process.argv.slice(2));