Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions core/ajax/ttscast.ajax.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,23 +109,25 @@
}
log::add('ttscast', 'debug', "[UPLOAD][CustomSound] filename: {$_FILES['fileCustomSound']['name']}");
$extension = strtolower(strrchr($_FILES['fileCustomSound']['name'], '.'));
if (!in_array($extension, array('.mp3'))) {
throw new Exception('[UPLOAD][CustomSound] Extension de fichier non valide (autorisé .mp3) : ' . $extension);
if (!in_array($extension, array('.mp3', '.wav', '.ogg', '.opus', '.flac'))) {
throw new Exception('[UPLOAD][CustomSound] Extension de fichier non valide (autorisé .mp3, .wav, .ogg, .opus, .flac) : ' . $extension);
}

$safeFilename = basename($_FILES['fileCustomSound']['name']);

# TODO limiter taille upload mp3 dans les customSounds ?
/* if (filesize($_FILES['fileCustomSound']['tmp_name']) > 10000) {
throw new Exception(__('[UPLOAD][CustomSound] Le fichier est trop gros (max. 10Ko)', __FILE__));
} */

$filepath = __DIR__ . "/../../data/media/custom/{$_FILES['fileCustomSound']['name']}";
$filepath = __DIR__ . "/../../data/media/custom/{$safeFilename}";
log::add('ttscast', 'debug', "[UPLOAD][CustomSound] filepath: {$filepath}");
file_put_contents($filepath, file_get_contents($_FILES['fileCustomSound']['tmp_name']));
if (!file_exists($filepath)) {
throw new Exception(__('[UPLOAD][CustomSound] Impossible de sauvegarder le fichier', __FILE__));
}
log::add('ttscast', 'info', "[UPLOAD][CustomSound] Upload OK :: {$_FILES['fileCustomSound']['name']}");
ajax::success("{$_FILES['fileCustomSound']['name']}");
log::add('ttscast', 'info', "[UPLOAD][CustomSound] Upload OK :: {$safeFilename}");
ajax::success("{$safeFilename}");
}

