YouTubeAdapter::getVendor()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Libcast\AssetDistributor\YouTube;
4
5
use Google_Http_MediaFileUpload as FileUpload;
6
use Google_Service_YouTube_VideoSnippet as Snippet;
7
use Google_Service_YouTube_VideoStatus as Status;
8
use Google_Service_YouTube_Video as Resource;
9
use Libcast\AssetDistributor\Adapter\AbstractAdapter;
10
use Libcast\AssetDistributor\Adapter\Adapter;
11
use Libcast\AssetDistributor\Asset\Asset;
12
use Libcast\AssetDistributor\Asset\Video;
13
use Libcast\AssetDistributor\Request;
14
use Symfony\Component\HttpFoundation\Session\Session;
15
16
class YouTubeAdapter extends AbstractAdapter implements Adapter
17
{
18
    /**
19
     *
20
     * @var string Json
21
     */
22
    protected $accessToken;
23
24
    /**
25
     *
26
     * @return string
27
     */
28
    public function getVendor()
29
    {
30
        return 'YouTube';
31
    }
32
33
    /**
34
     *
35
     * @return \Google_Service_YouTube
36
     */
37
    public function getClient()
38
    {
39
        if ($this->client) {
40
            return $this->client;
41
        }
42
43
        $google = new \Google_Client;
44
        $google->setClientId($this->getConfiguration('id'));
45
        $google->setClientSecret($this->getConfiguration('secret'));
46
        $google->setApplicationName($this->getConfiguration('application'));
47
        $google->setAccessType($this->getConfiguration('access_type'));
48
49
        $google->setScopes($this->getConfiguration('scopes'));
50
51
        $google->setRedirectUri($this->getConfiguration('redirectUri'));
52
53
        if ($token = $this->getCredentials()) {
54
            $google->setAccessToken($token);
55
56
            if ($google->isAccessTokenExpired()) {
57
                $json = json_decode($token);
58
                $google->refreshToken($json->refresh_token);
59
                $this->setCredentials($google->getAccessToken());
60
            }
61
62
            $this->isAuthenticated = true;
63
        }
64
65
        $this->client = new \Google_Service_YouTube($google); // Now getClient() returns \Google_Service_YouTube
66
67
        if (!$this->isAuthenticated()) {
68
            $this->authenticate();
69
        }
70
71
        return $this->client;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77
    public function authenticate()
78
    {
79
        $google = $this->getClient()->getClient();
80
        $request = Request::get();
81
        $session = new Session;
82
83
        if ($code = $request->query->get('code')) {
84
            if (!$requestState = $request->query->get('state')) {
85
                throw new \Exception('Missing state from YouTube request');
86
            }
87
88
            if (!$sessionState = $session->get('youtube_state')) {
89
                throw new \Exception('Missing state from YouTube session');
90
            }
91
92
            if (strval($requestState) !== strval($sessionState)) {
93
                throw new \Exception('YouTube session state and request state don\'t match');
94
            }
95
96
            $this->debug('Found YouTube oAuth code', ['code' => $code]);
97
            $google->authenticate($code);
98
99
            $session->set('youtube_token', $google->getAccessToken());
100
        }
101
102
        if ($token = $session->get('youtube_token')) {
103
            $this->debug('Found YouTube oAuth token', ['token' => $token]);
104
            $google->setAccessToken($token);
105
        }
106
107
        if ($accessToken = $google->getAccessToken()) {
108
            $this->accessToken = $accessToken;
109
            $this->setCredentials($accessToken);
110
        } else {
111
            $this->debug('Missing YouTube token, try to authenticate...');
112
113
            $state = mt_rand();
114
            $google->setState($state);
115
            $session->set('youtube_state', $state);
116
117
            $this->redirect($google->createAuthUrl());
118
        }
119
120
        // Clean query parameters as they may cause error
121
        // on other Adapter's authentication process
122
        $request->query->set('code', null);
123
        $request->query->set('state', null);
124
125
        $this->debug('YouTube account is authenticated');
126
127
        $this->isAuthenticated = true;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public static function support(Asset $asset)
134
    {
135
        if (!$asset instanceof Video) {
136
            return false;
137
        }
138
139
        return true;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function upload(Asset $asset)
146
    {
147
        if (!self::support($asset)) {
148
            throw new \Exception('YouTube adapter only handles video assets');
149
        }
150
151
        if (!is_null($this->retrieve($asset))) {
152
            return;
153
        }
154
155
        $youtube = $this->getClient();
156
157
        $client = $youtube->getClient();
158
        $client->setDefer(true);
159
160
        $request = $youtube->videos->insert('status,snippet', $this->getResource($asset)); /** @var \Psr\Http\Message\RequestInterface $request */
161
162
        $media = new FileUpload($client, $request, $asset->getMimetype(), null, true);
163
        $media->setChunkSize($chunkSize = 10 * 1024 * 1024);
164
        $media->setFileSize($asset->getSize());
165
166
        $status = false;
167
        $handle = fopen($asset->getPath(), 'rb');
168
        while (!$status and !feof($handle)) {
169
            $chunk = fread($handle, $chunkSize);
170
            $status = $media->nextChunk($chunk);
171
        }
172
        fclose($handle);
173
174
        $client->setDefer(false);
175
176
        $this->remember($asset, $status['id']);
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182
    public function update(Asset $asset)
183
    {
184
        if (!$video_id = $this->retrieve($asset)) {
185
            $this->upload($asset);
186
            return;
187
        }
188
189
        $youtube = $this->getClient();
190
191
        $resource = $this->getResource($asset);
192
        $resource->setId($video_id);
193
194
        $youtube->videos->update('status,snippet', $resource);
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 View Code Duplication
    public function remove(Asset $asset)
201
    {
202
        if (!$video_id = $this->retrieve($asset)) {
203
            throw new \Exception('Asset is unknown to YouTube');
204
        }
205
206
        $youtube = $this->getClient();
207
        $youtube->videos->delete($video_id);
208
209
        $this->forget($asset);
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    protected function retrieve(Asset $asset)
216
    {
217
        if (!$video_id = parent::retrieve($asset)) {
218
            return null;
219
        }
220
221
        $youtube = $this->getClient();
222
        $videos = $youtube->videos->listVideos('snippet', ['id' => $video_id]);
223
224
        return count($videos) ? $video_id : null;
225
    }
226
227
    /**
228
     *
229
     * @param Asset $asset
230
     * @return \Google_Service_YouTube_VideoSnippet
231
     */
232
    protected function getSnippet(Asset $asset)
233
    {
234
        $snippet = new Snippet;
235
        $snippet->setTitle($asset->getTitle());
236
237
        if ($description = $asset->getDescription()) {
238
            $snippet->setDescription($description);
239
        }
240
241
        if ($tags = $asset->getTags()) {
242
            $snippet->setTags($tags);
243
        }
244
245
        if ($category_id = $asset->getCategory($this->getVendor())) {
246
            $snippet->setCategoryId($category_id);
247
        }
248
249
        return $snippet;
250
    }
251
252
    /**
253
     *
254
     * @param Asset $asset
255
     * @return Status
256
     * @throws \Exception
257
     */
258
    protected function getStatus(Asset $asset)
259
    {
260
        $status = new Status;
261
262
        switch (true) {
263
            case $asset->isPublic():
264
                $status->setPrivacyStatus('public');
265
                break;
266
267
            case $asset->isPrivate():
268
                $status->setPrivacyStatus('private');
269
                break;
270
271
            case $asset->isHidden():
272
                $status->setPrivacyStatus('hidden');
273
                break;
274
275
            default:
276
                throw new \Exception('Missing asset visibility for YouTube');
277
        }
278
279
        return $status;
280
    }
281
282
    /**
283
     *
284
     * @param Asset $asset
285
     * @return \Google_Service_YouTube_Video
286
     */
287
    protected function getResource(Asset $asset)
288
    {
289
        $resource = new Resource;
290
        $resource->setSnippet($this->getSnippet($asset));
291
        $resource->setStatus($this->getStatus($asset));
292
293
        return $resource;
294
    }
295
}
296