chore: fix CS

This commit is contained in:
Phan An 2020-12-22 21:11:22 +01:00
parent a90d961440
commit 560d41bf1d
185 changed files with 801 additions and 642 deletions

View file

@ -1,15 +1,33 @@
name: CI
on:
push:
branches:
- master
- actions
on: [push]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: [ 7.4, 8.0 ]
fail-fast: false
steps:
- uses: actions/checkout@v1
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: composer:v2
coverage: xdebug
- name: Copy .env file
run: cp .env.ci .env
- name: Install dependencies
run: composer install --prefer-dist --no-interaction --no-scripts --no-progress
- name: Generate app key
run: php artisan key:generate --quiet
- name: Run code style checker
run: composer cs
- name: Run static analysis
run: composer analyze -- --no-progress
steps:
- uses: actions/checkout@v1
- name: Composer

View file

@ -23,7 +23,7 @@ class Application extends IlluminateApplication
*
* @throws InvalidArgumentException
*/
public function rev(string $file, string $manifestFile = null): string
public function rev(string $file, ?string $manifestFile = null): string
{
static $manifest = null;
@ -53,6 +53,6 @@ class Application extends IlluminateApplication
{
$cdnUrl = trim(config('koel.cdn.url'), '/ ');
return $cdnUrl ? $cdnUrl.'/'.trim(ltrim($name, '/')) : trim(asset($name));
return $cdnUrl ? $cdnUrl . '/' . trim(ltrim($name, '/')) : trim(asset($name));
}
}

View file

@ -20,6 +20,7 @@ class ChangePasswordCommand extends Command
public function __construct(Hash $hash)
{
parent::__construct();
$this->hash = $hash;
}

View file

@ -8,12 +8,12 @@ use App\Models\Setting;
use App\Models\User;
use App\Repositories\SettingRepository;
use App\Services\MediaCacheService;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Kernel as Artisan;
use Illuminate\Contracts\Hashing\Hasher as Hash;
use Illuminate\Contracts\Hashing\Hasher as Hash;
use Illuminate\Database\DatabaseManager as DB;
use Jackiedo\DotenvEditor\DotenvEditor;
use Throwable;
class InitCommand extends Command
{
@ -51,7 +51,7 @@ class InitCommand extends Command
{
$this->comment('Attempting to install or upgrade Koel.');
$this->comment('Remember, you can always install/upgrade manually following the guide here:');
$this->info('📙 '.config('koel.misc.docs_url').PHP_EOL);
$this->info('📙 ' . config('koel.misc.docs_url') . PHP_EOL);
if ($this->inNoInteractionMode()) {
$this->info('Running in no-interaction mode');
@ -64,25 +64,25 @@ class InitCommand extends Command
$this->maybeSeedDatabase();
$this->maybeSetMediaPath();
$this->maybeCompileFrontEndAssets();
} catch (Exception $e) {
} catch (Throwable $e) {
$this->error("Oops! Koel installation or upgrade didn't finish successfully.");
$this->error('Please try again, or visit '.config('koel.misc.docs_url').' for manual installation.');
$this->error('Please try again, or visit ' . config('koel.misc.docs_url') . ' for manual installation.');
$this->error('😥 Sorry for this. You deserve better.');
return;
}
$this->comment(PHP_EOL.'🎆 Success! Koel can now be run from localhost with `php artisan serve`.');
$this->comment(PHP_EOL . '🎆 Success! Koel can now be run from localhost with `php artisan serve`.');
if (Setting::get('media_path')) {
$this->comment('You can also scan for media with `php artisan koel:sync`.');
}
$this->comment('Again, visit 📙 '.config('koel.misc.docs_url').' for the official documentation.');
$this->comment('Again, visit 📙 ' . config('koel.misc.docs_url') . ' for the official documentation.');
$this->comment(
"Feeling generous and want to support Koel's development? Check out "
.config('koel.misc.sponsor_github_url')
.' 🤗'
. config('koel.misc.sponsor_github_url')
. ' 🤗'
);
$this->comment('Thanks for using Koel. You rock! 🤘');
}
@ -175,7 +175,7 @@ class InitCommand extends Command
return;
}
$this->info('The absolute path to your media directory. If this is skipped (left blank) now, you can set it later via the web interface.');
$this->info('The absolute path to your media directory. If this is skipped (left blank) now, you can set it later via the web interface.'); // @phpcs-ignore-line
while (true) {
$path = $this->ask('Media path', config('koel.media_path'));
@ -224,9 +224,9 @@ class InitCommand extends Command
$this->db->reconnect()->getPdo();
break;
} catch (Exception $e) {
} catch (Throwable $e) {
$this->error($e->getMessage());
$this->warn(PHP_EOL."Koel cannot connect to the database. Let's set it up.");
$this->warn(PHP_EOL . "Koel cannot connect to the database. Let's set it up.");
$this->setUpDatabase();
}
}

View file

@ -25,14 +25,13 @@ class SyncCommand extends Command
private $mediaSyncService;
private $settingRepository;
/**
* @var ProgressBar
*/
/** @var ProgressBar */
private $progressBar;
public function __construct(MediaSyncService $mediaSyncService, SettingRepository $settingRepository)
{
parent::__construct();
$this->mediaSyncService = $mediaSyncService;
$this->settingRepository = $settingRepository;
}
@ -43,8 +42,9 @@ class SyncCommand extends Command
public function handle(): void
{
$this->ensureMediaPath();
$record = $this->argument('record');
if (!$record = $this->argument('record')) {
if (!$record) {
$this->syncAll();
return;
@ -60,7 +60,7 @@ class SyncCommand extends Command
*/
protected function syncAll(): void
{
$this->info('Syncing media from '.Setting::get('media_path').PHP_EOL);
$this->info('Syncing media from ' . Setting::get('media_path') . PHP_EOL);
// Get the tags to sync.
// Notice that this is only meaningful for existing records.
@ -70,10 +70,10 @@ class SyncCommand extends Command
$this->mediaSyncService->sync(null, $tags, $this->option('force'), $this);
$this->output->writeln(
PHP_EOL.PHP_EOL
."<info>Completed! {$this->synced} new or updated song(s)</info>, "
."{$this->ignored} unchanged song(s), "
."and <comment>{$this->invalid} invalid file(s)</comment>."
PHP_EOL . PHP_EOL
. "<info>Completed! {$this->synced} new or updated song(s)</info>, "
. "{$this->ignored} unchanged song(s), "
. "and <comment>{$this->invalid} invalid file(s)</comment>."
);
}
@ -107,7 +107,7 @@ class SyncCommand extends Command
++$this->ignored;
} elseif ($result === FileSynchronizer::SYNC_RESULT_BAD_FILE) {
if ($this->option('verbose')) {
$this->error(PHP_EOL."'$name' is not a valid media file: ".$reason);
$this->error(PHP_EOL . "'$name' is not a valid media file: " . $reason);
}
++$this->invalid;

View file

@ -16,6 +16,7 @@ class TidyLibraryCommand extends Command
public function __construct(MediaSyncService $mediaSyncService)
{
parent::__construct();
$this->mediaSyncService = $mediaSyncService;
}

View file

@ -6,8 +6,8 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected function commands()
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__ . '/Commands');
}
}

View file

