Initial commit: webman project
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use support\Request;
|
||||
|
||||
class IndexController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return <<<EOF
|
||||
<style>
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
iframe {
|
||||
border: none;
|
||||
overflow: scroll;
|
||||
}
|
||||
</style>
|
||||
<iframe
|
||||
src="https://www.workerman.net/wellcome"
|
||||
width="100%"
|
||||
height="100%"
|
||||
allow="clipboard-write"
|
||||
sandbox="allow-scripts allow-same-origin allow-popups allow-downloads"
|
||||
></iframe>
|
||||
EOF;
|
||||
}
|
||||
|
||||
public function view(Request $request)
|
||||
{
|
||||
return view('index/view', ['name' => 'webman']);
|
||||
}
|
||||
|
||||
public function json(Request $request)
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
||||
@@ -0,0 +1,22 @@
|
||||
# 播放音乐
|
||||
POST http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_append",
|
||||
"params": {
|
||||
"tid": "234",
|
||||
"vol": 50,
|
||||
"urls": [
|
||||
{
|
||||
"name": "G.mp3",
|
||||
"uri": "http://192.168.42.168:8787/2026-05-10T172805.200.mpeg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
###
|
||||
GET http://192.168.42.168:8787/2026-05-10T170440.200.mpeg
|
||||
@@ -0,0 +1,9 @@
|
||||
# 下一曲
|
||||
POST http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_play_next"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 上一曲
|
||||
POST http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_play_previous"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# 播放文本转语音
|
||||
GET http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_append",
|
||||
"params": {
|
||||
"tid": "234",
|
||||
"vol": 50,
|
||||
"urls": [
|
||||
{
|
||||
"name": "t.mp3",
|
||||
"uri": "http://192.168.42.139:10008/tts_xf.single?text=前方1公里交通事故、请谨慎驾驶。&voice_name=xiaoyan&speed=50&volume=100&origin=http:///192.168.42.139:10008"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
###
|
||||
GET http://192.168.42.139:10008/tts_xf.single?text=前方1公里交通事故、请谨慎驾驶。&voice_name=xiaoyan&speed=50&volume=100&origin=http:///192.168.42.139:10008
|
||||
@@ -0,0 +1,5 @@
|
||||
#GET http://192.168.42.139:10008/tts_xf.multi?text=有车辆上磅39.5千克,请刷卡。&voice_name=xiaoyan&speed=50&volume=100&origin=http://192.168.42.139:10008&auto_delete=0
|
||||
|
||||
#GET http://192.168.42.139:10008/tts_xf.single?text=%E6%9C%89%E8%BD%A6%E8%BE%86%E4%B8%8A%E7%A3%8538.5%E5%8D%83%E5%85%8B%EF%BC%8C%E8%AF%B7%E5%88%B7%E5%8D%A1%E3%80%82&voice_name=xiaoyan&speed=50&volume=100
|
||||
|
||||
GET http://192.168.42.139:10008/tts_xf.single?text=有车辆上磅35.5千克,请刷卡。&voice_name=xiaoyan&speed=50&volume=100&origin=http:///192.168.42.139:10008
|
||||
@@ -0,0 +1,12 @@
|
||||
# 更改播放模式, mode:0-顺序播放;1-单曲循环;2-队列循环;
|
||||
POST http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_mode",
|
||||
"params": {
|
||||
"mode": 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# 调整队列音量
|
||||
POST http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_setvol",
|
||||
"params": {
|
||||
"vol": 60
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# 停止音乐
|
||||
POST http://192.168.42.4:8888
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"sn": "ls20://0202AD1AE849",
|
||||
"type": "req",
|
||||
"name": "songs_queue_clear"
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use Webman\MiddlewareInterface;
|
||||
use Webman\Http\Response;
|
||||
use Webman\Http\Request;
|
||||
|
||||
/**
|
||||
* Class StaticFile
|
||||
* @package app\middleware
|
||||
*/
|
||||
class StaticFile implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $handler): Response
|
||||
{
|
||||
// Access to files beginning with. Is prohibited
|
||||
if (strpos($request->path(), '/.') !== false) {
|
||||
return response('<h1>403 forbidden</h1>', 403);
|
||||
}
|
||||
/** @var Response $response */
|
||||
$response = $handler($request);
|
||||
// Add cross domain HTTP header
|
||||
/*$response->withHeaders([
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
]);*/
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use support\Model;
|
||||
|
||||
class Test extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'test';
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = false;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace app\process;
|
||||
|
||||
use Webman\App;
|
||||
|
||||
class Http extends App
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace app\process;
|
||||
|
||||
use FilesystemIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SplFileInfo;
|
||||
use Workerman\Timer;
|
||||
use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* Class FileMonitor
|
||||
* @package process
|
||||
*/
|
||||
class Monitor
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $paths = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $extensions = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array $loadedFiles = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected int $ppid = 0;
|
||||
|
||||
/**
|
||||
* Pause monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function pause(): void
|
||||
{
|
||||
file_put_contents(static::lockFile(), time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function resume(): void
|
||||
{
|
||||
clearstatcache();
|
||||
if (is_file(static::lockFile())) {
|
||||
unlink(static::lockFile());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether monitor is paused
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPaused(): bool
|
||||
{
|
||||
clearstatcache();
|
||||
return file_exists(static::lockFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock file
|
||||
* @return string
|
||||
*/
|
||||
protected static function lockFile(): string
|
||||
{
|
||||
return runtime_path('monitor.lock');
|
||||
}
|
||||
|
||||
/**
|
||||
* FileMonitor constructor.
|
||||
* @param $monitorDir
|
||||
* @param $monitorExtensions
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($monitorDir, $monitorExtensions, array $options = [])
|
||||
{
|
||||
$this->ppid = function_exists('posix_getppid') ? posix_getppid() : 0;
|
||||
static::resume();
|
||||
$this->paths = (array)$monitorDir;
|
||||
$this->extensions = $monitorExtensions;
|
||||
foreach (get_included_files() as $index => $file) {
|
||||
$this->loadedFiles[$file] = $index;
|
||||
if (strpos($file, 'webman-framework/src/support/App.php')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Worker::getAllWorkers()) {
|
||||
return;
|
||||
}
|
||||
$disableFunctions = explode(',', ini_get('disable_functions'));
|
||||
if (in_array('exec', $disableFunctions, true)) {
|
||||
echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n";
|
||||
} else {
|
||||
if ($options['enable_file_monitor'] ?? true) {
|
||||
Timer::add(1, function () {
|
||||
$this->checkAllFilesChange();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null);
|
||||
if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) {
|
||||
Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $monitorDir
|
||||
* @return bool
|
||||
*/
|
||||
public function checkFilesChange($monitorDir): bool
|
||||
{
|
||||
static $lastMtime, $tooManyFilesCheck;
|
||||
if (!$lastMtime) {
|
||||
$lastMtime = time();
|
||||
}
|
||||
clearstatcache();
|
||||
if (!is_dir($monitorDir)) {
|
||||
if (!is_file($monitorDir)) {
|
||||
return false;
|
||||
}
|
||||
$iterator = [new SplFileInfo($monitorDir)];
|
||||
} else {
|
||||
// recursive traversal directory
|
||||
$dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS);
|
||||
$iterator = new RecursiveIteratorIterator($dirIterator);
|
||||
}
|
||||
$count = 0;
|
||||
foreach ($iterator as $file) {
|
||||
$count ++;
|
||||
/** @var SplFileInfo $file */
|
||||
if (is_dir($file->getRealPath())) {
|
||||
continue;
|
||||
}
|
||||
// check mtime
|
||||
if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) {
|
||||
$lastMtime = $file->getMTime();
|
||||
if (DIRECTORY_SEPARATOR === '/' && isset($this->loadedFiles[$file->getRealPath()])) {
|
||||
echo "$file updated but cannot be reloaded because only auto-loaded files support reload.\n";
|
||||
continue;
|
||||
}
|
||||
$var = 0;
|
||||
exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var);
|
||||
if ($var) {
|
||||
continue;
|
||||
}
|
||||
// send SIGUSR1 signal to master process for reload
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
if ($masterPid = $this->getMasterPid()) {
|
||||
echo $file . " updated and reload\n";
|
||||
posix_kill($masterPid, SIGUSR1);
|
||||
} else {
|
||||
echo "Master process has gone away and can not reload\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
echo $file . " updated and reload\n";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!$tooManyFilesCheck && $count > 1000) {
|
||||
echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n";
|
||||
$tooManyFilesCheck = 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getMasterPid(): int
|
||||
{
|
||||
if ($this->ppid === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (function_exists('posix_kill') && !posix_kill($this->ppid, 0)) {
|
||||
echo "Master process has gone away\n";
|
||||
return $this->ppid = 0;
|
||||
}
|
||||
if (PHP_OS_FAMILY !== 'Linux') {
|
||||
return $this->ppid;
|
||||
}
|
||||
$cmdline = "/proc/$this->ppid/cmdline";
|
||||
if (!is_readable($cmdline) || !($content = file_get_contents($cmdline)) || (!str_contains($content, 'WorkerMan') && !str_contains($content, 'php'))) {
|
||||
// Process not exist
|
||||
$this->ppid = 0;
|
||||
}
|
||||
return $this->ppid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAllFilesChange(): bool
|
||||
{
|
||||
if (static::isPaused()) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->paths as $path) {
|
||||
if ($this->checkFilesChange($path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $memoryLimit
|
||||
* @return void
|
||||
*/
|
||||
public function checkMemory($memoryLimit): void
|
||||
{
|
||||
if (static::isPaused() || $memoryLimit <= 0) {
|
||||
return;
|
||||
}
|
||||
$masterPid = $this->getMasterPid();
|
||||
if ($masterPid <= 0) {
|
||||
echo "Master process has gone away\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$childrenFile = "/proc/$masterPid/task/$masterPid/children";
|
||||
if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) {
|
||||
return;
|
||||
}
|
||||
foreach (explode(' ', $children) as $pid) {
|
||||
$pid = (int)$pid;
|
||||
$statusFile = "/proc/$pid/status";
|
||||
if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) {
|
||||
continue;
|
||||
}
|
||||
$mem = 0;
|
||||
if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) {
|
||||
$mem = $match[1];
|
||||
}
|
||||
$mem = (int)($mem / 1024);
|
||||
if ($mem >= $memoryLimit) {
|
||||
posix_kill($pid, SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
* @param $memoryLimit
|
||||
* @return int
|
||||
*/
|
||||
protected function getMemoryLimit($memoryLimit): int
|
||||
{
|
||||
if ($memoryLimit === 0) {
|
||||
return 0;
|
||||
}
|
||||
$usePhpIni = false;
|
||||
if (!$memoryLimit) {
|
||||
$memoryLimit = ini_get('memory_limit');
|
||||
$usePhpIni = true;
|
||||
}
|
||||
|
||||
if ($memoryLimit == -1) {
|
||||
return 0;
|
||||
}
|
||||
$unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]);
|
||||
$memoryLimit = (int)$memoryLimit;
|
||||
if ($unit === 'g') {
|
||||
$memoryLimit = 1024 * $memoryLimit;
|
||||
} else if ($unit === 'k') {
|
||||
$memoryLimit = ($memoryLimit / 1024);
|
||||
} else if ($unit === 'm') {
|
||||
$memoryLimit = (int)($memoryLimit);
|
||||
} else if ($unit === 't') {
|
||||
$memoryLimit = (1024 * 1024 * $memoryLimit);
|
||||
} else {
|
||||
$memoryLimit = ($memoryLimit / (1024 * 1024));
|
||||
}
|
||||
if ($memoryLimit < 50) {
|
||||
$memoryLimit = 50;
|
||||
}
|
||||
if ($usePhpIni) {
|
||||
$memoryLimit = (0.8 * $memoryLimit);
|
||||
}
|
||||
return (int)$memoryLimit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace app\process;
|
||||
|
||||
use app\service\AudioService;
|
||||
use Workerman\Timer as WorkermanTimer;
|
||||
use Workerman\Worker;
|
||||
|
||||
class Timer
|
||||
{
|
||||
private const VAR_POS = 28;
|
||||
private const VAR_OFFSET = 0x0F;
|
||||
|
||||
private const HEX_DATA = [
|
||||
0x55, 0xAA, 0x00, 0x00, 0x00, 0x01, 0x00, 0xC1,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
|
||||
0x00, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x0D,
|
||||
0x0A
|
||||
];
|
||||
|
||||
private int $port;
|
||||
private int $maxValue;
|
||||
private float $intervalSec;
|
||||
private array $texts = [];
|
||||
private array $targets = []; // 目标列表 [['ip'=>'...','port'=>...], ...]
|
||||
private AudioService $audio;
|
||||
|
||||
public function onWorkerStart(Worker $worker): void
|
||||
{
|
||||
$this->port = (int)(getenv('TIMER_PORT') ?: 10000);
|
||||
$this->maxValue = (int)(getenv('TIMER_MAX') ?: 6);
|
||||
$this->intervalSec = (float)(getenv('TIMER_INTERVAL') ?: 1);
|
||||
|
||||
$this->loadTexts();
|
||||
$this->targets = $this->loadIps();
|
||||
$this->audio = new AudioService();
|
||||
|
||||
echo "Timer process is running.\n";
|
||||
echo "目标: ";
|
||||
foreach ($this->targets as $t) {
|
||||
echo "{$t['ip']}:{$t['port']} ";
|
||||
}
|
||||
echo "\n最大值: {$this->maxValue}, 间隔: {$this->intervalSec}s\n";
|
||||
|
||||
$value = 1;
|
||||
|
||||
$timerCallback = function () use (&$value) {
|
||||
$text = $this->texts[$value] ?? "当前: {$value}";
|
||||
|
||||
$okImg = $this->sendImage($value);
|
||||
$status = $okImg ? '图✓' : '图✗';
|
||||
echo "\r[{$value}] {$status} {$text}";
|
||||
$okAudio = $this->audio->sendText($text);
|
||||
$status .= ' ' . ($okAudio ? '音✓' : '音✗');
|
||||
echo "\r[{$value}] {$status} {$text}\n";
|
||||
|
||||
$value = $value >= $this->maxValue ? 1 : $value + 1;
|
||||
};
|
||||
|
||||
// 立即执行一次,再定时
|
||||
$timerCallback();
|
||||
WorkermanTimer::add($this->intervalSec, $timerCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载目标 IP 列表
|
||||
* 优先检查 TIMER_IPS 环境变量(逗号分隔,可带端口如 192.168.1.1:10001)
|
||||
* 否则回退到单个 TIMER_IP + TIMER_PORT
|
||||
*/
|
||||
private function loadIps(): array
|
||||
{
|
||||
$targets = [];
|
||||
|
||||
$ipsEnv = getenv('TIMER_IPS');
|
||||
if ($ipsEnv !== false && trim($ipsEnv) !== '') {
|
||||
// 多 IP 模式
|
||||
$parts = explode(',', $ipsEnv);
|
||||
foreach ($parts as $part) {
|
||||
$part = trim($part);
|
||||
if ($part === '') continue;
|
||||
if (strpos($part, ':') !== false) {
|
||||
[$ip, $portStr] = explode(':', $part, 2);
|
||||
$targets[] = [
|
||||
'ip' => trim($ip),
|
||||
'port' => (int)trim($portStr)
|
||||
];
|
||||
} else {
|
||||
$targets[] = [
|
||||
'ip' => $part,
|
||||
'port' => $this->port
|
||||
];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 兼容单 IP 旧配置
|
||||
$ip = getenv('TIMER_IP') ?: '192.168.42.1';
|
||||
$targets[] = [
|
||||
'ip' => $ip,
|
||||
'port' => $this->port
|
||||
];
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向所有目标发送 HEX 数据包
|
||||
* @return bool 全部发送成功返回 true,任一失败返回 false
|
||||
*/
|
||||
private function sendImage(int $variable): bool
|
||||
{
|
||||
$data = self::HEX_DATA;
|
||||
$data[self::VAR_POS] = ($variable + self::VAR_OFFSET) & 0xFF;
|
||||
$packedData = pack('C*', ...$data);
|
||||
|
||||
$allSuccess = true;
|
||||
foreach ($this->targets as $target) {
|
||||
try {
|
||||
$socket = @fsockopen($target['ip'], $target['port'], $errno, $errstr, 5);
|
||||
echo "\r图> 尝试连接 [{$target['ip']}:{$target['port']}]";
|
||||
if (!$socket) {
|
||||
echo "\r图> 连接失败 [{$target['ip']}:{$target['port']}]: {$errstr} ({$errno})\n";
|
||||
$allSuccess = false;
|
||||
continue;
|
||||
}
|
||||
echo "\r图> 已连接 [{$target['ip']}:{$target['port']}]";
|
||||
fwrite($socket, $packedData);
|
||||
fflush($socket);
|
||||
fclose($socket);
|
||||
}catch (\Exception $exception){
|
||||
echo "发送数据失败 [{$target['ip']}:{$target['port']}]: {$exception->getMessage()}\n";
|
||||
$allSuccess = false;
|
||||
}
|
||||
}
|
||||
return $allSuccess;
|
||||
}
|
||||
|
||||
private function loadTexts(): void
|
||||
{
|
||||
for ($i = 1; $i <= $this->maxValue; $i++) {
|
||||
$t = getenv('TIMER_TEXT_' . $i);
|
||||
if ($t !== false && $t !== '') {
|
||||
$this->texts[$i] = $t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
class AudioService
|
||||
{
|
||||
private string $host;
|
||||
private int $port;
|
||||
private string $sn;
|
||||
private string $tid;
|
||||
private int $vol;
|
||||
private string $ttsUrl;
|
||||
private string $ttsVoice;
|
||||
private int $ttsSpeed;
|
||||
private int $ttsVolume;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->host = getenv('AUDIO_MUSIC_HOST') ?: '192.168.42.4';
|
||||
$this->port = (int)(getenv('AUDIO_MUSIC_PORT') ?: 8888);
|
||||
$this->sn = getenv('AUDIO_SN') ?: 'ls20://0202AD1AE849';
|
||||
$this->tid = getenv('AUDIO_TID') ?: '234';
|
||||
$this->vol = (int)(getenv('AUDIO_VOL') ?: 50);
|
||||
$this->ttsUrl = getenv('AUDIO_TTS_URL') ?: 'http://192.168.42.139:10008/tts_xf.single';
|
||||
$this->ttsVoice = getenv('AUDIO_TTS_VOICE') ?: 'xiaoyan';
|
||||
$this->ttsSpeed = (int)(getenv('AUDIO_TTS_SPEED') ?: 50);
|
||||
$this->ttsVolume = (int)(getenv('AUDIO_TTS_VOLUME') ?: 100);
|
||||
}
|
||||
|
||||
public function sendText(string $text): bool
|
||||
{
|
||||
// $uri = $this->ttsUrl . '?' . http_build_query([
|
||||
// 'text' => $text,
|
||||
// 'voice_name' => $this->ttsVoice,
|
||||
// 'speed' => $this->ttsSpeed,
|
||||
// 'volume' => $this->ttsVolume,
|
||||
// ]);
|
||||
|
||||
$host = getenv('LOCAL_IP') ?: '192.168.42.168';
|
||||
$uri = 'http://' . $host . ':8787/' . $text;
|
||||
|
||||
return $this->sendCommand('songs_queue_append', [
|
||||
'tid' => $this->tid,
|
||||
'vol' => $this->vol,
|
||||
'urls' => [
|
||||
[
|
||||
'name' => 'G.mp3',
|
||||
'uri' => $uri,
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
private function sendCommand(string $name, ?array $params = null): bool
|
||||
{
|
||||
$payload = [
|
||||
'sn' => $this->sn,
|
||||
'type' => 'req',
|
||||
'name' => $name,
|
||||
];
|
||||
if ($params !== null) {
|
||||
$payload['params'] = $params;
|
||||
}
|
||||
|
||||
$json = json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
if (!$json) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$socket = @fsockopen($this->host, $this->port, $errno, $errstr, 5);
|
||||
if (!$socket) {
|
||||
echo "音乐设备连接失败: {$errstr} ({$errno})\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = $json;
|
||||
$request = "POST / HTTP/1.1\r\n"
|
||||
. "Host: {$this->host}:{$this->port}\r\n"
|
||||
. "Content-Type: application/json\r\n"
|
||||
. "Content-Length: " . strlen($body) . "\r\n"
|
||||
. "Connection: close\r\n"
|
||||
. "\r\n"
|
||||
. $body;
|
||||
|
||||
fwrite($socket, $request);
|
||||
fflush($socket);
|
||||
|
||||
$response = '';
|
||||
while (!feof($socket)) {
|
||||
$response .= fread($socket, 4096);
|
||||
}
|
||||
fclose($socket);
|
||||
|
||||
return $response !== false && strlen($response) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="/favicon.ico"/>
|
||||
<title>webman</title>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
hello <?=htmlspecialchars($name)?>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user