2020-06-07 20:43:04 +00:00
|
|
|
<?php
|
|
|
|
|
2024-02-23 18:36:02 +00:00
|
|
|
namespace App\Services\SongStorages;
|
2020-06-07 20:43:04 +00:00
|
|
|
|
2024-04-18 17:20:14 +00:00
|
|
|
use App\Enums\SongStorageType;
|
2020-06-07 20:43:04 +00:00
|
|
|
use App\Exceptions\MediaPathNotSetException;
|
|
|
|
use App\Exceptions\SongUploadFailedException;
|
|
|
|
use App\Models\Setting;
|
|
|
|
use App\Models\Song;
|
2024-01-04 11:35:36 +00:00
|
|
|
use App\Models\User;
|
2024-02-04 20:31:01 +00:00
|
|
|
use App\Services\FileScanner;
|
2024-01-04 21:51:32 +00:00
|
|
|
use App\Values\ScanConfiguration;
|
2024-02-23 16:03:54 +00:00
|
|
|
use Exception;
|
2020-06-07 20:43:04 +00:00
|
|
|
use Illuminate\Http\UploadedFile;
|
2024-01-04 21:51:32 +00:00
|
|
|
use Illuminate\Support\Facades\File;
|
2024-01-04 11:35:36 +00:00
|
|
|
use Throwable;
|
2020-06-07 20:43:04 +00:00
|
|
|
|
2020-12-22 20:11:22 +00:00
|
|
|
use function Functional\memoize;
|
|
|
|
|
2024-02-05 13:27:17 +00:00
|
|
|
final class LocalStorage extends SongStorage
|
2020-06-07 20:43:04 +00:00
|
|
|
{
|
2024-01-04 21:51:32 +00:00
|
|
|
public function __construct(private FileScanner $scanner)
|
2020-06-07 20:43:04 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-04 20:31:01 +00:00
|
|
|
public function storeUploadedFile(UploadedFile $file, User $uploader): Song
|
2020-06-07 20:43:04 +00:00
|
|
|
{
|
2024-04-04 22:20:42 +00:00
|
|
|
self::assertSupported();
|
|
|
|
|
2024-01-04 11:35:36 +00:00
|
|
|
$uploadDirectory = $this->getUploadDirectory($uploader);
|
|
|
|
$targetFileName = $this->getTargetFileName($file, $uploader);
|
|
|
|
|
|
|
|
$file->move($uploadDirectory, $targetFileName);
|
|
|
|
$targetPathName = $uploadDirectory . $targetFileName;
|
2020-06-07 20:43:04 +00:00
|
|
|
|
2024-01-04 11:35:36 +00:00
|
|
|
try {
|
2024-02-04 20:31:01 +00:00
|
|
|
$result = $this->scanner->setFile($targetPathName)
|
|
|
|
->scan(ScanConfiguration::make(
|
2024-01-23 22:50:50 +00:00
|
|
|
owner: $uploader,
|
|
|
|
makePublic: $uploader->preferences->makeUploadsPublic
|
2024-02-04 20:31:01 +00:00
|
|
|
));
|
2024-01-23 22:50:50 +00:00
|
|
|
} catch (Throwable $e) {
|
2024-01-04 21:51:32 +00:00
|
|
|
File::delete($targetPathName);
|
2024-01-23 22:50:50 +00:00
|
|
|
throw new SongUploadFailedException($e->getMessage());
|
2024-01-04 11:35:36 +00:00
|
|
|
}
|
2020-06-07 20:43:04 +00:00
|
|
|
|
2022-07-29 11:08:24 +00:00
|
|
|
if ($result->isError()) {
|
2024-01-04 21:51:32 +00:00
|
|
|
File::delete($targetPathName);
|
2022-07-29 11:08:24 +00:00
|
|
|
throw new SongUploadFailedException($result->error);
|
2020-06-07 20:43:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-04 21:51:32 +00:00
|
|
|
return $this->scanner->getSong();
|
2020-06-07 20:43:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-04 11:35:36 +00:00
|
|
|
private function getUploadDirectory(User $uploader): string
|
2020-06-07 20:43:04 +00:00
|
|
|
{
|
2024-01-04 11:35:36 +00:00
|
|
|
return memoize(static function () use ($uploader): string {
|
2020-06-07 20:43:04 +00:00
|
|
|
$mediaPath = Setting::get('media_path');
|
|
|
|
|
2024-01-04 11:35:36 +00:00
|
|
|
throw_unless((bool) $mediaPath, MediaPathNotSetException::class);
|
|
|
|
|
|
|
|
$dir = sprintf(
|
|
|
|
'%s%s__KOEL_UPLOADS_$%s__%s',
|
|
|
|
$mediaPath,
|
|
|
|
DIRECTORY_SEPARATOR,
|
|
|
|
$uploader->id,
|
|
|
|
DIRECTORY_SEPARATOR
|
|
|
|
);
|
|
|
|
|
2024-01-04 21:51:32 +00:00
|
|
|
File::ensureDirectoryExists($dir);
|
2020-06-07 20:43:04 +00:00
|
|
|
|
2024-01-04 11:35:36 +00:00
|
|
|
return $dir;
|
2020-11-14 16:57:25 +00:00
|
|
|
});
|
2020-06-07 20:43:04 +00:00
|
|
|
}
|
|
|
|
|
2024-01-04 11:35:36 +00:00
|
|
|
private function getTargetFileName(UploadedFile $file, User $uploader): string
|
2020-06-07 20:43:04 +00:00
|
|
|
{
|
|
|
|
// If there's no existing file with the same name in the upload directory, use the original name.
|
|
|
|
// Otherwise, prefix the original name with a hash.
|
|
|
|
// The whole point is to keep a readable file name when we can.
|
2024-01-10 00:47:09 +00:00
|
|
|
if (!File::exists($this->getUploadDirectory($uploader) . $file->getClientOriginalName())) {
|
2020-06-07 20:43:04 +00:00
|
|
|
return $file->getClientOriginalName();
|
|
|
|
}
|
|
|
|
|
2020-12-22 20:11:22 +00:00
|
|
|
return $this->getUniqueHash() . '_' . $file->getClientOriginalName();
|
2020-06-07 20:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getUniqueHash(): string
|
|
|
|
{
|
|
|
|
return substr(sha1(uniqid()), 0, 6);
|
|
|
|
}
|
2024-02-05 13:27:17 +00:00
|
|
|
|
|
|
|
public function supported(): bool
|
|
|
|
{
|
2024-04-18 17:20:14 +00:00
|
|
|
return SongStorageType::LOCAL->supported();
|
2024-02-05 13:27:17 +00:00
|
|
|
}
|
2024-02-23 16:03:54 +00:00
|
|
|
|
|
|
|
public function delete(Song $song, bool $backup = false): void
|
|
|
|
{
|
|
|
|
$path = $song->storage_metadata->getPath();
|
|
|
|
|
|
|
|
if ($backup) {
|
|
|
|
File::move($path, "$path.bak");
|
|
|
|
}
|
|
|
|
|
|
|
|
throw_unless(File::delete($path), new Exception("Failed to delete song file: $path"));
|
|
|
|
}
|
2020-06-07 20:43:04 +00:00
|
|
|
}
|