mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-06-25 16:19:19 +02:00
Phase 2: Replace PeerTubeService HTTP client with Guzzle
This commit is contained in:
File diff suppressed because one or more lines are too long
2
TODO.md
2
TODO.md
@@ -8,7 +8,7 @@
|
|||||||
- [x] Update phpstan.neon (scanDirectories replaces manual Parsedown scan)
|
- [x] Update phpstan.neon (scanDirectories replaces manual Parsedown scan)
|
||||||
- [x] Write docs/system-setup.md (PHP extension requirements)
|
- [x] Write docs/system-setup.md (PHP extension requirements)
|
||||||
- [x] Phase 1: Replace Parsedown with league/commonmark (4 call sites)
|
- [x] Phase 1: Replace Parsedown with league/commonmark (4 call sites)
|
||||||
- [ ] Phase 2: Replace PeerTubeService HTTP client with Guzzle
|
- [x] Phase 2: Replace PeerTubeService HTTP client with Guzzle
|
||||||
- [ ] Phase 3: Replace SmtpRelay SMTP socket with PHPMailer
|
- [ ] Phase 3: Replace SmtpRelay SMTP socket with PHPMailer
|
||||||
- [ ] Phase 4 (optional): Replace Crypto with defuse/php-encryption
|
- [ ] Phase 4 (optional): Replace Crypto with defuse/php-encryption
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PeerTubeService
|
* PeerTubeService
|
||||||
*
|
*
|
||||||
@@ -18,7 +21,7 @@
|
|||||||
* per process lifetime.
|
* per process lifetime.
|
||||||
*
|
*
|
||||||
* Upload uses the simple multipart upload API:
|
* Upload uses the simple multipart upload API:
|
||||||
* POST /api/v1/videos/upload — multipart form with CURLFile
|
* POST /api/v1/videos/upload — multipart form upload via Guzzle
|
||||||
*/
|
*/
|
||||||
class PeerTubeService
|
class PeerTubeService
|
||||||
{
|
{
|
||||||
@@ -28,6 +31,9 @@ class PeerTubeService
|
|||||||
/** @var array<string,int> In-memory channel name → ID cache. */
|
/** @var array<string,int> In-memory channel name → ID cache. */
|
||||||
private static array $channelCache = [];
|
private static array $channelCache = [];
|
||||||
|
|
||||||
|
/** @var Client|null Shared Guzzle client (lazy-init). */
|
||||||
|
private static ?Client $httpClient = null;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// DB CRUD
|
// DB CRUD
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -179,26 +185,27 @@ class PeerTubeService
|
|||||||
|
|
||||||
$token = self::obtainToken($s);
|
$token = self::obtainToken($s);
|
||||||
$baseUrl = $s['instance_url'];
|
$baseUrl = $s['instance_url'];
|
||||||
$mimeType = (new \finfo(FILEINFO_MIME_TYPE))->file($filePath);
|
|
||||||
|
|
||||||
// ── Simple multipart upload (non-resumable) ──
|
// ── Simple multipart upload (non-resumable) ──
|
||||||
$uploadUrl = $baseUrl . '/api/v1/videos/upload';
|
$uploadUrl = $baseUrl . '/api/v1/videos/upload';
|
||||||
|
|
||||||
$postFields = [
|
$multipart = [
|
||||||
'channelId' => $channelId,
|
['name' => 'channelId', 'contents' => $channelId],
|
||||||
'name' => $title,
|
['name' => 'name', 'contents' => $title],
|
||||||
'privacy' => (int)$s['privacy'],
|
['name' => 'privacy', 'contents' => (int)$s['privacy']],
|
||||||
'commentsEnabled' => true,
|
['name' => 'commentsEnabled', 'contents' => 'true'],
|
||||||
'category' => 15,
|
['name' => 'category', 'contents' => '15'],
|
||||||
'videofile' => new \CURLFile($filePath, $mimeType, $originalName),
|
['name' => 'videofile', 'contents' => fopen($filePath, 'r'), 'filename' => $originalName],
|
||||||
];
|
];
|
||||||
if ($description !== '') {
|
if ($description !== '') {
|
||||||
$postFields['description'] = $description;
|
$multipart[] = ['name' => 'description', 'contents' => $description];
|
||||||
}
|
}
|
||||||
|
|
||||||
$resp = self::httpRequest($uploadUrl, 'POST', $postFields, [
|
$resp = self::httpRequest($uploadUrl, 'POST', [
|
||||||
'Authorization: Bearer ' . $token,
|
'headers' => ['Authorization' => 'Bearer ' . $token],
|
||||||
], 600);
|
'multipart' => $multipart,
|
||||||
|
'timeout' => 600,
|
||||||
|
]);
|
||||||
|
|
||||||
if ($resp['status'] < 200 || $resp['status'] >= 300) {
|
if ($resp['status'] < 200 || $resp['status'] >= 300) {
|
||||||
$errJson = json_decode($resp['body'], true);
|
$errJson = json_decode($resp['body'], true);
|
||||||
@@ -235,9 +242,10 @@ class PeerTubeService
|
|||||||
try {
|
try {
|
||||||
$token = self::obtainToken($s);
|
$token = self::obtainToken($s);
|
||||||
$url = $s['instance_url'] . '/api/v1/videos/' . urlencode($uuid);
|
$url = $s['instance_url'] . '/api/v1/videos/' . urlencode($uuid);
|
||||||
$resp = self::httpRequest($url, 'GET', '', [
|
$resp = self::httpRequest($url, 'GET', [
|
||||||
'Authorization: Bearer ' . $token,
|
'headers' => ['Authorization' => 'Bearer ' . $token],
|
||||||
], 10);
|
'timeout' => 10,
|
||||||
|
]);
|
||||||
if ($resp['status'] !== 200) {
|
if ($resp['status'] !== 200) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -283,9 +291,10 @@ class PeerTubeService
|
|||||||
try {
|
try {
|
||||||
$token = self::obtainToken($s);
|
$token = self::obtainToken($s);
|
||||||
$url = rtrim($s['instance_url'], '/') . '/api/v1/video-channels/' . urlencode($name);
|
$url = rtrim($s['instance_url'], '/') . '/api/v1/video-channels/' . urlencode($name);
|
||||||
$resp = self::httpRequest($url, 'GET', '', [
|
$resp = self::httpRequest($url, 'GET', [
|
||||||
'Authorization: Bearer ' . $token,
|
'headers' => ['Authorization' => 'Bearer ' . $token],
|
||||||
], 10);
|
'timeout' => 10,
|
||||||
|
]);
|
||||||
|
|
||||||
if ($resp['status'] !== 200) {
|
if ($resp['status'] !== 200) {
|
||||||
return null;
|
return null;
|
||||||
@@ -319,7 +328,7 @@ class PeerTubeService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$url = rtrim($instanceUrl, '/') . '/api/v1/oauth-clients/local';
|
$url = rtrim($instanceUrl, '/') . '/api/v1/oauth-clients/local';
|
||||||
$response = self::httpRequest($url, 'GET', '', [], 10);
|
$response = self::httpRequest($url, 'GET', ['timeout' => 10]);
|
||||||
|
|
||||||
$json = json_decode($response['body'], true);
|
$json = json_decode($response['body'], true);
|
||||||
if ($response['status'] !== 200 || empty($json['client_id'])) {
|
if ($response['status'] !== 200 || empty($json['client_id'])) {
|
||||||
@@ -352,9 +361,9 @@ class PeerTubeService
|
|||||||
'password' => $s['password'],
|
'password' => $s['password'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$response = self::httpRequest($tokenUrl, 'POST', $body, [
|
$response = self::httpRequest($tokenUrl, 'POST', [
|
||||||
'Content-Type: application/x-www-form-urlencoded',
|
'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
|
||||||
'Content-Length: ' . strlen($body),
|
'body' => $body,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$json = json_decode($response['body'], true);
|
$json = json_decode($response['body'], true);
|
||||||
@@ -370,71 +379,44 @@ class PeerTubeService
|
|||||||
// HTTP helper
|
// HTTP helper
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
/**
|
||||||
// HTTP helper
|
* Shared Guzzle HTTP client (lazy-init with SSL verification, HTTP/2, 15s connect timeout).
|
||||||
// -------------------------------------------------------------------------
|
*/
|
||||||
|
private static function client(): Client
|
||||||
|
{
|
||||||
|
if (self::$httpClient === null) {
|
||||||
|
self::$httpClient = new Client([
|
||||||
|
'http_errors' => false,
|
||||||
|
'allow_redirects' => false,
|
||||||
|
'connect_timeout' => 15,
|
||||||
|
'version' => 2.0, // HTTP/2
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return self::$httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal cURL HTTP helper.
|
* Perform an HTTP request via Guzzle.
|
||||||
*
|
*
|
||||||
|
* @param array $options Guzzle request options (headers, body, multipart, timeout, etc.)
|
||||||
* @return array{status:int, body:string, headers:array<string,string>}
|
* @return array{status:int, body:string, headers:array<string,string>}
|
||||||
*/
|
*/
|
||||||
public static function httpRequest(
|
public static function httpRequest(string $url, string $method, array $options = []): array
|
||||||
string $url,
|
{
|
||||||
string $method,
|
try {
|
||||||
string|array $body,
|
$response = self::client()->request($method, $url, $options);
|
||||||
array $headers,
|
$headers = [];
|
||||||
int $timeout = 300
|
foreach ($response->getHeaders() as $name => $values) {
|
||||||
): array {
|
$headers[strtolower($name)] = end($values);
|
||||||
if (!function_exists('curl_init')) {
|
}
|
||||||
throw new \RuntimeException('L\'extension PHP cURL est requise pour l\'intégration PeerTube.');
|
return [
|
||||||
|
'status' => $response->getStatusCode(),
|
||||||
|
'body' => (string)$response->getBody(),
|
||||||
|
'headers' => $headers,
|
||||||
|
];
|
||||||
|
} catch (GuzzleException $e) {
|
||||||
|
throw new \RuntimeException('Erreur réseau PeerTube : ' . $e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt_array($ch, [
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_TIMEOUT => $timeout,
|
|
||||||
CURLOPT_CONNECTTIMEOUT => 15,
|
|
||||||
CURLOPT_FOLLOWLOCATION => false, // Must be false to capture Location header
|
|
||||||
CURLOPT_MAXREDIRS => 3,
|
|
||||||
CURLOPT_SSL_VERIFYPEER => true,
|
|
||||||
CURLOPT_SSL_VERIFYHOST => 2,
|
|
||||||
CURLOPT_HTTPHEADER => $headers,
|
|
||||||
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
|
|
||||||
CURLOPT_HEADERFUNCTION => function ($ch, $headerLine) use (&$responseHeaders) {
|
|
||||||
$len = strlen($headerLine);
|
|
||||||
$parts = explode(':', $headerLine, 2);
|
|
||||||
if (count($parts) === 2) {
|
|
||||||
$responseHeaders[strtolower(trim($parts[0]))] = trim($parts[1]);
|
|
||||||
}
|
|
||||||
return $len;
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
$responseHeaders = [];
|
|
||||||
|
|
||||||
if ($method === 'POST') {
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
|
||||||
} elseif ($method === 'PUT' || $method === 'PATCH') {
|
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
|
||||||
} elseif ($method === 'DELETE') {
|
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
|
||||||
} elseif ($method === 'HEAD') {
|
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'HEAD');
|
|
||||||
curl_setopt($ch, CURLOPT_NOBODY, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$responseBody = curl_exec($ch);
|
|
||||||
$status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$error = curl_error($ch);
|
|
||||||
|
|
||||||
if ($responseBody === false && $method !== 'HEAD') {
|
|
||||||
throw new \RuntimeException('Erreur réseau PeerTube : ' . $error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['status' => $status, 'body' => (string)$responseBody, 'headers' => $responseHeaders];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[1779231711]
|
[1779232047]
|
||||||
Reference in New Issue
Block a user