2015-12-13 04:42:28 +00:00
|
|
|
<?php
|
|
|
|
|
2017-02-14 06:53:02 +00:00
|
|
|
namespace Tests\Feature;
|
|
|
|
|
2016-02-02 07:47:00 +00:00
|
|
|
use App\Events\LibraryChanged;
|
2016-02-04 15:04:53 +00:00
|
|
|
use App\Libraries\WatchRecord\InotifyWatchRecord;
|
2015-12-13 04:42:28 +00:00
|
|
|
use App\Models\Album;
|
2016-04-24 04:37:04 +00:00
|
|
|
use App\Models\Artist;
|
2016-05-05 15:03:30 +00:00
|
|
|
use App\Models\File;
|
2015-12-13 04:42:28 +00:00
|
|
|
use App\Models\Song;
|
2018-08-19 09:05:33 +00:00
|
|
|
use App\Services\MediaService;
|
|
|
|
use Exception;
|
2017-12-09 18:34:27 +00:00
|
|
|
use getID3;
|
2018-08-19 09:05:33 +00:00
|
|
|
use getid3_exception;
|
2015-12-14 13:22:39 +00:00
|
|
|
use Illuminate\Foundation\Testing\WithoutMiddleware;
|
2016-05-05 15:03:30 +00:00
|
|
|
use Mockery as m;
|
2015-12-13 04:42:28 +00:00
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
class MediaSyncTest extends TestCase
|
2015-12-13 04:42:28 +00:00
|
|
|
{
|
2017-08-05 16:56:11 +00:00
|
|
|
use WithoutMiddleware;
|
2015-12-13 04:42:28 +00:00
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
/** @var MediaService */
|
|
|
|
private $mediaService;
|
|
|
|
|
|
|
|
public function setUp()
|
|
|
|
{
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
$this->mediaService = app(MediaService::class);
|
|
|
|
}
|
|
|
|
|
2017-08-05 18:55:02 +00:00
|
|
|
protected function tearDown()
|
|
|
|
{
|
|
|
|
m::close();
|
|
|
|
parent::tearDown();
|
|
|
|
}
|
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function songs_can_be_synced()
|
2015-12-13 04:42:28 +00:00
|
|
|
{
|
2016-02-02 07:47:00 +00:00
|
|
|
$this->expectsEvents(LibraryChanged::class);
|
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2015-12-13 04:42:28 +00:00
|
|
|
|
|
|
|
// Standard mp3 files under root path should be recognized
|
2016-03-20 14:14:02 +00:00
|
|
|
$this->seeInDatabase('songs', [
|
|
|
|
'path' => $this->mediaPath.'/full.mp3',
|
|
|
|
// Track # should be recognized
|
|
|
|
'track' => 5,
|
|
|
|
]);
|
2015-12-13 04:42:28 +00:00
|
|
|
|
|
|
|
// Ogg files and audio files in subdirectories should be recognized
|
|
|
|
$this->seeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.ogg']);
|
|
|
|
|
2016-08-07 10:30:55 +00:00
|
|
|
// GitHub issue #380. folder.png should be copied and used as the cover for files
|
|
|
|
// under subdir/
|
|
|
|
$song = Song::wherePath($this->mediaPath.'/subdir/back-in-black.ogg')->first();
|
|
|
|
$this->assertNotNull($song->album->cover);
|
|
|
|
|
2016-01-26 06:30:48 +00:00
|
|
|
// File search shouldn't be case-sensitive.
|
2017-04-20 11:14:28 +00:00
|
|
|
$this->seeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
|
2016-01-26 06:30:48 +00:00
|
|
|
|
2015-12-13 04:42:28 +00:00
|
|
|
// 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);
|
|
|
|
|
2016-04-24 04:37:04 +00:00
|
|
|
// Compilation albums, artists and songs must be recognized
|
|
|
|
$song = Song::whereTitle('This song belongs to a compilation')->first();
|
2017-04-29 03:49:14 +00:00
|
|
|
$this->assertNotNull($song->artist_id);
|
2016-04-24 04:37:04 +00:00
|
|
|
$this->assertTrue($song->album->is_compilation);
|
|
|
|
$this->assertEquals(Artist::VARIOUS_ID, $song->album->artist_id);
|
|
|
|
|
2015-12-13 04:42:28 +00:00
|
|
|
$currentCover = $album->cover;
|
|
|
|
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
|
|
|
|
|
|
|
// Modified file should be recognized
|
|
|
|
touch($song->path, $time = time());
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2015-12-13 04:42:28 +00:00
|
|
|
$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);
|
|
|
|
}
|
2016-02-02 07:47:00 +00:00
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function songs_can_be_force_synced()
|
2016-03-22 08:22:39 +00:00
|
|
|
{
|
|
|
|
$this->expectsEvents(LibraryChanged::class);
|
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
// Make some modification to the records
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
2016-11-24 04:07:57 +00:00
|
|
|
$originalTitle = $song->title;
|
|
|
|
$originalLyrics = $song->lyrics;
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
$song->update([
|
|
|
|
'title' => "It's John Cena!",
|
|
|
|
'lyrics' => 'Booom Wroooom',
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Resync without forcing
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
// Validate that the changes are not lost
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
|
|
|
$this->assertEquals("It's John Cena!", $song->title);
|
|
|
|
$this->assertEquals('Booom Wroooom', $song->lyrics);
|
|
|
|
|
|
|
|
// Resync with force
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath, [], true);
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
// All is lost.
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
2016-11-24 04:07:57 +00:00
|
|
|
$this->assertEquals($originalTitle, $song->title);
|
|
|
|
$this->assertEquals($originalLyrics, $song->lyrics);
|
2016-03-22 08:22:39 +00:00
|
|
|
}
|
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function songs_can_be_synced_with_selectively_tags()
|
2016-03-22 08:22:39 +00:00
|
|
|
{
|
|
|
|
$this->expectsEvents(LibraryChanged::class);
|
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
// Make some modification to the records
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
2016-11-24 04:07:57 +00:00
|
|
|
$originalTitle = $song->title;
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
$song->update([
|
|
|
|
'title' => "It's John Cena!",
|
|
|
|
'lyrics' => 'Booom Wroooom',
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Sync only the selective tags
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath, ['title'], true);
|
2016-03-22 08:22:39 +00:00
|
|
|
|
|
|
|
// Validate that the specified tags are changed, other remains the same
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
2016-11-24 04:07:57 +00:00
|
|
|
$this->assertEquals($originalTitle, $song->title);
|
2016-03-22 08:22:39 +00:00
|
|
|
$this->assertEquals('Booom Wroooom', $song->lyrics);
|
|
|
|
}
|
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function all_tags_are_catered_for_if_syncing_new_file()
|
2016-07-05 10:14:12 +00:00
|
|
|
{
|
2017-08-05 21:20:05 +00:00
|
|
|
// First we sync the test directory to get the data
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2017-08-05 21:20:05 +00:00
|
|
|
|
|
|
|
// Now delete the first song.
|
2016-07-05 10:14:12 +00:00
|
|
|
$song = Song::orderBy('id')->first();
|
|
|
|
$song->delete();
|
|
|
|
|
2017-08-05 21:20:05 +00:00
|
|
|
// Selectively sync only one tag
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath, ['track'], true);
|
2017-06-03 16:21:53 +00:00
|
|
|
|
2017-08-05 21:20:05 +00:00
|
|
|
// but we still expect the whole song to be added back with all info
|
|
|
|
$addedSong = Song::findOrFail($song->id)->toArray();
|
2017-06-03 16:21:53 +00:00
|
|
|
$song = $song->toArray();
|
2017-08-05 21:20:05 +00:00
|
|
|
array_forget($addedSong, 'created_at');
|
2017-06-09 23:46:12 +00:00
|
|
|
array_forget($song, 'created_at');
|
2017-06-03 16:21:53 +00:00
|
|
|
$this->assertEquals($song, $addedSong);
|
2016-07-05 10:14:12 +00:00
|
|
|
}
|
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function added_song_is_synced_when_watching()
|
2016-02-02 07:47:00 +00:00
|
|
|
{
|
2017-01-06 03:04:08 +00:00
|
|
|
$this->expectsEvents(LibraryChanged::class);
|
|
|
|
|
2016-02-02 07:47:00 +00:00
|
|
|
$path = $this->mediaPath.'/blank.mp3';
|
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->syncByWatchRecord(new InotifyWatchRecord("CLOSE_WRITE,CLOSE $path"));
|
2016-02-02 07:47:00 +00:00
|
|
|
|
|
|
|
$this->seeInDatabase('songs', ['path' => $path]);
|
|
|
|
}
|
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function deleted_song_is_synced_when_watching()
|
2016-02-02 07:47:00 +00:00
|
|
|
{
|
|
|
|
$this->expectsEvents(LibraryChanged::class);
|
|
|
|
|
|
|
|
$this->createSampleMediaSet();
|
|
|
|
$song = Song::orderBy('id', 'desc')->first();
|
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->syncByWatchRecord(new InotifyWatchRecord("DELETE {$song->path}"));
|
2016-02-02 07:47:00 +00:00
|
|
|
|
|
|
|
$this->notSeeInDatabase('songs', ['id' => $song->id]);
|
|
|
|
}
|
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function deleted_directory_is_synced_when_watching()
|
2016-02-02 07:47:00 +00:00
|
|
|
{
|
|
|
|
$this->expectsEvents(LibraryChanged::class);
|
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2016-02-02 07:47:00 +00:00
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->syncByWatchRecord(new InotifyWatchRecord("MOVED_FROM,ISDIR {$this->mediaPath}/subdir"));
|
2016-02-02 07:47:00 +00:00
|
|
|
|
|
|
|
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/sic.mp3']);
|
2017-04-20 11:14:28 +00:00
|
|
|
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
|
2016-02-02 07:47:00 +00:00
|
|
|
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.mp3']);
|
|
|
|
}
|
2016-05-05 15:03:30 +00:00
|
|
|
|
2018-08-19 09:05:33 +00:00
|
|
|
/**
|
|
|
|
* @test
|
|
|
|
*
|
|
|
|
* @throws getid3_exception
|
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function html_entities_in_tags_are_recognized_and_saved_properly()
|
2016-05-05 15:03:30 +00:00
|
|
|
{
|
|
|
|
$getID3 = m::mock(getID3::class, [
|
|
|
|
'analyze' => [
|
|
|
|
'tags' => [
|
|
|
|
'id3v2' => [
|
|
|
|
'title' => ['水谷広実'],
|
|
|
|
'album' => ['小岩井こ Random'],
|
|
|
|
'artist' => ['佐倉綾音 Unknown'],
|
|
|
|
],
|
|
|
|
],
|
|
|
|
'encoding' => 'UTF-8',
|
|
|
|
'playtime_seconds' => 100,
|
|
|
|
],
|
|
|
|
]);
|
|
|
|
|
2016-11-24 04:07:57 +00:00
|
|
|
$info = (new File(__DIR__.'/songs/blank.mp3', $getID3))->getInfo();
|
2016-05-05 15:03:30 +00:00
|
|
|
|
|
|
|
$this->assertEquals('佐倉綾音 Unknown', $info['artist']);
|
|
|
|
$this->assertEquals('小岩井こ Random', $info['album']);
|
|
|
|
$this->assertEquals('水谷広実', $info['title']);
|
|
|
|
}
|
2016-09-17 15:56:58 +00:00
|
|
|
|
2017-12-09 18:34:27 +00:00
|
|
|
/**
|
|
|
|
* @test
|
2017-12-09 20:10:55 +00:00
|
|
|
*
|
2018-08-19 09:05:33 +00:00
|
|
|
* @throws Exception
|
2017-12-09 18:34:27 +00:00
|
|
|
*/
|
2017-08-05 18:55:02 +00:00
|
|
|
public function hidden_files_can_optionally_be_ignored_when_syncing()
|
2016-09-17 15:56:58 +00:00
|
|
|
{
|
2016-09-19 02:08:50 +00:00
|
|
|
config(['koel.ignore_dot_files' => false]);
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2016-09-17 15:56:58 +00:00
|
|
|
$this->seeInDatabase('albums', ['name' => 'Hidden Album']);
|
|
|
|
|
2016-09-19 02:08:50 +00:00
|
|
|
config(['koel.ignore_dot_files' => true]);
|
2018-08-19 09:05:33 +00:00
|
|
|
$this->mediaService->sync($this->mediaPath);
|
2016-09-17 15:56:58 +00:00
|
|
|
$this->notSeeInDatabase('albums', ['name' => 'Hidden Album']);
|
|
|
|
}
|
2015-12-13 04:42:28 +00:00
|
|
|
}
|