feat: integrate with Spotify

This commit is contained in:
Phan An 2022-07-17 00:42:29 +02:00
parent 1e38150f26
commit 878815659f
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
29 changed files with 262 additions and 258 deletions

View file

@ -1,16 +0,0 @@
<?php
namespace App\Events;
use App\Models\Album;
use App\Values\AlbumInformation;
use Illuminate\Queue\SerializesModels;
class AlbumInformationFetched extends Event
{
use SerializesModels;
public function __construct(public Album $album, public AlbumInformation $information)
{
}
}

View file

@ -1,16 +0,0 @@
<?php
namespace App\Events;
use App\Models\Artist;
use App\Values\ArtistInformation;
use Illuminate\Queue\SerializesModels;
class ArtistInformationFetched
{
use SerializesModels;
public function __construct(public Artist $artist, public ArtistInformation $information)
{
}
}

View file

@ -11,6 +11,7 @@ use App\Repositories\SongRepository;
use App\Services\ApplicationInformationService;
use App\Services\ITunesService;
use App\Services\LastfmService;
use App\Services\SpotifyService;
use App\Services\YouTubeService;
use Illuminate\Contracts\Auth\Authenticatable;
@ -36,7 +37,8 @@ class DataController extends Controller
'playlists' => $this->playlistRepository->getAllByCurrentUser(),
'current_user' => UserResource::make($this->user),
'use_last_fm' => $this->lastfmService->used(),
'use_you_tube' => $this->youTubeService->enabled(),
'use_spotify' => SpotifyService::enabled(),
'use_you_tube' => $this->youTubeService->enabled(), // @todo clean this mess up
'use_i_tunes' => $this->iTunesService->used(),
'allow_download' => config('koel.download.allow'),
'supports_transcoding' => config('koel.streaming.ffmpeg_path')

View file

