2022-06-10 10:47:46 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
2022-09-15 09:07:25 +00:00
|
|
|
use App\Events\LibraryChanged;
|
2022-06-10 10:47:46 +00:00
|
|
|
use App\Models\Album;
|
|
|
|
use App\Models\Artist;
|
|
|
|
use App\Models\Song;
|
|
|
|
use App\Repositories\SongRepository;
|
|
|
|
use App\Values\SongUpdateData;
|
2022-09-15 09:07:25 +00:00
|
|
|
use Illuminate\Support\Arr;
|
2022-06-10 10:47:46 +00:00
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
use Illuminate\Support\Facades\DB;
|
2022-09-15 09:07:25 +00:00
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
use Throwable;
|
2022-06-10 10:47:46 +00:00
|
|
|
|
|
|
|
class SongService
|
|
|
|
{
|
2022-09-15 09:07:25 +00:00
|
|
|
public function __construct(private SongRepository $songRepository, private LoggerInterface $logger)
|
2022-06-10 10:47:46 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @return Collection|array<array-key, Song> */
|
|
|
|
public function updateSongs(array $songIds, SongUpdateData $data): Collection
|
|
|
|
{
|
|
|
|
$updatedSongs = collect();
|
|
|
|
|
|
|
|
DB::transaction(function () use ($songIds, $data, $updatedSongs): void {
|
|
|
|
foreach ($songIds as $id) {
|
|
|
|
/** @var Song|null $song */
|
|
|
|
$song = Song::with('album', 'album.artist', 'artist')->find($id);
|
|
|
|
|
2022-07-29 06:47:10 +00:00
|
|
|
if ($song) {
|
|
|
|
$updatedSongs->push($this->updateSong($song, $data));
|
2022-06-10 10:47:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return $updatedSongs;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function updateSong(Song $song, SongUpdateData $data): Song
|
|
|
|
{
|
2022-07-06 13:08:40 +00:00
|
|
|
$maybeSetAlbumArtist = static function (Album $album) use ($data): void {
|
2022-10-10 08:29:24 +00:00
|
|
|
if ($data->albumArtistName) {
|
2022-07-06 13:08:40 +00:00
|
|
|
$album->artist_id = Artist::getOrCreate($data->albumArtistName)->id;
|
2022-07-06 16:08:55 +00:00
|
|
|
$album->save();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$maybeSetAlbum = static function () use ($data, $song, $maybeSetAlbumArtist): void {
|
|
|
|
if ($data->albumName) {
|
2022-10-10 08:29:24 +00:00
|
|
|
$album = Album::getOrCreate($song->artist, $data->albumName);
|
|
|
|
$song->album_id = $album->id;
|
2022-07-06 16:08:55 +00:00
|
|
|
|
2022-10-10 08:29:24 +00:00
|
|
|
$maybeSetAlbumArtist($album);
|
2022-07-06 13:08:40 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-10 08:29:24 +00:00
|
|
|
// if album artist name is provided, get/create an album with that artist and assign it to the song
|
|
|
|
if ($data->albumArtistName) {
|
|
|
|
$album = Album::getOrCreate(Artist::getOrCreate($data->albumArtistName), $data->albumName);
|
|
|
|
$song->album_id = $album->id;
|
|
|
|
} else {
|
|
|
|
$maybeSetAlbum();
|
|
|
|
}
|
2022-06-10 10:47:46 +00:00
|
|
|
|
2022-10-10 08:29:24 +00:00
|
|
|
if ($data->artistName) {
|
|
|
|
$artist = Artist::getOrCreate($data->artistName);
|
|
|
|
$song->artist_id = $artist->id;
|
2022-07-06 16:08:55 +00:00
|
|
|
} else {
|
|
|
|
$maybeSetAlbum();
|
2022-06-10 10:47:46 +00:00
|
|
|
}
|
|
|
|
|
2022-09-23 06:21:29 +00:00
|
|
|
// For string attributes like title, lyrics, and genre, we use "??" because empty strings still have effects
|
|
|
|
$song->title = $data->title ?? $song->title;
|
|
|
|
$song->lyrics = $data->lyrics ?? $song->lyrics;
|
|
|
|
$song->genre = $data->genre ?? $song->genre;
|
|
|
|
|
2022-06-10 10:47:46 +00:00
|
|
|
$song->track = $data->track ?: $song->track;
|
2022-09-23 06:21:29 +00:00
|
|
|
$song->year = $data->year ?: $song->year;
|
2022-06-10 10:47:46 +00:00
|
|
|
$song->disc = $data->disc ?: $song->disc;
|
|
|
|
|
2022-07-06 13:08:40 +00:00
|
|
|
$song->push();
|
2022-06-10 10:47:46 +00:00
|
|
|
|
|
|
|
return $this->songRepository->getOne($song->id);
|
|
|
|
}
|
2022-09-15 09:07:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<string>|string $ids
|
|
|
|
*/
|
|
|
|
public function deleteSongs(array|string $ids): void
|
|
|
|
{
|
|
|
|
$ids = Arr::wrap($ids);
|
|
|
|
|
|
|
|
DB::transaction(function () use ($ids): void {
|
|
|
|
$shouldBackUp = config('koel.backup_on_delete');
|
|
|
|
|
|
|
|
/** @var Collection|array<array-key, Song> $songs */
|
|
|
|
$songs = Song::query()->findMany($ids);
|
|
|
|
|
|
|
|
Song::destroy($ids);
|
|
|
|
|
|
|
|
$songs->each(function (Song $song) use ($shouldBackUp): void {
|
|
|
|
try {
|
|
|
|
if ($shouldBackUp) {
|
|
|
|
rename($song->path, $song->path . '.bak');
|
|
|
|
} else {
|
|
|
|
unlink($song->path);
|
|
|
|
}
|
|
|
|
} catch (Throwable $e) {
|
|
|
|
$this->logger->error('Failed to remove song file', [
|
|
|
|
'path' => $song->path,
|
|
|
|
'exception' => $e,
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
event(new LibraryChanged());
|
|
|
|
});
|
|
|
|
}
|
2022-06-10 10:47:46 +00:00
|
|
|
}
|