@ -24,7 +24,7 @@ class AlbumInformationFetched extends Event
}
/**
* @return mixed[]
* @return array<mixed>
*/
public function getInformation(): array
{

View file

@ -24,7 +24,7 @@ class ArtistInformationFetched
}
/**
* @return mixed[]
* @return array<mixed>
*/
public function getInformation(): array
{

View file

@ -7,9 +7,9 @@ abstract class Event
/**
* Get the channels the event should be broadcast on.
*
* @return array
* @return array<mixed>
*/
public function broadcastOn()
public function broadcastOn(): array
{
return [];
}

View file

@ -13,7 +13,7 @@ class SongLikeToggled extends Event
public $interaction;
public $user;
public function __construct(Interaction $interaction, User $user = null)
public function __construct(Interaction $interaction, ?User $user = null)
{
$this->interaction = $interaction;
$this->user = $user ?: auth()->user();

View file

@ -10,7 +10,7 @@ use Illuminate\Support\Facades\Facade;
*/
class Download extends Facade
{
protected static function getFacadeAccessor()
protected static function getFacadeAccessor(): string
{
return 'Download';
}

View file

@ -4,9 +4,9 @@ namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class iTunes extends Facade
class ITunes extends Facade
{
protected static function getFacadeAccessor()
protected static function getFacadeAccessor(): string
{
return 'iTunes';
}

View file

@ -9,7 +9,7 @@ use Illuminate\Support\Facades\Facade;
*/
class Util extends Facade
{
protected static function getFacadeAccessor()
protected static function getFacadeAccessor(): string
{
return 'Util';
}

13
app/Facades/YouTube.php Normal file
View file

@ -0,0 +1,13 @@
<?php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class YouTube extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'YouTube';
}
}

View file

@ -10,11 +10,11 @@ use Throwable;
class SmartPlaylistRuleParameterFactory
{
/**
* @param mixed[] $value
* @param array<mixed> $value
*
* @throws Throwable
*
* @return string[]
* @return array<string>
*/
public function createParameters(string $model, string $operator, array $value): array
{

View file

@ -4,12 +4,12 @@ namespace App\Helpers;
function album_cover_path(string $fileName): string
{
return public_path(config('koel.album_cover_dir').$fileName);
return public_path(config('koel.album_cover_dir') . $fileName);
}
function album_cover_url(string $fileName): string
{
return app()->staticUrl(config('koel.album_cover_dir').$fileName);
return app()->staticUrl(config('koel.album_cover_dir') . $fileName);
}
/**
@ -22,10 +22,10 @@ function album_thumbnail_url(string $fileName): string
function artist_image_path(string $fileName): string
{
return public_path(config('koel.artist_image_dir').$fileName);
return public_path(config('koel.artist_image_dir') . $fileName);
}
function artist_image_url(string $fileName): string
{
return app()->staticUrl(config('koel.artist_image_dir').$fileName);
return app()->staticUrl(config('koel.artist_image_dir') . $fileName);
}

View file

@ -9,7 +9,7 @@ use App\Repositories\PlaylistRepository;
use App\Repositories\SettingRepository;
use App\Repositories\UserRepository;
use App\Services\ApplicationInformationService;
use App\Services\iTunesService;
use App\Services\ITunesService;
use App\Services\LastfmService;
use App\Services\MediaCacheService;
use App\Services\YouTubeService;
@ -35,7 +35,7 @@ class DataController extends Controller
public function __construct(
LastfmService $lastfmService,
YouTubeService $youTubeService,
iTunesService $iTunesService,
ITunesService $iTunesService,
MediaCacheService $mediaCacheService,
SettingRepository $settingRepository,
PlaylistRepository $playlistRepository,

View file

@ -16,6 +16,7 @@ class RecentlyPlayedController extends Controller
?Authenticatable $currentUser
) {
parent::__construct($interactionService, $currentUser);
$this->interactionRepository = $interactionRepository;
}

View file

@ -13,6 +13,7 @@ class SongController extends Controller
public function __construct(MediaInformationService $mediaInformationService, YouTubeService $youTubeService)
{
parent::__construct($mediaInformationService);
$this->youTubeService = $youTubeService;
}

View file

@ -38,9 +38,14 @@ class SongController extends Controller
$compilation = (bool) trim(array_get($tags, 'albumartist'));
$album = Album::getOrCreate($artist, array_get($tags, 'album'), $compilation);
$cover = array_get($tags, 'cover');
if ($cover = array_get($tags, 'cover')) {
$this->mediaMetadataService->writeAlbumCover($album, base64_decode($cover['data']), $cover['extension']);
if ($cover) {
$this->mediaMetadataService->writeAlbumCover(
$album,
base64_decode($cover['data'], true),
$cover['extension']
);
}
$song = Song::updateOrCreate(['id' => $this->helperService->getFileHash($path)], [

View file

@ -13,6 +13,7 @@ class FavoritesController extends Controller
public function __construct(DownloadService $downloadService, InteractionRepository $interactionRepository)
{
parent::__construct($downloadService);
$this->interactionRepository = $interactionRepository;
}

View file

@ -13,6 +13,7 @@ class SongController extends Controller
public function __construct(DownloadService $downloadService, SongRepository $songRepository)
{
parent::__construct($downloadService);
$this->songRepository = $songRepository;
}

View file

@ -4,16 +4,16 @@ namespace App\Http\Controllers;
use App\Http\Requests\API\ViewSongOnITunesRequest;
use App\Models\Album;
use App\Services\iTunesService;
use App\Services\ITunesService;
use App\Services\TokenManager;
use Illuminate\Http\Response;
class iTunesController extends Controller
class ITunesController extends Controller
{
private $iTunesService;
private $tokenManager;
public function __construct(iTunesService $iTunesService, TokenManager $tokenManager)
public function __construct(ITunesService $iTunesService, TokenManager $tokenManager)
{
$this->iTunesService = $iTunesService;
$this->tokenManager = $tokenManager;

View file

@ -5,6 +5,7 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class Authenticate
{
@ -15,7 +16,7 @@ class Authenticate
$this->auth = $auth;
}
public function handle(Request $request, Closure $next)
public function handle(Request $request, Closure $next): Response
{
if ($this->auth->guest()) {
if ($request->ajax() || $request->route()->getName() === 'play') {

View file

@ -5,6 +5,7 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Routing\UrlGenerator;
use Symfony\Component\HttpFoundation\Response;
class ForceHttps
{
@ -15,7 +16,7 @@ class ForceHttps
$this->url = $url;
}
public function handle(Request $request, Closure $next)
public function handle(Request $request, Closure $next): Response
{
if (config('koel.force_https')) {
$this->url->forceScheme('https');

View file

@ -4,6 +4,7 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Authenticate requests from Object Storage services (like S3).
@ -11,7 +12,7 @@ use Illuminate\Http\Request;
*/
class ObjectStorageAuthenticate
{
public function handle(Request $request, Closure $next)
public function handle(Request $request, Closure $next): Response
{
if ($request->appKey !== config('app.key')) {
return response('Unauthorized.', 401);

View file

@ -11,6 +11,7 @@ abstract class AbstractMediaImageUpdateRequest extends Request
return auth()->user()->is_admin;
}
/** @return array<mixed> */
public function rules(): array
{
return [
@ -20,15 +21,15 @@ abstract class AbstractMediaImageUpdateRequest extends Request
public function getFileContentAsBinaryString(): string
{
[$_, $data] = explode(',', $this->{$this->getImageFieldName()});
[, $data] = explode(',', $this->{$this->getImageFieldName()});
return base64_decode($data);
return base64_decode($data, true);
}
public function getFileExtension(): string
{
[$type, $data] = explode(';', $this->{$this->getImageFieldName()});
[$_, $extension] = explode('/', $type);
[$type,] = explode(';', $this->{$this->getImageFieldName()});
[, $extension] = explode('/', $type);
return $extension;
}

View file

@ -3,10 +3,11 @@
namespace App\Http\Requests\API;
/**
* @property string[] $songs
* @property array<string> $songs
*/
class BatchInteractionRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -9,6 +9,7 @@ use App\Http\Requests\API\Request;
*/
class StorePlayCountRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -8,6 +8,7 @@ namespace App\Http\Requests\API;
*/
class LastfmCallbackRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -7,6 +7,7 @@ namespace App\Http\Requests\API;
*/
class LastfmSetSessionKeyRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -6,6 +6,7 @@ use App\Http\Requests\API\Request as BaseRequest;
class Request extends BaseRequest
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -6,11 +6,12 @@ use App\Http\Requests\API\ObjectStorage\S3\Request as BaseRequest;
/**
* @property string $bucket
* @property string[] $tags
* @property array<string> $tags
* @property string $key
*/
class PutSongRequest extends BaseRequest
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -10,6 +10,7 @@ use App\Http\Requests\API\ObjectStorage\S3\Request as BaseRequest;
*/
class RemoveSongRequest extends BaseRequest
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -3,12 +3,13 @@
namespace App\Http\Requests\API;
/**
* @property string[] $songs
* @property array<string> $songs
* @property string $name
* @property array $rules
*/
class PlaylistStoreRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -3,10 +3,11 @@
namespace App\Http\Requests\API;
/**
* @property string[] $songs
* @property array<string> $songs
*/
class PlaylistSyncRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -7,11 +7,12 @@ namespace App\Http\Requests\API;
*/
class ProfileUpdateRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [
'name' => 'required',
'email' => 'required|email|unique:users,email,'.auth()->user()->id,
'email' => 'required|email|unique:users,email,' . auth()->user()->id,
];
}
}

View file

@ -7,6 +7,7 @@ namespace App\Http\Requests\API;
*/
class ScrobbleStoreRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return ['timestamp' => 'required|numeric'];

View file

@ -12,6 +12,7 @@ class SettingRequest extends Request
return auth()->user()->is_admin;
}
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -3,8 +3,8 @@
namespace App\Http\Requests\API;
/**
* @property string[] $songs
* @property mixed[] $data
* @property array<string> $songs
* @property array<mixed> $data
*/
class SongUpdateRequest extends Request
{
@ -13,6 +13,7 @@ class SongUpdateRequest extends Request
return $this->user()->is_admin;
}
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -13,6 +13,7 @@ class UploadRequest extends AbstractRequest
return auth()->user()->is_admin;
}
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -8,6 +8,7 @@ namespace App\Http\Requests\API;
*/
class UserLoginRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -15,6 +15,7 @@ class UserStoreRequest extends Request
return auth()->user()->is_admin;
}
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -17,6 +17,7 @@ class UserUpdateRequest extends Request
return auth()->user()->is_admin;
}
/** @return array<mixed> */
public function rules(): array
{
/** @var User $user */
@ -24,7 +25,7 @@ class UserUpdateRequest extends Request
return [
'name' => 'required',
'email' => 'required|email|unique:users,email,'.$user->id,
'email' => 'required|email|unique:users,email,' . $user->id,
];
}
}

View file

@ -8,6 +8,7 @@ namespace App\Http\Requests\API;
*/
class ViewSongOnITunesRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -11,6 +11,7 @@ abstract class AbstractRequest extends FormRequest
return true;
}
/** @return array<mixed> */
public function rules(): array
{
return [];

View file

@ -7,6 +7,7 @@ namespace App\Http\Requests\Download;
*/
class SongRequest extends Request
{
/** @return array<mixed> */
public function rules(): array
{
return [

View file

@ -16,6 +16,5 @@ abstract class Job
| provides access to the "onQueue" and "delay" queue helper methods.
|
*/
use Queueable;
}

View file

@ -10,6 +10,7 @@ class InotifyWatchRecord extends WatchRecord implements WatchRecordInterface
public function __construct(string $input)
{
parent::__construct($input);
$this->parse($input);
}

View file

@ -53,7 +53,7 @@ abstract class WatchRecord implements WatchRecordInterface
return $this->path;
}
public function __toString()
public function __toString(): string
{
return $this->input;
}

View file

@ -4,7 +4,7 @@ namespace App\Libraries\WatchRecord;
interface WatchRecordInterface
{
public function parse(string $string);
public function parse(string $string): void;
public function getPath(): string;

View file

@ -4,7 +4,7 @@ namespace App\Listeners;
use App\Events\AlbumInformationFetched;
use App\Services\MediaMetadataService;
use Exception;
use Throwable;
class DownloadAlbumCover
{
@ -26,7 +26,7 @@ class DownloadAlbumCover
if (!$album->has_cover && $image && ini_get('allow_url_fopen')) {
try {
$this->mediaMetadataService->downloadAlbumCover($album, $image);
} catch (Exception $e) {
} catch (Throwable $e) {
}
}
}

View file

@ -4,7 +4,7 @@ namespace App\Listeners;
use App\Events\ArtistInformationFetched;
use App\Services\MediaMetadataService;
use Exception;
use Throwable;
class DownloadArtistImage
{
@ -26,7 +26,7 @@ class DownloadArtistImage
if (!$artist->has_image && $image && ini_get('allow_url_fopen')) {
try {
$this->mediaMetadataService->downloadArtistImage($artist, $image);
} catch (Exception $e) {
} catch (Throwable $e) {
}
}
}

View file

@ -13,6 +13,6 @@ class JWTEventListener
public function subscribe(Dispatcher $events): void
{
$events->listen('tymon.jwt.valid', self::class.'@onValidUser');
$events->listen('tymon.jwt.valid', self::class . '@onValidUser');
}
}

View file

@ -17,8 +17,9 @@ class LoveTrackOnLastfm
public function handle(SongLikeToggled $event): void
{
if (!$this->lastfm->enabled() ||
!($sessionKey = $event->user->lastfm_session_key) ||
if (
!$this->lastfm->enabled() ||
!$event->user->lastfm_session_key ||
$event->interaction->song->artist->is_unknown
) {
return;

View file

@ -17,7 +17,7 @@ class TidyLibrary
/**
* @throws Exception
*/
public function handle()
public function handle(): void
{
$this->mediaSyncService->tidy();
}

View file

@ -17,10 +17,7 @@ class UpdateLastfmNowPlaying
public function handle(SongStartedPlaying $event): void
{
if (!$this->lastfm->enabled() ||
!($sessionKey = $event->user->lastfm_session_key) ||
$event->song->artist->is_unknown
) {
if (!$this->lastfm->enabled() || !$event->user->lastfm_session_key || $event->song->artist->is_unknown) {
return;
}

View file

@ -2,8 +2,6 @@
namespace App\Models;
use function App\Helpers\album_cover_path;
use function App\Helpers\album_cover_url;
use App\Traits\SupportsDeleteWhereIDsNotIn;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
@ -12,19 +10,23 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use function App\Helpers\album_cover_path;
use function App\Helpers\album_cover_url;
/**
* @property string $cover The album cover's file name
* @property string $cover The album cover's file name
* @property string|null $cover_path The absolute path to the cover file
* @property bool $has_cover If the album has a non-default cover image
* @property int $id
* @property string $name Name of the album
* @property bool $is_compilation If the album is a compilation from multiple artists
* @property Artist $artist The album's artist
* @property int $artist_id
* @property Collection $songs
* @property bool $is_unknown If the album is the Unknown Album
* @property bool $has_cover If the album has a non-default cover image
* @property int $id
* @property string $name Name of the album
* @property bool $is_compilation If the album is a compilation from multiple artists
* @property Artist $artist The album's artist
* @property int $artist_id
* @property Collection $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. Notice that this doesn't guarantee the thumbnail exists.
* @property string|null $thumbnail_path The full path to the thumbnail.
* Notice that this doesn't guarantee the thumbnail exists.
* @property string|null $thumbnail The public URL to the album's thumbnail
*
* @method static self firstOrCreate(array $where, array $params = [])
@ -38,9 +40,9 @@ class Album extends Model
use HasFactory;
use SupportsDeleteWhereIDsNotIn;
const UNKNOWN_ID = 1;
const UNKNOWN_NAME = 'Unknown Album';
const UNKNOWN_COVER = 'unknown-album.png';
public const UNKNOWN_ID = 1;
public const UNKNOWN_NAME = 'Unknown Album';
public const UNKNOWN_COVER = 'unknown-album.png';
protected $guarded = ['id'];
protected $hidden = ['updated_at'];

View file

@ -3,8 +3,6 @@
namespace App\Models;
use App\Facades\Util;
use function App\Helpers\artist_image_path;
use function App\Helpers\artist_image_url;
use App\Traits\SupportsDeleteWhereIDsNotIn;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
@ -13,6 +11,9 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use function App\Helpers\artist_image_path;
use function App\Helpers\artist_image_url;
/**
* @property int $id
* @property string $name
@ -34,10 +35,10 @@ class Artist extends Model
use HasFactory;
use SupportsDeleteWhereIDsNotIn;
const UNKNOWN_ID = 1;
const UNKNOWN_NAME = 'Unknown Artist';
const VARIOUS_ID = 2;
const VARIOUS_NAME = 'Various Artists';
public const UNKNOWN_ID = 1;
public const UNKNOWN_NAME = 'Unknown Artist';
public const VARIOUS_ID = 2;
public const VARIOUS_NAME = 'Various Artists';
protected $guarded = ['id'];
protected $hidden = ['created_at', 'updated_at'];
@ -87,7 +88,9 @@ class Artist extends Model
public static function getOrCreate(?string $name = null): self
{
// Remove the BOM from UTF-8/16/32, as it will mess up the database constraints.
if ($encoding = Util::detectUTFEncoding($name)) {
$encoding = Util::detectUTFEncoding($name);
if ($encoding) {
$name = mb_convert_encoding($name, 'UTF-8', $encoding);
}

View file

@ -89,7 +89,13 @@ class Rule
private function validateOperator(string $operator): void
{
if (!in_array($operator, self::VALID_OPERATORS, true)) {
throw new InvalidArgumentException(sprintf('%s is not a valid value for operators. Valid values are: %s', $operator, implode(', ', self::VALID_OPERATORS)));
throw new InvalidArgumentException(
sprintf(
'%s is not a valid value for operators. Valid values are: %s',
$operator,
implode(', ', self::VALID_OPERATORS)
)
);
}
}
}

View file

@ -23,15 +23,13 @@ class Setting extends Model
/**
* Get a setting value.
*
* @return mixed|string
* @return mixed
*/
public static function get(string $key)
{
if ($record = self::find($key)) {
return $record->value;
}
$record = self::find($key);
return null;
return $record ? $record->value : null;
}
/**
@ -39,7 +37,6 @@ class Setting extends Model
*
* @param string|array $key the key of the setting, or an associative array of settings,
* in which case $value will be discarded
* @param mixed $value
*/
public static function set($key, $value = null): void
{
@ -57,8 +54,6 @@ class Setting extends Model
/**
* Serialize the setting value before saving into the database.
* This makes settings more flexible.
*
* @param mixed $value
*/
public function setValueAttribute($value): void
{
@ -68,8 +63,6 @@ class Setting extends Model
/**
* Get the unserialized setting value.
*
* @param mixed $value
*
* @return mixed
*/
public function getValueAttribute($value)

View file

@ -14,19 +14,19 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
/**
* @property string $path
* @property string $title
* @property Album $album
* @property Artist $artist
* @property string[] $s3_params
* @property float $length
* @property string $lyrics
* @property int $track
* @property int $disc
* @property int $album_id
* @property string $id
* @property int $artist_id
* @property int $mtime
* @property string $path
* @property string $title
* @property Album $album
* @property Artist $artist
* @property array<string> $s3_params
* @property float $length
* @property string $lyrics
* @property int $track
* @property int $disc
* @property int $album_id
* @property string $id
* @property int $artist_id
* @property int $mtime
*
* @method static self updateOrCreate(array $where, array $params)
* @method static Builder select(string $string)
@ -48,14 +48,9 @@ class Song extends Model
* Attributes to be hidden from JSON outputs.
* Here we specify to hide lyrics as well to save some bandwidth (actually, lots of it).
* Lyrics can then be queried on demand.
*
* @var array
*/
protected $hidden = ['lyrics', 'updated_at', 'path', 'mtime'];
/**
* @var array
*/
protected $casts = [
'length' => 'float',
'mtime' => 'int',
@ -64,12 +59,6 @@ class Song extends Model
];
protected $keyType = 'string';
/**
* Indicates if the IDs are auto-incrementing.
*
* @var bool
*/
public $incrementing = false;
public function artist(): BelongsTo
@ -95,14 +84,16 @@ class Song extends Model
/**
* Update song info.
*
* @param string[] $ids
* @param string[] $data the data array, with these supported fields:
* - title
* - artistName
* - albumName
* - lyrics
* All of these are optional, in which case the info will not be changed
* (except for lyrics, which will be emptied)
* @param array<string> $ids
* @param array<string> $data the data array, with these supported fields:
* - title
* - artistName
* - albumName
* - lyrics
* All of these are optional, in which case the info will not be changed
* (except for lyrics, which will be emptied)
*
* @return Collection|array<Song>
*/
public static function updateInfo(array $ids, array $data): Collection
{
@ -164,9 +155,11 @@ class Song extends Model
case 1: // ALL, or forcing compilation status to be Yes
$isCompilation = true;
break;
case 2: // Keep current compilation status
$isCompilation = $this->album->artist_id === Artist::VARIOUS_ID;
break;
default:
$isCompilation = false;
break;
@ -197,7 +190,7 @@ class Song extends Model
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;
$path = rtrim(trim($path), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
return $query->where('path', 'LIKE', "$path%");
}
@ -234,7 +227,7 @@ class Song extends Model
/**
* Get the bucket and key name of an S3 object.
*
* @return string[]|null
* @return array<string>|null
*/
public function getS3ParamsAttribute(): ?array
{
@ -247,10 +240,7 @@ class Song extends Model
return compact('bucket', 'key');
}
/**
* Return the ID of the song when it's converted to string.
*/
public function __toString()
public function __toString(): string
{
return $this->id;
}

View file

@ -3,17 +3,15 @@
namespace App\Models;
use App\Facades\Download;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use RuntimeException;
use Throwable;
use ZipArchive;
class SongZipArchive
{
/**
* @var ZipArchive
*/
/** @var ZipArchive */
private $archive;
/**
@ -60,7 +58,7 @@ class SongZipArchive
try {
$path = Download::fromSong($song);
$this->archive->addFile($path, $this->generateZipContentFileNameFromPath($path));
} catch (Exception $e) {
} catch (Throwable $e) {
Log::error($e);
}
@ -95,7 +93,7 @@ class SongZipArchive
++$this->fileNames[$name];
$parts = explode('.', $name);
$ext = $parts[count($parts) - 1];
$parts[count($parts) - 1] = $this->fileNames[$name].".$ext";
$parts[count($parts) - 1] = $this->fileNames[$name] . ".$ext";
$name = implode('.', $parts);
} else {
$this->fileNames[$name] = 1;

View file

@ -30,7 +30,6 @@ class User extends Authenticatable
/**
* The preferences that we don't want to show to the client.
*
* @var array
*/
private const HIDDEN_PREFERENCES = ['lastfm_session_key'];
@ -114,7 +113,7 @@ class User extends Authenticatable
/**
* User preferences are stored as a serialized associative array.
*
* @param mixed[] $value
* @param array<mixed> $value
*/
public function setPreferencesAttribute(array $value): void
{
@ -124,7 +123,7 @@ class User extends Authenticatable
/**
* Unserialize the user preferences back to an array before returning.
*
* @return mixed[]
* @return array<mixed>
*/
public function getPreferencesAttribute(?string $value): array
{

View file

@ -3,8 +3,8 @@
namespace App\Observers;
use App\Models\Album;
use Exception;
use Illuminate\Log\Logger;
use Throwable;
class AlbumObserver
{
@ -28,7 +28,7 @@ class AlbumObserver
try {
unlink($album->cover_path);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
}
}

View file

@ -7,6 +7,7 @@ use Illuminate\Database\Schema\Builder;
use Illuminate\Database\SQLiteConnection;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Factory as Validator;
use Laravel\Tinker\TinkerServiceProvider;
class AppServiceProvider extends ServiceProvider
{
@ -35,7 +36,7 @@ class AppServiceProvider extends ServiceProvider
public function register(): void
{
if ($this->app->environment() !== 'production') {
$this->app->register(\Laravel\Tinker\TinkerServiceProvider::class);
$this->app->register(TinkerServiceProvider::class);
}
}
}

View file

@ -26,7 +26,7 @@ class AuthServiceProvider extends ServiceProvider
/**
* Register any application authentication / authorization services.
*/
public function boot()
public function boot(): void
{
$this->registerPolicies();

View file

@ -10,9 +10,8 @@ class BroadcastServiceProvider extends ServiceProvider
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
public function boot(): void
{
Broadcast::routes();

View file

@ -57,7 +57,7 @@ class EventServiceProvider extends ServiceProvider
/**
* Register any other events for your application.
*/
public function boot()
public function boot(): void
{
parent::boot();

View file

@ -2,15 +2,15 @@
namespace App\Providers;
use App\Services\iTunesService;
use App\Services\ITunesService;
use Illuminate\Support\ServiceProvider;
class iTunesServiceProvider extends ServiceProvider
class ITunesServiceProvider extends ServiceProvider
{
public function register(): void
{
app()->singleton('iTunes', static function (): iTunesService {
return app(iTunesService::class);
app()->singleton('iTunes', static function (): ITunesService {
return app(ITunesService::class);
});
}
}

View file

@ -19,9 +19,8 @@ class RouteServiceProvider extends ServiceProvider
/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
public function boot(): void
{
parent::boot();
}
@ -29,9 +28,8 @@ class RouteServiceProvider extends ServiceProvider
/**
* Define the routes for the application.
*
* @return void
*/
public function map()
public function map(): void
{
$this->mapApiRoutes();
$this->mapWebRoutes();
@ -42,9 +40,8 @@ class RouteServiceProvider extends ServiceProvider
*
* These routes all receive session state, CSRF protection, etc.
*
* @return void
*/
protected function mapWebRoutes()
protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->namespace)
@ -56,9 +53,8 @@ class RouteServiceProvider extends ServiceProvider
*
* These routes are typically stateless.
*
* @return void
*/
protected function mapApiRoutes()
protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')

View file

@ -20,8 +20,10 @@ class StreamerServiceProvider extends ServiceProvider
switch (config('koel.streaming.method')) {
case 'x-sendfile':
return new XSendFileStreamer();
case 'x-accel-redirect':
return new XAccelRedirectStreamer();
default:
return new PHPStreamer();
}

View file

@ -2,10 +2,10 @@
namespace App\Repositories;
use Exception;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Throwable;
abstract class AbstractRepository implements RepositoryInterface
{
@ -25,7 +25,7 @@ abstract class AbstractRepository implements RepositoryInterface
// rendering the whole installation failing.
try {
$this->auth = app(Guard::class);
} catch (Exception $e) {
} catch (Throwable $e) {
}
}
@ -34,11 +34,13 @@ abstract class AbstractRepository implements RepositoryInterface
return $this->model->find($id);
}
/** @return Collection|array<Model> */
public function getByIds(array $ids): Collection
{
return $this->model->whereIn($this->model->getKeyName(), $ids)->get();
}
/** @return Collection|array<Model> */
public function getAll(): Collection
{
return $this->model->all();

View file

@ -12,14 +12,13 @@ class AlbumRepository extends AbstractRepository
return Album::class;
}
/** @return array<int> */
public function getNonEmptyAlbumIds(): array
{
$ids = Song::select('album_id')
return Song::select('album_id')
->groupBy('album_id')
->get()
->pluck('album_id')
->toArray();
return $ids;
}
}

View file

@ -12,6 +12,7 @@ class ArtistRepository extends AbstractRepository
return Artist::class;
}
/** @return array<int> */
public function getNonEmptyArtistIds(): array
{
return Song::select('artist_id')

View file

@ -17,9 +17,7 @@ class InteractionRepository extends AbstractRepository
return Interaction::class;
}
/**
* Get all songs favorited by a user.
*/
/** @return Collection|array<Interaction> */
public function getUserFavorites(User $user): Collection
{
return $this->model->where([
@ -31,9 +29,7 @@ class InteractionRepository extends AbstractRepository
->pluck('song');
}
/**
* @return Interaction[]
*/
/** @return array<Interaction> */
public function getRecentlyPlayed(User $user, ?int $count = null): array
{
/** @var Builder $query */

View file

@ -15,6 +15,7 @@ class PlaylistRepository extends AbstractRepository
return Playlist::class;
}
/** @return Collection|array<Playlist> */
public function getAllByCurrentUser(): Collection
{
return $this->byCurrentUser()->orderBy('name')->get();

View file

@ -9,15 +9,11 @@ interface RepositoryInterface
{
public function getModelClass(): string;
/**
* @param int|string $id
*/
public function getOneById($id): ?Model;
/**
* @param int[]|string[] $ids
*/
/** @return Collection|array<Model> */
public function getByIds(array $ids): Collection;
/** @return Collection|array<Model> */
public function getAll(): Collection;
}

View file

@ -11,6 +11,7 @@ class SettingRepository extends AbstractRepository
return Setting::class;
}
/** @return array<mixed> */
public function getAllAsKeyValueArray(): array
{
return $this->model->pluck('value', 'key')->all();

View file

@ -12,6 +12,7 @@ class SongRepository extends AbstractRepository
public function __construct(HelperService $helperService)
{
parent::__construct();
$this->helperService = $helperService;
}

View file

@ -3,6 +3,7 @@
namespace App\Repositories\Traits;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
trait ByCurrentUser
@ -12,6 +13,7 @@ trait ByCurrentUser
return $this->model->whereUserId($this->auth->id());
}
/** @return Collection|array<Model> */
public function getAllByCurrentUser(): Collection
{
return $this->byCurrentUser()->get();

View file

@ -10,7 +10,7 @@ class ImageData implements Rule
public function passes($attribute, $value): bool
{
try {
[$header, $_] = explode(';', $value);
[$header,] = explode(';', $value);
return (bool) preg_match('/data:image\/(jpe?g|png|gif)/i', $header);
} catch (Throwable $exception) {

View file

@ -48,7 +48,7 @@ abstract class AbstractApiClient
* @param bool $appendKey Whether to automatically append the API key into the URI.
* While it's usually the case, some services (like Last.fm) requires
* an "API signature" of the request. Appending an API key will break the request.
* @param mixed[] $params An array of parameters
* @param array<mixed> $params An array of parameters
*
* @return mixed|SimpleXMLElement|null
*/
@ -77,7 +77,7 @@ abstract class AbstractApiClient
* Make an HTTP call to the external resource.
*
* @param string $method The HTTP method
* @param mixed[] $args An array of parameters
* @param array<mixed> $args An array of parameters
*
* @throws InvalidArgumentException
*
@ -90,8 +90,8 @@ abstract class AbstractApiClient
}
$uri = $args[0];
$opts = isset($args[1]) ? $args[1] : [];
$appendKey = isset($args[2]) ? $args[2] : true;
$opts = $args[1] ?? [];
$appendKey = $args[2] ?? true;
return $this->request($method, $uri, $appendKey, $opts);
}
@ -108,14 +108,14 @@ abstract class AbstractApiClient
$uri = "/$uri";
}
$uri = $this->getEndpoint().$uri;
$uri = $this->getEndpoint() . $uri;
}
if ($appendKey) {
if (parse_url($uri, PHP_URL_QUERY)) {
$uri .= "&{$this->keyParam}=".$this->getKey();
$uri .= "&{$this->keyParam}=" . $this->getKey();
} else {
$uri .= "?{$this->keyParam}=".$this->getKey();
$uri .= "?{$this->keyParam}=" . $this->getKey();
}
}

View file

@ -3,10 +3,10 @@
namespace App\Services;
use App\Application;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Cache\Repository as Cache;
use Illuminate\Log\Logger;
use Throwable;
class ApplicationInformationService
{
@ -33,7 +33,7 @@ class ApplicationInformationService
return json_decode(
$this->client->get('https://api.github.com/repos/phanan/koel/tags')->getBody()
)[0]->name;
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
return Application::KOEL_VERSION;

View file

@ -33,12 +33,16 @@ class DownloadService
switch (get_class($mixed)) {
case Song::class:
return $this->fromSong($mixed);
case Collection::class:
return $this->fromMultipleSongs($mixed);
case Album::class:
return $this->fromAlbum($mixed);
case Artist::class:
return $this->fromArtist($mixed);
case Playlist::class:
return $this->fromPlaylist($mixed);
}
@ -48,13 +52,13 @@ class DownloadService
public function fromSong(Song $song): string
{
if ($s3Params = $song->s3_params) {
if ($song->s3_params) {
// The song is hosted on Amazon S3.
// We download it back to our local server first.
$url = $this->s3Service->getSongPublicUrl($song);
abort_unless($url, 404);
$localPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.basename($s3Params['key']);
$localPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . basename($song->s3_params['key']);
// The following function requires allow_url_fopen to be ON.
// We're just assuming that to be the case here.

View file

@ -6,13 +6,13 @@ use App\Models\Album;
use App\Models\Artist;
use App\Models\Song;
use App\Repositories\SongRepository;
use Exception;
use getID3;
use getid3_lib;
use Illuminate\Contracts\Cache\Repository as Cache;
use InvalidArgumentException;
use SplFileInfo;
use Symfony\Component\Finder\Finder;
use Throwable;
class FileSynchronizer
{
@ -27,19 +27,13 @@ class FileSynchronizer
private $cache;
private $finder;
/**
* @var SplFileInfo
*/
/** @var SplFileInfo */
private $splFileInfo;
/**
* @var int
*/
/** @var int */
private $fileModifiedTime;
/**
* @var string
*/
/** @var string */
private $filePath;
/**
@ -57,9 +51,7 @@ class FileSynchronizer
*/
private $song;
/**
* @var string|null
*/
/** @var string|null */
private $syncError;
public function __construct(
@ -78,9 +70,7 @@ class FileSynchronizer
$this->finder = $finder;
}
/**
* @param string|SplFileInfo $path
*/
/** @param string|SplFileInfo $path */
public function setFile($path): self
{
$this->splFileInfo = $path instanceof SplFileInfo ? $path : new SplFileInfo($path);
@ -88,7 +78,7 @@ class FileSynchronizer
// Workaround for #344, where getMTime() fails for certain files with Unicode names on Windows.
try {
$this->fileModifiedTime = $this->splFileInfo->getMTime();
} catch (Exception $e) {
} catch (Throwable $e) {
// Not worth logging the error. Just use current stamp for mtime.
$this->fileModifiedTime = time();
}
@ -103,6 +93,8 @@ class FileSynchronizer
/**
* Get all applicable info from the file.
*
* @return array<mixed>
*/
public function getFileInfo(): array
{
@ -124,7 +116,7 @@ class FileSynchronizer
'album' => '',
'albumartist' => '',
'compilation' => false,
'title' => basename($this->filePath, '.'.pathinfo($this->filePath, PATHINFO_EXTENSION)), // default to be file name
'title' => basename($this->filePath, '.' . pathinfo($this->filePath, PATHINFO_EXTENSION)),
'length' => $info['playtime_seconds'],
'track' => $this->getTrackNumberFromInfo($info),
'disc' => (int) array_get($info, 'comments.part_of_a_set.0', 1),
@ -134,7 +126,9 @@ class FileSynchronizer
'mtime' => $this->fileModifiedTime,
];
if (!$comments = array_get($info, 'comments_html')) {
$comments = array_get($info, 'comments_html');
if (!$comments) {
return $props;
}
@ -147,8 +141,8 @@ class FileSynchronizer
/**
* Sync the song with all available media info against the database.
*
* @param string[] $tags The (selective) tags to sync (if the song exists)
* @param bool $force Whether to force syncing, even if the file is unchanged
* @param array<string> $tags The (selective) tags to sync (if the song exists)
* @param bool $force Whether to force syncing, even if the file is unchanged
*/
public function sync(array $tags, bool $force = false): int
{
@ -156,7 +150,9 @@ class FileSynchronizer
return self::SYNC_RESULT_UNMODIFIED;
}
if (!$info = $this->getFileInfo()) {
$info = $this->getFileInfo();
if (!$info) {
return self::SYNC_RESULT_BAD_FILE;
}
@ -218,14 +214,14 @@ class FileSynchronizer
/**
* Try to generate a cover for an album based on extracted data, or use the cover file under the directory.
*
* @param mixed[]|null $coverData
* @param array<mixed>|null $coverData
*/
private function generateAlbumCover(Album $album, ?array $coverData): void
{
// If the album has no cover, we try to get the cover image from existing tag data
if ($coverData) {
$extension = explode('/', $coverData['image_mime']);
$extension = empty($extension[1]) ? 'png' : $extension[1];
$extension = $extension[1] ? 'png' : $extension[1];
$this->mediaMetadataService->writeAlbumCover($album, $coverData['data'], $extension);
@ -233,7 +229,9 @@ class FileSynchronizer
}
// Or, if there's a cover image under the same directory, use it.
if ($cover = $this->getCoverFileUnderSameDirectory()) {
$cover = $this->getCoverFileUnderSameDirectory();
if ($cover) {
$extension = pathinfo($cover, PATHINFO_EXTENSION);
$this->mediaMetadataService->writeAlbumCover($album, file_get_contents($cover), $extension);
}
@ -249,16 +247,16 @@ class FileSynchronizer
private function getCoverFileUnderSameDirectory(): ?string
{
// As directory scanning can be expensive, we cache and reuse the result.
return $this->cache->remember(md5($this->filePath.'_cover'), 24 * 60, function (): ?string {
return $this->cache->remember(md5($this->filePath . '_cover'), 24 * 60, function (): ?string {
$matches = array_keys(
iterator_to_array(
$this->finder->create()
->depth(0)
->ignoreUnreadableDirs()
->files()
->followLinks()
->name('/(cov|fold)er\.(jpe?g|png)$/i')
->in(dirname($this->filePath))
->depth(0)
->ignoreUnreadableDirs()
->files()
->followLinks()
->name('/(cov|fold)er\.(jpe?g|png)$/i')
->in(dirname($this->filePath))
)
);
@ -272,7 +270,7 @@ class FileSynchronizer
{
try {
return (bool) exif_imagetype($path);
} catch (Exception $e) {
} catch (Throwable $e) {
return false;
}
}

View file

@ -10,6 +10,6 @@ class HelperService
*/
public function getFileHash(string $path): string
{
return md5(config('app.key').$path);
return md5(config('app.key') . $path);
}
}

View file

@ -2,9 +2,9 @@
namespace App\Services;
use Exception;
use Throwable;
class iTunesService extends AbstractApiClient implements ApiConsumerInterface
class ITunesService extends AbstractApiClient implements ApiConsumerInterface
{
/**
* Determines whether to use iTunes services.
@ -29,7 +29,7 @@ class iTunesService extends AbstractApiClient implements ApiConsumerInterface
24 * 60 * 7,
function () use ($term, $album, $artist): ?string {
$params = [
'term' => $term.($album ? " $album" : '').($artist ? " $artist" : ''),
'term' => $term . ($album ? " $album" : '') . ($artist ? " $artist" : ''),
'media' => 'music',
'entity' => 'song',
'limit' => 1,
@ -45,12 +45,10 @@ class iTunesService extends AbstractApiClient implements ApiConsumerInterface
$trackUrl = $response->results[0]->trackViewUrl;
$connector = parse_url($trackUrl, PHP_URL_QUERY) ? '&' : '?';
$trackUrl .= "{$connector}at=".config('koel.itunes.affiliate_id');
return $trackUrl;
return $trackUrl . "{$connector}at=" . config('koel.itunes.affiliate_id');
}
);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
return null;

View file

@ -23,7 +23,8 @@ class ImageWriter
->make($data)
->resize(
$config['max_width'] ?? self::DEFAULT_MAX_WIDTH,
null, static function (Constraint $constraint): void {
null,
static function (Constraint $constraint): void {
$constraint->upsize();
$constraint->aspectRatio();
}

View file

@ -56,9 +56,9 @@ class InteractionService
/**
* Like several songs at once as a user.
*
* @param string[] $songIds
* @param array<string> $songIds
*
* @return Interaction[] the array of Interaction objects
* @return array<Interaction> the array of Interaction objects
*/
public function batchLike(array $songIds, User $user): array
{
@ -82,7 +82,7 @@ class InteractionService
/**
* Unlike several songs at once.
*
* @param string[] $songIds
* @param array<string> $songIds
*/
public function batchUnlike(array $songIds, User $user): void
{

View file

@ -2,14 +2,12 @@
namespace App\Services;
use Exception;
use Throwable;
class LastfmService extends AbstractApiClient implements ApiConsumerInterface
{
/**
* Override the key param, since, again, Lastfm wants to be different.
*
* @var string
*/
protected $keyParam = 'api_key';
@ -32,9 +30,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Get information about an artist.
*
* @param string $name Name of the artist
*
* @return mixed[]|null
* @return array<mixed>|null
*/
public function getArtistInformation(string $name): ?array
{
@ -54,7 +50,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
return $this->buildArtistInformation($response->artist);
});
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
return null;
@ -64,9 +60,9 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Build a Koel-usable array of artist information using the data from Last.fm.
*
* @param object $data
* @param mixed $data
*
* @return mixed[]
* @return array<mixed>
*/
private function buildArtistInformation($data): array
{
@ -83,7 +79,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Get information about an album.
*
* @return mixed[]|null
* @return array<mixed>|null
*/
public function getAlbumInformation(string $albumName, string $artistName): ?array
{
@ -107,7 +103,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
return $this->buildAlbumInformation($response->album);
});
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
return null;
@ -117,9 +113,9 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Build a Koel-usable array of album information using the data from Last.fm.
*
* @param object $data
* @param mixed $data
*
* @return mixed[]
* @return array<mixed>
*/
private function buildAlbumInformation($data): array
{
@ -156,7 +152,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
try {
return $this->get("/?$query&format=json", [], false)->session->key;
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
return null;
@ -166,11 +162,11 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Scrobble a song.
*
* @param string $artist The artist name
* @param string $track The track name
* @param string $artist The artist name
* @param string $track The track name
* @param string|int $timestamp The UNIX timestamp
* @param string $album The album name
* @param string $sk The session key
* @param string $album The album name
* @param string $sk The session key
*/
public function scrobble(string $artist, string $track, $timestamp, string $album, string $sk): void
{
@ -184,7 +180,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
try {
$this->post('/', $this->buildAuthCallParams($params), false);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
}
}
@ -192,10 +188,10 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Love or unlove a track on Last.fm.
*
* @param string $track The track name
* @param string $track The track name
* @param string $artist The artist's name
* @param string $sk The session key
* @param bool $love Whether to love or unlove. Such cheesy terms... urrgggh
* @param string $sk The session key
* @param bool $love Whether to love or unlove. Such cheesy terms... urrgggh
*/
public function toggleLoveTrack(string $track, string $artist, string $sk, ?bool $love = true): void
{
@ -204,7 +200,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
try {
$this->post('/', $this->buildAuthCallParams($params), false);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
}
}
@ -212,11 +208,11 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Update a track's "now playing" on Last.fm.
*
* @param string $artist Name of the artist
* @param string $track Name of the track
* @param string $album Name of the album
* @param string $artist Name of the artist
* @param string $track Name of the track
* @param string $album Name of the album
* @param int|float $duration Duration of the track, in seconds
* @param string $sk The session key
* @param string $sk The session key
*/
public function updateNowPlaying(string $artist, string $track, string $album, $duration, string $sk): void
{
@ -229,7 +225,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
try {
$this->post('/', $this->buildAuthCallParams($params), false);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
}
}
@ -242,12 +238,12 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
*
* @see http://www.last.fm/api/webauth#5
*
* @param array $params the array of parameters
* @param bool $toString Whether to turn the array into a query string
* @param array $params the array of parameters
* @param bool $toString Whether to turn the array into a query string
*
* @return array|string
* @return array<mixed>|string
*/
public function buildAuthCallParams(array $params, bool $toString = false)
public function buildAuthCallParams(array $params, bool $toString = false): string
{
$params['api_key'] = $this->getKey();
ksort($params);
@ -257,7 +253,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
$str = '';
foreach ($params as $name => $value) {
$str .= $name.$value;
$str .= $name . $value;
}
$str .= $this->getSecret();
@ -268,6 +264,7 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
}
$query = '';
foreach ($params as $key => $value) {
$query .= "$key=$value&";
}
@ -277,8 +274,6 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
/**
* Correctly format a value returned by Last.fm.
*
* @param string|array $value
*/
protected function formatText(?string $value): string
{

View file

@ -22,7 +22,7 @@ class MediaCacheService
* Get media data.
* If caching is enabled, the data will be retrieved from the cache.
*
* @return mixed[]
* @return array<mixed>
*/
public function get(): array
{
@ -38,7 +38,7 @@ class MediaCacheService
/**
* Query fresh data from the database.
*
* @return mixed[]
* @return array<mixed>
*/
private function query(): array
{

View file

@ -19,7 +19,7 @@ class MediaInformationService
/**
* Get extra information about an album from Last.fm.
*
* @return array|null the album info in an array format, or null on failure
* @return array<mixed>|null the album info in an array format, or null on failure
*/
public function getAlbumInformation(Album $album): ?array
{
@ -43,7 +43,7 @@ class MediaInformationService
/**
* Get extra information about an artist from Last.fm.
*
* @return array|null the artist info in an array format, or null on failure
* @return array<mixed>|null the artist info in an array format, or null on failure
*/
public function getArtistInformation(Artist $artist): ?array
{

View file

@ -2,12 +2,13 @@
namespace App\Services;
use function App\Helpers\album_cover_path;
use function App\Helpers\artist_image_path;
use App\Models\Album;
use App\Models\Artist;
use Exception;
use Psr\Log\LoggerInterface;
use Throwable;
use function App\Helpers\album_cover_path;
use function App\Helpers\artist_image_path;
class MediaMetadataService
{
@ -52,7 +53,7 @@ class MediaMetadataService
$album->update(['cover' => basename($destination)]);
$this->createThumbnailForAlbum($album);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
}
}
@ -88,7 +89,7 @@ class MediaMetadataService
}
$artist->update(['image' => basename($destination)]);
} catch (Exception $e) {
} catch (Throwable $e) {
$this->logger->error($e);
}
}

View file

@ -23,8 +23,6 @@ class MediaSyncService
/**
* All applicable tags in a media file that we cater for.
* Note that each isn't necessarily a valid ID3 tag name.
*
* @var array
*/
public const APPLICABLE_TAGS = [
'artist',
@ -81,10 +79,10 @@ class MediaSyncService
/**
* Sync the media. Oh sync the media.
*
* @param string[] $tags The tags to sync.
* Only taken into account for existing records.
* New records will have all tags synced in regardless.
* @param bool $force Whether to force syncing even unchanged files
* @param array<string> $tags The tags to sync.
* Only taken into account for existing records.
* New records will have all tags synced in regardless.
* @param bool $force Whether to force syncing even unchanged files
* @param SyncCommand $syncCommand the SyncMedia command object, to log to console if executed by artisan
*
* @throws Exception
@ -117,9 +115,11 @@ class MediaSyncService
case FileSynchronizer::SYNC_RESULT_SUCCESS:
$results['success'][] = $path;
break;
case FileSynchronizer::SYNC_RESULT_UNMODIFIED:
$results['unmodified'][] = $path;
break;
default:
$results['bad_files'][] = $path;
break;
@ -147,7 +147,7 @@ class MediaSyncService
*
* @param string $path The directory's full path
*
* @return SplFileInfo[]
* @return array<SplFileInfo>
*/
public function gatherFiles(string $path): array
{
@ -162,40 +162,24 @@ class MediaSyncService
);
}
/**
* Sync media using a watch record.
*
* @throws Exception
*/
public function syncByWatchRecord(WatchRecordInterface $record): void
{
$this->logger->info("New watch record received: '{$record->getPath()}'");
$record->isFile() ? $this->syncFileRecord($record) : $this->syncDirectoryRecord($record);
}
/**
* Sync a file's watch record.
*
* @throws Exception
*/
private function syncFileRecord(WatchRecordInterface $record): void
{
$path = $record->getPath();
$this->logger->info("'$path' is a file.");
// If the file has been deleted...
if ($record->isDeleted()) {
$this->handleDeletedFileRecord($path);
}
// Otherwise, it's a new or changed file. Try to sync it in.
elseif ($record->isNewOrModified()) {
} elseif ($record->isNewOrModified()) {
$this->handleNewOrModifiedFileRecord($path);
}
}
/**
* Sync a directory's watch record.
*/
private function syncDirectoryRecord(WatchRecordInterface $record): void
{
$path = $record->getPath();
@ -213,7 +197,7 @@ class MediaSyncService
* If the input array is empty or contains only invalid items, we use all tags.
* Otherwise, we only use the valid items in it.
*
* @param string[] $tags
* @param array<string> $tags
*/
public function setTags(array $tags = []): void
{
@ -225,11 +209,6 @@ class MediaSyncService
}
}
/**
* Tidy up the library by deleting empty albums and artists.
*
* @throws Exception
*/
public function tidy(): void
{
$inUseAlbums = $this->albumRepository->getNonEmptyAlbumIds();
@ -249,19 +228,17 @@ class MediaSyncService
}
if (config('koel.memory_limit')) {
ini_set('memory_limit', config('koel.memory_limit').'M');
ini_set('memory_limit', config('koel.memory_limit') . 'M');
}
}
/**
* @throws Exception
*/
private function handleDeletedFileRecord(string $path): void
{
if ($song = $this->songRepository->getOneByPath($path)) {
$song = $this->songRepository->getOneByPath($path);
if ($song) {
$song->delete();
$this->logger->info("$path deleted.");
event(new LibraryChanged());
} else {
$this->logger->info("$path doesn't exist in our database--skipping.");
@ -283,7 +260,9 @@ class MediaSyncService
private function handleDeletedDirectoryRecord(string $path): void
{
if ($count = Song::inDirectory($path)->delete()) {
$count = Song::inDirectory($path)->delete();
if ($count) {
$this->logger->info("Deleted $count song(s) under $path");
event(new LibraryChanged());

View file

@ -28,9 +28,7 @@ class S3Service implements ObjectStorageInterface
// Here we specify that the request is valid for 1 hour.
// We'll also cache the public URL for future reuse.
$request = $this->s3Client->createPresignedRequest($cmd, '+1 hour');
$url = (string) $request->getUri();
return $url;
return (string) $request->getUri();
});
}
}

View file

@ -22,10 +22,11 @@ class SmartPlaylistService
$this->songRepository = $songRepository;
}
/** @return Collection|array<Song> */
public function getSongs(Playlist $playlist): Collection
{
if (!$playlist->is_smart) {
throw new RuntimeException($playlist->name.' is not a smart playlist.');
throw new RuntimeException($playlist->name . ' is not a smart playlist.');
}
$rules = $this->addRequiresUserRules($playlist->rules, $playlist->user);
@ -53,7 +54,7 @@ class SmartPlaylistService
* (basically everything related to interactions).
* For those, we create an additional "user_id" rule.
*
* @param array[] $rules
* @return array<mixed>
*/
public function addRequiresUserRules(array $rules, User $user): array
{
@ -75,10 +76,11 @@ class SmartPlaylistService
return $rules;
}
/** @return array<mixed> */
private function createRequireUserRule(User $user, string $modelPrefix): array
{
return [
'model' => $modelPrefix.'user_id',
'model' => $modelPrefix . 'user_id',
'operator' => 'is',
'value' => [$user->id],
];

View file

@ -3,7 +3,6 @@
namespace App\Services\Streamers;
use DaveRandom\Resume\FileResource;
use function DaveRandom\Resume\get_request_header;
use DaveRandom\Resume\InvalidRangeHeaderException;
use DaveRandom\Resume\NonExistentFileException;
use DaveRandom\Resume\RangeSet;
@ -13,9 +12,11 @@ use DaveRandom\Resume\UnreadableFileException;
use DaveRandom\Resume\UnsatisfiableRangeException;
use Symfony\Component\HttpFoundation\Response;
use function DaveRandom\Resume\get_request_header;
class PHPStreamer extends Streamer implements DirectStreamerInterface
{
public function stream()
public function stream(): void
{
try {
$rangeSet = RangeSet::createFromHeader(get_request_header('Range'));

View file

@ -3,6 +3,8 @@
namespace App\Services\Streamers;
use App\Services\S3Service;
use Illuminate\Http\RedirectResponse;
use Illuminate\Routing\Redirector;
class S3Streamer extends Streamer implements ObjectStorageStreamerInterface
{
@ -11,12 +13,15 @@ class S3Streamer extends Streamer implements ObjectStorageStreamerInterface
public function __construct(S3Service $s3Service)
{
parent::__construct();
$this->s3Service = $s3Service;
}
/**
* Stream the current song through S3.
* Actually, we just redirect the request to the S3 object's location.
*
* @return Redirector|RedirectResponse
*/
public function stream()
{

Some files were not shown because too many files have changed in this diff Show more