Inheritance: extends Illuminate\Database\Eloquent\Model
Beispiel #1
0
 /**
  * Generate the downloadable path for a song.
  *
  * @param Song $song
  *
  * @return string
  */
 protected function fromSong(Song $song)
 {
     if ($s3Params = $song->s3_params) {
         // The song is hosted on Amazon S3.
         // We download it back to our local server first.
         $localPath = rtrim(sys_get_temp_dir(), '/') . '/' . basename($s3Params['key']);
         $url = $song->getObjectStoragePublicUrl();
         abort_unless($url, 404);
         // The following function require allow_url_fopen to be ON.
         // We're just assuming that to be the case here.
         copy($url, $localPath);
     } else {
         // The song is hosted locally. Make sure the file exists.
         abort_unless(file_exists($song->path), 404);
         $localPath = $song->path;
     }
     // The BinaryFileResponse factory only accept ASCII-only file names.
     if (ctype_print($localPath)) {
         return $localPath;
     }
     // For those with high-byte characters in names, we copy it into a safe name
     // as a workaround.
     $newPath = rtrim(sys_get_temp_dir(), '/') . '/' . utf8_decode(basename($song->path));
     if ($s3Params) {
         // If the file is downloaded from S3, we rename it directly.
         // This will save us some disk space.
         rename($localPath, $newPath);
     } else {
         // Else we copy it to another file to not mess up the original one.
         copy($localPath, $newPath);
     }
     return $newPath;
 }
Beispiel #2
0
 public function testMultipleSongs()
 {
     $songs = Song::take(2)->get();
     $mocked = Download::shouldReceive('from')->once()->andReturn($this->mediaPath . '/blank.mp3');
     // should be a zip file, but we're testing here…
     $this->get("api/download/songs?songs[]={$songs[0]->id}&songs[]={$songs[1]->id}")->seeStatusCode(200);
 }
Beispiel #3
0
 public function testSync()
 {
     $media = new Media();
     $media->sync($this->mediaPath);
     // Standard mp3 files under root path should be recognized
     $this->seeInDatabase('songs', ['path' => $this->mediaPath . '/full.mp3']);
     // Ogg files and audio files in subdirectories should be recognized
     $this->seeInDatabase('songs', ['path' => $this->mediaPath . '/subdir/back-in-black.ogg']);
     // Non-audio files shouldn't be recognized
     $this->notSeeInDatabase('songs', ['path' => $this->mediaPath . '/rubbish.log']);
     // Broken/corrupted audio files shouldn't be recognized
     $this->notSeeInDatabase('songs', ['path' => $this->mediaPath . '/fake.mp3']);
     // Artists should be created
     $this->seeInDatabase('artists', ['name' => 'Cuckoo']);
     $this->seeInDatabase('artists', ['name' => 'Koel']);
     // Albums should be created
     $this->seeInDatabase('albums', ['name' => 'Koel Testing Vol. 1']);
     // Albums and artists should be correctly linked
     $album = Album::whereName('Koel Testing Vol. 1')->first();
     $this->assertEquals('Koel', $album->artist->name);
     $currentCover = $album->cover;
     $song = Song::orderBy('id', 'desc')->first();
     // Modified file should be recognized
     touch($song->path, $time = time());
     $media->sync($this->mediaPath);
     $song = Song::find($song->id);
     $this->assertEquals($time, $song->mtime);
     // Albums with a non-default cover should have their covers overwritten
     $this->assertEquals($currentCover, Album::find($album->id)->cover);
 }
Beispiel #4
0
 /**
  * Remove a song whose info matches with data sent from AWS.
  *
  * @param RemoveSongRequest $request
  *
  * @return \Illuminate\Http\JsonResponse
  */
 public function remove(RemoveSongRequest $request)
 {
     abort_unless($song = Song::byPath("s3://{$request->bucket}/{$request->key}"), 404);
     $song->delete();
     event(new LibraryChanged());
     return response()->json();
 }
Beispiel #5
0
 public function testSearchVideosRelatedToSong()
 {
     $this->createSampleMediaSet();
     $song = Song::first();
     // We test on the facade here
     YouTubeFacade::shouldReceive('searchVideosRelatedToSong')->once();
     $this->visit("/api/youtube/search/song/{$song->id}");
 }