@ -0,0 +1,11 @@
<?php
namespace App\Http\Requests;
/**
* @property-read string $state
* @property-read string $code
*/
class SpotifyCallbackRequest extends AbstractRequest
{
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Listeners;
use App\Events\AlbumInformationFetched;
use App\Services\MediaMetadataService;
use Throwable;
class DownloadAlbumCover
{
public function __construct(private MediaMetadataService $mediaMetadataService)
{
}
public function handle(AlbumInformationFetched $event): void
{
// If our current album has no cover, and Last.fm has one, steal it?
if (!$event->album->has_cover && $event->information->cover && ini_get('allow_url_fopen')) {
try {
$this->mediaMetadataService->downloadAlbumCover($event->album, $event->information->cover);
} catch (Throwable) {
}
}
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Listeners;
use App\Events\ArtistInformationFetched;
use App\Services\MediaMetadataService;
use Throwable;
class DownloadArtistImage
{
public function __construct(private MediaMetadataService $mediaMetadataService)
{
}
public function handle(ArtistInformationFetched $event): void
{
// If our artist has no image, and Last.fm has one, we steal it?
if (!$event->artist->has_image && $event->information->image && ini_get('allow_url_fopen')) {
try {
$this->mediaMetadataService->downloadArtistImage($event->artist, $event->information->image);
} catch (Throwable) {
}
}
}
}

View file

@ -2,8 +2,6 @@
namespace App\Providers;
use App\Events\AlbumInformationFetched;
use App\Events\ArtistInformationFetched;
use App\Events\LibraryChanged;
use App\Events\MediaSyncCompleted;
use App\Events\SongLikeToggled;
@ -12,8 +10,6 @@ use App\Events\SongsBatchUnliked;
use App\Events\SongStartedPlaying;
use App\Listeners\ClearMediaCache;
use App\Listeners\DeleteNonExistingRecordsPostSync;
use App\Listeners\DownloadAlbumCover;
use App\Listeners\DownloadArtistImage;
use App\Listeners\LoveMultipleTracksOnLastfm;
use App\Listeners\LoveTrackOnLastfm;
use App\Listeners\PruneLibrary;
@ -49,14 +45,6 @@ class EventServiceProvider extends ServiceProvider
ClearMediaCache::class,
],
AlbumInformationFetched::class => [
DownloadAlbumCover::class,
],
ArtistInformationFetched::class => [
DownloadArtistImage::class,
],
MediaSyncCompleted::class => [
DeleteNonExistingRecordsPostSync::class,
],

View file

@ -13,7 +13,7 @@ use SimpleXMLElement;
use Webmozart\Assert\Assert;
/**
* @method object get(string $uri, array $data = [], bool $appendKey = true)
* @method object|null get(string $uri, array $data = [], bool $appendKey = true)
* @method object post($uri, array $data = [], bool $appendKey = true)
* @method object put($uri, array $data = [], bool $appendKey = true)
* @method object patch($uri, array $data = [], bool $appendKey = true)

View file

@ -128,7 +128,7 @@ class FileSynchronizer
if ($cover) {
$extension = pathinfo($cover, PATHINFO_EXTENSION);
$this->mediaMetadataService->writeAlbumCover($album, file_get_contents($cover), $extension);
$this->mediaMetadataService->writeAlbumCover($album, $cover, $extension);
}
} catch (Throwable) {
}

View file

@ -17,10 +17,10 @@ class ImageWriter
$this->imageManager = $imageManager;
}
public function writeFromBinaryData(string $destination, string $data, array $config = []): void
public function write(string $destination, object|string $source, array $config = []): void
{
$img = $this->imageManager
->make($data)
->make($source)
->resize(
$config['max_width'] ?? self::DEFAULT_MAX_WIDTH,
null,

View file

@ -2,6 +2,8 @@
namespace App\Services;
use App\Models\Album;
use App\Models\Artist;
use App\Models\User;
use App\Values\AlbumInformation;
use App\Values\ArtistInformation;
@ -34,13 +36,13 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
return $this->getKey() && $this->getSecret();
}
public function getArtistInformation(string $name): ?ArtistInformation
public function getArtistInformation(Artist $artist): ?ArtistInformation
{
if (!$this->enabled()) {
return null;
}
$name = urlencode($name);
$name = urlencode($artist->name);
try {
return $this->cache->remember(
@ -59,14 +61,14 @@ class LastfmService extends AbstractApiClient implements ApiConsumerInterface
}
}
public function getAlbumInformation(string $albumName, string $artistName): ?AlbumInformation
public function getAlbumInformation(Album $album): ?AlbumInformation
{
if (!$this->enabled()) {
return null;
}
$albumName = urlencode($albumName);
$artistName = urlencode($artistName);
$albumName = urlencode($album->name);
$artistName = urlencode($album->artist->name);
try {
$cacheKey = md5("lastfm_album_{$albumName}_{$artistName}");

View file

@ -2,21 +2,18 @@
namespace App\Services;
use App\Events\AlbumInformationFetched;
use App\Events\ArtistInformationFetched;
use App\Models\Album;
use App\Models\Artist;
use App\Repositories\AlbumRepository;
use App\Repositories\ArtistRepository;
use App\Values\AlbumInformation;
use App\Values\ArtistInformation;
use Throwable;
class MediaInformationService
{
public function __construct(
private LastfmService $lastfmService,
private AlbumRepository $albumRepository,
private ArtistRepository $artistRepository
private SpotifyService $spotifyService,
private MediaMetadataService $mediaMetadataService
) {
}
@ -26,12 +23,18 @@ class MediaInformationService
return null;
}
$info = $this->lastfmService->getAlbumInformation($album->name, $album->artist->name);
$info = $this->lastfmService->getAlbumInformation($album) ?: new AlbumInformation();
if ($info) {
event(new AlbumInformationFetched($album, $info));
// The album cover may have been updated.
$info->cover = $this->albumRepository->getOneById($album->id)->cover;
if (!$album->has_cover) {
try {
$cover = $this->spotifyService->tryGetAlbumCover($album);
if ($cover) {
$this->mediaMetadataService->downloadAlbumCover($album, $cover);
$info->cover = $album->refresh()->cover;
}
} catch (Throwable) {
}
}
return $info;
@ -43,12 +46,18 @@ class MediaInformationService
return null;
}
$info = $this->lastfmService->getArtistInformation($artist->name);
$info = $this->lastfmService->getArtistInformation($artist) ?: new ArtistInformation();
if ($info) {
event(new ArtistInformationFetched($artist, $info));
// The artist image may have been updated.
$info->image = $this->artistRepository->getOneById($artist->id)->image;
if (!$artist->has_image) {
try {
$image = $this->spotifyService->tryGetArtistImage($artist);
if ($image) {
$this->mediaMetadataService->downloadArtistImage($artist, $image);
$info->image = $artist->refresh()->image;
}
} catch (Throwable) {
}
}
return $info;

View file

@ -20,26 +20,26 @@ class MediaMetadataService
public function downloadAlbumCover(Album $album, string $imageUrl): void
{
$extension = explode('.', $imageUrl);
$this->writeAlbumCover($album, file_get_contents($imageUrl), last($extension));
$this->writeAlbumCover($album, $imageUrl, 'png');
}
/**
* Write an album cover image file with binary data and update the Album with the new cover attribute.
*
* @param string $source Path, URL, or even binary data. See https://image.intervention.io/v2/api/make.
* @param string $destination The destination path. Automatically generated if empty.
*/
public function writeAlbumCover(
Album $album,
string $binaryData,
string $source,
string $extension,
string $destination = '',
bool $cleanUp = true
): void {
try {
$extension = trim(strtolower($extension), '. ');
$destination = $destination ?: $this->generateAlbumCoverPath($extension);
$this->imageWriter->writeFromBinaryData($destination, $binaryData);
$destination = $destination ?: $this->generateAlbumCoverPath($album, $extension);
$this->imageWriter->write($destination, $source);
if ($cleanUp) {
$this->deleteAlbumCoverFiles($album);
@ -54,26 +54,26 @@ class MediaMetadataService
public function downloadArtistImage(Artist $artist, string $imageUrl): void
{
$extension = explode('.', $imageUrl);
$this->writeArtistImage($artist, file_get_contents($imageUrl), last($extension));
$this->writeArtistImage($artist, $imageUrl, '.png');
}
/**
* Write an artist image file with binary data and update the Artist with the new image attribute.
*
* @param string $source Path, URL, or even binary data. See https://image.intervention.io/v2/api/make.
* @param string $destination The destination path. Automatically generated if empty.
*/
public function writeArtistImage(
Artist $artist,
string $binaryData,
string $source,
string $extension,
string $destination = '',
bool $cleanUp = true
): void {
try {
$extension = trim(strtolower($extension), '. ');
$destination = $destination ?: $this->generateArtistImagePath($extension);
$this->imageWriter->writeFromBinaryData($destination, $binaryData);
$destination = $destination ?: $this->generateArtistImagePath($artist, $extension);
$this->imageWriter->write($destination, $source);
if ($cleanUp && $artist->has_image) {
@unlink($artist->image_path);
@ -85,24 +85,14 @@ class MediaMetadataService
}
}
/**
* Generate the absolute path for an album cover image.
*
* @param string $extension The extension of the cover (without dot)
*/
private function generateAlbumCoverPath(string $extension): string
private function generateAlbumCoverPath(Album $album, string $extension): string
{
return album_cover_path(sprintf('%s.%s', sha1(uniqid()), $extension));
return album_cover_path(sprintf('%s.%s', sha1((string) $album->id), trim($extension, '.')));
}
/**
* Generate the absolute path for an artist image.
*
* @param string $extension The extension of the cover (without dot)
*/
private function generateArtistImagePath($extension): string
private function generateArtistImagePath(Artist $artist, string $extension): string
{
return artist_image_path(sprintf('%s.%s', sha1(uniqid()), $extension));
return artist_image_path(sprintf('%s.%s', sha1((string) $artist->id), trim($extension, '.')));
}
/**
@ -124,11 +114,7 @@ class MediaMetadataService
private function createThumbnailForAlbum(Album $album): void
{
$this->imageWriter->writeFromBinaryData(
$album->thumbnail_path,
file_get_contents($album->cover_path),
['max_width' => 48, 'blur' => 10]
);
$this->imageWriter->write($album->thumbnail_path, $album->cover_path, ['max_width' => 48, 'blur' => 10]);
}
private function deleteAlbumCoverFiles(Album $album): void

View file

@ -0,0 +1,78 @@
<?php
namespace App\Services;
use App\Models\Album;
use App\Models\Artist;
use Carbon\Carbon;
use Illuminate\Cache\Repository as Cache;
use Illuminate\Support\Arr;
use LogicException;
use SpotifyWebAPI\Session as SpotifySession;
use SpotifyWebAPI\SpotifyWebAPI;
class SpotifyService
{
private ?SpotifySession $session;
public function __construct(private SpotifyWebAPI $client, private Cache $cache)
{
if (static::enabled()) {
$this->session = new SpotifySession(config('koel.spotify.client_id'), config('koel.spotify.client_secret'));
$this->client->setOptions(['return_assoc' => true]);
$this->client->setAccessToken($this->getAccessToken());
}
}
public static function enabled(): bool
{
return config('koel.spotify.client_id') && config('koel.spotify.client_secret');
}
private function getAccessToken(): string
{
if (!$this->session) {
throw new LogicException();
}
if (!$this->cache->has('spotify.access_token')) {
$this->session->requestCredentialsToken();
$token = $this->session->getAccessToken();
$this->cache->put('spotify.access_token', $token, Carbon::now()->addMinutes(59));
}
return $this->cache->get('spotify.access_token');
}
public function tryGetArtistImage(Artist $artist): ?string
{
if (!static::enabled()) {
return null;
}
return Arr::get(
$this->client->search($artist->name, 'artist', ['limit' => 1]),
'artists.items.0.images.0.url'
);
}
public function tryGetAlbumCover(Album $album): ?string
{
if (!static::enabled()) {
return null;
}
if ($album->is_unknown || $album->artist->is_unknown || $album->artist->is_various) {
return null;
}
if ($album->name === Album::UNKNOWN_NAME) {
return null;
}
return Arr::get(
$this->client->search("{$album->artist->name} {$album->name}", 'album', ['limit' => 1]),
'albums.items.0.images.0.url'
);
}
}

View file

@ -8,11 +8,11 @@ final class AlbumInformation implements Arrayable
{
use FormatsLastFmText;
private function __construct(
public ?string $url,
public ?string $cover,
public array $wiki,
public array $tracks
public function __construct(
public ?string $url = null,
public ?string $cover = null,
public array $wiki = ['summary' => '', 'full' => ''],
public array $tracks = []
) {
}
@ -20,7 +20,6 @@ final class AlbumInformation implements Arrayable
{
return new self(
url: $data->url,
cover: count($data->image) > 3 ? $data->image[3]->{'#text'} : $data->image[0]->{'#text'},
wiki: [
'summary' => isset($data->wiki) ? self::formatLastFmText($data->wiki->summary) : '',
'full' => isset($data->wiki) ? self::formatLastFmText($data->wiki->content) : '',

View file

@ -8,15 +8,17 @@ final class ArtistInformation implements Arrayable
{
use FormatsLastFmText;
private function __construct(public ?string $url, public ?string $image, public array $bio)
{
public function __construct(
public ?string $url = null,
public ?string $image = null,
public array $bio = ['summary' => '', 'full' => '']
) {
}
public static function fromLastFmData(object $data): self
{
return new self(
url: $data->url,
image: count($data->image) > 3 ? $data->image[3]->{'#text'} : $data->image[0]->{'#text'},
bio: [
'summary' => isset($data->bio) ? self::formatLastFmText($data->bio->summary) : '',
'full' => isset($data->bio) ? self::formatLastFmText($data->bio->content) : '',

View file

@ -32,7 +32,8 @@
"webmozart/assert": "^1.10",
"laravel/sanctum": "^2.15",
"laravel/scout": "^9.4",
"nunomaduro/collision": "^6.2"
"nunomaduro/collision": "^6.2",
"jwilsson/spotify-web-api-php": "^5.2"
},
"require-dev": {
"mockery/mockery": "~1.0",

52
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1c1082c46c0ad0c38cbfba8ac6e81733",
"content-hash": "0c30334f89353cc94db1ff66167be897",
"packages": [
{
"name": "algolia/algoliasearch-client-php",
@ -1901,6 +1901,56 @@
},
"time": "2021-09-22T16:34:51+00:00"
},
{
"name": "jwilsson/spotify-web-api-php",
"version": "5.2.0",
"source": {
"type": "git",
"url": "https://github.com/jwilsson/spotify-web-api-php.git",
"reference": "0d6dc349669c3cf50cf39fe3c226ca438eec0489"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/jwilsson/spotify-web-api-php/zipball/0d6dc349669c3cf50cf39fe3c226ca438eec0489",
"reference": "0d6dc349669c3cf50cf39fe3c226ca438eec0489",
"shasum": ""
},
"require": {
"ext-curl": "*",
"php": "^7.3 || ^8.0"
},
"require-dev": {
"php-coveralls/php-coveralls": "^2.5",
"phpunit/phpunit": "^9.4",
"squizlabs/php_codesniffer": "^3.0"
},
"type": "library",
"autoload": {
"psr-4": {
"SpotifyWebAPI\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan Wilsson",
"email": "jonathan.wilsson@gmail.com"
}
],
"description": "A PHP wrapper for Spotify's Web API.",
"homepage": "https://github.com/jwilsson/spotify-web-api-php",
"keywords": [
"spotify"
],
"support": {
"issues": "https://github.com/jwilsson/spotify-web-api-php/issues",
"source": "https://github.com/jwilsson/spotify-web-api-php/tree/5.2.0"
},
"time": "2022-07-16T07:32:37+00:00"
},
{
"name": "laravel/framework",
"version": "v9.19.0",

View file

@ -68,6 +68,21 @@ return [
'endpoint' => 'https://ws.audioscrobbler.com/2.0',
],
/*
|--------------------------------------------------------------------------
| Last.FM Integration
|--------------------------------------------------------------------------
|
| See wiki on how to integrate with Last.FM
|
*/
'spotify' => [
'client_id' => env('SPOTIFY_CLIENT_ID'),
'client_secret' => env('SPOTIFY_CLIENT_SECRET'),
],
/*
|--------------------------------------------------------------------------
| CDN

View file

@ -17,9 +17,7 @@
href="https://www.last.fm/about/trackmymusic"
rel="noopener"
target="_blank"
>
scrobbling
</a>.
>scrobbling</a>.
</p>
<div class="buttons">
<Btn class="connect" @click.prevent="connect">

View file

@ -1,4 +1,5 @@
import { Cache, httpService } from '@/services'
import { albumStore, artistStore, songStore } from '@/stores'
export const mediaInfoService = {
async fetchForArtist (artist: Artist) {
@ -8,6 +9,10 @@ export const mediaInfoService = {
const info = await httpService.get<ArtistInfo | null>(`artists/${artist.id}/information`)
info && Cache.set(cacheKey, info)
if (info?.image) {
artistStore.byId(artist.id)!.image = info.image
}
return info
},
@ -18,6 +23,11 @@ export const mediaInfoService = {
const info = await httpService.get<AlbumInfo | null>(`albums/${album.id}/information`)
info && Cache.set(cacheKey, info)
if (info?.cover) {
albumStore.byId(album.id)!.cover = info.cover
songStore.byAlbum(album)!.forEach(song => (song.album_cover = info.cover))
}
return info
}
}

View file

@ -13,6 +13,7 @@ interface CommonStoreState {
settings: Settings
use_i_tunes: boolean
use_last_fm: boolean
use_spotify: boolean
users: User[]
use_you_tube: boolean,
song_count: number,
@ -30,6 +31,7 @@ export const commonStore = {
settings: {} as Settings,
use_i_tunes: false,
use_last_fm: false,
use_spotify: false,
users: [],
use_you_tube: false,
song_count: 0,

View file

@ -123,7 +123,7 @@ interface AlbumTrack {
}
interface AlbumInfo {
image: string | null
cover: string | null
readonly tracks: AlbumTrack[]
wiki?: {
summary: string

View file

@ -3,20 +3,28 @@
<head>
<title>Authentication successful!</title>
<meta charset="utf-8">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
</style>
</head>
<body>
<h3>Perfecto!</h3>
<h3>Perfecto!</h3>
<p>Koel has successfully connected to your Last.fm account and is now restarting for the exciting features.</p>
<p>This window will automatically close in 3 seconds.</p>
<p>Koel has successfully connected to your Last.fm account and is now restarting for the exciting features.</p>
<p>This window will automatically close in 3 seconds.</p>
<script>
window.opener.onbeforeunload = function () {};
window.opener.location.reload(false);
<script>
window.opener.onbeforeunload = function () {
}
window.opener.location.reload(false)
window.setTimeout(function () {
window.close()
}, 3000)
</script>
window.setTimeout(function () {
window.close();
}, 3000);
</script>
</body>
</html>

View file

@ -37,7 +37,6 @@ Route::prefix('api')->middleware('api')->group(static function (): void {
Route::get('ping', static fn () => null);
Route::middleware('auth')->group(static function (): void {
Route::post('broadcasting/auth', static function (Request $request) {
$pusher = new Pusher(
config('broadcasting.connections.pusher.key'),
@ -108,7 +107,7 @@ Route::prefix('api')->middleware('api')->group(static function (): void {
});
});
// Object-storage (S3) routes
// Object-storage (S3) routes
Route::middleware('os.auth')->prefix('os/s3')->group(static function (): void {
Route::post('song', [S3SongController::class, 'put'])->name('s3.song.put'); // we follow AWS's convention here.
Route::delete('song', [S3SongController::class, 'remove'])->name('s3.song.remove'); // and here.

View file

@ -1,38 +0,0 @@
<?php
namespace Tests\Integration\Listeners;
use App\Events\AlbumInformationFetched;
use App\Models\Album;
use App\Services\MediaMetadataService;
use Mockery\MockInterface;
use phpmock\mockery\PHPMockery;
use Tests\TestCase;
class DownloadAlbumCoverTest extends TestCase
{
/** @var MediaMetadataService|MockInterface */
private $mediaMetaDataService;
public function setUp(): void
{
parent::setUp();
$this->mediaMetaDataService = self::mock(MediaMetadataService::class);
PHPMockery::mock('App\Listeners', 'ini_get')->andReturn(true);
}
public function testHandle(): void
{
/** @var Album $album */
$album = Album::factory()->make(['cover' => null]);
$event = new AlbumInformationFetched($album, ['image' => 'https://foo.bar/baz.jpg']);
$this->mediaMetaDataService
->shouldReceive('downloadAlbumCover')
->once()
->with($album, 'https://foo.bar/baz.jpg');
event($event);
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace Tests\Integration\Listeners;
use App\Events\ArtistInformationFetched;
use App\Models\Artist;
use App\Services\MediaMetadataService;
use Mockery\MockInterface;
use phpmock\mockery\PHPMockery;
use Tests\TestCase;
class DownloadArtistImageTest extends TestCase
{
/** @var MediaMetadataService|MockInterface */
private $mediaMetaDataService;
public function setUp(): void
{
parent::setUp();
$this->mediaMetaDataService = self::mock(MediaMetadataService::class);
PHPMockery::mock('App\Listeners', 'ini_get')->andReturn(true);
}
public function testHandle(): void
{
/** @var Artist $artist */
$artist = Artist::factory()->make(['image' => null]);
$event = new ArtistInformationFetched($artist, ['image' => 'https://foo.bar/baz.jpg']);
$this->mediaMetaDataService
->shouldReceive('downloadArtistImage')
->once()
->with($artist, 'https://foo.bar/baz.jpg');
event($event);
}
}

View file

@ -25,7 +25,7 @@ class LastfmServiceTest extends TestCase
]);
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
$info = $api->getArtistInformation($artist->name);
$info = $api->getArtistInformation($artist);
self::assertEquals([
'url' => 'https://www.last.fm/music/Kamelot',
@ -51,7 +51,7 @@ class LastfmServiceTest extends TestCase
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
self::assertNull($api->getArtistInformation($artist->name));
self::assertNull($api->getArtistInformation($artist));
}
public function testGetAlbumInformation(): void
@ -71,7 +71,7 @@ class LastfmServiceTest extends TestCase
]);
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
$info = $api->getAlbumInformation($album->name, $album->artist->name);
$info = $api->getAlbumInformation($album);
self::assertEquals([
'url' => 'https://www.last.fm/music/Kamelot/Epica',
@ -109,6 +109,6 @@ class LastfmServiceTest extends TestCase
$api = new LastfmService($client, app(Cache::class), app(Logger::class));
self::assertNull($api->getAlbumInformation($album->name, $album->artist->name));
self::assertNull($api->getAlbumInformation($album));
}
}

View file

@ -8,12 +8,14 @@ use App\Services\ImageWriter;
use App\Services\MediaMetadataService;
use Illuminate\Log\Logger;
use Mockery;
use Mockery\LegacyMockInterface;
use Mockery\MockInterface;
use Tests\TestCase;
class MediaMetadataServiceTest extends TestCase
{
private $mediaMetadataService;
private $imageWriter;
private MediaMetadataService $mediaMetadataService;
private LegacyMockInterface|ImageWriter|MockInterface $imageWriter;
public function setUp(): void
{