mirror of
https://github.com/koel/koel
synced 2024-11-10 14:44:13 +00:00
Refactor Download service
This commit is contained in:
parent
c706391422
commit
caf13fbac0
4 changed files with 140 additions and 50 deletions
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace App\Http\Requests\API;
|
||||
|
||||
/**
|
||||
* @property string media_path
|
||||
*/
|
||||
class SettingRequest extends Request
|
||||
{
|
||||
/**
|
||||
|
|
|
@ -15,6 +15,7 @@ use Log;
|
|||
* @property string image
|
||||
* @property bool is_unknown
|
||||
* @property bool is_various
|
||||
* @property \Illuminate\Database\Eloquent\Collection songs
|
||||
*/
|
||||
class Artist extends Model
|
||||
{
|
||||
|
|
128
app/Models/SongZipArchive.php
Normal file
128
app/Models/SongZipArchive.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Facades\Download;
|
||||
use Exception;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use ZipArchive;
|
||||
|
||||
class SongZipArchive
|
||||
{
|
||||
/**
|
||||
* @var ZipArchive
|
||||
*/
|
||||
protected $archive;
|
||||
|
||||
/**
|
||||
* Path to the zip archive
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Names of the files in the archive
|
||||
* Format: [file-name.mp3' => currentFileIndex]
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fileNames = [];
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($path = '')
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
throw new Exception('Downloading multiple files requires ZipArchive module.');
|
||||
}
|
||||
|
||||
// We use system's temp dir instead of storage_path() here, so that the generated files
|
||||
// can be cleaned up automatically after server reboot.
|
||||
$this->path = $path ?: $path = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.'koel-download-'.uniqid().'.zip';
|
||||
|
||||
$this->archive = new ZipArchive();
|
||||
|
||||
if ($this->archive->open($this->path, ZipArchive::CREATE) !== true) {
|
||||
throw new Exception('Cannot create zip file.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple songs into the archive.
|
||||
*
|
||||
* @param Collection $songs
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addSongs(Collection $songs)
|
||||
{
|
||||
$songs->each(function ($song) {
|
||||
$this->addSong($song);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single song into the archive.
|
||||
*
|
||||
* @param Song $song
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addSong(Song $song)
|
||||
{
|
||||
try {
|
||||
$path = Download::fromSong($song);
|
||||
|
||||
// We add all files into the zip archive as a flat structure.
|
||||
// As a result, there can be duplicate file names.
|
||||
// The following several lines are to make sure each file name is unique.
|
||||
$name = basename($path);
|
||||
if (array_key_exists($name, $this->fileNames)) {
|
||||
++$this->fileNames[$name];
|
||||
$parts = explode('.', $name);
|
||||
$ext = $parts[count($parts) - 1];
|
||||
$parts[count($parts) - 1] = $this->fileNames[$name].".$ext";
|
||||
$name = implode('.', $parts);
|
||||
} else {
|
||||
$this->fileNames[$name] = 1;
|
||||
}
|
||||
|
||||
$this->archive->addFile($path, $name);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish (close) the archive.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function finish()
|
||||
{
|
||||
$this->archive->close();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the archive.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
|
@ -6,10 +6,10 @@ use App\Models\Album;
|
|||
use App\Models\Artist;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\Song;
|
||||
use App\Models\SongZipArchive;
|
||||
use Exception;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use ZipArchive;
|
||||
|
||||
class Download
|
||||
{
|
||||
|
@ -46,12 +46,12 @@ class Download
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function fromSong(Song $song)
|
||||
public function fromSong(Song $song)
|
||||
{
|
||||
if ($s3Params = $song->s3_params) {
|
||||
// The song is hosted on Amazon S3.
|
||||
// We download it back to our local server first.
|
||||
$localPath = rtrim(sys_get_temp_dir(), '/').'/'.basename($s3Params['key']);
|
||||
$localPath = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.basename($s3Params['key']);
|
||||
$url = $song->getObjectStoragePublicUrl();
|
||||
|
||||
abort_unless($url, 404);
|
||||
|
@ -72,7 +72,7 @@ class Download
|
|||
|
||||
// For those with high-byte characters in names, we copy it into a safe name
|
||||
// as a workaround.
|
||||
$newPath = rtrim(sys_get_temp_dir(), '/').'/'.utf8_decode(basename($song->path));
|
||||
$newPath = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.utf8_decode(basename($song->path));
|
||||
|
||||
if ($s3Params) {
|
||||
// If the file is downloaded from S3, we rename it directly.
|
||||
|
@ -101,52 +101,10 @@ class Download
|
|||
return $this->fromSong($songs->first());
|
||||
}
|
||||
|
||||
if (!class_exists('ZipArchive')) {
|
||||
throw new Exception('Downloading multiple files requires ZipArchive module.');
|
||||
}
|
||||
|
||||
// Start gathering the songs into a zip file.
|
||||
$zip = new ZipArchive();
|
||||
|
||||
// We use system's temp dir instead of storage_path() here, so that the generated files
|
||||
// can be cleaned up automatically after server reboot.
|
||||
$filename = rtrim(sys_get_temp_dir(), '/').'/koel-download-'.uniqid().'.zip';
|
||||
if ($zip->open($filename, ZipArchive::CREATE) !== true) {
|
||||
throw new Exception('Cannot create zip file.');
|
||||
}
|
||||
|
||||
$localNames = [
|
||||
// The data will follow this format:
|
||||
// 'duplicated-name.mp3' => currentFileIndex
|
||||
];
|
||||
|
||||
$songs->each(function ($song) use ($zip, &$localNames) {
|
||||
try {
|
||||
$path = $this->fromSong($song);
|
||||
|
||||
// We add all files into the zip archive as a flat structure.
|
||||
// As a result, there can be duplicate file names.
|
||||
// The following several lines are to make sure each file name is unique.
|
||||
$name = basename($path);
|
||||
if (array_key_exists($name, $localNames)) {
|
||||
++$localNames[$name];
|
||||
$parts = explode('.', $name);
|
||||
$ext = $parts[count($parts) - 1];
|
||||
$parts[count($parts) - 1] = $localNames[$name].".$ext";
|
||||
$name = implode('.', $parts);
|
||||
} else {
|
||||
$localNames[$name] = 1;
|
||||
}
|
||||
|
||||
$zip->addFile($path, $name);
|
||||
} catch (Exception $e) {
|
||||
Log::error($e);
|
||||
}
|
||||
});
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $filename;
|
||||
return (new SongZipArchive())
|
||||
->addSongs($songs)
|
||||
->finish()
|
||||
->getPath();
|
||||
}
|
||||
|
||||
protected function fromPlaylist(Playlist $playlist)
|
||||
|
|
Loading…
Reference in a new issue