Beispiel #6
0
 public function testWatchSingleFileDeleted()
 {
     $this->expectsEvents(LibraryChanged::class);
     $this->createSampleMediaSet();
     $song = Song::orderBy('id', 'desc')->first();
     (new Media())->syncByWatchRecord(new InotifyWatchRecord("DELETE {$song->path}"));
     $this->notSeeInDatabase('songs', ['id' => $song->id]);
 }
Beispiel #7
0
 /**
  * Fired every time a LibraryChanged event is triggered.
  * Remove empty albums and artists from our system.
  */
 public function handle()
 {
     $inUseAlbums = Song::select('album_id')->groupBy('album_id')->get()->lists('album_id');
     $inUseAlbums[] = Album::UNKNOWN_ID;
     Album::whereNotIn('id', $inUseAlbums)->delete();
     $inUseArtists = Album::select('artist_id')->groupBy('artist_id')->get()->lists('artist_id');
     $inUseArtists[] = Artist::UNKNOWN_ID;
     Artist::whereNotIn('id', $inUseArtists)->delete();
 }
Beispiel #8
0
 public function testWatchSingleFileDeleted()
 {
     $this->expectsEvents(LibraryChanged::class);
     $this->createSampleMediaSet();
     $song = Song::orderBy('id', 'desc')->first();
     $record = m::mock(FSWatchRecord::class, ['isDeleted' => true, 'getPath' => $song->path, 'isFile' => true, 'isValidEvent' => true], ["{$song->path} IsFile"]);
     (new Media())->syncFSWatchRecord($record);
     $this->notSeeInDatabase('songs', ['id' => $song->id]);
 }
Beispiel #9
0
 public function testScrobble()
 {
     $this->withoutEvents();
     $this->createSampleMediaSet();
     $song = Song::first();
     $ts = time();
     m::mock(Lastfm::class, ['enabled' => true])->shouldReceive('scrobble')->with($song->album->artist->name, $song->title, $ts, $song->album->name, 'bar');
     $this->post("/api/{$song->id}/scrobble/{$ts}");
 }
Beispiel #10
0
 /**
  * @param Menu $menuModel
  * @param Slider $slider
  * @param Alphabet $alphabet
  * @param Song $song
  * @param Performer $performer
  * @param Lessons $lessons
  * @param Request $request
  */
 public function __construct(Menu $menuModel, Slider $slider, Alphabet $alphabet, Song $song, Performer $performer, Lessons $lessons, Request $request)
 {
     $this->performer = $performer;
     #Models performer
     $this->song = $song;
     #Models song
     $this->lessons = $lessons;
     #Models lessons
     $this->request = $request;
     #request
     $this->data['menu']['left'] = $menuModel->getLeftMenu();
     $this->data['menu']['right'] = $menuModel->getRightMenu();
     $this->data['slider'] = $slider->getActive();
     $this->data['alphabet'] = $alphabet->getActive();
     $this->data['countPerformer'] = count($performer->getActive());
     $this->data['countSong'] = count($song->getActive());
     $this->data['countLessons'] = count($lessons->getAll());
     $URL = $_SERVER['REQUEST_URI'];
     $this->data['url_lang'] = substr($URL, 1, 2);
 }
Beispiel #11
0
 /**
  * BaseStreamer constructor.
  *
  * @param $song Song|string A Song object, or its ID.
  */
 public function __construct($song)
 {
     $this->song = $song instanceof Song ? $song : Song::findOrFail($song);
     if (!file_exists($this->song->path)) {
         abort(404);
     }
     // Hard code the content type instead of relying on PHP's fileinfo()
     // or even Symfony's MIMETypeGuesser, since they appear to be wrong sometimes.
     $this->contentType = 'audio/' . pathinfo($this->song->path, PATHINFO_EXTENSION);
     // Turn off error reporting to make sure our stream isn't interfered.
     @error_reporting(0);
 }
