mirror of
https://github.com/koel/koel
synced 2025-02-17 13:58:28 +00:00
feat(build): uprade to Laravel 10 (and PHP 8.1)
This commit is contained in:
parent
902c439fed
commit
3b7d47cb25
48 changed files with 1912 additions and 1717 deletions
|
@ -10,12 +10,12 @@ use Illuminate\Database\Query\JoinClause;
|
|||
|
||||
class AlbumBuilder extends Builder
|
||||
{
|
||||
public function isStandard(): static
|
||||
public function isStandard(): self
|
||||
{
|
||||
return $this->whereNot('albums.id', Album::UNKNOWN_ID);
|
||||
}
|
||||
|
||||
public function accessibleBy(User $user): static
|
||||
public function accessibleBy(User $user): self
|
||||
{
|
||||
if (License::isCommunity()) {
|
||||
// With the Community license, all albums are accessible by all users.
|
||||
|
|
|
@ -10,12 +10,12 @@ use Illuminate\Database\Query\JoinClause;
|
|||
|
||||
class ArtistBuilder extends Builder
|
||||
{
|
||||
public function isStandard(): static
|
||||
public function isStandard(): self
|
||||
{
|
||||
return $this->whereNotIn('artists.id', [Artist::UNKNOWN_ID, Artist::VARIOUS_ID]);
|
||||
}
|
||||
|
||||
public function accessibleBy(User $user): static
|
||||
public function accessibleBy(User $user): self
|
||||
{
|
||||
if (License::isCommunity()) {
|
||||
// With the Community license, all artists are accessible by all users.
|
||||
|
|
|
@ -32,7 +32,7 @@ class SongBuilder extends Builder
|
|||
'albums.name',
|
||||
];
|
||||
|
||||
public function inDirectory(string $path): static
|
||||
public function inDirectory(string $path): self
|
||||
{
|
||||
// Make sure the path ends with a directory separator.
|
||||
$path = rtrim(trim($path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
|
@ -40,7 +40,7 @@ class SongBuilder extends Builder
|
|||
return $this->where('path', 'LIKE', "$path%");
|
||||
}
|
||||
|
||||
public function withMetaFor(User $user, bool $requiresInteractions = false): static
|
||||
public function withMetaFor(User $user, bool $requiresInteractions = false): self
|
||||
{
|
||||
$joinClosure = static function (JoinClause $join) use ($user): void {
|
||||
$join->on('interactions.song_id', '=', 'songs.id')->where('interactions.user_id', $user->id);
|
||||
|
@ -65,7 +65,7 @@ class SongBuilder extends Builder
|
|||
);
|
||||
}
|
||||
|
||||
public function accessibleBy(User $user, bool $withTableName = true): static
|
||||
public function accessibleBy(User $user, bool $withTableName = true): self
|
||||
{
|
||||
if (License::isCommunity()) {
|
||||
// In the Community Edition, all songs are accessible by all users.
|
||||
|
@ -78,7 +78,7 @@ class SongBuilder extends Builder
|
|||
});
|
||||
}
|
||||
|
||||
public function sort(string $column, string $direction): static
|
||||
public function sort(string $column, string $direction): self
|
||||
{
|
||||
$column = self::normalizeSortColumn($column);
|
||||
|
||||
|
@ -105,7 +105,7 @@ class SongBuilder extends Builder
|
|||
: $column;
|
||||
}
|
||||
|
||||
public function storedOnCloud(): static
|
||||
public function storedOnCloud(): self
|
||||
{
|
||||
return $this->whereNotNull('storage')
|
||||
->where('storage', '!=', '');
|
||||
|
|
|
@ -14,7 +14,7 @@ class AuthController extends Controller
|
|||
{
|
||||
use ThrottlesLogins;
|
||||
|
||||
public function __construct(private AuthenticationService $auth)
|
||||
public function __construct(private readonly AuthenticationService $auth)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ class AuthController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
public function logout(Request $request): Response
|
||||
{
|
||||
attempt(fn () => $this->auth->logoutViaBearerToken($request->bearerToken()));
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
|
|||
|
||||
class GetOneTimeTokenController extends Controller
|
||||
{
|
||||
/** @var User $user */
|
||||
/** @param User $user */
|
||||
public function __invoke(AuthenticationService $auth, Authenticatable $user)
|
||||
{
|
||||
return response()->json(['token' => $auth->generateOneTimeToken($user)]);
|
||||
|
|
|
@ -18,7 +18,7 @@ class LikeMultipleSongsController extends Controller
|
|||
InteractionService $interactionService,
|
||||
Authenticatable $user
|
||||
) {
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::query()->findMany($request->songs);
|
||||
$songs->each(fn (Song $song) => $this->authorize('access', $song));
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ use Laravel\Scout\Searchable;
|
|||
* @property string $name Name of the album
|
||||
* @property Artist $artist The album's artist
|
||||
* @property int $artist_id
|
||||
* @property Collection $songs
|
||||
* @property Collection<array-key, Song> $songs
|
||||
* @property bool $is_unknown If the album is the Unknown Album
|
||||
* @property string|null $thumbnail_name The file name of the album's thumbnail
|
||||
* @property string|null $thumbnail_path The full path to the thumbnail.
|
||||
|
|
|
@ -21,7 +21,7 @@ use Laravel\Scout\Searchable;
|
|||
* @property string|null $image Public URL to the artist's image
|
||||
* @property bool $is_unknown If the artist is Unknown Artist
|
||||
* @property bool $is_various If the artist is Various Artist
|
||||
* @property Collection $songs
|
||||
* @property Collection<array-key, Song> $songs
|
||||
* @property bool $has_image If the artist has a (non-default) image
|
||||
* @property string|null $image_path Absolute path to the artist's image
|
||||
* @property float|string $length Total length of the artist's songs in seconds (dynamically calculated)
|
||||
|
@ -29,7 +29,7 @@ use Laravel\Scout\Searchable;
|
|||
* @property string|int $song_count Total number of songs by the artist (dynamically calculated)
|
||||
* @property string|int $album_count Total number of albums by the artist (dynamically calculated)
|
||||
* @property Carbon $created_at
|
||||
* @property Collection|array<array-key, Album> $albums
|
||||
* @property Collection<array-key, Album> $albums
|
||||
*/
|
||||
class Artist extends Model
|
||||
{
|
||||
|
|
|
@ -24,17 +24,17 @@ use LogicException;
|
|||
* @property bool $is_smart
|
||||
* @property int $user_id
|
||||
* @property User $user
|
||||
* @property Collection|array<array-key, Song> $songs
|
||||
* @property Collection<array-key, Song> $songs
|
||||
* @property array<string> $song_ids
|
||||
* @property ?SmartPlaylistRuleGroupCollection $rule_groups
|
||||
* @property ?SmartPlaylistRuleGroupCollection $rules
|
||||
* @property Carbon $created_at
|
||||
* @property bool $own_songs_only
|
||||
* @property Collection|array<array-key, User> $collaborators
|
||||
* @property Collection<array-key, User> $collaborators
|
||||
* @property-read bool $is_collaborative
|
||||
* @property-read ?string $cover The playlist cover's URL
|
||||
* @property-read ?string $cover_path
|
||||
* @property-read Collection|array<array-key, PlaylistFolder> $folders
|
||||
* @property-read Collection<array-key, PlaylistFolder> $folders
|
||||
*/
|
||||
class Playlist extends Model
|
||||
{
|
||||
|
@ -180,7 +180,7 @@ class Playlist extends Model
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Collection|array<array-key, Song>|Song|array<string> $songs
|
||||
* @param Collection<array-key, Song>|Song|array<string> $songs
|
||||
*/
|
||||
public function removeSongs(Collection|Song|array $songs): void
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ use Illuminate\Support\Str;
|
|||
* @property string $id
|
||||
* @property string $name
|
||||
* @property User $user
|
||||
* @property Collection|array<array-key, Playlist> $playlists
|
||||
* @property Collection<array-key, Playlist> $playlists
|
||||
* @property int $user_id
|
||||
* @property Carbon $created_at
|
||||
*/
|
||||
|
|
|
@ -28,15 +28,15 @@ use Laravel\Sanctum\PersonalAccessToken;
|
|||
* @property string $password
|
||||
* @property-read bool $has_custom_avatar
|
||||
* @property-read string $avatar
|
||||
* @property Collection|array<array-key, Playlist> $playlists
|
||||
* @property Collection|array<array-key, PlaylistFolder> $playlist_folders
|
||||
* @property Collection<array-key, Playlist> $playlists
|
||||
* @property Collection<array-key, PlaylistFolder> $playlist_folders
|
||||
* @property PersonalAccessToken $currentAccessToken
|
||||
* @property ?Carbon $invitation_accepted_at
|
||||
* @property ?User $invitedBy
|
||||
* @property ?string $invitation_token
|
||||
* @property ?Carbon $invited_at
|
||||
* @property-read bool $is_prospect
|
||||
* @property Collection|array<array-key, Playlist> $collaboratedPlaylists
|
||||
* @property Collection<array-key, Playlist> $collaboratedPlaylists
|
||||
* @property ?string $sso_provider
|
||||
* @property ?string $sso_id
|
||||
* @property bool $is_sso
|
||||
|
|
|
@ -13,7 +13,6 @@ use Illuminate\Database\Schema\Builder;
|
|||
use Illuminate\Database\SQLiteConnection;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Factory as Validator;
|
||||
use SpotifyWebAPI\Session as SpotifySession;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
|
@ -21,14 +20,14 @@ class AppServiceProvider extends ServiceProvider
|
|||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(Builder $schema, DatabaseManager $db, Validator $validator): void
|
||||
public function boot(Builder $schema, DatabaseManager $db): void
|
||||
{
|
||||
// Fix utf8mb4-related error starting from Laravel 5.4
|
||||
$schema->defaultStringLength(191);
|
||||
|
||||
// Enable on delete cascade for sqlite connections
|
||||
if ($db->connection() instanceof SQLiteConnection) {
|
||||
$db->statement($db->raw('PRAGMA foreign_keys = ON'));
|
||||
$db->statement($db->raw('PRAGMA foreign_keys = ON')->getValue($db->getQueryGrammar()));
|
||||
}
|
||||
|
||||
// disable wrapping JSON resource in a `data` key
|
||||
|
|
|
@ -9,6 +9,7 @@ use App\Models\User;
|
|||
use Illuminate\Contracts\Pagination\Paginator;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Illuminate\Support\Collection as BaseCollection;
|
||||
|
||||
class ArtistRepository extends Repository
|
||||
{
|
||||
|
@ -43,7 +44,7 @@ class ArtistRepository extends Repository
|
|||
}
|
||||
|
||||
/** @return Collection|array<array-key, Artist> */
|
||||
public function getMany(array $ids, bool $inThatOrder = false, ?User $user = null): Collection
|
||||
public function getMany(array $ids, bool $inThatOrder = false, ?User $user = null): Collection | BaseCollection
|
||||
{
|
||||
$artists = Artist::query()
|
||||
->isStandard()
|
||||
|
|
|
@ -11,9 +11,9 @@ interface RepositoryInterface
|
|||
|
||||
public function findOne($id): ?Model;
|
||||
|
||||
/** @return Collection|array<Model> */
|
||||
/** @return Collection<Model> */
|
||||
public function getMany(array $ids, bool $inThatOrder = false): Collection;
|
||||
|
||||
/** @return Collection|array<Model> */
|
||||
/** @return Collection<Model> */
|
||||
public function getAll(): Collection;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ class GenreRepository
|
|||
->groupBy('genre')
|
||||
->orderBy('genre')
|
||||
->get()
|
||||
->transform(static fn (object $record): Genre => Genre::make(
|
||||
name: $record->genre ?: Genre::NO_GENRE,
|
||||
songCount: $record->song_count,
|
||||
length: $record->length
|
||||
->transform(static fn (object $record): Genre => Genre::make( // @phpstan-ignore-line
|
||||
name: $record->genre ?: Genre::NO_GENRE, // @phpstan-ignore-line
|
||||
songCount: $record->song_count, // @phpstan-ignore-line
|
||||
length: $record->length // @phpstan-ignore-line
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use Illuminate\Support\Collection;
|
|||
|
||||
class PlaylistRepository extends Repository
|
||||
{
|
||||
/** @return array<array-key, Playlist>|Collection<Playlist> */
|
||||
/** @return Collection<array-key, Playlist> */
|
||||
public function getAllAccessibleByUser(User $user): Collection
|
||||
{
|
||||
$ownPlaylists = Playlist::query()
|
||||
|
|
|
@ -38,7 +38,7 @@ abstract class Repository implements RepositoryInterface
|
|||
return $this->model->find($id);
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, Model> */
|
||||
/** @return Collection<array-key, Model> */
|
||||
public function getMany(array $ids, bool $inThatOrder = false): Collection
|
||||
{
|
||||
$models = $this->model::query()->find($ids);
|
||||
|
@ -46,7 +46,7 @@ abstract class Repository implements RepositoryInterface
|
|||
return $inThatOrder ? $models->orderByArray($ids) : $models;
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, Model> */
|
||||
/** @return Collection<array-key, Model> */
|
||||
public function getAll(): Collection
|
||||
{
|
||||
return $this->model->all();
|
||||
|
|
|
@ -50,9 +50,9 @@ class InteractionService
|
|||
/**
|
||||
* Like several songs at once as a user.
|
||||
*
|
||||
* @param array<array-key, Song>|Collection $songs
|
||||
* @param Collection<array-key, Song> $songs
|
||||
*
|
||||
* @return array<Interaction>|Collection The array of Interaction objects
|
||||
* @return Collection<array-key, Interaction> The array of Interaction objects
|
||||
*/
|
||||
public function likeMany(Collection $songs, User $user): Collection
|
||||
{
|
||||
|
|
|
@ -83,7 +83,7 @@ class LastfmService implements MusicEncyclopedia
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Collection|array<array-key, Song> $songs
|
||||
* @param Collection<array-key, Song> $songs
|
||||
*/
|
||||
public function batchToggleLoveTracks(Collection $songs, User $user, bool $love): void
|
||||
{
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace App\Services;
|
|||
|
||||
use App\Models\Album;
|
||||
use App\Models\Artist;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class LibraryManager
|
||||
|
@ -12,10 +11,7 @@ class LibraryManager
|
|||
/**
|
||||
* Delete albums and artists that have no songs.
|
||||
*
|
||||
* @return array{
|
||||
* albums: Collection<array-key, Album>,
|
||||
* artists: Collection<array-key, Artist>,
|
||||
* }
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function prune(bool $dryRun = false): array
|
||||
{
|
||||
|
|
|
@ -49,7 +49,7 @@ class PlaylistCollaborationService
|
|||
return $collaborationToken->playlist;
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, PlaylistCollaborator> */
|
||||
/** @return Collection<array-key, PlaylistCollaborator> */
|
||||
public function getCollaborators(Playlist $playlist): Collection
|
||||
{
|
||||
return $playlist->collaborators->unless(
|
||||
|
|
|
@ -86,7 +86,7 @@ class PlaylistService
|
|||
return $playlist;
|
||||
}
|
||||
|
||||
/** @return Collection<Song>|array<array-key, Song> */
|
||||
/** @return Collection<array-key, Song> */
|
||||
public function addSongsToPlaylist(Playlist $playlist, Collection|Song|array $songs, User $user): Collection
|
||||
{
|
||||
return DB::transaction(function () use ($playlist, $songs, $user) {
|
||||
|
|
|
@ -24,7 +24,7 @@ class SongService
|
|||
) {
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, Song> */
|
||||
/** @return Collection<array-key, Song> */
|
||||
public function updateSongs(array $ids, SongUpdateData $data): Collection
|
||||
{
|
||||
if (count($ids) === 1) {
|
||||
|
@ -96,7 +96,7 @@ class SongService
|
|||
{
|
||||
if (License::isPlus()) {
|
||||
/**
|
||||
* @var Collection|array<array-key, Song> $collaborativeSongs
|
||||
* @var Collection<array-key, Song> $collaborativeSongs
|
||||
* Songs that are in collaborative playlists and can't be marked as private as a result
|
||||
*/
|
||||
$collaborativeSongs = Song::query()
|
||||
|
@ -128,7 +128,7 @@ class SongService
|
|||
DB::transaction(function () use ($ids): void {
|
||||
$shouldBackUp = config('koel.backup_on_delete');
|
||||
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::query()->findMany($ids);
|
||||
|
||||
Song::destroy($ids);
|
||||
|
|
|
@ -17,8 +17,8 @@ final class DropboxStorage extends CloudStorage
|
|||
{
|
||||
public function __construct(
|
||||
protected FileScanner $scanner,
|
||||
private DropboxFilesystem $filesystem,
|
||||
private array $config
|
||||
private readonly DropboxFilesystem $filesystem,
|
||||
private readonly array $config
|
||||
) {
|
||||
parent::__construct($scanner);
|
||||
|
||||
|
@ -78,7 +78,7 @@ final class DropboxStorage extends CloudStorage
|
|||
return $this->filesystem->temporaryUrl($song->storage_metadata->getPath());
|
||||
}
|
||||
|
||||
protected function supported(): bool
|
||||
public function supported(): bool
|
||||
{
|
||||
return SongStorageTypes::supported(SongStorageTypes::DROPBOX);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class S3CompatibleStorage extends CloudStorage
|
|||
Storage::disk('s3')->delete('test.txt');
|
||||
}
|
||||
|
||||
protected function supported(): bool
|
||||
public function supported(): bool
|
||||
{
|
||||
return SongStorageTypes::supported(SongStorageTypes::S3);
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ final class S3LambdaStorage extends S3CompatibleStorage
|
|||
$song->delete();
|
||||
}
|
||||
|
||||
protected function supported(): bool
|
||||
public function supported(): bool
|
||||
{
|
||||
return SongStorageTypes::supported(SongStorageTypes::S3_LAMBDA);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class UserInvitationService
|
|||
{
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, User> */
|
||||
/** @return Collection<array-key, User> */
|
||||
public function invite(array $emails, bool $isAdmin, User $invitor): Collection
|
||||
{
|
||||
return DB::transaction(function () use ($emails, $isAdmin, $invitor) {
|
||||
|
|
|
@ -11,25 +11,25 @@ final class ScanResultCollection extends Collection
|
|||
return new self();
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, ScanResult> */
|
||||
/** @return Collection<array-key, ScanResult> */
|
||||
public function valid(): Collection
|
||||
{
|
||||
return $this->filter(static fn (ScanResult $result): bool => $result->isValid());
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, ScanResult> */
|
||||
/** @return Collection<array-key, ScanResult> */
|
||||
public function success(): Collection
|
||||
{
|
||||
return $this->filter(static fn (ScanResult $result): bool => $result->isSuccess());
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, ScanResult> */
|
||||
/** @return Collection<array-key, ScanResult> */
|
||||
public function skipped(): Collection
|
||||
{
|
||||
return $this->filter(static fn (ScanResult $result): bool => $result->isSkipped());
|
||||
}
|
||||
|
||||
/** @return Collection|array<array-key, ScanResult> */
|
||||
/** @return Collection<array-key, ScanResult> */
|
||||
public function error(): Collection
|
||||
{
|
||||
return $this->filter(static fn (ScanResult $result): bool => $result->isError());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "phanan/koel",
|
||||
"name": "koel/koel",
|
||||
"description": "Personal audio streaming service that works.",
|
||||
"keywords": [
|
||||
"audio",
|
||||
|
@ -9,29 +9,28 @@
|
|||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"laravel/framework": "^9.0",
|
||||
"php": ">=8.1",
|
||||
"laravel/framework": "^10.0",
|
||||
"james-heinrich/getid3": "^1.9",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"pusher/pusher-php-server": "^4.0",
|
||||
"pusher/pusher-php-server": "^7.0",
|
||||
"predis/predis": "~1.0",
|
||||
"jackiedo/dotenv-editor": "^2.0",
|
||||
"jackiedo/dotenv-editor": "^2.1",
|
||||
"ext-exif": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-json": "*",
|
||||
"ext-SimpleXML": "*",
|
||||
"daverandom/resume": "^0.0.3",
|
||||
"laravel/helpers": "^1.0",
|
||||
"laravel/helpers": "^1.7",
|
||||
"intervention/image": "^2.5",
|
||||
"doctrine/dbal": "^3.0",
|
||||
"lstrojny/functional-php": "^1.14",
|
||||
"teamtnt/laravel-scout-tntsearch-driver": "^11.1",
|
||||
"teamtnt/laravel-scout-tntsearch-driver": "^14.0",
|
||||
"algolia/algoliasearch-client-php": "^3.3",
|
||||
"laravel/ui": "^3.2",
|
||||
"webmozart/assert": "^1.10",
|
||||
"laravel/sanctum": "^2.15",
|
||||
"laravel/scout": "^9.4",
|
||||
"laravel/sanctum": "^3.2",
|
||||
"laravel/scout": "^10.0",
|
||||
"nunomaduro/collision": "^6.2",
|
||||
"jwilsson/spotify-web-api-php": "^5.2",
|
||||
"meilisearch/meilisearch-php": "^0.24.0",
|
||||
|
@ -40,7 +39,8 @@
|
|||
"spatie/flysystem-dropbox": "^3.0",
|
||||
"saloonphp/saloon": "^3.8",
|
||||
"saloonphp/laravel-plugin": "^3.0",
|
||||
"laravel/socialite": "^5.12"
|
||||
"laravel/socialite": "^5.12",
|
||||
"laravel/ui": "^4.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "~1.0",
|
||||
|
@ -49,8 +49,8 @@
|
|||
"dms/phpunit-arraysubset-asserts": "^0.2.1",
|
||||
"fakerphp/faker": "^1.13",
|
||||
"slevomat/coding-standard": "^7.0",
|
||||
"nunomaduro/larastan": "^2.1",
|
||||
"laravel/tinker": "^2.7"
|
||||
"laravel/tinker": "^2.9",
|
||||
"larastan/larastan": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-zip": "Allow downloading multiple songs as Zip archives"
|
||||
|
|
3356
composer.lock
generated
3356
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@ class CopyArtistToContributingArtist extends Migration
|
|||
{
|
||||
public function up(): void
|
||||
{
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::with('album', 'album.artist')->get();
|
||||
|
||||
$songs->each(static function (Song $song): void {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('personal_access_tokens', static function (Blueprint $table): void {
|
||||
$table->timestamp('expires_at')->nullable();
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
includes:
|
||||
- ./vendor/nunomaduro/larastan/extension.neon
|
||||
- ./vendor/larastan/larastan/extension.neon
|
||||
|
||||
parameters:
|
||||
paths:
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-else class="genres">
|
||||
<li v-for="i in 20" :key="i">
|
||||
<ul v-else class="text-center">
|
||||
<li v-for="i in 20" :key="i" class="inline-block">
|
||||
<GenreItemSkeleton />
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
namespace Tests\Feature;
|
||||
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Events\PlaybackStarted;
|
||||
use App\Events\SongLikeToggled;
|
||||
use App\Models\Interaction;
|
||||
use App\Models\Song;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
use function Tests\create_user;
|
||||
|
@ -15,7 +17,7 @@ class InteractionTest extends TestCase
|
|||
{
|
||||
public function testIncreasePlayCount(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
Event::fake(PlaybackStarted::class);
|
||||
|
||||
$user = create_user();
|
||||
|
||||
|
@ -41,7 +43,7 @@ class InteractionTest extends TestCase
|
|||
|
||||
public function testToggleLike(): void
|
||||
{
|
||||
$this->expectsEvents(SongLikeToggled::class);
|
||||
Event::fake(SongLikeToggled::class);
|
||||
|
||||
$user = create_user();
|
||||
|
||||
|
@ -63,15 +65,17 @@ class InteractionTest extends TestCase
|
|||
'song_id' => $song->id,
|
||||
'liked' => 0,
|
||||
]);
|
||||
|
||||
Event::assertDispatched(SongLikeToggled::class);
|
||||
}
|
||||
|
||||
public function testToggleLikeBatch(): void
|
||||
{
|
||||
$this->expectsEvents(MultipleSongsLiked::class);
|
||||
Event::fake(MultipleSongsLiked::class);
|
||||
|
||||
$user = create_user();
|
||||
|
||||
/** @var Collection|array<Song> $songs */
|
||||
/** @var Collection<Song> $songs */
|
||||
$songs = Song::factory(2)->create();
|
||||
$songIds = $songs->pluck('id')->all();
|
||||
|
||||
|
@ -94,5 +98,7 @@ class InteractionTest extends TestCase
|
|||
'liked' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
Event::assertDispatched(MultipleSongsLiked::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
|
||||
namespace Tests\Feature\KoelPlus;
|
||||
|
||||
use App\Events\MultipleSongsLiked;
|
||||
use App\Events\MultipleSongsUnliked;
|
||||
use App\Events\SongLikeToggled;
|
||||
use App\Models\Song;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\PlusTestCase;
|
||||
|
||||
use function Tests\create_user;
|
||||
|
@ -12,7 +16,7 @@ class InteractionTest extends PlusTestCase
|
|||
{
|
||||
public function testPolicyForRegisterPlay(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
Event::fake(SongLikeToggled::class);
|
||||
|
||||
$owner = create_user();
|
||||
|
||||
|
@ -37,7 +41,7 @@ class InteractionTest extends PlusTestCase
|
|||
|
||||
public function testPolicyForToggleLike(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
Event::fake(SongLikeToggled::class);
|
||||
|
||||
$owner = create_user();
|
||||
|
||||
|
@ -62,7 +66,7 @@ class InteractionTest extends PlusTestCase
|
|||
|
||||
public function testPolicyForBatchLike(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
Event::fake(MultipleSongsLiked::class);
|
||||
|
||||
$owner = create_user();
|
||||
|
||||
|
@ -92,7 +96,7 @@ class InteractionTest extends PlusTestCase
|
|||
|
||||
public function testPolicyForBatchUnlike(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
Event::fake(MultipleSongsUnliked::class);
|
||||
|
||||
$owner = create_user();
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class SongTest extends PlusTestCase
|
|||
|
||||
Song::factory(2)->public()->create();
|
||||
|
||||
/** @var Collection<Song>|array<array-key, Song> $ownSongs */
|
||||
/** @var Collection<array-key, Song> $ownSongs */
|
||||
$ownSongs = Song::factory(3)->for($user, 'owner')->create();
|
||||
|
||||
$this->getAs('api/songs?own_songs_only=true', $user)
|
||||
|
|
|
@ -62,7 +62,7 @@ class PlaylistSongTest extends TestCase
|
|||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->create();
|
||||
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::factory(2)->create();
|
||||
|
||||
$this->postAs("api/playlists/$playlist->id/songs", ['songs' => $songs->pluck('id')->all()], $playlist->user)
|
||||
|
@ -78,7 +78,7 @@ class PlaylistSongTest extends TestCase
|
|||
|
||||
$toRemainSongs = Song::factory(5)->create();
|
||||
|
||||
/** @var Collection|array<array-key, Song> $toBeRemovedSongs */
|
||||
/** @var Collection<array-key, Song> $toBeRemovedSongs */
|
||||
$toBeRemovedSongs = Song::factory(2)->create();
|
||||
|
||||
$playlist->addSongs($toRemainSongs->merge($toBeRemovedSongs));
|
||||
|
|
|
@ -14,8 +14,6 @@ class ScrobbleTest extends TestCase
|
|||
{
|
||||
public function testLastfmScrobble(): void
|
||||
{
|
||||
$this->withoutEvents();
|
||||
|
||||
$user = create_user();
|
||||
|
||||
/** @var Song $song */
|
||||
|
|
|
@ -31,7 +31,7 @@ class SongTest extends TestCase
|
|||
|
||||
public function testDelete(): void
|
||||
{
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::factory(3)->create();
|
||||
|
||||
$this->deleteAs('api/songs', ['songs' => $songs->pluck('id')->all()], create_admin())
|
||||
|
@ -42,7 +42,7 @@ class SongTest extends TestCase
|
|||
|
||||
public function testUnauthorizedDelete(): void
|
||||
{
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::factory(3)->create();
|
||||
|
||||
$this->deleteAs('api/songs', ['songs' => $songs->pluck('id')->all()])
|
||||
|
@ -128,7 +128,7 @@ class SongTest extends TestCase
|
|||
], create_admin())
|
||||
->assertOk();
|
||||
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::query()->whereIn('id', $songIds)->get();
|
||||
|
||||
// All of these songs must now belong to a new album and artist set
|
||||
|
@ -150,7 +150,7 @@ class SongTest extends TestCase
|
|||
|
||||
public function testMultipleUpdateCreatingNewAlbumsAndArtists(): void
|
||||
{
|
||||
/** @var array<array-key, Song>|Collection $originalSongs */
|
||||
/** @var Collection<array-key, Song> $originalSongs */
|
||||
$originalSongs = Song::factory(3)->create();
|
||||
$originalSongIds = $originalSongs->pluck('id')->all();
|
||||
$originalAlbumNames = $originalSongs->pluck('album.name')->all();
|
||||
|
@ -168,7 +168,7 @@ class SongTest extends TestCase
|
|||
], create_admin())
|
||||
->assertOk();
|
||||
|
||||
/** @var array<array-key, Song>|Collection $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::query()->whereIn('id', $originalSongIds)->get()->orderByArray($originalSongIds);
|
||||
|
||||
// Even though the album name doesn't change, a new artist should have been created
|
||||
|
|
|
@ -10,6 +10,7 @@ use App\Exceptions\PlaylistCollaborationTokenExpiredException;
|
|||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistCollaborationToken;
|
||||
use App\Services\PlaylistCollaborationService;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\PlusTestCase;
|
||||
|
||||
use function Tests\create_user;
|
||||
|
@ -49,7 +50,7 @@ class PlaylistCollaborationServiceTest extends PlusTestCase
|
|||
|
||||
public function testAcceptUsingToken(): void
|
||||
{
|
||||
$this->expectsEvents(NewPlaylistCollaboratorJoined::class);
|
||||
Event::fake(NewPlaylistCollaboratorJoined::class);
|
||||
|
||||
/** @var PlaylistCollaborationToken $token */
|
||||
$token = PlaylistCollaborationToken::factory()->create();
|
||||
|
@ -59,12 +60,13 @@ class PlaylistCollaborationServiceTest extends PlusTestCase
|
|||
$this->service->acceptUsingToken($token->token, $user);
|
||||
|
||||
self::assertTrue($token->refresh()->playlist->collaborators->contains($user));
|
||||
Event::assertDispatched(NewPlaylistCollaboratorJoined::class);
|
||||
}
|
||||
|
||||
public function testFailsToAcceptExpiredToken(): void
|
||||
{
|
||||
$this->expectException(PlaylistCollaborationTokenExpiredException::class);
|
||||
$this->doesntExpectEvents(NewPlaylistCollaboratorJoined::class);
|
||||
Event::fake(NewPlaylistCollaboratorJoined::class);
|
||||
|
||||
/** @var PlaylistCollaborationToken $token */
|
||||
$token = PlaylistCollaborationToken::factory()->create();
|
||||
|
@ -75,6 +77,7 @@ class PlaylistCollaborationServiceTest extends PlusTestCase
|
|||
$this->service->acceptUsingToken($token->token, $user);
|
||||
|
||||
self::assertFalse($token->refresh()->playlist->collaborators->contains($user));
|
||||
Event::assertNotDispatched(NewPlaylistCollaboratorJoined::class);
|
||||
}
|
||||
|
||||
public function testGetCollaborators(): void
|
||||
|
|
|
@ -36,7 +36,7 @@ class DeleteNonExistingRecordsPostSyncTest extends TestCase
|
|||
|
||||
public function testHandle(): void
|
||||
{
|
||||
/** @var Collection|array<Song> $songs */
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
$songs = Song::factory(4)->create();
|
||||
|
||||
self::assertCount(4, Song::all());
|
||||
|
|
|
@ -9,6 +9,7 @@ use App\Models\Interaction;
|
|||
use App\Models\Song;
|
||||
use App\Services\InteractionService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
use function Tests\create_user;
|
||||
|
@ -36,7 +37,7 @@ class InteractionServiceTest extends TestCase
|
|||
|
||||
public function testToggleLike(): void
|
||||
{
|
||||
$this->expectsEvents(SongLikeToggled::class);
|
||||
Event::fake(SongLikeToggled::class);
|
||||
|
||||
/** @var Interaction $interaction */
|
||||
$interaction = Interaction::factory()->create();
|
||||
|
@ -45,11 +46,12 @@ class InteractionServiceTest extends TestCase
|
|||
$this->interactionService->toggleLike($interaction->song, $interaction->user);
|
||||
|
||||
self::assertNotSame($currentLiked, $interaction->refresh()->liked);
|
||||
Event::assertDispatched(SongLikeToggled::class);
|
||||
}
|
||||
|
||||
public function testLikeMultipleSongs(): void
|
||||
{
|
||||
$this->expectsEvents(MultipleSongsLiked::class);
|
||||
Event::fake(MultipleSongsLiked::class);
|
||||
|
||||
/** @var Collection $songs */
|
||||
$songs = Song::factory(2)->create();
|
||||
|
@ -66,11 +68,13 @@ class InteractionServiceTest extends TestCase
|
|||
|
||||
self::assertTrue($interaction->liked);
|
||||
});
|
||||
|
||||
Event::assertDispatched(MultipleSongsLiked::class);
|
||||
}
|
||||
|
||||
public function testUnlikeMultipleSongs(): void
|
||||
{
|
||||
$this->expectsEvents(MultipleSongsUnliked::class);
|
||||
Event::fake(MultipleSongsUnliked::class);
|
||||
$user = create_user();
|
||||
|
||||
/** @var Collection $interactions */
|
||||
|
@ -81,5 +85,7 @@ class InteractionServiceTest extends TestCase
|
|||
$interactions->each(static function (Interaction $interaction): void {
|
||||
self::assertFalse($interaction->refresh()->liked);
|
||||
});
|
||||
|
||||
Event::assertDispatched(MultipleSongsUnliked::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ use App\Values\ScanConfiguration;
|
|||
use App\Values\WatchRecord\InotifyWatchRecord;
|
||||
use getID3;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -37,11 +38,13 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testScan(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
Event::fake(MediaScanCompleted::class);
|
||||
|
||||
$owner = create_admin();
|
||||
$this->scanner->scan(ScanConfiguration::make(owner: $owner));
|
||||
|
||||
Event::assertDispatched(MediaScanCompleted::class);
|
||||
|
||||
// Standard mp3 files under root path should be recognized
|
||||
self::assertDatabaseHas(Song::class, [
|
||||
'path' => $this->path('/full.mp3'),
|
||||
|
@ -92,8 +95,6 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testModifiedFileIsRescanned(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
|
||||
$config = ScanConfiguration::make(owner: create_admin());
|
||||
$this->scanner->scan($config);
|
||||
|
||||
|
@ -108,7 +109,7 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testRescanWithoutForceDoesNotResetData(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
Event::fake(MediaScanCompleted::class);
|
||||
|
||||
$config = ScanConfiguration::make(owner: create_admin());
|
||||
|
||||
|
@ -131,7 +132,7 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testForceScanResetsData(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
Event::fake(MediaScanCompleted::class);
|
||||
|
||||
$owner = create_admin();
|
||||
$this->scanner->scan(ScanConfiguration::make(owner: $owner));
|
||||
|
@ -156,7 +157,7 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testScanWithIgnoredTags(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
Event::fake(MediaScanCompleted::class);
|
||||
|
||||
$owner = create_admin();
|
||||
$this->scanner->scan(ScanConfiguration::make(owner: $owner));
|
||||
|
@ -179,7 +180,7 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testScanAllTagsForNewFilesRegardlessOfIgnoredOption(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
Event::fake(MediaScanCompleted::class);
|
||||
|
||||
$owner = create_admin();
|
||||
$this->scanner->scan(ScanConfiguration::make(owner: $owner));
|
||||
|
@ -229,7 +230,7 @@ class MediaScannerTest extends TestCase
|
|||
|
||||
public function testScanDeletedDirectoryViaWatch(): void
|
||||
{
|
||||
$this->expectsEvents(MediaScanCompleted::class);
|
||||
Event::fake(MediaScanCompleted::class);
|
||||
|
||||
$config = ScanConfiguration::make(owner: create_admin());
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class PlaylistServiceTest extends TestCase
|
|||
|
||||
public function testCreatePlaylistWithSongs(): void
|
||||
{
|
||||
/** @var array<array-key, Song>|Collection $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::factory(3)->create();
|
||||
|
||||
$user = create_user();
|
||||
|
@ -239,7 +239,7 @@ class PlaylistServiceTest extends TestCase
|
|||
/** @var Playlist $playlist */
|
||||
$playlist = Playlist::factory()->create();
|
||||
|
||||
/** @var Collection|array<array-key, Song> $songs */
|
||||
/** @var Collection<array-key, Song> $songs */
|
||||
$songs = Song::factory(4)->create();
|
||||
$ids = $songs->pluck('id')->all();
|
||||
$playlist->addSongs($songs);
|
||||
|
|
|
@ -47,7 +47,7 @@ class PlaylistFolderServiceTest extends TestCase
|
|||
{
|
||||
$user = create_user();
|
||||
|
||||
/** @var Collection|array<array-key, Playlist> $playlists */
|
||||
/** @var Collection<array-key, Playlist> $playlists */
|
||||
$playlists = Playlist::factory()->for($user)->count(3)->create();
|
||||
|
||||
/** @var PlaylistFolder $folder */
|
||||
|
@ -63,7 +63,7 @@ class PlaylistFolderServiceTest extends TestCase
|
|||
/** @var PlaylistFolder $folder */
|
||||
$folder = PlaylistFolder::factory()->create();
|
||||
|
||||
/** @var Collection|array<array-key, Playlist> $playlists */
|
||||
/** @var Collection<array-key, Playlist> $playlists */
|
||||
$playlists = Playlist::factory()->count(3)->create();
|
||||
$folder->playlists()->attach($playlists->pluck('id')->all());
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests\Unit\Services\SongStorages;
|
||||
|
||||
use App\Exceptions\KoelPlusRequiredException;
|
||||
use App\Models\Song;
|
||||
use App\Services\SongStorages\DropboxStorage;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -11,6 +12,12 @@ class DropboxStorageTest extends TestCase
|
|||
public function testSupported(): void
|
||||
{
|
||||
$this->expectException(KoelPlusRequiredException::class);
|
||||
app(DropboxStorage::class);
|
||||
|
||||
/** @var Song $song */
|
||||
$song = Song::factory()->create();
|
||||
|
||||
/** @var DropboxStorage $service */
|
||||
$service = app(DropboxStorage::class);
|
||||
$service->getSongPresignedUrl($song);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests\Unit\Services\SongStorages;
|
||||
|
||||
use App\Exceptions\KoelPlusRequiredException;
|
||||
use App\Models\Song;
|
||||
use App\Services\SongStorages\S3CompatibleStorage;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -11,6 +12,12 @@ class S3CompatibleStorageTest extends TestCase
|
|||
public function testSupported(): void
|
||||
{
|
||||
$this->expectException(KoelPlusRequiredException::class);
|
||||
app(S3CompatibleStorage::class);
|
||||
|
||||
/** @var Song $song */
|
||||
$song = Song::factory()->create();
|
||||
|
||||
/** @var S3CompatibleStorage $service */
|
||||
$service = app(S3CompatibleStorage::class);
|
||||
$service->getSongPresignedUrl($song);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue