PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.9
ووسلام – همگام سازی ووکامرس و باسلام v1.10.9
1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
sync-basalam / includes / Services / MediaUploadService.php

MediaUploadService.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.9, at includes/Services/MediaUploadService.php

214 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam\Services;
4
5 use SyncBasalam\Admin\Settings\SettingsConfig;
6 use SyncBasalam\Config\Endpoints;
7 use SyncBasalam\Logger\Logger;
8
9 defined('ABSPATH') || exit;
10
11 class MediaUploadService
12 {
13 private const UPLOAD_PREFERENCE = 'presigned_post';
14
15 private $apiService;
16
17 public function __construct($apiService = null)
18 {
19 $this->apiService = $apiService ?: syncBasalamContainer()->get(ApiServiceManager::class);
20 }
21
22 public function uploadPhoto(string $filePath, array $options = []): array
23 {
24 return $this->upload($filePath, 'product.photo', 'تصویر', $options);
25 }
26
27 public function uploadVideo(string $filePath, array $options = []): array
28 {
29 return $this->upload($filePath, 'product.video', 'ویدیو', $options);
30 }
31
32 private function upload(string $filePath, string $fileType, string $fileLabel, array $options): array
33 {
34 $token = (string) syncBasalamSettings()->getSettings(SettingsConfig::TOKEN);
35 if ($token === '') throw new \RuntimeException('توکن باسلا�
36 یافت نشد. ابتدا اتصال باسلا�
37 را انجا�
38 دهید.');
39
40 $mimeType = $this->detectMimeType($filePath);
41 $fileSize = filesize($filePath);
42 $checksum = hash_file('sha256', $filePath);
43
44 if ($fileSize === false || $checksum === false) {
45 throw new \RuntimeException('خواندن اطلاعات فایل ' . esc_html($fileLabel) . ' برای آپلود نا�
46 وفق بود.');
47 }
48
49 $headers = ['Authorization' => 'Bearer ' . $token];
50 $request = $this->apiService->post(Endpoints::MEDIA_UPLOAD_REQUEST, [
51 'file_name' => basename($filePath),
52 'mime_type' => $mimeType,
53 'size' => (int) $fileSize,
54 'file_type' => $fileType,
55 'checksum_sha256' => $checksum,
56 'upload_preference' => self::UPLOAD_PREFERENCE,
57 'multipart_part_size_bytes' => 0,
58 ], $headers);
59
60 $uploadRequest = $this->decodeBody($request['body'] ?? null);
61 $fileId = $uploadRequest['file_id'] ?? null;
62 $strategy = $uploadRequest['upload_strategy'] ?? '';
63
64 if (!$fileId || $strategy !== 'presigned_post') {
65 Logger::error('پاسخ درخواست آپلود ' . esc_html($fileLabel) . ' باسلا�
66
67 عتبر نیست.', [
68 'status_code' => $request['status_code'] ?? 0,
69 'upload_strategy' => $strategy,
70 'response_body' => $uploadRequest,
71 ]);
72 throw new \RuntimeException('باسلا�
73 لینک �
74 عتبر آپلود ' . esc_html($fileLabel) . ' برنگرداند.');
75 }
76
77 $staging = $uploadRequest['staging'] ?? [];
78 $uploadUrl = $staging['url'] ?? '';
79 $fields = $staging['fields'] ?? [];
80
81 if (!is_string($uploadUrl) || $uploadUrl === '' || !is_array($fields) || empty($fields)) {
82 throw new \RuntimeException('اطلاعات لینک آپلود ' . esc_html($fileLabel) . ' باسلا�
83 ناقص است.');
84 }
85
86 $uploadResponse = $this->apiService->upload($uploadUrl, $filePath, $fields, [], $options);
87 $uploadStatus = (int) ($uploadResponse['status_code'] ?? 0);
88
89 if ($uploadStatus < 200 || $uploadStatus >= 300) {
90 throw new \RuntimeException($uploadResponse['error'] ?? 'ارسال فایل ' . esc_html($fileLabel) . ' به لینک باسلا�
91 نا�
92 وفق بود.');
93 }
94
95 $complete = $this->apiService->post(Endpoints::MEDIA_UPLOAD_COMPLETE, [
96 'file_id' => $fileId,
97 ], $headers);
98 $completeStatus = (int) ($complete['status_code'] ?? 0);
99
100 if ($completeStatus < 200 || $completeStatus >= 300) {
101 throw new \RuntimeException($complete['error'] ?? 'نهایی‌سازی آپلود ' . esc_html($fileLabel) . ' در باسلا�
102 نا�
103 وفق بود.');
104 }
105
106 $completedMedia = $this->decodeBody($complete['body'] ?? null);
107 $normalized = $this->normalizeCompletedMedia($completedMedia, (string) $fileId);
108
109 if ($normalized === null) {
110 $maxAttempts = $fileType === 'product.video' ? 60 : 15;
111 $normalized = $this->waitForCompletedMedia((string) $fileId, $headers, $fileLabel, $maxAttempts);
112 }
113
114 if ($normalized === null) {
115 Logger::error('پاسخ نهایی آپلود ' . esc_html($fileLabel) . ' باسلا�
116 شناسه رسانه ندارد.', [
117 'file_id' => $fileId,
118 'status_code' => $complete['status_code'] ?? 0,
119 'response_body' => $completedMedia,
120 ]);
121 throw new \RuntimeException('باسلا�
122 شناسه نهایی ' . esc_html($fileLabel) . ' آپلودشده را برنگرداند.');
123 }
124
125 return $normalized;
126 }
127
128 private function waitForCompletedMedia(string $fileId, array $headers, string $fileLabel, int $maxAttempts): ?array
129 {
130 $url = sprintf(Endpoints::MEDIA_UPLOAD_STATUS, rawurlencode($fileId));
131
132 for ($attempt = 0; $attempt < $maxAttempts; $attempt++) {
133 $status = $this->apiService->get($url, $headers);
134 $statusCode = (int) ($status['status_code'] ?? 0);
135
136 if ($statusCode >= 200 && $statusCode < 300) {
137 $statusBody = $this->decodeBody($status['body'] ?? null);
138 $normalized = $this->normalizeCompletedMedia($statusBody, $fileId);
139 if ($normalized !== null) return $normalized;
140
141 if (isset($statusBody['status']) && in_array($statusBody['status'], ['failed', 'aborted'], true)) {
142 throw new \RuntimeException('پردازش ' . esc_html($fileLabel) . ' آپلودشده در باسلا�
143 نا�
144 وفق بود.');
145 }
146 }
147
148 if ($attempt < $maxAttempts - 1) sleep(1);
149 }
150
151 return null;
152 }
153
154 private function detectMimeType(string $filePath): string
155 {
156 $fileType = wp_check_filetype($filePath);
157 if (!empty($fileType['type'])) return (string) $fileType['type'];
158
159 if (function_exists('finfo_open')) {
160 $finfo = finfo_open(FILEINFO_MIME_TYPE);
161 if ($finfo) {
162 $mimeType = finfo_file($finfo, $filePath);
163 finfo_close($finfo);
164 if (is_string($mimeType) && $mimeType !== '') return $mimeType;
165 }
166 }
167
168 return 'application/octet-stream';
169 }
170
171 private function decodeBody($body): array
172 {
173 if (is_array($body)) return $body;
174 if (!is_string($body) || $body === '') return [];
175
176 $decoded = json_decode($body, true);
177 return is_array($decoded) ? $decoded : [];
178 }
179
180 private function normalizeCompletedMedia(array $body, string $requestFileId): ?array
181 {
182 $data = isset($body['data']) && is_array($body['data']) ? $body['data'] : $body;
183 $ready = $data['ready'] ?? null;
184 $status = isset($data['status']) && is_string($data['status']) ? strtolower($data['status']) : null;
185
186 if (isset($data['media']) && is_array($data['media'])) $data = $data['media'];
187 if (isset($data['file']) && is_array($data['file'])) $data = $data['file'];
188
189 if (isset($data['status']) && is_string($data['status'])) $status = strtolower($data['status']);
190 if (array_key_exists('ready', $data)) $ready = $data['ready'];
191
192 if (in_array($status, ['failed', 'aborted'], true)) {
193 throw new \RuntimeException('پردازش فایل آپلودشده در باسلا�
194 نا�
195 وفق بود.');
196 }
197
198 if ($ready === false || in_array($status, ['initiated', 'uploading', 'processing', 'queued'], true)) {
199 return null;
200 }
201
202 $mediaId = $data['id'] ?? $data['media_id'] ?? null;
203 if ($mediaId === null || $mediaId === '') return null;
204
205 $url = $data['urls']['primary'] ?? $data['url'] ?? $data['media_url'] ?? null;
206
207 return [
208 'file_id' => $mediaId,
209 'url' => is_string($url) ? $url : null,
210 'upload_file_id' => $requestFileId,
211 ];
212 }
213 }
214