Completed
Push — 1.5 ( 01329b...a1b284 )
by Paweł
13s
created

MediaManager::retryDecider()   B

Complexity

Conditions 6
Paths 1

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.7537
c 0
b 0
f 0
cc 6
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Superdesk Web Publisher Content Bundle.
5
 *
6
 * Copyright 2016 Sourcefabric z.ú. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2016 Sourcefabric z.ú
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace SWP\Bundle\ContentBundle\Manager;
16
17
use GuzzleHttp\Client;
18
use GuzzleHttp\Handler\CurlHandler;
19
use GuzzleHttp\HandlerStack;
20
use GuzzleHttp\Middleware;
21
use Hoa\Mime\Mime;
22
use Psr\Log\LoggerInterface;
23
use SWP\Bundle\ContentBundle\Doctrine\ArticleMediaRepositoryInterface;
24
use SWP\Bundle\ContentBundle\Factory\FileFactoryInterface;
25
use SWP\Bundle\ContentBundle\Model\ArticleMedia;
26
use SWP\Bundle\ContentBundle\Model\FileInterface;
27
use Symfony\Component\HttpFoundation\File\UploadedFile;
28
use League\Flysystem\Filesystem;
29
use Symfony\Component\Routing\RouterInterface;
30
use GuzzleHttp\Exception\ConnectException;
31
use GuzzleHttp\Exception\RequestException;
32
use GuzzleHttp\Psr7\Request;
33
use GuzzleHttp\Psr7\Response;
34
35
class MediaManager implements MediaManagerInterface
36
{
37
    /**
38
     * @var Filesystem
39
     */
40
    protected $filesystem;
41
42
    /**
43
     * @var RouterInterface
44
     */
45
    protected $router;
46
47
    /**
48
     * @var ArticleMediaRepositoryInterface
49
     */
50
    protected $mediaRepository;
51
52
    /**
53
     * @var FileFactoryInterface
54
     */
55
    protected $fileFactory;
56
57
    /**
58
     * @var LoggerInterface
59
     */
60
    private $logger;
61
62
    public function __construct(
63
        ArticleMediaRepositoryInterface $mediaRepository,
64
        Filesystem $filesystem,
65
        RouterInterface $router,
66
        FileFactoryInterface $fileFactory,
67
        LoggerInterface $logger
68
    ) {
69
        $this->mediaRepository = $mediaRepository;
70
        $this->filesystem = $filesystem;
71
        $this->router = $router;
72
        $this->fileFactory = $fileFactory;
73
        $this->logger = $logger;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function handleUploadedFile(UploadedFile $uploadedFile, $mediaId)
80
    {
81
        $mediaId = ArticleMedia::handleMediaId($mediaId);
82
        $asset = $this->createMediaAsset($uploadedFile, $mediaId);
83
        $this->saveFile($uploadedFile, $mediaId);
84
        $this->mediaRepository->persist($asset);
85
86
        return $asset;
87
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function getFile(FileInterface $media)
93
    {
94
        return $this->filesystem->read($this->getMediaBasePath().'/'.$media->getAssetId().'.'.$media->getFileExtension());
95
    }
96
97
    /**
98
     * {@inheritdoc}
99
     */
100
    public function saveFile(UploadedFile $uploadedFile, $fileName)
101
    {
102
        $extension = $this->guessExtension($uploadedFile);
103
        $filePath = $this->getMediaBasePath().'/'.$fileName.'.'.$extension;
104
105
        if ($this->filesystem->has($filePath)) {
106
            return true;
107
        }
108
109
        $stream = fopen($uploadedFile->getRealPath(), 'r+');
110
        $result = $this->filesystem->writeStream($filePath, $stream);
111
        fclose($stream);
112
113
        return $result;
114
    }
115
116
    public function downloadFile(string $url, string $mediaId, string $mimeType = null): UploadedFile
117
    {
118
        $pathParts = \pathinfo($url);
119
        if (null === $mimeType) {
120
            $mimeType = Mime::getMimeFromExtension($pathParts['extension']);
121
        }
122
123
        $handlerStack = HandlerStack::create(new CurlHandler());
124
        $handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));
125
        $client = new Client(array('handler' => $handlerStack));
126
        $tempLocation = \rtrim(\sys_get_temp_dir(), '/').DIRECTORY_SEPARATOR.\sha1($mediaId.date('his'));
127
        $client->request('GET', $url, ['sink' => $tempLocation]);
128
129
        return new UploadedFile($tempLocation, $mediaId, $mimeType, \strlen($tempLocation), null, true);
130
    }
131
132
    /**
133
     * {@inheritdoc}
134
     */
135
    public function getMediaPublicUrl(FileInterface $media)
136
    {
137
        return $this->getMediaUri($media, RouterInterface::ABSOLUTE_URL);
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public function getMediaUri(FileInterface $media, $type = RouterInterface::ABSOLUTE_PATH)
144
    {
145
        return $this->router->generate('swp_media_get', [
146
            'mediaId' => $media->getAssetId(),
147
            'extension' => $media->getFileExtension(),
148
        ], $type);
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154
    public function createMediaAsset(UploadedFile $uploadedFile, string $assetId): FileInterface
155
    {
156
        $extension = $this->guessExtension($uploadedFile);
157
158
        return $this->fileFactory->createWith($assetId, $extension);
159
    }
160
161
    /**
162
     * @return string
163
     */
164
    protected function getMediaBasePath(): string
165
    {
166
        $pathElements = ['swp', 'media'];
167
168
        return implode('/', $pathElements);
169
    }
170
171
    private function guessExtension(UploadedFile $uploadedFile): string
172
    {
173
        $extension = $uploadedFile->guessExtension();
174
175
        if ('mpga' === $extension && 'mp3' === $uploadedFile->getExtension()) {
176
            $extension = 'mp3';
177
        }
178
179
        return $extension;
180
    }
181
182
    public function retryDecider()
183
    {
184
        return function (
185
            $retries,
186
            Request $request,
187
            Response $response = null,
188
            RequestException $exception = null
189
        ): bool {
190
            $retry = false;
191
            if ($retries >= 4) {
192
                $this->logger->error(\sprintf('Maximum number of retires reached'));
193
194
                return false;
195
            }
196
197
            // Retry connection exceptions
198
            if ($exception instanceof ConnectException) {
199
                $retry = true;
200
            }
201
202
            if ($response) {
203
                // Retry on server errors
204
                if ($response->getStatusCode() >= 400) {
205
                    $retry = true;
206
                }
207
            }
208
209
            if (true === $retry) {
210
                $this->logger->info(\sprintf('Retry downloading %s', $request->getUri()));
211
            }
212
213
            return $retry;
214
        };
215
    }
216
217
    public function retryDelay()
218
    {
219
        return function ($numberOfRetries): int {
220
            return 1000 * $numberOfRetries;
221
        };
222
    }
223
}
224