Beispiel #12
0
 public function testBatchLikeAndUnlike()
 {
     $user = factory(User::class)->create();
     $songs = Song::orderBy('id')->take(2)->get();
     $songIds = array_pluck($songs->toArray(), 'id');
     $this->actingAs($user)->post('api/interaction/batch/like', ['ids' => $songIds]);
     foreach ($songs as $song) {
         $this->seeInDatabase('interactions', ['user_id' => $user->id, 'song_id' => $song->id, 'liked' => 1]);
     }
     $this->actingAs($user)->post('api/interaction/batch/unlike', ['ids' => $songIds]);
     foreach ($songs as $song) {
         $this->seeInDatabase('interactions', ['user_id' => $user->id, 'song_id' => $song->id, 'liked' => 0]);
     }
 }
Beispiel #13
0
 public function testSyncPlaylist()
 {
     $user = factory(User::class)->create();
     $playlist = factory(Playlist::class)->create(['user_id' => $user->id]);
     $songs = Song::orderBy('id')->take(4)->get();
     $playlist->songs()->attach(array_pluck($songs->toArray(), 'id'));
     $removedSong = $songs->pop();
     $this->actingAs($user)->put("api/playlist/{$playlist->id}/sync", ['songs' => array_pluck($songs->toArray(), 'id')]);
     // We should still see the first 3 songs, but not the removed one
     foreach ($songs as $song) {
         $this->seeInDatabase('playlist_song', ['playlist_id' => $playlist->id, 'song_id' => $song->id]);
     }
     $this->notSeeInDatabase('playlist_song', ['playlist_id' => $playlist->id, 'song_id' => $removedSong->id]);
 }
 /**
  * Register any other events for your application.
  *
  * @param \Illuminate\Contracts\Events\Dispatcher $events
  */
 public function boot(DispatcherContract $events)
 {
     parent::boot($events);
     // Generate a unique hash for a song from its path to be the ID
     Song::creating(function ($song) {
         $song->id = Media::getHash($song->path);
     });
     // Remove the cover file if the album is deleted
     Album::deleted(function ($album) {
         if ($album->hasCover) {
             @unlink(app()->publicPath() . '/public/img/covers/' . $album->cover);
         }
     });
 }
Beispiel #15
0
 public function testSingleUpdateAllInfoYesCompilation()
 {
     $admin = factory(User::class, 'admin')->create();
     $this->createSampleMediaSet();
     $song = Song::orderBy('id', 'desc')->first();
     $this->actingAs($admin)->put('/api/songs', ['songs' => [$song->id], 'data' => ['title' => 'Foo Bar', 'artistName' => 'John Cena', 'albumName' => 'One by One', 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1, 'compilationState' => 1]])->seeStatusCode(200);
     $compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'One by One')->first();
     $this->assertNotNull($compilationAlbum);
     $contributingArtist = Artist::whereName('John Cena')->first();
     $this->assertNotNull($contributingArtist);
     $this->seeInDatabase('songs', ['id' => $song->id, 'contributing_artist_id' => $contributingArtist->id, 'album_id' => $compilationAlbum->id, 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1]);
     // Now try changing stuff and make sure things work.
     // Case 1: Keep compilation state and artist the same
     $this->actingAs($admin)->put('/api/songs', ['songs' => [$song->id], 'data' => ['title' => 'Barz Qux', 'artistName' => 'John Cena', 'albumName' => 'Two by Two', 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1, 'compilationState' => 2]])->seeStatusCode(200);
     $compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'Two by Two')->first();
     $this->assertNotNull($compilationAlbum);
     $contributingArtist = Artist::whereName('John Cena')->first();
     $this->assertNotNull($contributingArtist);
     $this->seeInDatabase('songs', ['id' => $song->id, 'contributing_artist_id' => $contributingArtist->id, 'album_id' => $compilationAlbum->id]);
     // Case 2: Keep compilation state, but change the artist.
     $this->actingAs($admin)->put('/api/songs', ['songs' => [$song->id], 'data' => ['title' => 'Barz Qux', 'artistName' => 'Foo Fighters', 'albumName' => 'One by One', 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1, 'compilationState' => 2]])->seeStatusCode(200);
     $compilationAlbum = Album::whereArtistIdAndName(Artist::VARIOUS_ID, 'One by One')->first();
     $this->assertNotNull($compilationAlbum);
     $contributingArtist = Artist::whereName('Foo Fighters')->first();
     $this->assertNotNull($contributingArtist);
     $this->seeInDatabase('songs', ['id' => $song->id, 'contributing_artist_id' => $contributingArtist->id, 'album_id' => $compilationAlbum->id]);
     // Case 3: Change compilation state only
     $this->actingAs($admin)->put('/api/songs', ['songs' => [$song->id], 'data' => ['title' => 'Barz Qux', 'artistName' => 'Foo Fighters', 'albumName' => 'One by One', 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1, 'compilationState' => 0]])->seeStatusCode(200);
     $artist = Artist::whereName('Foo Fighters')->first();
     $this->assertNotNull($artist);
     $album = Album::whereArtistIdAndName($artist->id, 'One by One')->first();
     $this->seeInDatabase('songs', ['id' => $song->id, 'contributing_artist_id' => null, 'album_id' => $album->id]);
     // Case 3: Change compilation state and artist
     // Remember to set the compliation state back to 1
     $this->actingAs($admin)->put('/api/songs', ['songs' => [$song->id], 'data' => ['title' => 'Barz Qux', 'artistName' => 'Foo Fighters', 'albumName' => 'One by One', 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1, 'compilationState' => 1]])->put('/api/songs', ['songs' => [$song->id], 'data' => ['title' => 'Twilight of the Thunder God', 'artistName' => 'Amon Amarth', 'albumName' => 'Twilight of the Thunder God', 'lyrics' => 'Thor! Nanananananana Batman.', 'track' => 1, 'compilationState' => 0]])->seeStatusCode(200);
     $artist = Artist::whereName('Amon Amarth')->first();
     $this->assertNotNull($artist);
     $album = Album::whereArtistIdAndName($artist->id, 'Twilight of the Thunder God')->first();
     $this->assertNotNull($album);
     $this->seeInDatabase('songs', ['id' => $song->id, 'contributing_artist_id' => null, 'album_id' => $album->id, 'lyrics' => 'Thor! Nanananananana Batman.']);
 }
