From 8495452762a9fbd6b608a8041e42607027f4e30d Mon Sep 17 00:00:00 2001 From: An Phan Date: Mon, 21 Dec 2015 21:49:00 +0800 Subject: [PATCH] Like/unlike now work with Last.fm --- app/Events/SongLikeToggled.php | 52 ++++++++++++++++++++++++++ app/Listeners/LoveTrackOnLastfm.php | 48 ++++++++++++++++++++++++ app/Models/Artist.php | 5 +++ app/Models/Interaction.php | 15 ++++++-- app/Models/Song.php | 6 +-- app/Models/User.php | 20 ++++++++++ app/Providers/EventServiceProvider.php | 4 +- app/Services/Lastfm.php | 29 ++++++++++++-- tests/InteractionTest.php | 5 +++ tests/LastfmTest.php | 28 +++++++++++++- 10 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 app/Events/SongLikeToggled.php create mode 100644 app/Listeners/LoveTrackOnLastfm.php diff --git a/app/Events/SongLikeToggled.php b/app/Events/SongLikeToggled.php new file mode 100644 index 00000000..168ec81b --- /dev/null +++ b/app/Events/SongLikeToggled.php @@ -0,0 +1,52 @@ +interaction = $interaction; + $this->user = $user ?: auth()->user(); + } + + /** + * Get the channels the event should be broadcast on. + * + * @return array + */ + public function broadcastOn() + { + return []; + } +} diff --git a/app/Listeners/LoveTrackOnLastfm.php b/app/Listeners/LoveTrackOnLastfm.php new file mode 100644 index 00000000..8df6f754 --- /dev/null +++ b/app/Listeners/LoveTrackOnLastfm.php @@ -0,0 +1,48 @@ +lastfm = $lastfm; + } + + /** + * Handle the event. + * + * @param SongLikeToggled $event + */ + public function handle(SongLikeToggled $event) + { + if (!$this->lastfm->enabled() || + !($sessionKey = $event->user->getLastfmSessionKey()) || + $event->interaction->song->album->artist->isUnknown() + ) { + return; + } + + $this->lastfm->toggleLoveTrack( + $event->interaction->song->title, + $event->interaction->song->album->artist->name, + $sessionKey, + $event->interaction->liked + ); + } +} diff --git a/app/Models/Artist.php b/app/Models/Artist.php index 0657f97f..4ce94e33 100644 --- a/app/Models/Artist.php +++ b/app/Models/Artist.php @@ -24,6 +24,11 @@ class Artist extends Model return $this->hasMany(Album::class); } + public function isUnknown() + { + return $this->id === self::UNKNOWN_ID; + } + /** * Sometimes the tags extracted from getID3 are HTML entity encoded. * This makes sure they are always sane. diff --git a/app/Models/Interaction.php b/app/Models/Interaction.php index 7e7ea793..fdb65c95 100644 --- a/app/Models/Interaction.php +++ b/app/Models/Interaction.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Events\SongLikeToggled; use App\Traits\CanFilterByUser; use DB; use Illuminate\Database\Eloquent\Model; @@ -80,6 +81,8 @@ class Interaction extends Model $interaction->liked = !$interaction->liked; $interaction->save(); + event(new SongLikeToggled($interaction)); + return $interaction; } @@ -108,6 +111,8 @@ class Interaction extends Model $interaction->liked = true; $interaction->save(); + event(new SongLikeToggled($interaction)); + $result[] = $interaction; } @@ -124,9 +129,11 @@ class Interaction extends Model */ public static function batchUnlike(array $songIds, User $user) { - return DB::table('interactions') - ->whereIn('song_id', $songIds) - ->whereUserId($user->id) - ->update(['liked' => false]); + foreach(self::whereIn('song_id', $songIds)->whereUserId($user->id)->get() as $interaction) { + $interaction->liked = false; + $interaction->save(); + + event(new SongLikeToggled($interaction)); + } } } diff --git a/app/Models/Song.php b/app/Models/Song.php index c8b47e54..2f4eedcb 100644 --- a/app/Models/Song.php +++ b/app/Models/Song.php @@ -48,14 +48,12 @@ class Song extends Model public function scrobble($timestamp) { // Don't scrobble the unknown guys. No one knows them. - if ($this->album->artist->id === Artist::UNKNOWN_ID) { + if ($this->album->artist->isUnknown()) { return false; } - auth()->user()->setHidden([]); - // If the current user hasn't connected to Last.fm, don't do shit. - if (!$sessionKey = auth()->user()->getPreference('lastfm_session_key')) { + if (!$sessionKey = auth()->user()->getLastfmSessionKey()) { return false; } diff --git a/app/Models/User.php b/app/Models/User.php index 95288f00..64ae8711 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -112,6 +112,26 @@ AuthenticatableContract, $this->update(compact('preferences')); } + /** + * Determine if the user is connected to Last.fm. + * + * @return bool + */ + public function connectedToLastfm() + { + return !!$this->getLastfmSessionKey(); + } + + /** + * Get the user's Last.fm session key. + * + * @return string|null The key if found, or null if user isn't connected to Last.fm + */ + public function getLastfmSessionKey() + { + return $this->getPreference('lastfm_session_key'); + } + /** * User preferences are stored as a serialized associative array. * diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 0e94ad23..1941ebd4 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -16,8 +16,8 @@ class EventServiceProvider extends ServiceProvider * @var array */ protected $listen = [ - 'App\Events\SomeEvent' => [ - 'App\Listeners\EventListener', + 'App\Events\SongLikeToggled' => [ + 'App\Listeners\LoveTrackOnLastfm', ], ]; diff --git a/app/Services/Lastfm.php b/app/Services/Lastfm.php index 6b330e66..df9d2c95 100644 --- a/app/Services/Lastfm.php +++ b/app/Services/Lastfm.php @@ -190,15 +190,14 @@ class Lastfm extends RESTfulService * * @return bool */ - public function scrobble($artist, $track, $timestamp, $album = null, $sk = null) + public function scrobble($artist, $track, $timestamp, $album, $sk) { - $params = compact('artist', 'track', 'timestamp'); + $params = compact('artist', 'track', 'timestamp', 'sk'); if ($album) { $params['album'] = $album; } - $params['sk'] = $sk ?: auth()->user()->getPreference('lastfm_session_key'); $params['method'] = 'track.scrobble'; try { @@ -210,6 +209,30 @@ class Lastfm extends RESTfulService } } + /** + * Love or unlove a track on Last.fm. + * + * @param string $track The track name + * @param string $artist The artist's name + * @param string $sk The session key + * @param boolean $love Whether to love or unlove. Such cheesy terms... urrgggh + * + * @return bool + */ + public function toggleLoveTrack($track, $artist, $sk, $love = true) + { + $params = compact('track', 'artist', 'sk'); + $params['method'] = $love ? 'track.love' : 'track.unlove'; + + try { + return (bool) $this->post('/', $this->buildAuthCallParams($params), false); + } catch (\Exception $e) { + Log::error($e); + + return false; + } + } + /** * Build the parameters to use for _authenticated_ Last.fm API calls. * Such calls require: diff --git a/tests/InteractionTest.php b/tests/InteractionTest.php index 7115e7e3..ddb93791 100644 --- a/tests/InteractionTest.php +++ b/tests/InteractionTest.php @@ -1,5 +1,6 @@ expectsEvents(SongLikeToggled::class); + $user = factory(User::class)->create(); $song = Song::orderBy('id')->first(); @@ -69,6 +72,8 @@ class InteractionTest extends TestCase public function testBatchLikeAndUnlike() { + $this->expectsEvents(SongLikeToggled::class); + $user = factory(User::class)->create(); $songs = Song::orderBy('id')->take(2)->get(); diff --git a/tests/LastfmTest.php b/tests/LastfmTest.php index 23fa551e..3e017569 100644 --- a/tests/LastfmTest.php +++ b/tests/LastfmTest.php @@ -1,6 +1,11 @@ callback($request, $lastfm); - $this->assertEquals('bar', $user->getPreference('lastfm_session_key')); + $this->assertEquals('bar', $user->getLastfmSessionKey()); } public function testControllerDisconnect() { $user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]); $this->actingAs($user)->delete('api/lastfm/disconnect'); - $this->assertNull($user->getPreference('lastfm_session_key')); + $this->assertNull($user->getLastfmSessionKey()); + } + + public function testLoveTrack() + { + $this->withoutEvents(); + $this->createSampleMediaSet(); + + $user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]); + + $interaction = Interaction::create([ + 'user_id' => $user->id, + 'song_id' => Song::first()->id, + ]); + + $lastfm = m::mock(Lastfm::class, ['enabled' => true]); + $lastfm->shouldReceive('toggleLoveTrack') + ->withArgs([$interaction->song->title, $interaction->song->album->artist->name, 'bar', false]); + + (new LoveTrackOnLastfm($lastfm))->handle(new SongLikeToggled($interaction, $user)); } }