Passed
Pull Request — master (#4)
by
unknown
04:30
created

Session::getLastRecordingId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SquareetLabs\LaravelOpenVidu;
4
5
use Exception;
6
use GuzzleHttp\Client;
7
use GuzzleHttp\RequestOptions;
8
use Illuminate\Support\Facades\Cache;
9
use JsonSerializable;
10
use SquareetLabs\LaravelOpenVidu\Builders\ConnectionBuilder;
11
use SquareetLabs\LaravelOpenVidu\Builders\PublisherBuilder;
12
use SquareetLabs\LaravelOpenVidu\Builders\SessionPropertiesBuilder;
13
use SquareetLabs\LaravelOpenVidu\Enums\MediaMode;
14
use SquareetLabs\LaravelOpenVidu\Enums\OpenViduRole;
15
use SquareetLabs\LaravelOpenVidu\Enums\OutputMode;
16
use SquareetLabs\LaravelOpenVidu\Enums\RecordingLayout;
17
use SquareetLabs\LaravelOpenVidu\Enums\RecordingMode;
18
use SquareetLabs\LaravelOpenVidu\Enums\Uri;
19
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduConnectionNotFoundException;
20
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduException;
21
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduSessionNotFoundException;
22
use SquareetLabs\LaravelOpenVidu\Exceptions\OpenViduTokenCantCreateException;
23
24
/**
25
 * Class Session
26
 * @package SquareetLabs\LaravelOpenVidu
27
 */
28
class Session implements JsonSerializable
29
{
30
    /** @var  Client */
31
    private $client;
32
33
    /** @var  string */
34
    private $sessionId;
35
36
    /** @var SessionProperties */
37
    private $properties;
38
39
    /** @var bool */
40
    private $recording;
41
42
    /** @var string */
43
    private $lastRecordingId;
44
45
    /** @var array */
46
    private $activeConnections = [];
47
48
    /** @var int */
49
    private $createdAt;
50
51
    /**
52
     * Session constructor.
53
     * @param  Client  $client
54
     * @param  SessionProperties|null  $properties
55
     * @throws OpenViduException
56
     */
57
    public function __construct(Client $client, ?SessionProperties $properties = null)
58
    {
59
        $this->client = $client;
60
        $this->properties = $properties ? $properties : new SessionProperties(MediaMode::ROUTED, RecordingMode::MANUAL, OutputMode::COMPOSED, RecordingLayout::BEST_FIT);
61
        $this->sessionId = $this->getSessionId();
62
    }
63
64
    /**
65
     * @return string
66
     * @throws OpenViduException
67
     */
68
    public function getSessionId()
69
    {
70
        if (empty($this->sessionId)) {
71
            $response = $this->client->post(Uri::SESSION_URI, [
72
                RequestOptions::JSON => $this->properties->toArray()
73
            ]);
74
            switch ($response->getStatusCode()) {
75
                case 200:
76
                    return json_decode($response->getBody()->getContents())->id;
77
                case 409:
78
                    return $this->properties->getCustomSessionId();
79
                default:
80
                    throw new OpenViduException("Invalid response status code ".$response->getStatusCode(), $response->getStatusCode());
81
            }
82
        } else {
83
            return $this->sessionId;
84
        }
85
    }
86
87
    /**
88
     ** Gets a new token associated to Session object with default values for
89
     * {@see TokenOptions}. This always translates into a
90
     * new request to OpenVidu Server
91
     *
92
     * @param  TokenOptions|null  $tokenOptions
93
     * @return string The generated token
94
     * @throws OpenViduException
95
     */
96
    public function generateToken(?TokenOptions $tokenOptions = null)
97
    {
98
        $this->getSessionId();
99
        try {
100
            if (!$tokenOptions) {
101
                $tokenOptions = new TokenOptions(OpenViduRole::PUBLISHER);;
102
            }
103
            $response = $this->client->post(Uri::TOKEN_URI, [
104
                RequestOptions::JSON => array_merge($tokenOptions->toArray(), ['session' => $this->sessionId])
105
            ]);
106
            return json_decode($response->getBody()->getContents());
107
        } catch (Exception $e) {
108
            throw new OpenViduTokenCantCreateException($e->getMessage(), $e);
109
        }
110
    }
111
112
    /**
113
     * Gracefully closes the Session: unpublishes all streams and evicts every
114
     * participant
115
     * @throws OpenViduException
116
     */
117
    public function close()
118
    {
119
        $response = $this->client->delete(Uri::SESSION_URI.'/'.$this->sessionId);
120
        switch ($response->getStatusCode()) {
121
            case 204:
122
                Cache::store('openvidu')->forget($this->sessionId);
123
                return true;
124
            case 404:
125
                throw new OpenViduSessionNotFoundException();
126
                break;
127
            default:
128
                throw new OpenViduException("Invalid response status code ".$response->getStatusCode(), $response->getStatusCode());
129
        }
130
    }
131
132
    /**
133
     * Updates every property of the Session with the current status it has in
134
     * OpenVidu Server. This is especially useful for getting the list of active
135
     * connections to the Session
136
     * ({@see getActiveConnections()}) and use
137
     * those values to call
138
     * {@see forceDisconnect(Connection)} or
139
     * {@link forceUnpublish(Publisher)}. <br>
140
     *
141
     * To update every Session object owned by OpenVidu object, call
142
     * {@see fetch()}
143
     *
144
     * @return bool true if the Session status has changed with respect to the server,
145
     * false if not. This applies to any property or sub-property of the object
146
     */
147
148
    public function fetch()
149
    {
150
        $response = $this->client->get(Uri::SESSION_URI.'/'.$this->sessionId, [
151
            'headers' => [
152
                'Content-Type' => 'application/x-www-form-urlencoded',
153
                'Accept' => 'application/json',
154
            ]
155
        ]);
156
        if ($response->getStatusCode() === 200) {
157
            $beforeJSON = $this->toJson();
158
            $this->fromJson($response->getBody()->getContents());
159
            $afterJSON = $this->toJson();
160
            if ($beforeJSON !== $afterJSON) {
161
                Cache::store('openvidu')->update($this->sessionId, $this->toJson());
162
                return true;
163
            }
164
        }
165
        return false;
166
    }
167
168
    /**
169
     * Convert the model instance to JSON.
170
     *
171
     * @param  int  $options
172
     * @return string
173
     *
174
     */
175
    public function toJson($options = 0): string
176
    {
177
        $json = json_encode($this->jsonSerialize(), $options);
178
        return $json;
179
    }
180
181
    /**
182
     * Specify data which should be serialized to JSON
183
     * @link https://php.net/manual/en/jsonserializable.jsonserialize.php
184
     * @return mixed data which can be serialized by <b>json_encode</b>,
185
     * which is a value of any type other than a resource.
186
     * @since 5.4.0
187
     */
188
    public function jsonSerialize()
189
    {
190
        return $this->toArray();
191
    }
192
193
    /**
194
     * Convert the model instance to an array.
195
     *
196
     * @return array
197
     */
198
    public function toArray(): array
199
    {
200
        $array = ['sessionId' => $this->sessionId, 'properties' => $this->properties->toArray(), 'recording' => $this->recording, 'createdAt' => $this->createdAt];
201
        foreach ($this->activeConnections as $connection) {
202
            $array['activeConnections'][] = $connection->toArray();
203
        }
204
205
        foreach ($array as $key => $value) {
206
            if (is_null($value) || $value == '') {
207
                unset($array[$key]);
208
            }
209
        }
210
        return $array;
211
    }
212
213
    /**
214
     * @param  string  $json
215
     * @return Session
216
     */
217
    public function fromJson(string $json): Session
218
    {
219
        return $this->fromArray(json_decode($json, true));
220
    }
221
222
    /**
223
     * @param  array  $sessionArray
224
     * @return Session
225
     */
226
    public function fromArray(array $sessionArray): Session
227
    {
228
        $this->sessionId = $sessionArray['sessionId'];
229
        $this->createdAt = $sessionArray['createdAt'] ?? null;
230
        $this->recording = $sessionArray['recording'] ?? null;
231
232
        if (array_key_exists('properties', $sessionArray)) {
233
            $this->properties = SessionPropertiesBuilder::build($sessionArray['properties']);
234
        }
235
236
        $this->activeConnections = [];
237
        if (array_key_exists('connections', $sessionArray)) {
238
            foreach ($sessionArray['connections'] as $connection) {
239
                $publishers = [];
240
                $ensure = $connection['content'] ?? $connection;
241
                foreach ($ensure['publishers'] as $publisher) {
242
                    $publishers[] = PublisherBuilder::build($publisher);
243
                }
244
                $subscribers = [];
245
                foreach ($ensure->subscribers as $subscriber) {
246
                    $subscribers[] = $subscriber->streamId;
247
                }
248
                $this->activeConnections[] = ConnectionBuilder::build($ensure, $publishers, $subscribers);
249
            }
250
        }
251
        return $this;
252
    }
253
254
    /**
255
     * Forces the user with Connection `connectionId` to leave the session. OpenVidu Browser will trigger the proper events on the client-side
256
     * (`streamDestroyed`, `connectionDestroyed`, `sessionDisconnected`) with reason set to `"forceDisconnectByServer"`
257
     *
258
     *
259
     * @param  string  $connectionId
260
     * @throws OpenViduException
261
     */
262
    public function forceDisconnect(string $connectionId)
263
    {
264
        $response = $this->client->delete(Uri::SESSION_URI.'/'.$this->sessionId.'/connection/'.$connectionId, [
265
            'headers' => [
266
                'Content-Type' => 'application/x-www-form-urlencoded',
267
                'Accept' => 'application/json',
268
            ]
269
        ]);
270
        switch ($response->getStatusCode()) {
271
            case 204:
272
                $this->leaveSession($connectionId);
273
                Cache::store('openvidu')->update($this->sessionId, $this->toJson());
274
                break;
275
            case 400:
276
                throw new OpenViduSessionNotFoundException();
277
                break;
278
            case 404:
279
                throw new OpenViduConnectionNotFoundException();
280
                break;
281
            default:
282
                throw new OpenViduException("Invalid response status code ".$response->getStatusCode(), $response->getStatusCode());
283
        }
284
    }
285
286
    /**
287
     * Get `connection` parameter from activeConnections array {@see Connection::getConnectionId()} for getting each `connectionId` property).
288
     * Remember to call {@see fetch()} before to fetch the current actual properties of the Session from OpenVidu Server
289
     * @param  string  $connectionId
290
     */
291
292
    private function leaveSession(string $connectionId)
293
    {
294
        $connectionClosed = null;
295
        $this->activeConnections = array_filter($this->activeConnections, function (Connection $connection) use (&$connectionClosed, $connectionId) {
296
            if ($connection->getConnectionId() !== $connectionId) {
297
                return true;
298
            }
299
            $connectionClosed = $connection;
300
            return false;
301
        });
302
        if ($connectionClosed != null) {
303
            foreach ($connectionClosed->getPublishers() as $publisher) {
304
                foreach ($this->activeConnections as $con) {
305
                    $con->unsubscribe($publisher->getStreamId());
306
                }
307
            }
308
        }
309
    }
310
311
    /**
312
     * Forces some user to unpublish a Stream. OpenVidu Browser will trigger the
313
     * proper events on the client-side (<code>streamDestroyed</code>) with reason
314
     * set to "forceUnpublishByServer". <br>
315
     *
316
     * You can get <code>streamId</code> parameter with
317
     * {@see Session::getActiveConnections()} and then for
318
     * each Connection you can call
319
     * {@see  Connection::getPublishers()}. Finally
320
     * {@see Publisher::getStreamId()}) will give you the
321
     * <code>streamId</code>. Remember to call
322
     * {@see fetch()} before to fetch the current
323
     * actual properties of the Session from OpenVidu Server
324
     *
325
     * @param  string  $streamId
326
     * @return void
327
     * @throws OpenViduConnectionNotFoundException
328
     * @throws OpenViduException
329
     * @throws OpenViduSessionNotFoundException
330
     */
331
    public function forceUnpublish(string $streamId)
332
    {
333
        $response = $this->client->delete(Uri::SESSION_URI.'/'.$this->sessionId.'/stream/'.$streamId, [
334
            'headers' => [
335
                'Content-Type' => 'application/x-www-form-urlencoded',
336
                'Accept' => 'application/json',
337
            ]
338
        ]);
339
        switch ($response->getStatusCode()) {
340
            case 204:
341
                foreach ($this->activeConnections as $connection) {
342
                    $connection->unpublish($streamId);
343
                    $connection->unsubscribe($streamId);
344
                }
345
                Cache::store('openvidu')->update($this->sessionId, $this->toJson());
346
                break;
347
            case 400:
348
                throw new OpenViduSessionNotFoundException();
349
                break;
350
            case 404:
351
                throw new OpenViduConnectionNotFoundException();
352
                break;
353
            default:
354
                throw new OpenViduException("Invalid response status code ".$response->getStatusCode(), $response->getStatusCode());
355
        }
356
    }
357
358
    /**
359
     * Returns the list of active connections to the session. <strong>This value
360
     * will remain unchanged since the last time method
361
     * {@see fetch()} was called</strong>.
362
     * Exceptions to this rule are:
363
     * <ul>
364
     * <li>Calling {@see Session::forceUnpublish(string)}
365
     * updates each affected Connection status</li>
366
     * <li>Calling {@see Session::forceDisconnect(string)}
367
     * updates each affected Connection status</li>
368
     * </ul>
369
     * <br>
370
     * To get the list of active connections with their current actual value, you
371
     * must call first {@see Session::fetch()} and then
372
     * {@see Session::getActiveConnections()}
373
     */
374
    public function getActiveConnections(): array
375
    {
376
        return $this->activeConnections;
377
    }
378
379
    /**
380
     * Returns whether the session is being recorded or not
381
     */
382
    public function isBeingRecorded(): bool
383
    {
384
        return !!$this->recording;
385
    }
386
387
    /**
388
     * Set value
389
     * @param  bool  $recording
390
     */
391
    public function setIsBeingRecorded(bool $recording)
392
    {
393
        $this->recording = $recording;
394
        Cache::store('openvidu')->update($this->sessionId, $this->toJson());
395
    }
396
397
    public function getLastRecordingId() {
398
        return $this->lastRecordingId;        
399
    }
400
401
    public function setLastRecordingId(string $lastRecordingId) {
402
        $this->lastRecordingId = $lastRecordingId;
403
        Cache::store('openvidu')->update($this->sessionId, $this->toJson());
404
    }
405
406
    /**
407
     * @return string
408
     * @throws OpenViduException
409
     */
410
    public function __toString(): string
411
    {
412
        return $this->getSessionId();
413
    }
414
}
415