mirror of
https://code.castopod.org/adaures/castopod.git
synced 2024-10-06 00:16:48 +02:00
fix(import): rewrite download_file helper to output curl response directly to file
This prevents memory exhaustion when downloading large files
This commit is contained in:
parent
281eefc6a3
commit
eb7ad2f7e1
@ -136,7 +136,9 @@ if (! function_exists('publication_pill')) {
|
|||||||
$customClass .
|
$customClass .
|
||||||
'">' .
|
'">' .
|
||||||
$label .
|
$label .
|
||||||
($publicationStatus === 'with_podcast' ? '<Icon glyph="error-warning" class="flex-shrink-0 ml-1 text-lg" />' : '') .
|
($publicationStatus === 'with_podcast' ? icon('error-warning-fill', [
|
||||||
|
'class' => 'flex-shrink-0 ml-1 text-lg',
|
||||||
|
]) : '') .
|
||||||
'</span>';
|
'</span>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,47 +9,12 @@ declare(strict_types=1);
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use CodeIgniter\Files\File;
|
use CodeIgniter\Files\File;
|
||||||
use CodeIgniter\HTTP\ResponseInterface;
|
|
||||||
use Config\Mimes;
|
use Config\Mimes;
|
||||||
use Config\Services;
|
|
||||||
|
|
||||||
if (! function_exists('download_file')) {
|
if (! function_exists('download_file')) {
|
||||||
function download_file(string $fileUrl, string $mimetype = ''): File
|
function download_file(string $fileUrl, string $mimetype = ''): File
|
||||||
{
|
{
|
||||||
$client = Services::curlrequest();
|
$fileExtension = pathinfo(parse_url($fileUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
|
||||||
|
|
||||||
$response = $client->get($fileUrl, [
|
|
||||||
'headers' => [
|
|
||||||
'User-Agent' => 'Castopod/' . CP_VERSION,
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
// redirect to new file location
|
|
||||||
$newFileUrl = $fileUrl;
|
|
||||||
while (
|
|
||||||
in_array(
|
|
||||||
$response->getStatusCode(),
|
|
||||||
[
|
|
||||||
ResponseInterface::HTTP_MOVED_PERMANENTLY,
|
|
||||||
ResponseInterface::HTTP_FOUND,
|
|
||||||
ResponseInterface::HTTP_SEE_OTHER,
|
|
||||||
ResponseInterface::HTTP_NOT_MODIFIED,
|
|
||||||
ResponseInterface::HTTP_TEMPORARY_REDIRECT,
|
|
||||||
ResponseInterface::HTTP_PERMANENT_REDIRECT,
|
|
||||||
],
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
$newFileUrl = trim($response->header('location')->getValue());
|
|
||||||
$response = $client->get($newFileUrl, [
|
|
||||||
'headers' => [
|
|
||||||
'User-Agent' => 'Castopod/' . CP_VERSION,
|
|
||||||
],
|
|
||||||
'http_errors' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fileExtension = pathinfo(parse_url($newFileUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
|
|
||||||
$extension = $fileExtension === '' ? Mimes::guessExtensionFromType($mimetype) : $fileExtension;
|
$extension = $fileExtension === '' ? Mimes::guessExtensionFromType($mimetype) : $fileExtension;
|
||||||
$tmpFilename =
|
$tmpFilename =
|
||||||
time() .
|
time() .
|
||||||
@ -57,9 +22,26 @@ if (! function_exists('download_file')) {
|
|||||||
bin2hex(random_bytes(10)) .
|
bin2hex(random_bytes(10)) .
|
||||||
'.' .
|
'.' .
|
||||||
$extension;
|
$extension;
|
||||||
$tmpfileKey = WRITEPATH . 'uploads/' . $tmpFilename;
|
$tmpfilePath = WRITEPATH . 'uploads/' . $tmpFilename;
|
||||||
file_put_contents($tmpfileKey, $response->getBody());
|
|
||||||
|
|
||||||
return new File($tmpfileKey);
|
$file = fopen($tmpfilePath, 'w');
|
||||||
|
$ch = curl_init();
|
||||||
|
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $fileUrl);
|
||||||
|
|
||||||
|
// output directly to file
|
||||||
|
curl_setopt($ch, CURLOPT_FILE, $file);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['User-Agent: Castopod/' . CP_VERSION]);
|
||||||
|
|
||||||
|
// follow redirects up to 20, like Apple Podcasts
|
||||||
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
|
||||||
|
|
||||||
|
curl_exec($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
|
||||||
|
return new File($tmpfilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ $isEpisodeArea = isset($podcast) && isset($episode);
|
|||||||
<div class="flex flex-wrap items-center truncate">
|
<div class="flex flex-wrap items-center truncate">
|
||||||
<?php if (($isEpisodeArea && $episode->is_premium) || ($isPodcastArea && $podcast->is_premium)): ?>
|
<?php if (($isEpisodeArea && $episode->is_premium) || ($isPodcastArea && $podcast->is_premium)): ?>
|
||||||
<div class="inline-flex items-center">
|
<div class="inline-flex items-center">
|
||||||
|
<?php // @icon('exchange-dollar-fill')?>
|
||||||
<IconButton uri="<?= route_to('subscription-list', $podcast->id) ?>" glyph="exchange-dollar-fill" variant="secondary" size="large" class="p-0 mr-2 border-0"><?= ($isEpisodeArea && $episode->is_premium) ? lang('PremiumPodcasts.episode_is_premium') : lang('PremiumPodcasts.podcast_is_premium') ?></IconButton>
|
<IconButton uri="<?= route_to('subscription-list', $podcast->id) ?>" glyph="exchange-dollar-fill" variant="secondary" size="large" class="p-0 mr-2 border-0"><?= ($isEpisodeArea && $episode->is_premium) ? lang('PremiumPodcasts.episode_is_premium') : lang('PremiumPodcasts.podcast_is_premium') ?></IconButton>
|
||||||
<Heading tagName="h1" size="large" class="truncate"><?= $this->renderSection('pageTitle') ?></Heading>
|
<Heading tagName="h1" size="large" class="truncate"><?= $this->renderSection('pageTitle') ?></Heading>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user