if (init('action') == 'uploadCustomRadios') {
Expand Down
4 changes: 2 additions & 2 deletions core/class/ttscast.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ public function getSoundList()
$filesReturn = '';
try {
$filesArray = array();
foreach (glob(dirname(__FILE__) . '/../../data/media/*.mp3') as $fileName) {
foreach (glob(dirname(__FILE__) . '/../../data/media/*.{mp3,wav,ogg,opus,flac}', GLOB_BRACE) as $fileName) {
$filesArray[pathinfo($fileName, PATHINFO_BASENAME)] = ucwords(str_replace(["_", "-"], " ", pathinfo($fileName, PATHINFO_FILENAME)));
}
natsort($filesArray);
Expand All @@ -833,7 +833,7 @@ public function getCustomSoundList()
$filesReturn = '';
try {
$filesArray = array();
foreach (glob(dirname(__FILE__) . '/../../data/media/custom/*.mp3') as $fileName) {
foreach (glob(dirname(__FILE__) . '/../../data/media/custom/*.{mp3,wav,ogg,opus,flac}', GLOB_BRACE) as $fileName) {
$filesArray[pathinfo($fileName, PATHINFO_BASENAME)] = ucwords(str_replace(["_", "-"], " ", pathinfo($fileName, PATHINFO_FILENAME)));
}
natsort($filesArray);
Expand Down
28 changes: 23 additions & 5 deletions core/php/ttscast.audio.proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
die();
}

// Validation stricte : MD5 hex (32 car.) + extension audio autorisée
$mimeTypes = [
'mp3' => 'audio/mp3',
'wav' => 'audio/wav',
Expand All @@ -33,15 +32,34 @@
'flac' => 'audio/flac',
];

$type = isset($_GET['type']) ? $_GET['type'] : '';
$file = isset($_GET['file']) ? $_GET['file'] : '';
if (!preg_match('/^([a-f0-9]{32})\.(mp3|wav|ogg|opus|flac)$/', $file, $matches)) {

if ($type === 'tts') {
// Validation stricte : MD5 hex (32 car.) + extension audio autorisée
if (!preg_match('/^([a-f0-9]{32})\.(mp3|wav|ogg|opus|flac)$/', $file, $matches)) {
http_response_code(400);
die();
}
$mime = $mimeTypes[$matches[2]];
$filePath = dirname(dirname(__DIR__)) . '/data/cache/' . $file;

} elseif ($type === 'sounds' || $type === 'customsounds') {
// Validation : nom de fichier sûr (pas de séparateur de répertoire, extension autorisée)
$safeFile = basename($file);
if (!preg_match('/^([a-zA-Z0-9._-]+)\.(mp3|wav|ogg|opus|flac)$/', $safeFile, $matches)) {
http_response_code(400);
die();
}
$mime = $mimeTypes[$matches[2]];
$subDir = ($type === 'customsounds') ? 'custom/' : '';
$filePath = dirname(dirname(__DIR__)) . '/data/media/' . $subDir . $safeFile;

} else {
http_response_code(400);
die();
}

$mime = $mimeTypes[$matches[2]];
$filePath = dirname(dirname(__DIR__)) . '/data/cache/' . $file;

if (!file_exists($filePath) || !is_file($filePath)) {
http_response_code(404);
die();
Expand Down
4 changes: 2 additions & 2 deletions plugin_info/configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -1069,11 +1069,11 @@
</div>
<div class="form-group">
<label class="col-lg-3 control-label">{{Ajouter un fichier :: Custom Sound}}
<sup><i class="fas fa-question-circle tooltips" title="{{Upload un fichier (.mp3) pour l'ajouter au répertoire des Custom Sounds}}"></i></sup>
<sup><i class="fas fa-question-circle tooltips" title="{{Upload un fichier (.mp3, .wav, .ogg, .opus, .flac) pour l'ajouter au répertoire des Custom Sounds}}"></i></sup>
</label>
<div class="col-lg-1">
<a class="btn btn-info btn-file">
<i class="fas fa-file-upload"></i> {{Ajouter un Custom Sound (.mp3)}}<input class="pluginAction" data-action="uploadCustomSound" type="file" name="fileCustomSound" style="display: inline-block;" accept=".mp3" />
<i class="fas fa-file-upload"></i> {{Ajouter un Custom Sound}}<input class="pluginAction" data-action="uploadCustomSound" type="file" name="fileCustomSound" style="display: inline-block;" accept=".mp3,.wav,.ogg,.opus,.flac" />
</a>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion plugin_info/info.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "ttscast",
"name": "TTS Cast",
"pluginVersion": "1.9.0",
"pluginVersion": "1.9.1",
"description": {
"fr_FR": "Plugin pour gérer ses équipements Google, type Google Home, Nest Mini, Nest Hub (Max), Chromecast. Il permet de générer des notifications TTS (Text To Speech) et de les diffuser sur les équipements Google. Il permet également de diffuser des sons (mp3), des vidéos YouTube, une page Web, ou encore une radio en streaming sur ces mêmes équipements.",
"en_US": "Plugin to manage Google equipment, such as Google Home, Nest Mini, Nest Hub (Max), Chromecast. It allows you to generate TTS (Text To Speech) notifications and broadcast them to Google devices. It also allows you to broadcast sounds (mp3), YouTube videos, a web page, or even streaming radio on the same equipment.",
Expand Down
21 changes: 14 additions & 7 deletions resources/ttscastd/ttscastd.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import wave
import io

from urllib.parse import urlencode, urlparse
from urllib.parse import urlencode, urlparse, quote
from uuid import UUID

# Import pour Jeedom
Expand Down Expand Up @@ -2540,21 +2540,28 @@ def controllerSounds(cast, _googleUUID, _value, _options, _controller):
cast.set_volume(volume=_volume / 100)

if (_controller == 'customsounds'):
soundURL = f'{myConfig.ttsWebSrvMedia}custom/{_value}'
soundURL = f'{myConfig.ttsWebSrvMediaProxy}?type=customsounds&file={quote(_value, safe="")}'
else:
soundURL = f'{myConfig.ttsWebSrvMedia}{_value}'
soundURL = f'{myConfig.ttsWebSrvMediaProxy}?type=sounds&file={quote(_value, safe="")}'
logging.debug(f'[DAEMON][controllerActions] {soundType} :: FilePath :: {soundURL}')

soundThumb = f'{myConfig.ttsWebSrvImages}tts.png'
soundAlbumName = "Jeedom"
soundTitle = f"TTSCast {soundType}"
soundArtist = _value

_soundMimeTypes = {
'mp3': 'audio/mp3', 'wav': 'audio/wav',
'ogg': 'audio/ogg', 'opus': 'audio/ogg; codecs=opus', 'flac': 'audio/flac'
}
_ext = os.path.splitext(_value)[1].lstrip('.').lower()
soundMimeType = _soundMimeTypes.get(_ext, 'audio/mp3')

app_name = "default_media_receiver"
# app_name = "bubbleupnp"
app_data = {
"media_id": soundURL,
"media_type": "audio/mp3",
"media_type": soundMimeType,
"stream_type": "BUFFERED",
"title": soundTitle,
"thumb": soundThumb,
Expand Down Expand Up @@ -3557,8 +3564,8 @@ def shutdown():
if args.ttsweb:
# Normalize base URL once for all paths (supports Jeedom in subdirectories)
ttsweb_base_url = args.ttsweb.rstrip('/')
myConfig.ttsWebSrvCache = f'{ttsweb_base_url}/plugins/ttscast/core/php/ttscast.audio.proxy.php?file='
myConfig.ttsWebSrvMedia = f'{ttsweb_base_url}/plugins/ttscast/data/media/'
myConfig.ttsWebSrvCache = f'{ttsweb_base_url}/plugins/ttscast/core/php/ttscast.audio.proxy.php?type=tts&file='
myConfig.ttsWebSrvMediaProxy = f'{ttsweb_base_url}/plugins/ttscast/core/php/ttscast.audio.proxy.php'
myConfig.ttsWebSrvImages = f'{ttsweb_base_url}/plugins/ttscast/data/images/'
myConfig.ttsWebSrvJeeTTS = f'{ttsweb_base_url}/core/api/'

Expand Down Expand Up @@ -3619,7 +3626,7 @@ def shutdown():
logging.info('[DAEMON][MAIN] Cmd Wait Timeout: %s', str(myConfig.cmdWaitTimeout))
logging.info('[DAEMON][MAIN] CallBack: %s', myConfig.callBack)
logging.info('[DAEMON][MAIN] Jeedom WebSrvCache: %s', myConfig.ttsWebSrvCache)
logging.info('[DAEMON][MAIN] Jeedom WebSrvMedia: %s', myConfig.ttsWebSrvMedia)
logging.info('[DAEMON][MAIN] Jeedom WebSrvMediaProxy: %s', myConfig.ttsWebSrvMediaProxy)
logging.info('[DAEMON][MAIN] Jeedom WebSrvImages: %s', myConfig.ttsWebSrvImages)
logging.info('[DAEMON][MAIN] Jeedom WebSrvJeeTTS: %s', myConfig.ttsWebSrvJeeTTS)

Expand Down
2 changes: 1 addition & 1 deletion resources/ttscastd/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Config:
# soundsCustomPath = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'data/media/custom'))

ttsWebSrvCache = ''
ttsWebSrvMedia = ''
ttsWebSrvMediaProxy = ''
ttsWebSrvImages = ''
ttsWebSrvJeeTTS = ''

Expand Down