mirror of
https://github.com/koel/koel
synced 2024-11-24 05:03:05 +00:00
feat: replace some attributes with casts
This commit is contained in:
parent
90a47d59b5
commit
4a10aa9915
8 changed files with 92 additions and 43 deletions
28
app/Casts/SongLyricsCast.php
Normal file
28
app/Casts/SongLyricsCast.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SongLyricsCast implements CastsAttributes
|
||||
{
|
||||
/** @param string|null $value */
|
||||
public function get(Model $model, string $key, mixed $value, array $attributes): string
|
||||
{
|
||||
if (!$value) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Since we're displaying the lyrics using <pre>, replace breaks with newlines and strip all tags.
|
||||
$value = strip_tags(preg_replace('#<br\s*/?>#i', PHP_EOL, $value));
|
||||
|
||||
// also remove the timestamps that often come with LRC files
|
||||
return preg_replace('/\[\d{2}:\d{2}.\d{2}]\s*/m', '', $value);
|
||||
}
|
||||
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): mixed
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
}
|
24
app/Casts/SongStorageCast.php
Normal file
24
app/Casts/SongStorageCast.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use App\Enums\SongStorageType;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SongStorageCast implements CastsAttributes
|
||||
{
|
||||
/** @param string|null $value */
|
||||
public function get(Model $model, string $key, mixed $value, array $attributes): SongStorageType
|
||||
{
|
||||
return SongStorageType::tryFrom($value) ?? SongStorageType::LOCAL;
|
||||
}
|
||||
|
||||
/** @param SongStorageType|string|null $value */
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): string
|
||||
{
|
||||
$type = $value instanceof SongStorageType ? $value : SongStorageType::tryFrom($value);
|
||||
|
||||
return $type->value;
|
||||
}
|
||||
}
|
28
app/Casts/SongTitleCast.php
Normal file
28
app/Casts/SongTitleCast.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
use App\Models\Song;
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SongTitleCast implements CastsAttributes
|
||||
{
|
||||
/**
|
||||
* @param Song $model
|
||||
* @param string|null $value
|
||||
*/
|
||||
public function get(Model $model, string $key, mixed $value, array $attributes): string
|
||||
{
|
||||
// If the title is empty, we "guess" the title by extracting the filename from the song's path.
|
||||
return $value ?: pathinfo($model->path, PATHINFO_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
*/
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): string
|
||||
{
|
||||
return html_entity_decode($value);
|
||||
}
|
||||
}
|
|
@ -193,12 +193,12 @@ class Playlist extends Model
|
|||
|
||||
protected function isCollaborative(): Attribute
|
||||
{
|
||||
return Attribute::get(fn (): bool => !$this->is_smart &&
|
||||
LicenseFacade::isPlus()
|
||||
return Attribute::get(fn (): bool => !$this->is_smart
|
||||
&& LicenseFacade::isPlus()
|
||||
&& $this->collaborators->isNotEmpty());
|
||||
}
|
||||
|
||||
/** @return array<mixed> */
|
||||
/** @inheritdoc */
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
@ -4,6 +4,9 @@ namespace App\Models;
|
|||
|
||||
use App\Builders\SongBuilder;
|
||||
use App\Casts\Podcast\EpisodeMetadataCast;
|
||||
use App\Casts\SongLyricsCast;
|
||||
use App\Casts\SongStorageCast;
|
||||
use App\Casts\SongTitleCast;
|
||||
use App\Enums\PlayableType;
|
||||
use App\Enums\SongStorageType;
|
||||
use App\Models\Concerns\SupportsDeleteWhereValueNotIn;
|
||||
|
@ -79,11 +82,14 @@ class Song extends Model
|
|||
protected $hidden = ['updated_at', 'path', 'mtime'];
|
||||
|
||||
protected $casts = [
|
||||
'title' => SongTitleCast::class,
|
||||
'lyrics' => SongLyricsCast::class,
|
||||
'length' => 'float',
|
||||
'mtime' => 'int',
|
||||
'track' => 'int',
|
||||
'disc' => 'int',
|
||||
'is_public' => 'bool',
|
||||
'storage' => SongStorageCast::class,
|
||||
'episode_metadata' => EpisodeMetadataCast::class,
|
||||
];
|
||||
|
||||
|
@ -148,14 +154,6 @@ class Song extends Model
|
|||
return Attribute::get(fn () => $this->podcast_id ? PlayableType::PODCAST_EPISODE : PlayableType::SONG);
|
||||
}
|
||||
|
||||
protected function title(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
get: fn (?string $value) => $value ?: pathinfo($this->path, PATHINFO_FILENAME),
|
||||
set: static fn (string $value) => html_entity_decode($value)
|
||||
);
|
||||
}
|
||||
|
||||
public function accessibleBy(User $user): bool
|
||||
{
|
||||
if ($this->isEpisode()) {
|
||||
|
@ -170,31 +168,6 @@ class Song extends Model
|
|||
return $this->owner_id === $user->id;
|
||||
}
|
||||
|
||||
protected function lyrics(): Attribute
|
||||
{
|
||||
$normalizer = static function (?string $value): string {
|
||||
// Since we're displaying the lyrics using <pre>, replace breaks with newlines and strip all tags.
|
||||
$value = strip_tags(preg_replace('#<br\s*/?>#i', PHP_EOL, $value));
|
||||
|
||||
// also remove the timestamps that often come with LRC files
|
||||
return preg_replace('/\[\d{2}:\d{2}.\d{2}]\s*/m', '', $value);
|
||||
};
|
||||
|
||||
return new Attribute(get: $normalizer, set: $normalizer);
|
||||
}
|
||||
|
||||
protected function storage(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
get: static fn (?string $raw) => SongStorageType::tryFrom($raw) ?? SongStorageType::LOCAL,
|
||||
set: static function (SongStorageType|string|null $type) {
|
||||
$type = $type instanceof SongStorageType ? $type : SongStorageType::tryFrom($type);
|
||||
|
||||
return $type->value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function storageMetadata(): Attribute
|
||||
{
|
||||
return new Attribute(
|
||||
|
|
|
@ -40,7 +40,7 @@ use Laravel\Sanctum\PersonalAccessToken;
|
|||
* @property ?string $sso_provider
|
||||
* @property ?string $sso_id
|
||||
* @property bool $is_sso
|
||||
* @property Collection<array-key, Podcast> $podcast
|
||||
* @property Collection<array-key, Podcast> $podcasts
|
||||
*/
|
||||
class User extends Authenticatable
|
||||
{
|
||||
|
|
|
@ -9,11 +9,6 @@ use Illuminate\Support\Collection;
|
|||
/** @extends Repository<Podcast> */
|
||||
class PodcastRepository extends Repository
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Podcast::class);
|
||||
}
|
||||
|
||||
public function findOneByUrl(string $url): ?Podcast
|
||||
{
|
||||
return $this->findOneBy(['url' => $url]);
|
||||
|
|
|
@ -7,11 +7,12 @@ use Tests\TestCase;
|
|||
|
||||
class SongTest extends TestCase
|
||||
{
|
||||
public function testLyricsDoNotContainTimestamps(): void
|
||||
public function testRetrievedLyricsDoNotContainTimestamps(): void
|
||||
{
|
||||
/** @var Song $song */
|
||||
$song = Song::factory()->create(['lyrics' => "[00:00.00]Line 1\n[00:01.00]Line 2\n[00:02.00]Line 3"]);
|
||||
|
||||
self::assertSame("Line 1\nLine 2\nLine 3", $song->lyrics);
|
||||
self::assertSame("[00:00.00]Line 1\n[00:01.00]Line 2\n[00:02.00]Line 3", $song->getAttributes()['lyrics']);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue