mirror of
https://github.com/koel/koel
synced 2024-11-14 00:17:13 +00:00
feat: replace attempt() with built-in rescue() (#1833)
This commit is contained in:
parent
0034c777a9
commit
b859f0bfec
20 changed files with 43 additions and 65 deletions
|
@ -9,11 +9,9 @@ class SmartPlaylistRulesCast implements CastsAttributes
|
||||||
{
|
{
|
||||||
public function get($model, string $key, $value, array $attributes): ?SmartPlaylistRuleGroupCollection
|
public function get($model, string $key, $value, array $attributes): ?SmartPlaylistRuleGroupCollection
|
||||||
{
|
{
|
||||||
if (!$value) {
|
return $value
|
||||||
return null;
|
? rescue(static fn () => SmartPlaylistRuleGroupCollection::create(json_decode($value, true)))
|
||||||
}
|
: null;
|
||||||
|
|
||||||
return attempt(static fn () => SmartPlaylistRuleGroupCollection::create(json_decode($value, true)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set($model, string $key, $value, array $attributes): ?string
|
public function set($model, string $key, $value, array $attributes): ?string
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
use App\Facades\License;
|
use App\Facades\License;
|
||||||
use Illuminate\Support\Facades\File as FileFacade;
|
use Illuminate\Support\Facades\File as FileFacade;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,34 +63,14 @@ function koel_version(): string
|
||||||
return trim(FileFacade::get(base_path('.version')));
|
return trim(FileFacade::get(base_path('.version')));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function rescue_if($condition, callable $callback): mixed
|
||||||
* @throws Throwable
|
|
||||||
*/
|
|
||||||
function attempt(callable $callback, bool $log = true, bool $throw = false): mixed
|
|
||||||
{
|
{
|
||||||
try {
|
return value($condition) ? rescue($callback) : null;
|
||||||
return $callback();
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
if (app()->runningUnitTests() || $throw) {
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($log) {
|
|
||||||
Log::error('Failed attempt', ['error' => $e]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function attempt_if($condition, callable $callback, bool $log = true): mixed
|
function rescue_unless($condition, callable $callback): mixed
|
||||||
{
|
{
|
||||||
return value($condition) ? attempt($callback, $log) : null;
|
return !value($condition) ? rescue($callback) : null;
|
||||||
}
|
|
||||||
|
|
||||||
function attempt_unless($condition, callable $callback, bool $log = true): mixed
|
|
||||||
{
|
|
||||||
return !value($condition) ? attempt($callback, $log) : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function gravatar(string $email, int $size = 192): string
|
function gravatar(string $email, int $size = 192): string
|
||||||
|
@ -146,5 +125,5 @@ function get_mtime(string|SplFileInfo $file): int
|
||||||
$file = is_string($file) ? new SplFileInfo($file) : $file;
|
$file = is_string($file) ? new SplFileInfo($file) : $file;
|
||||||
|
|
||||||
// Workaround for #344, where getMTime() fails for certain files with Unicode names on Windows.
|
// Workaround for #344, where getMTime() fails for certain files with Unicode names on Windows.
|
||||||
return attempt(static fn () => $file->getMTime()) ?? time();
|
return rescue(static fn () => $file->getMTime()) ?? time();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class AuthController extends Controller
|
||||||
|
|
||||||
public function logout(Request $request): Response
|
public function logout(Request $request): Response
|
||||||
{
|
{
|
||||||
attempt(fn () => $this->auth->logoutViaBearerToken($request->bearerToken()));
|
rescue(fn () => $this->auth->logoutViaBearerToken($request->bearerToken()));
|
||||||
|
|
||||||
return response()->noContent();
|
return response()->noContent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ class SpotifyClient
|
||||||
) {
|
) {
|
||||||
if (SpotifyService::enabled()) {
|
if (SpotifyService::enabled()) {
|
||||||
$this->wrapped->setOptions(['return_assoc' => true]);
|
$this->wrapped->setOptions(['return_assoc' => true]);
|
||||||
attempt(fn () => $this->setAccessToken());
|
rescue(fn () => $this->setAccessToken());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class WriteSyncLog
|
||||||
? $event->results->map($transformer)
|
? $event->results->map($transformer)
|
||||||
: $event->results->error()->map($transformer);
|
: $event->results->error()->map($transformer);
|
||||||
|
|
||||||
attempt(static function () use ($messages): void {
|
rescue(static function () use ($messages): void {
|
||||||
$file = storage_path('logs/sync-' . now()->format('Ymd-His') . '.log');
|
$file = storage_path('logs/sync-' . now()->format('Ymd-His') . '.log');
|
||||||
File::put($file, implode(PHP_EOL, $messages->toArray()));
|
File::put($file, implode(PHP_EOL, $messages->toArray()));
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,7 @@ class SongZipArchive
|
||||||
|
|
||||||
public function addSong(Song $song): static
|
public function addSong(Song $song): static
|
||||||
{
|
{
|
||||||
attempt(function () use ($song): void {
|
rescue(function () use ($song): void {
|
||||||
$path = Download::getLocalPath($song);
|
$path = Download::getLocalPath($song);
|
||||||
$this->archive->addFile($path, $this->generateZipContentFileNameFromPath($path));
|
$this->archive->addFile($path, $this->generateZipContentFileNameFromPath($path));
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,6 @@ class AlbumObserver
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
attempt(static fn () => unlink($album->cover_path));
|
rescue(static fn () => unlink($album->cover_path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ abstract class Repository implements RepositoryInterface
|
||||||
|
|
||||||
// This instantiation may fail during a console command if e.g. APP_KEY is empty,
|
// This instantiation may fail during a console command if e.g. APP_KEY is empty,
|
||||||
// rendering the whole installation failing.
|
// rendering the whole installation failing.
|
||||||
attempt(fn () => $this->auth = app(Guard::class), false);
|
rescue(fn () => $this->auth = app(Guard::class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function guessModelClass(): string
|
private static function guessModelClass(): string
|
||||||
|
|
|
@ -10,9 +10,9 @@ class ImageData implements ValidationRule
|
||||||
{
|
{
|
||||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
$passes = attempt(static function () use ($value) {
|
$passes = rescue(static function () use ($value) {
|
||||||
return (bool) preg_match('/data:image\/(jpe?g|png|webp|gif)/i', Str::before($value, ';'));
|
return (bool) preg_match('/data:image\/(jpe?g|png|webp|gif)/i', Str::before($value, ';'));
|
||||||
}, false) ?? false;
|
}) ?? false;
|
||||||
|
|
||||||
if (!$passes) {
|
if (!$passes) {
|
||||||
$fail('Invalid DataURL string');
|
$fail('Invalid DataURL string');
|
||||||
|
|
|
@ -14,14 +14,14 @@ class SupportedAudioFile implements ValidationRule
|
||||||
|
|
||||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
$passes = attempt(static function () use ($value) {
|
$passes = rescue(static function () use ($value) {
|
||||||
Assert::oneOf(
|
Assert::oneOf(
|
||||||
Arr::get((new getID3())->analyze($value->getRealPath()), 'fileformat'),
|
Arr::get((new getID3())->analyze($value->getRealPath()), 'fileformat'),
|
||||||
self::SUPPORTED_FORMATS
|
self::SUPPORTED_FORMATS
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, false) ?? false;
|
}) ?? false;
|
||||||
|
|
||||||
if (!$passes) {
|
if (!$passes) {
|
||||||
$fail('Unsupported audio file');
|
$fail('Unsupported audio file');
|
||||||
|
|
|
@ -11,7 +11,7 @@ class ValidSmartPlaylistRulePayload implements ValidationRule
|
||||||
{
|
{
|
||||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||||
{
|
{
|
||||||
$passes = (bool) attempt(static fn () => SmartPlaylistRuleGroupCollection::create(Arr::wrap($value)));
|
$passes = (bool) rescue(static fn () => SmartPlaylistRuleGroupCollection::create(Arr::wrap($value)));
|
||||||
|
|
||||||
if (!$passes) {
|
if (!$passes) {
|
||||||
$fail('Invalid smart playlist rules');
|
$fail('Invalid smart playlist rules');
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ApplicationInformationService
|
||||||
*/
|
*/
|
||||||
public function getLatestVersionNumber(): string
|
public function getLatestVersionNumber(): string
|
||||||
{
|
{
|
||||||
return attempt(function () {
|
return rescue(function () {
|
||||||
return Cache::remember('latestKoelVersion', now()->addDay(), function (): string {
|
return Cache::remember('latestKoelVersion', now()->addDay(), function (): string {
|
||||||
return json_decode($this->client->get('https://api.github.com/repos/koel/koel/tags')->getBody())[0]
|
return json_decode($this->client->get('https://api.github.com/repos/koel/koel/tags')->getBody())[0]
|
||||||
->name;
|
->name;
|
||||||
|
|
|
@ -111,7 +111,7 @@ class FileScanner
|
||||||
*/
|
*/
|
||||||
private function tryGenerateAlbumCover(Album $album, ?array $coverData): void
|
private function tryGenerateAlbumCover(Album $album, ?array $coverData): void
|
||||||
{
|
{
|
||||||
attempt(function () use ($album, $coverData): void {
|
rescue(function () use ($album, $coverData): void {
|
||||||
// If the album has no cover, we try to get the cover image from existing tag data
|
// If the album has no cover, we try to get the cover image from existing tag data
|
||||||
if ($coverData) {
|
if ($coverData) {
|
||||||
$this->mediaMetadataService->writeAlbumCover($album, $coverData['data']);
|
$this->mediaMetadataService->writeAlbumCover($album, $coverData['data']);
|
||||||
|
@ -125,7 +125,7 @@ class FileScanner
|
||||||
if ($cover) {
|
if ($cover) {
|
||||||
$this->mediaMetadataService->writeAlbumCover($album, $cover);
|
$this->mediaMetadataService->writeAlbumCover($album, $cover);
|
||||||
}
|
}
|
||||||
}, false);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +157,7 @@ class FileScanner
|
||||||
|
|
||||||
private static function isImage(string $path): bool
|
private static function isImage(string $path): bool
|
||||||
{
|
{
|
||||||
return attempt(static fn () => (bool) exif_imagetype($path)) ?? false;
|
return rescue(static fn () => (bool) exif_imagetype($path)) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,7 +20,7 @@ class ITunesService
|
||||||
|
|
||||||
public function getTrackUrl(string $trackName, Album $album): ?string
|
public function getTrackUrl(string $trackName, Album $album): ?string
|
||||||
{
|
{
|
||||||
return attempt(function () use ($trackName, $album): ?string {
|
return rescue(function () use ($trackName, $album): ?string {
|
||||||
$request = new GetTrackRequest($trackName, $album);
|
$request = new GetTrackRequest($trackName, $album);
|
||||||
$hash = md5(serialize($request->query()));
|
$hash = md5(serialize($request->query()));
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ class LastfmService implements MusicEncyclopedia
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return attempt_if(static::enabled(), function () use ($artist): ?ArtistInformation {
|
return rescue_if(static::enabled(), function () use ($artist): ?ArtistInformation {
|
||||||
return Cache::remember(
|
return Cache::remember(
|
||||||
"lastfm.artist.$artist->id",
|
"lastfm.artist.$artist->id",
|
||||||
now()->addWeek(),
|
now()->addWeek(),
|
||||||
|
@ -63,7 +63,7 @@ class LastfmService implements MusicEncyclopedia
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return attempt_if(static::enabled(), function () use ($album): ?AlbumInformation {
|
return rescue_if(static::enabled(), function () use ($album): ?AlbumInformation {
|
||||||
return Cache::remember(
|
return Cache::remember(
|
||||||
"lastfm.album.$album->id",
|
"lastfm.album.$album->id",
|
||||||
now()->addWeek(),
|
now()->addWeek(),
|
||||||
|
@ -74,12 +74,12 @@ class LastfmService implements MusicEncyclopedia
|
||||||
|
|
||||||
public function scrobble(Song $song, User $user, int $timestamp): void
|
public function scrobble(Song $song, User $user, int $timestamp): void
|
||||||
{
|
{
|
||||||
attempt(fn () => $this->connector->send(new ScrobbleRequest($song, $user, $timestamp)));
|
rescue(fn () => $this->connector->send(new ScrobbleRequest($song, $user, $timestamp)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toggleLoveTrack(Song $song, User $user, bool $love): void
|
public function toggleLoveTrack(Song $song, User $user, bool $love): void
|
||||||
{
|
{
|
||||||
attempt(fn () => $this->connector->send(new ToggleLoveTrackRequest($song, $user, $love)));
|
rescue(fn () => $this->connector->send(new ToggleLoveTrackRequest($song, $user, $love)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,7 +101,7 @@ class LastfmService implements MusicEncyclopedia
|
||||||
|
|
||||||
public function updateNowPlaying(Song $song, User $user): void
|
public function updateNowPlaying(Song $song, User $user): void
|
||||||
{
|
{
|
||||||
attempt(fn () => $this->connector->send(new UpdateNowPlayingRequest($song, $user)));
|
rescue(fn () => $this->connector->send(new UpdateNowPlayingRequest($song, $user)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSessionKey(string $token): ?string
|
public function getSessionKey(string $token): ?string
|
||||||
|
|
|
@ -26,7 +26,7 @@ class MediaInformationService
|
||||||
return Cache::remember("album.info.$album->id", now()->addWeek(), function () use ($album): AlbumInformation {
|
return Cache::remember("album.info.$album->id", now()->addWeek(), function () use ($album): AlbumInformation {
|
||||||
$info = $this->encyclopedia->getAlbumInformation($album) ?: AlbumInformation::make();
|
$info = $this->encyclopedia->getAlbumInformation($album) ?: AlbumInformation::make();
|
||||||
|
|
||||||
attempt_unless($album->has_cover, function () use ($info, $album): void {
|
rescue_unless($album->has_cover, function () use ($info, $album): void {
|
||||||
$this->mediaMetadataService->tryDownloadAlbumCover($album);
|
$this->mediaMetadataService->tryDownloadAlbumCover($album);
|
||||||
$info->cover = $album->cover;
|
$info->cover = $album->cover;
|
||||||
});
|
});
|
||||||
|
@ -47,7 +47,7 @@ class MediaInformationService
|
||||||
function () use ($artist): ArtistInformation {
|
function () use ($artist): ArtistInformation {
|
||||||
$info = $this->encyclopedia->getArtistInformation($artist) ?: ArtistInformation::make();
|
$info = $this->encyclopedia->getArtistInformation($artist) ?: ArtistInformation::make();
|
||||||
|
|
||||||
attempt_unless($artist->has_image, function () use ($artist, $info): void {
|
rescue_unless($artist->has_image, function () use ($artist, $info): void {
|
||||||
$this->mediaMetadataService->tryDownloadArtistImage($artist);
|
$this->mediaMetadataService->tryDownloadArtistImage($artist);
|
||||||
$info->image = $artist->image;
|
$info->image = $artist->image;
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,7 +31,7 @@ class MediaMetadataService
|
||||||
*/
|
*/
|
||||||
public function writeAlbumCover(Album $album, string $source, ?string $destination = '', bool $cleanUp = true): void
|
public function writeAlbumCover(Album $album, string $source, ?string $destination = '', bool $cleanUp = true): void
|
||||||
{
|
{
|
||||||
attempt(function () use ($album, $source, $destination, $cleanUp): void {
|
rescue(function () use ($album, $source, $destination, $cleanUp): void {
|
||||||
$destination = $destination ?: $this->generateAlbumCoverPath();
|
$destination = $destination ?: $this->generateAlbumCoverPath();
|
||||||
$this->imageWriter->write($destination, $source);
|
$this->imageWriter->write($destination, $source);
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class MediaMetadataService
|
||||||
?string $destination = '',
|
?string $destination = '',
|
||||||
bool $cleanUp = true
|
bool $cleanUp = true
|
||||||
): void {
|
): void {
|
||||||
attempt(function () use ($artist, $source, $destination, $cleanUp): void {
|
rescue(function () use ($artist, $source, $destination, $cleanUp): void {
|
||||||
$destination = $destination ?: $this->generateArtistImagePath();
|
$destination = $destination ?: $this->generateArtistImagePath();
|
||||||
$this->imageWriter->write($destination, $source);
|
$this->imageWriter->write($destination, $source);
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ class MediaMetadataService
|
||||||
|
|
||||||
public function writePlaylistCover(Playlist $playlist, string $source): void
|
public function writePlaylistCover(Playlist $playlist, string $source): void
|
||||||
{
|
{
|
||||||
attempt(function () use ($playlist, $source): void {
|
rescue(function () use ($playlist, $source): void {
|
||||||
$destination = $this->generatePlaylistCoverPath();
|
$destination = $this->generatePlaylistCoverPath();
|
||||||
$this->imageWriter->write($destination, $source);
|
$this->imageWriter->write($destination, $source);
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ class ConvertUserPreferencesFromArrayToJson extends Migration
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
User::all()->each(static function (User $user): void {
|
User::all()->each(static function (User $user): void {
|
||||||
attempt(static function () use ($user): void {
|
rescue(static function () use ($user): void {
|
||||||
$preferences = unserialize($user->getRawOriginal('preferences'));
|
$preferences = unserialize($user->getRawOriginal('preferences'));
|
||||||
$user->preferences->lastFmSessionKey = Arr::get($preferences, 'lastfm_session_key');
|
$user->preferences->lastFmSessionKey = Arr::get($preferences, 'lastfm_session_key');
|
||||||
$user->save();
|
$user->save();
|
||||||
}, false);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ return new class extends Migration {
|
||||||
Schema::table('songs', static function (Blueprint $table): void {
|
Schema::table('songs', static function (Blueprint $table): void {
|
||||||
// This migration is actually to fix a mistake that the original one was deleted.
|
// This migration is actually to fix a mistake that the original one was deleted.
|
||||||
// Therefore, we just "try" it and ignore on error.
|
// Therefore, we just "try" it and ignore on error.
|
||||||
attempt_if(Schema::hasColumn('songs', 'contributing_artist_id'), static function () use ($table): void {
|
rescue_if(Schema::hasColumn('songs', 'contributing_artist_id'), static function () use ($table): void {
|
||||||
Schema::disableForeignKeyConstraints();
|
Schema::disableForeignKeyConstraints();
|
||||||
|
|
||||||
if (DB::getDriverName() !== 'sqlite') { // @phpstan-ignore-line
|
if (DB::getDriverName() !== 'sqlite') { // @phpstan-ignore-line
|
||||||
|
@ -20,7 +20,7 @@ return new class extends Migration {
|
||||||
|
|
||||||
$table->dropColumn('contributing_artist_id');
|
$table->dropColumn('contributing_artist_id');
|
||||||
Schema::enableForeignKeyConstraints();
|
Schema::enableForeignKeyConstraints();
|
||||||
}, false);
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
namespace Tests\Unit\Rules;
|
namespace Tests\Unit\Rules;
|
||||||
|
|
||||||
use App\Rules\ValidSmartPlaylistRulePayload;
|
use App\Rules\ValidSmartPlaylistRulePayload;
|
||||||
|
use Exception;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
class ValidSmartPlaylistRulePayloadTest extends TestCase
|
class ValidSmartPlaylistRulePayloadTest extends TestCase
|
||||||
{
|
{
|
||||||
|
@ -94,9 +94,10 @@ class ValidSmartPlaylistRulePayloadTest extends TestCase
|
||||||
/** @dataProvider provideInvalidPayloads */
|
/** @dataProvider provideInvalidPayloads */
|
||||||
public function testInvalidCases($value): void
|
public function testInvalidCases($value): void
|
||||||
{
|
{
|
||||||
$this->expectException(Throwable::class);
|
$this->expectExceptionMessage('Invalid smart playlist rules');
|
||||||
(new ValidSmartPlaylistRulePayload())->validate('rules', $value, static fn ($foo) => $foo);
|
|
||||||
self::addToAssertionCount(1);
|
$fail = static fn (string $message) => throw new Exception($message);
|
||||||
|
(new ValidSmartPlaylistRulePayload())->validate('rules', $value, $fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return array<mixed> */
|
/** @return array<mixed> */
|
||||||
|
|
Loading…
Reference in a new issue