Beispiel #16
0
 /**
  * Bootstrap any application services.
  *
  * @return void
  */
 public function boot()
 {
     // when a user changes an item ....
     Item::updated(function ($item) {
         // .... update the 'changer' field on the parent model (plan)
         $plan = Plan::find($item->plan_id);
         $plan->update(['changer' => Auth::user()->first_name]);
     });
     /**
      * Provide the data of the last update to the list of songs
      */
     $lastSongUpdated_at = Song::select('updated_at')->orderby('updated_at', 'desc')->first()->updated_at;
     view()->share('lastSongUpdated_at', $lastSongUpdated_at);
     // provide the name of the current Main Presenter to all views
     view()->share('serverSideMainPresenter', getMainPresenter());
     // provide the PATH to the (custom) logos to all views
     if (strtolower(env('USE_CUSTOM_LOGOS')) == 'yes') {
         view()->share('logoPath', 'images/custom/');
     } else {
         view()->share('logoPath', 'images/');
     }
     // provide a list (array) of user-id's with Admin rights to all views (for page feedback messages)
     // (the condition is needed in order to avoid an error in artisan  when no migration has happened yet!)
     if (\Schema::hasTable('users')) {
         view()->share('administrators', findAdmins('id'));
     }
     // detect mobile users
     $isMobileUser = false;
     $useragent = 'local';
     if (isset($_SERVER['HTTP_USER_AGENT'])) {
         $useragent = $_SERVER['HTTP_USER_AGENT'];
     }
     if (preg_match('/(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i', $useragent) || preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\\-|your|zeto|zte\\-/i', substr($useragent, 0, 4))) {
         $isMobileUser = true;
     }
     // provide this to all views
     view()->share('isMobileUser', $isMobileUser);
 }
