'float', 'mtime' => 'int', 'track' => 'int', 'disc' => 'int', ]; protected $keyType = 'string'; public function artist(): BelongsTo { return $this->belongsTo(Artist::class); } public function album(): BelongsTo { return $this->belongsTo(Album::class); } public function playlists(): BelongsToMany { return $this->belongsToMany(Playlist::class); } public function interactions(): HasMany { return $this->hasMany(Interaction::class); } /** * Scope a query to only include songs in a given directory. */ public function scopeInDirectory(Builder $query, string $path): Builder { // Make sure the path ends with a directory separator. $path = rtrim(trim($path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; return $query->where('path', 'LIKE', "$path%"); } 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) ); } protected function lyrics(): Attribute { // Since we're displaying the lyrics using
, replace breaks with newlines and strip all tags.
        $normalizer = static fn (?string $value): string => strip_tags(preg_replace('##i', PHP_EOL, $value));

        return new Attribute(get: $normalizer, set: $normalizer);
    }

    public static function withMeta(User $scopedUser, ?Builder $query = null): Builder
    {
        $query ??= static::query();

        return $query
            ->with('artist', 'album', 'album.artist')
            ->leftJoin('interactions', static function (JoinClause $join) use ($scopedUser): void {
                $join->on('interactions.song_id', '=', 'songs.id')
                    ->where('interactions.user_id', $scopedUser->id);
            })
            ->join('albums', 'songs.album_id', '=', 'albums.id')
            ->join('artists', 'songs.artist_id', '=', 'artists.id')
            ->select(
                'songs.*',
                'albums.name',
                'artists.name',
                'interactions.liked',
                'interactions.play_count'
            );
    }

    public function scopeWithMeta(Builder $query, User $scopedUser): Builder
    {
        return static::withMeta($scopedUser, $query);
    }

    /** @return array */
    public function toSearchableArray(): array
    {
        $array = [
            'id' => $this->id,
            'title' => $this->title,
        ];

        if (!$this->artist->is_unknown && !$this->artist->is_various) {
            $array['artist'] = $this->artist->name;
        }

        return $array;
    }

    public function __toString(): string
    {
        return $this->id;
    }
}