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;

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\Database\DatabaseManager as DB;
use Jackiedo\DotenvEditor\DotenvEditor;
use Throwable;
class InitCommand extends Command
{
@ -64,7 +64,7 @@ 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('😥 Sorry for this. You deserve better.');
@ -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,7 +224,7 @@ 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->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;

View file

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

View file

@ -6,7 +6,7 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected function commands()
protected function commands(): void
{
$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

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

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 */

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

@ -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,6 +10,9 @@ 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|null $cover_path The absolute path to the cover file
@ -24,7 +25,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @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

@ -18,7 +18,7 @@ use Illuminate\Support\Collection;
* @property string $title
* @property Album $album
* @property Artist $artist
* @property string[] $s3_params
* @property array<string> $s3_params
* @property float $length
* @property string $lyrics
* @property int $track
@ -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:
* @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;
@ -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);
}

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);
}

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,7 +141,7 @@ 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 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);
}
@ -272,7 +270,7 @@ class FileSynchronizer
{
try {
return (bool) exif_imagetype($path);
} catch (Exception $e) {
} catch (Throwable $e) {
return false;
}
}

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.
@ -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;
@ -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);
}
}
@ -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);
}
}
@ -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);
}
}
@ -245,9 +241,9 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
* @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);
@ -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,7 +79,7 @@ class MediaSyncService
/**
* Sync the media. Oh sync the media.
*
* @param string[] $tags The tags to sync.
* @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
@ -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();
@ -253,15 +232,13 @@ class MediaSyncService
}
}
/**
* @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,6 +22,7 @@ class SmartPlaylistService
$this->songRepository = $songRepository;
}
/** @return Collection|array<Song> */
public function getSongs(Playlist $playlist): Collection
{
if (!$playlist->is_smart) {
@ -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,6 +76,7 @@ class SmartPlaylistService
return $rules;
}
/** @return array<mixed> */
private function createRequireUserRule(User $user, string $modelPrefix): array
{
return [

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()
{

View file

@ -6,14 +6,10 @@ use App\Models\Song;
class Streamer
{
/**
* @var Song|string
*/
/** @var Song|string */
protected $song;
/**
* @var string
*/
/** @var string */
protected $contentType;
public function __construct()

View file

@ -8,5 +8,6 @@ interface StreamerInterface
{
public function setSong(Song $song): void;
/** @return mixed */
public function stream();
}

View file

@ -6,9 +6,10 @@ use App\Exceptions\MediaPathNotSetException;
use App\Exceptions\SongUploadFailedException;
use App\Models\Setting;
use App\Models\Song;
use function Functional\memoize;
use Illuminate\Http\UploadedFile;
use function Functional\memoize;
class UploadService
{
private const UPLOAD_DIRECTORY = '__KOEL_UPLOADS__';

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