Beispiel #17
0
 public function testMultipleUpdateSomeInfo()
 {
     $this->createSampleMediaSet();
     $originalSongs = Song::orderBy('id', 'desc')->take(3)->get();
     $songIds = $originalSongs->pluck('id')->toArray();
     $this->actingAs(factory(User::class, 'admin')->create())->put('/api/songs', ['songs' => $songIds, 'data' => ['title' => 'Foo Bar', 'artistName' => 'John Cena', 'albumName' => '', 'lyrics' => 'Lorem ipsum dolor sic amet.', 'track' => 1]])->seeStatusCode(200);
     $songs = Song::orderBy('id', 'desc')->take(3)->get();
     // Even though the album name doesn't change, a new artist should have been created
     // and thus, a new album with the same name was created as well.
     $this->assertEquals($songs[0]->album->name, $originalSongs[0]->album->name);
     $this->assertNotEquals($songs[0]->album->id, $originalSongs[0]->album->id);
     $this->assertEquals($songs[1]->album->name, $originalSongs[1]->album->name);
     $this->assertNotEquals($songs[1]->album->id, $originalSongs[1]->album->id);
     $this->assertEquals($songs[2]->album->name, $originalSongs[2]->album->name);
     $this->assertNotEquals($songs[2]->album->id, $originalSongs[2]->album->id);
     // And of course, the new artist is...
     $this->assertEquals('John Cena', $songs[0]->album->artist->name);
     // JOHN CENA!!!
     $this->assertEquals('John Cena', $songs[1]->album->artist->name);
     // JOHN CENA!!!
     $this->assertEquals('John Cena', $songs[2]->album->artist->name);
     // And... JOHN CENAAAAAAAAAAA!!!
 }
Beispiel #18
0
 /**
  * Check if a media file is new or changed.
  * A file is considered existing and unchanged only when:
  * - its hash (ID) can be found in the database, and
  * - its last modified time is the same with that of the comparing file.
  *
  * @param SplFileInfo $file
  *
  * @return bool
  */
 protected function isNewOrChanged(SplFileInfo $file)
 {
     return !Song::whereIdAndMtime($this->getHash($file->getPathname()), $file->getMTime())->count();
 }
Beispiel #19
0
 /**
  * Sync the song with all available media info against the database.
  *
  * @param array $tags  The (selective) tags to sync (if the song exists)
  * @param bool  $force Whether to force syncing, even if the file is unchaged
  *
  * @return bool|Song A Song object on success,
  *                   true if file exists but is unmodified,
  *                   or false on an error.
  */
 public function sync($tags, $force = false)
 {
     // If the file is not new or changed and we're not forcing update, don't do anything.
     if (!$this->isNewOrChanged() && !$force) {
         return true;
     }
     // If the file is invalid, don't do anything.
     if (!($info = $this->getInfo())) {
         return false;
     }
     if ($this->isChanged() || $force) {
         // This is a changed file, or the user is forcing updates.
         // We cater for the tags by removing those not specified.
         $info = array_intersect_key($info, array_flip($tags));
         $artist = isset($info['artist']) ? Artist::get($info['artist']) : $this->song->album->artist;
         $album = isset($info['album']) ? Album::get($artist, $info['album']) : $this->song->album;
     } else {
         $album = Album::get(Artist::get($info['artist']), $info['album']);
     }
     if (!empty($info['cover']) && !$album->has_cover) {
         try {
             $album->generateCover($info['cover']);
         } catch (Exception $e) {
             Log::error($e);
         }
     }
     $info['album_id'] = $album->id;
     // Remove these values from the info array, so that we can just use the array as model's input data.
     array_forget($info, ['artist', 'album', 'cover']);
     $song = Song::updateOrCreate(['id' => $this->hash], $info);
     $song->save();
     return $song;
 }
Beispiel #20
0
 /**
  * Tidy up the library by deleting empty albums and artists.
  */
 public function tidy()
 {
     $inUseAlbums = Song::select('album_id')->groupBy('album_id')->get()->lists('album_id')->toArray();
     $inUseAlbums[] = Album::UNKNOWN_ID;
     Album::whereNotIn('id', $inUseAlbums)->delete();
     $inUseArtists = Album::select('artist_id')->groupBy('artist_id')->get()->lists('artist_id')->toArray();
     $contributingArtists = Song::distinct()->select('contributing_artist_id')->groupBy('contributing_artist_id')->get()->lists('contributing_artist_id')->toArray();
     $inUseArtists = array_merge($inUseArtists, $contributingArtists);
     $inUseArtists[] = Artist::UNKNOWN_ID;
     $inUseArtists[] = Artist::VARIOUS_ID;
     Artist::whereNotIn('id', $inUseArtists)->delete();
 }
