2018-08-29 06:15:11 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Repositories;
|
|
|
|
|
2022-06-10 10:47:46 +00:00
|
|
|
use App\Models\Album;
|
|
|
|
use App\Models\Artist;
|
|
|
|
use App\Models\Playlist;
|
2018-08-29 06:15:11 +00:00
|
|
|
use App\Models\Song;
|
2022-06-10 10:47:46 +00:00
|
|
|
use App\Models\User;
|
2020-12-23 11:03:22 +00:00
|
|
|
use App\Repositories\Traits\Searchable;
|
2021-12-06 16:12:47 +00:00
|
|
|
use App\Services\Helper;
|
2022-06-10 10:47:46 +00:00
|
|
|
use Illuminate\Contracts\Database\Query\Builder;
|
|
|
|
use Illuminate\Contracts\Pagination\Paginator;
|
|
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
|
|
use Webmozart\Assert\Assert;
|
2018-08-29 06:15:11 +00:00
|
|
|
|
|
|
|
class SongRepository extends AbstractRepository
|
|
|
|
{
|
2020-12-23 11:03:22 +00:00
|
|
|
use Searchable;
|
|
|
|
|
2022-06-10 10:47:46 +00:00
|
|
|
private const SORT_COLUMNS_NORMALIZE_MAP = [
|
|
|
|
'title' => 'songs.title',
|
|
|
|
'track' => 'songs.track',
|
|
|
|
'length' => 'songs.length',
|
|
|
|
'disc' => 'songs.disc',
|
|
|
|
'artistName' => 'artists.name',
|
|
|
|
'albumName' => 'albums.name',
|
|
|
|
];
|
2018-08-29 06:15:11 +00:00
|
|
|
|
2022-06-10 10:47:46 +00:00
|
|
|
private const VALID_SORT_COLUMNS = ['songs.title', 'songs.track', 'songs.length', 'artists.name', 'albums.name'];
|
|
|
|
private const DEFAULT_QUEUE_LIMIT = 500;
|
|
|
|
|
|
|
|
public function __construct(private Helper $helper)
|
2018-08-29 06:15:11 +00:00
|
|
|
{
|
2019-01-01 11:53:20 +00:00
|
|
|
parent::__construct();
|
2018-08-29 06:15:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getOneByPath(string $path): ?Song
|
|
|
|
{
|
2021-12-06 16:12:47 +00:00
|
|
|
return $this->getOneById($this->helper->getFileHash($path));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<Song> */
|
|
|
|
public function getAllHostedOnS3(): Collection
|
|
|
|
{
|
|
|
|
return Song::hostedOnS3()->get();
|
2018-08-29 06:15:11 +00:00
|
|
|
}
|
2022-06-10 10:47:46 +00:00
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getRecentlyAdded(int $count = 10, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())->latest()->limit($count)->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getMostPlayed(int $count = 7, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
$scopedUser ??= $this->auth->user();
|
|
|
|
|
|
|
|
return Song::withMeta($scopedUser)
|
|
|
|
->where('interactions.play_count', '>', 0)
|
|
|
|
->orderByDesc('interactions.play_count')
|
|
|
|
->limit($count)
|
|
|
|
->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getRecentlyPlayed(int $count = 7, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
$scopedUser ??= $this->auth->user();
|
|
|
|
|
|
|
|
return Song::withMeta($scopedUser)
|
|
|
|
->where('interactions.play_count', '>', 0)
|
|
|
|
->orderByDesc('interactions.updated_at')
|
|
|
|
->limit($count)
|
|
|
|
->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getForListing(
|
|
|
|
string $sortColumn,
|
|
|
|
string $sortDirection,
|
|
|
|
?User $scopedUser = null,
|
|
|
|
int $perPage = 50
|
|
|
|
): Paginator {
|
|
|
|
return self::applySort(
|
|
|
|
Song::withMeta($scopedUser ?? $this->auth->user()),
|
|
|
|
$sortColumn,
|
|
|
|
$sortDirection
|
|
|
|
)
|
|
|
|
->simplePaginate($perPage);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getForQueue(
|
|
|
|
string $sortColumn,
|
|
|
|
string $sortDirection,
|
|
|
|
?User $scopedUser = null,
|
|
|
|
int $limit = self::DEFAULT_QUEUE_LIMIT
|
|
|
|
): Collection {
|
|
|
|
return self::applySort(
|
|
|
|
Song::withMeta($scopedUser ?? $this->auth->user()),
|
|
|
|
$sortColumn,
|
|
|
|
$sortDirection
|
|
|
|
)
|
|
|
|
->limit($limit)
|
|
|
|
->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getFavorites(?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())->where('interactions.liked', true)->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getByAlbum(Album $album, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())
|
|
|
|
->where('album_id', $album->id)
|
|
|
|
->orderBy('songs.track')
|
|
|
|
->orderBy('songs.disc')
|
|
|
|
->orderBy('songs.title')
|
|
|
|
->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getByArtist(Artist $artist, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())
|
|
|
|
->where('songs.artist_id', $artist->id)
|
|
|
|
->orderBy('albums.name')
|
|
|
|
->orderBy('songs.track')
|
|
|
|
->orderBy('songs.disc')
|
|
|
|
->orderBy('songs.title')
|
|
|
|
->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getByStandardPlaylist(Playlist $playlist, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())
|
|
|
|
->leftJoin('playlist_song', 'songs.id', '=', 'playlist_song.song_id')
|
|
|
|
->leftJoin('playlists', 'playlists.id', '=', 'playlist_song.playlist_id')
|
|
|
|
->where('playlists.id', $playlist->id)
|
|
|
|
->orderBy('songs.title')
|
|
|
|
->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getRandom(int $limit, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())->inRandomOrder()->limit($limit)->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function getByIds(array $ids, ?User $scopedUser = null): Collection
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())->whereIn('songs.id', $ids)->get();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getOne($id, ?User $scopedUser = null): Song
|
|
|
|
{
|
|
|
|
return Song::withMeta($scopedUser ?? $this->auth->user())->findOrFail($id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function count(): int
|
|
|
|
{
|
|
|
|
return Song::count();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getTotalLength(): float
|
|
|
|
{
|
|
|
|
return Song::sum('length');
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function normalizeSortColumn(string $column): string
|
|
|
|
{
|
|
|
|
return key_exists($column, self::SORT_COLUMNS_NORMALIZE_MAP)
|
|
|
|
? self::SORT_COLUMNS_NORMALIZE_MAP[$column]
|
|
|
|
: $column;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function applySort(Builder $query, string $column, string $direction): Builder
|
|
|
|
{
|
|
|
|
$column = self::normalizeSortColumn($column);
|
|
|
|
|
|
|
|
Assert::oneOf($column, self::VALID_SORT_COLUMNS);
|
|
|
|
Assert::oneOf(strtolower($direction), ['asc', 'desc']);
|
|
|
|
|
|
|
|
$query->orderBy($column, $direction);
|
|
|
|
|
|
|
|
if ($column === 'artists.name') {
|
|
|
|
$query->orderBy('albums.name')
|
|
|
|
->orderBy('songs.track')
|
|
|
|
->orderBy('songs.disc')
|
|
|
|
->orderBy('songs.title');
|
|
|
|
} elseif ($column === 'albums.name') {
|
|
|
|
$query->orderBy('artists.name')
|
|
|
|
->orderBy('songs.track')
|
|
|
|
->orderBy('songs.disc')
|
|
|
|
->orderBy('songs.title');
|
|
|
|
} elseif ($column === 'track') {
|
|
|
|
$query->orderBy('songs.track')
|
|
|
|
->orderBy('song.disc');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $query;
|
|
|
|
}
|
2018-08-29 06:15:11 +00:00
|
|
|
}
|