diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml index 277cf3ed..7edf30c4 100644 --- a/.github/workflows/appstore-build-publish.yml +++ b/.github/workflows/appstore-build-publish.yml @@ -13,7 +13,7 @@ on: types: [published] env: - PHP_VERSION: 8.1 + PHP_VERSION: 8.2 jobs: build_and_publish: diff --git a/appinfo/info.xml b/appinfo/info.xml index bf8fad0b..ab271f6c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -62,7 +62,7 @@ Known providers: More details on how to set this up in the [admin docs](https://docs.nextcloud.com/server/latest/admin_manual/ai/index.html) ]]> - 2.10.0 + 2.11.0-dev agpl Julien Veyssier Assistant diff --git a/composer.lock b/composer.lock index 7abcf318..9f640298 100644 --- a/composer.lock +++ b/composer.lock @@ -143,16 +143,16 @@ }, { "name": "phpoffice/math", - "version": "0.2.0", + "version": "0.3.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/Math.git", - "reference": "fc2eb6d1a61b058d5dac77197059db30ee3c8329" + "reference": "fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/Math/zipball/fc2eb6d1a61b058d5dac77197059db30ee3c8329", - "reference": "fc2eb6d1a61b058d5dac77197059db30ee3c8329", + "url": "https://api.github.com/repos/PHPOffice/Math/zipball/fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a", + "reference": "fc31c8f57a7a81f962cbf389fd89f4d9d06fc99a", "shasum": "" }, "require": { @@ -189,50 +189,49 @@ ], "support": { "issues": "https://github.com/PHPOffice/Math/issues", - "source": "https://github.com/PHPOffice/Math/tree/0.2.0" + "source": "https://github.com/PHPOffice/Math/tree/0.3.0" }, - "time": "2024-08-12T07:30:45+00:00" + "time": "2025-05-29T08:31:49+00:00" }, { "name": "phpoffice/phpword", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/PHPOffice/PHPWord.git", - "reference": "8392134ce4b5dba65130ba956231a1602b848b7f" + "reference": "6d75328229bc93790b37e93741adf70646cea958" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/8392134ce4b5dba65130ba956231a1602b848b7f", - "reference": "8392134ce4b5dba65130ba956231a1602b848b7f", + "url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/6d75328229bc93790b37e93741adf70646cea958", + "reference": "6d75328229bc93790b37e93741adf70646cea958", "shasum": "" }, "require": { "ext-dom": "*", + "ext-gd": "*", "ext-json": "*", "ext-xml": "*", + "ext-zip": "*", "php": "^7.1|^8.0", - "phpoffice/math": "^0.2" + "phpoffice/math": "^0.3" }, "require-dev": { - "dompdf/dompdf": "^2.0", - "ext-gd": "*", + "dompdf/dompdf": "^2.0 || ^3.0", "ext-libxml": "*", - "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.3", - "mpdf/mpdf": "^8.1", + "mpdf/mpdf": "^7.0 || ^8.0", "phpmd/phpmd": "^2.13", - "phpstan/phpstan-phpunit": "@stable", + "phpstan/phpstan": "^0.12.88 || ^1.0.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", "phpunit/phpunit": ">=7.0", "symfony/process": "^4.4 || ^5.0", "tecnickcom/tcpdf": "^6.5" }, "suggest": { "dompdf/dompdf": "Allows writing PDF", - "ext-gd2": "Allows adding images", "ext-xmlwriter": "Allows writing OOXML and ODF", - "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template", - "ext-zip": "Allows writing OOXML and ODF" + "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template" }, "type": "library", "autoload": { @@ -242,7 +241,7 @@ }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-3.0" + "LGPL-3.0-only" ], "authors": [ { @@ -298,9 +297,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PHPWord/issues", - "source": "https://github.com/PHPOffice/PHPWord/tree/1.3.0" + "source": "https://github.com/PHPOffice/PHPWord/tree/1.4.0" }, - "time": "2024-08-30T18:03:42+00:00" + "time": "2025-06-05T10:32:36+00:00" }, { "name": "ralouphie/mimey", @@ -790,12 +789,12 @@ "source": { "type": "git", "url": "https://github.com/nextcloud-deps/ocp.git", - "reference": "869be299538564cdcd8b88682d028297212ec8e8" + "reference": "f2c8f4bc174bed3f1c8edebb5b61bdd1e4fd8efe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/869be299538564cdcd8b88682d028297212ec8e8", - "reference": "869be299538564cdcd8b88682d028297212ec8e8", + "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/f2c8f4bc174bed3f1c8edebb5b61bdd1e4fd8efe", + "reference": "f2c8f4bc174bed3f1c8edebb5b61bdd1e4fd8efe", "shasum": "" }, "require": { @@ -831,7 +830,7 @@ "issues": "https://github.com/nextcloud-deps/ocp/issues", "source": "https://github.com/nextcloud-deps/ocp/tree/master" }, - "time": "2025-12-02T00:53:42+00:00" + "time": "2025-12-03T00:53:16+00:00" }, { "name": "nextcloud/openapi-extractor", @@ -2832,89 +2831,6 @@ ], "time": "2020-09-28T06:39:44+00:00" }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, { "name": "theseer/tokenizer", "version": "1.3.1", diff --git a/lib/Listener/CSPListener.php b/lib/Listener/CSPListener.php index 22a680bc..82d19fd7 100644 --- a/lib/Listener/CSPListener.php +++ b/lib/Listener/CSPListener.php @@ -30,6 +30,7 @@ public function handle(Event $event): void { $csp = new ContentSecurityPolicy(); $csp->addAllowedWorkerSrcDomain('blob:'); + $csp->addAllowedConnectDomain('blob:'); $event->addPolicy($csp); } } diff --git a/lib/TaskProcessing/AudioToAudioChatProvider.php b/lib/TaskProcessing/AudioToAudioChatProvider.php index 336e72e3..61c7576a 100644 --- a/lib/TaskProcessing/AudioToAudioChatProvider.php +++ b/lib/TaskProcessing/AudioToAudioChatProvider.php @@ -143,6 +143,10 @@ public function process(?string $userId, array $input, callable $reportProgress) Application::APP_ID . ':internal', $userId, ); + // the setIncludeWatermark method was introduced in NC 33 + if (method_exists($task, 'setIncludeWatermark')) { + $task->setIncludeWatermark(false); + } $taskOutput = $this->taskProcessingService->runTaskProcessingTask($task); $outputAudioFileId = $taskOutput['speech']; diff --git a/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php b/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php index fae2a81f..07288a33 100644 --- a/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php +++ b/lib/TaskProcessing/ContextAgentAudioInteractionProvider.php @@ -143,6 +143,10 @@ public function process(?string $userId, array $input, callable $reportProgress) Application::APP_ID . ':internal', $userId, ); + // the setIncludeWatermark method was introduced in NC 33 + if (method_exists($task, 'setIncludeWatermark')) { + $task->setIncludeWatermark(false); + } $ttsTaskOutput = $this->taskProcessingService->runTaskProcessingTask($task); $outputAudioFileId = $ttsTaskOutput['speech']; $outputAudioFileContent = $this->taskProcessingService->getOutputFileContent($outputAudioFileId); diff --git a/src/audioUtils.js b/src/audioUtils.js deleted file mode 100644 index 79caff3a..00000000 --- a/src/audioUtils.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -import * as lamejs from '@breezystack/lamejs' - -export function convertWavToMp3(wavBlob) { - return new Promise((resolve, reject) => { - const reader = new FileReader() - - reader.onload = function() { - const arrayBuffer = this.result - - // Create a WAV decoder - const wavDecoder = lamejs.WavHeader.readHeader(new DataView(arrayBuffer)) - - // Get the WAV audio data as an array of samples - const wavSamples = new Int16Array(arrayBuffer, wavDecoder.dataOffset, wavDecoder.dataLen / 2) - - // Create an MP3 encoder - const mp3Encoder = new lamejs.Mp3Encoder(wavDecoder.channels, wavDecoder.sampleRate, 128) - - // Encode the WAV samples to MP3 - const mp3Buffer = mp3Encoder.encodeBuffer(wavSamples) - - // Finalize the MP3 encoding - const mp3Data = mp3Encoder.flush() - - // Combine the MP3 header and data into a new ArrayBuffer - const mp3BufferWithHeader = new Uint8Array(mp3Buffer.length + mp3Data.length) - mp3BufferWithHeader.set(mp3Buffer, 0) - mp3BufferWithHeader.set(mp3Data, mp3Buffer.length) - - // Create a Blob from the ArrayBuffer - const mp3Blob = new Blob([mp3BufferWithHeader], { type: 'audio/mp3' }) - - resolve(mp3Blob) - } - - reader.onerror = function(error) { - reject(error) - } - - // Read the input blob as an ArrayBuffer - reader.readAsArrayBuffer(wavBlob) - }) -} diff --git a/src/components/ChattyLLM/InputArea.vue b/src/components/ChattyLLM/InputArea.vue index d7927ee7..95941711 100644 --- a/src/components/ChattyLLM/InputArea.vue +++ b/src/components/ChattyLLM/InputArea.vue @@ -132,7 +132,7 @@ export default { const url = generateOcsUrl('/apps/assistant/api/v1/input-file') const formData = new FormData() formData.append('data', blob) - formData.append('filename', 'chat-input.mp3') + formData.append('filename', 'chat-input.wav') axios.post(url, formData).then(response => { this.$emit('submit-audio', response.data.ocs.data.fileId) }).catch(error => { diff --git a/src/components/fields/AudioRecorderWrapper.vue b/src/components/fields/AudioRecorderWrapper.vue index 2043d7ca..8862d83a 100644 --- a/src/components/fields/AudioRecorderWrapper.vue +++ b/src/components/fields/AudioRecorderWrapper.vue @@ -49,7 +49,6 @@ import NcButton from '@nextcloud/vue/components/NcButton' import { showError } from '@nextcloud/dialogs' -import { convertWavToMp3 } from '../../audioUtils.js' import { MediaRecorder, register } from 'extendable-media-recorder' import { connect } from 'extendable-media-recorder-wav-encoder' @@ -210,8 +209,7 @@ export default { this.killStreams() if (!this.aborted) { const wavBlob = new Blob(this.chunks, { type: this.mediaRecorder.mimeType }) - const mp3Blob = await convertWavToMp3(wavBlob) - this.$emit('new-recording', mp3Blob) + this.$emit('new-recording', wavBlob) this.$emit('update:is-recording', false) } this.resetComponentData() diff --git a/src/components/fields/ListOfMediaField.vue b/src/components/fields/ListOfMediaField.vue index b3718b11..05443bf9 100644 --- a/src/components/fields/ListOfMediaField.vue +++ b/src/components/fields/ListOfMediaField.vue @@ -218,7 +218,7 @@ export default { const url = generateOcsUrl('/apps/assistant/api/v1/input-file') const formData = new FormData() formData.append('data', blob) - formData.append('filename', 'recording.mp3') + formData.append('filename', 'recording.wav') axios.post(url, formData).then(response => { const fileId = response.data.ocs.data.fileId if (this.value === null) { diff --git a/src/components/fields/MediaField.vue b/src/components/fields/MediaField.vue index 220bffaf..c6104e69 100644 --- a/src/components/fields/MediaField.vue +++ b/src/components/fields/MediaField.vue @@ -194,7 +194,7 @@ export default { const url = generateOcsUrl('/apps/assistant/api/v1/input-file') const formData = new FormData() formData.append('data', blob) - formData.append('filename', 'recording.mp3') + formData.append('filename', 'recording.wav') axios.post(url, formData).then(response => { this.$emit('update:value', response.data.ocs.data.fileId) this.filePath = response.data.ocs.data.filePath