Beispiel #21
0
 public function testGetObjectStoragePublicUrl()
 {
     $song = Song::first();
     $song->path = 's3://foo/bar.mp3';
     $fakeUrl = 'http://aws.com/foo/bar.mp3';
     $client = m::mock(AwsClient::class, ['getCommand' => null, 'createPresignedRequest' => m::mock(Request::class, ['getUri' => $fakeUrl])]);
     Cache::shouldReceive('get')->once()->with("OSUrl/{$song->id}");
     Cache::shouldReceive('put')->once()->with("OSUrl/{$song->id}", $fakeUrl, 60);
     $this->assertEquals($fakeUrl, $song->getObjectStoragePublicUrl($client));
 }
Beispiel #22
0
 /**
  * Update songs info.
  *
  * @param SongUpdateRequest $request
  *
  * @return \Illuminate\Http\JsonResponse
  */
 public function update(SongUpdateRequest $request)
 {
     return response()->json(Song::updateInfo($request->songs, $request->data));
 }
Beispiel #23
0
 /**
  * Search for YouTube videos related to a song (using its title and artist name).
  *
  * @param Request $request
  * @param Song    $song
  *
  * @return \Illuminate\Http\JsonResponse
  */
 public function searchVideosRelatedToSong(Request $request, Song $song)
 {
     return response()->json($song->getRelatedYouTubeVideos($request->pageToken));
 }
Beispiel #24
0
 /**
  * Display the specified resource.
  *
  * @param  int  $id
  * @return \Illuminate\Http\Response
  */
 public function show($society, $id, $mode = "view")
 {
     $data['soc'] = Society::find($society);
     $data['song'] = Song::find($id);
     $data['lyrics'] = "";
     $data['chords'] = $this->_getChords($data['song']->lyrics);
     $lines = explode(PHP_EOL, $data['song']->lyrics);
     foreach ($lines as $line) {
         $line = str_replace("\r", '', $line);
         $line = str_replace("\n", '', $line);
         if (strpos($line, ']')) {
             $line = "<chordline>" . $line . "</chordline>";
             $line = str_replace('[', '<chord>', $line);
             $line = str_replace(']', '</chord>', $line);
         }
         $line = $line . "<br>";
         $line = str_replace('{', '<strong>', $line);
         $line = str_replace('}', '</strong>', $line);
         $data['lyrics'] = $data['lyrics'] . $line;
     }
     if ($mode == "view") {
         return View::make('songs.show', $data);
     } else {
         $this->pdf($data);
     }
 }
Beispiel #25
0
 /**
  * Download a song or multiple songs.
  *
  * @param SongRequest $request
  *
  * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
  */
 public function download(SongRequest $request)
 {
     $songs = Song::whereIn('id', $request->songs)->get();
     return response()->download(Download::from($songs));
 }
Beispiel #26
0
 /**
  * Sync the song with all available media info against the database.
  *
  * @param array $tags  The (selective) tags to sync (if the song exists)
  * @param bool  $force Whether to force syncing, even if the file is unchanged
  *
  * @return bool|Song A Song object on success,
  *                   true if file exists but is unmodified,
  *                   or false on an error.
  */
 public function sync($tags, $force = false)
 {
     // If the file is not new or changed and we're not forcing update, don't do anything.
     if (!$this->isNewOrChanged() && !$force) {
         return true;
     }
     // If the file is invalid, don't do anything.
     if (!($info = $this->getInfo())) {
         return false;
     }
     // Fixes #366. If the file is new, we use all tags by simply setting $force to false.
     if ($this->isNew()) {
         $force = false;
     }
     $artist = null;
     if ($this->isChanged() || $force) {
         // This is a changed file, or the user is forcing updates.
         // In such a case, the user must have specified a list of tags to sync.
         // A sample command could be: ./artisan koel:sync --force --tags=artist,album,lyrics
         // We cater for these tags by removing those not specified.
         // There's a special case with 'album' though.
         // If 'compilation' tag is specified, 'album' must be counted in as well.
         // But if 'album' isn't specified, we don't want to update normal albums.
         // This variable is to keep track of this state.
         $changeCompilationAlbumOnly = false;
         if (in_array('compilation', $tags, true) && !in_array('album', $tags, true)) {
             $tags[] = 'album';
             $changeCompilationAlbumOnly = true;
         }
         $info = array_intersect_key($info, array_flip($tags));
         // If the "artist" tag is specified, use it.
         // Otherwise, re-use the existing model value.
         $artist = isset($info['artist']) ? Artist::get($info['artist']) : $this->song->album->artist;
         $isCompilation = (bool) array_get($info, 'compilation');
         // If the "album" tag is specified, use it.
         // Otherwise, re-use the existing model value.
         if (isset($info['album'])) {
             $album = $changeCompilationAlbumOnly ? $this->song->album : Album::get($artist, $info['album'], $isCompilation);
         } else {
             $album = $this->song->album;
         }
     } else {
         // The file is newly added.
         $isCompilation = (bool) array_get($info, 'compilation');
         $artist = Artist::get($info['artist']);
         $album = Album::get($artist, $info['album'], $isCompilation);
     }
     if (!$album->has_cover) {
         // If the album has no cover, we try to get the cover image from existing tag data
         if (!empty($info['cover'])) {
             try {
                 $album->generateCover($info['cover']);
             } catch (Exception $e) {
                 Log::error($e);
             }
         } elseif ($cover = $this->getCoverFileUnderSameDirectory()) {
             $album->copyCoverFile($cover);
         }
     }
     $info['album_id'] = $album->id;
     // If the song is part of a compilation, make sure we properly set its
     // artist and contributing artist attributes.
     if ($isCompilation) {
         $info['contributing_artist_id'] = $artist->id;
     }
     // Remove these values from the info array, so that we can just use the array as model's input data.
     array_forget($info, ['artist', 'albumartist', 'album', 'cover', 'compilation']);
     return Song::updateOrCreate(['id' => $this->hash], $info);
 }
 /**
  * Download all songs in a playlist.
  *
  * @param Request $request
  *
  * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
  */
 public function download(Request $request)
 {
     return response()->download(Download::from(Song::getFavorites($request->user())));
 }
