/
MediaTest.php
247 lines (192 loc) · 8.19 KB
/
MediaTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<?php
namespace Tests\Feature;
use App\Events\LibraryChanged;
use App\Libraries\WatchRecord\InotifyWatchRecord;
use App\Models\Album;
use App\Models\Artist;
use App\Models\File;
use App\Models\Song;
use App\Services\Media;
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Mockery as m;
class MediaTest extends TestCase
{
use WithoutMiddleware;
protected function tearDown()
{
m::close();
parent::tearDown();
}
/** @test */
public function songs_can_be_synced()
{
$this->expectsEvents(LibraryChanged::class);
$media = new Media();
$media->sync($this->mediaPath);
// Standard mp3 files under root path should be recognized
$this->seeInDatabase('songs', [
'path' => $this->mediaPath.'/full.mp3',
// Track # should be recognized
'track' => 5,
]);
// Ogg files and audio files in subdirectories should be recognized
$this->seeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.ogg']);
// 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);
// File search shouldn't be case-sensitive.
$this->seeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
// 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);
// Compilation albums, artists and songs must be recognized
$song = Song::whereTitle('This song belongs to a compilation')->first();
$this->assertNotNull($song->artist_id);
$this->assertTrue($song->album->is_compilation);
$this->assertEquals(Artist::VARIOUS_ID, $song->album->artist_id);
$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);
}
/** @test */
public function songs_can_be_force_synced()
{
$this->expectsEvents(LibraryChanged::class);
$media = new Media();
$media->sync($this->mediaPath);
// Make some modification to the records
$song = Song::orderBy('id', 'desc')->first();
$originalTitle = $song->title;
$originalLyrics = $song->lyrics;
$song->update([
'title' => "It's John Cena!",
'lyrics' => 'Booom Wroooom',
]);
// Resync without forcing
$media->sync($this->mediaPath);
// 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
$media->sync($this->mediaPath, [], true);
// All is lost.
$song = Song::orderBy('id', 'desc')->first();
$this->assertEquals($originalTitle, $song->title);
$this->assertEquals($originalLyrics, $song->lyrics);
}
/** @test */
public function songs_can_be_synced_with_selectively_tags()
{
$this->expectsEvents(LibraryChanged::class);
$media = new Media();
$media->sync($this->mediaPath);
// Make some modification to the records
$song = Song::orderBy('id', 'desc')->first();
$originalTitle = $song->title;
$song->update([
'title' => "It's John Cena!",
'lyrics' => 'Booom Wroooom',
]);
// Sync only the selective tags
$media->sync($this->mediaPath, ['title'], true);
// Validate that the specified tags are changed, other remains the same
$song = Song::orderBy('id', 'desc')->first();
$this->assertEquals($originalTitle, $song->title);
$this->assertEquals('Booom Wroooom', $song->lyrics);
}
/** @test */
public function all_tags_are_catered_for_if_syncing_new_file()
{
// First we sync the test directory to get the data
$media = new Media();
$media->sync($this->mediaPath);
// Now delete the first song.
$song = Song::orderBy('id')->first();
$song->delete();
// Selectively sync only one tag
$media->sync($this->mediaPath, ['track'], true);
// but we still expect the whole song to be added back with all info
$addedSong = Song::findOrFail($song->id)->toArray();
$song = $song->toArray();
array_forget($addedSong, 'created_at');
array_forget($song, 'created_at');
$this->assertEquals($song, $addedSong);
}
/** @test */
public function added_song_is_synced_when_watching()
{
$this->expectsEvents(LibraryChanged::class);
$path = $this->mediaPath.'/blank.mp3';
(new Media())->syncByWatchRecord(new InotifyWatchRecord("CLOSE_WRITE,CLOSE $path"));
$this->seeInDatabase('songs', ['path' => $path]);
}
/** @test */
public function deleted_song_is_synced_when_watching()
{
$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]);
}
/** @test */
public function deleted_directory_is_synced_when_watching()
{
$this->expectsEvents(LibraryChanged::class);
$media = new Media();
$media->sync($this->mediaPath);
$media->syncByWatchRecord(new InotifyWatchRecord("MOVED_FROM,ISDIR {$this->mediaPath}/subdir"));
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/sic.mp3']);
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/no-name.mp3']);
$this->notSeeInDatabase('songs', ['path' => $this->mediaPath.'/subdir/back-in-black.mp3']);
}
/** @test */
public function html_entities_in_tags_are_recognized_and_saved_properly()
{
$getID3 = m::mock(getID3::class, [
'analyze' => [
'tags' => [
'id3v2' => [
'title' => ['水谷広実'],
'album' => ['小岩井こ Random'],
'artist' => ['佐倉綾音 Unknown'],
],
],
'encoding' => 'UTF-8',
'playtime_seconds' => 100,
],
]);
$info = (new File(__DIR__.'/songs/blank.mp3', $getID3))->getInfo();
$this->assertEquals('佐倉綾音 Unknown', $info['artist']);
$this->assertEquals('小岩井こ Random', $info['album']);
$this->assertEquals('水谷広実', $info['title']);
}
/** @test */
public function hidden_files_can_optionally_be_ignored_when_syncing()
{
config(['koel.ignore_dot_files' => false]);
$media = new Media();
$media->sync($this->mediaPath);
$this->seeInDatabase('albums', ['name' => 'Hidden Album']);
config(['koel.ignore_dot_files' => true]);
$media->sync($this->mediaPath);
$this->notSeeInDatabase('albums', ['name' => 'Hidden Album']);
}
}