Beispiel #28
0
 /**
  * Scrobble a song.
  * 
  * @param Song   $song
  * @param string $timestamp The UNIX timestamp when the song started playing.
  * 
  * @return \Illuminate\Http\JsonResponse
  */
 public function scrobble(Song $song, $timestamp)
 {
     return response()->json($song->scrobble($timestamp));
 }
Beispiel #29
0
 /**
  * Sync media using a watch record.
  *
  * @param WatchRecordInterface $record      The watch record.
  * @param SyncMedia|null       $syncCommand The SyncMedia command object, to log to console if executed by artisan.
  */
 public function syncByWatchRecord(WatchRecordInterface $record, SyncMedia $syncCommand = null)
 {
     Log::info("New watch record received: '{$record}'");
     $path = $record->getPath();
     if ($record->isFile()) {
         Log::info("'{$path}' is a file.");
         // If the file has been deleted...
         if ($record->isDeleted()) {
             // ...and it has a record in our database, remove it.
             if ($song = Song::byPath($path)) {
                 $song->delete();
                 Log::info("{$path} deleted.");
                 event(new LibraryChanged());
             } else {
                 Log::info("{$path} doesn't exist in our database--skipping.");
             }
         } elseif ($record->isNewOrModified()) {
             $result = (new File($path))->sync($this->tags);
             Log::info($result instanceof Song ? "Synchronized {$path}" : "Invalid file {$path}");
         }
         return;
     }
     // Record is a directory.
     Log::info("'{$path}' is a directory.");
     if ($record->isDeleted()) {
         // The directory is removed. We remove all songs in it.
         if ($count = Song::inDirectory($path)->delete()) {
             Log::info("Deleted {$count} song(s) under {$path}");
             event(new LibraryChanged());
         } else {
             Log::info("{$path} is empty--no action needed.");
         }
     } elseif ($record->isNewOrModified()) {
         foreach ($this->gatherFiles($path) as $file) {
             (new File($file))->sync($this->tags);
         }
         Log::info("Synced all song(s) under {$path}");
     }
 }
 public function testUpdateNowPlaying()
 {
     $this->withoutEvents();
     $this->createSampleMediaSet();
     $user = factory(User::class)->create(['preferences' => ['lastfm_session_key' => 'bar']]);
     $song = Song::first();
     $lastfm = m::mock(Lastfm::class, ['enabled' => true]);
     $lastfm->shouldReceive('updateNowPlaying')->with($song->album->artist->name, $song->title, $song->album->name, $song->length, 'bar');
     (new UpdateLastfmNowPlaying($lastfm))->handle(new SongStartedPlaying($song, $user));
 }