From 59a59deb40e3cc56ffe6d9fc0af74f928e555a92 Mon Sep 17 00:00:00 2001 From: Phan An Date: Sat, 22 Apr 2017 23:42:45 +0800 Subject: [PATCH 01/20] Fix wrong config path --- resources/assets/js/config/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/assets/js/config/index.js b/resources/assets/js/config/index.js index 7c71eaa0..88424dd5 100644 --- a/resources/assets/js/config/index.js +++ b/resources/assets/js/config/index.js @@ -1,4 +1,4 @@ export default { - unknownCover: (typeof window !== 'undefined' ? window.location.href : '/') + 'public/img/covers/unknown-album.png', + unknownCover: (typeof window !== 'undefined' ? window.location.href.replace(window.location.hash, '') : '/') + 'public/img/covers/unknown-album.png', appTitle: 'Koel' } From a96c0e22fbc330e79bd07cbcb2f32a51c9298832 Mon Sep 17 00:00:00 2001 From: Phan An Date: Mon, 24 Apr 2017 00:01:02 +0800 Subject: [PATCH 02/20] Compilation fixes getting in shape --- app/Http/Controllers/API/DataController.php | 7 +- app/Models/Album.php | 1 + app/Models/File.php | 13 +- app/Models/Song.php | 83 ++-- app/Services/Media.php | 16 +- app/Traits/SupportsDeleteWhereIDsNotIn.php | 2 + composer.json | 3 +- composer.lock | 405 +++++++++++++++++- ...159_copy_artist_to_contributing_artist.php | 34 ++ ...61504_drop_is_complication_from_albums.php | 30 ++ .../main-wrapper/main-content/album.vue | 9 +- .../main-wrapper/main-content/artist.vue | 11 +- .../js/components/modals/edit-songs-form.vue | 26 +- .../js/components/shared/album-item.vue | 6 +- .../js/components/shared/artist-item.vue | 8 +- .../assets/js/mixins/album-attributes.js | 15 + .../assets/js/mixins/artist-attributes.js | 33 ++ resources/assets/js/stores/album.js | 25 +- resources/assets/js/stores/artist.js | 51 +-- resources/assets/js/stores/shared.js | 6 +- resources/assets/js/stores/song.js | 86 ++-- tests/BrowserKitTestCase.php | 1 + tests/Feature/SongTest.php | 11 +- 23 files changed, 698 insertions(+), 184 deletions(-) create mode 100644 database/migrations/2017_04_21_092159_copy_artist_to_contributing_artist.php create mode 100644 database/migrations/2017_04_22_161504_drop_is_complication_from_albums.php create mode 100644 resources/assets/js/mixins/album-attributes.js create mode 100644 resources/assets/js/mixins/artist-attributes.js diff --git a/app/Http/Controllers/API/DataController.php b/app/Http/Controllers/API/DataController.php index 7c41a667..f3bed095 100644 --- a/app/Http/Controllers/API/DataController.php +++ b/app/Http/Controllers/API/DataController.php @@ -4,6 +4,9 @@ namespace App\Http\Controllers\API; use App\Application; use App\Models\Interaction; +use App\Models\Artist; +use App\Models\Album; +use App\Models\Song; use App\Models\Playlist; use App\Models\Setting; use App\Models\User; @@ -29,7 +32,9 @@ class DataController extends Controller } return response()->json([ - 'artists' => MediaCache::get(), + 'artists' => Artist::orderBy('name')->get(), + 'songs' => Song::all(), + 'albums' => Album::orderBy('name')->get(), 'settings' => auth()->user()->is_admin ? Setting::pluck('value', 'key')->all() : [], 'playlists' => $playlists, 'interactions' => Interaction::byCurrentUser()->get(), diff --git a/app/Models/Album.php b/app/Models/Album.php index 01782301..a4600bb8 100644 --- a/app/Models/Album.php +++ b/app/Models/Album.php @@ -28,6 +28,7 @@ class Album extends Model protected $guarded = ['id']; protected $hidden = ['updated_at']; protected $casts = ['artist_id' => 'integer']; + protected $appends = ['is_compilation']; public function artist() { diff --git a/app/Models/File.php b/app/Models/File.php index 01dd5ef5..e84b35e6 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -93,7 +93,7 @@ class File /** * Get all applicable ID3 info from the file. * - * @return array|void + * @return array */ public function getInfo() { @@ -176,7 +176,7 @@ class File array_get($comments, 'part_of_a_compilation', [false])[0] || $props['albumartist'] ); - return $this->info = $props; + return $props; } /** @@ -264,12 +264,7 @@ class File } $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; - } + $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']); @@ -318,7 +313,7 @@ class File /** * Get the last parsing error's text. * - * @return syncError + * @return string */ public function getSyncError() { diff --git a/app/Models/Song.php b/app/Models/Song.php index 2b897520..8838c2cb 100644 --- a/app/Models/Song.php +++ b/app/Models/Song.php @@ -9,6 +9,7 @@ use Aws\AwsClient; use Cache; use Illuminate\Database\Eloquent\Model; use Lastfm; +use Media; use YouTube; /** @@ -16,7 +17,6 @@ use YouTube; * @property string title * @property Album album * @property int contributing_artist_id - * @property Artist contributingArtist * @property Artist artist * @property string s3_params * @property float length @@ -57,9 +57,9 @@ class Song extends Model */ public $incrementing = false; - public function contributingArtist() + public function artist() { - return $this->belongsTo(ContributingArtist::class); + return $this->belongsTo(ContributingArtist::class, 'contributing_artist_id'); } public function album() @@ -129,11 +129,11 @@ class Song extends Model public static function updateInfo($ids, $data) { /* - * An array of the updated songs. + * A collection of the updated songs. * - * @var array + * @var \Illuminate\Support\Collection */ - $updatedSongs = []; + $updatedSongs = collect(); $ids = (array) $ids; // If we're updating only one song, take into account the title, lyrics, and track number. @@ -144,22 +144,26 @@ class Song extends Model continue; } - $updatedSongs[] = $song->updateSingle( + $updatedSongs->push($song->updateSingle( $single ? trim($data['title']) : $song->title, trim($data['albumName'] ?: $song->album->name), trim($data['artistName']) ?: $song->artist->name, $single ? trim($data['lyrics']) : $song->lyrics, $single ? (int) $data['track'] : $song->track, (int) $data['compilationState'] - ); + )); } // Our library may have been changed. Broadcast an event to tidy it up if need be. - if ($updatedSongs) { + if ($updatedSongs->count()) { event(new LibraryChanged()); } - return $updatedSongs; + return [ + 'artists' => Artist::whereIn('id', $updatedSongs->pluck('contributing_artist_id'))->get(), + 'albums' => Album::whereIn('id', $updatedSongs->pluck('album_id'))->get(), + 'songs' => $updatedSongs, + ]; } /** @@ -176,30 +180,30 @@ class Song extends Model */ public function updateSingle($title, $albumName, $artistName, $lyrics, $track, $compilationState) { - // If the artist name is "Various Artists", it's a compilation song no matter what. if ($artistName === Artist::VARIOUS_NAME) { + // If the artist name is "Various Artists", it's a compilation song no matter what. $compilationState = 1; + // and since we can't determine the real contributing artist, it's "Unknown" + $artistName = Artist::UNKNOWN_NAME; } - // If the compilation state is "no change," we determine it via the current - // "contributing_artist_id" field value. - if ($compilationState === 2) { - $compilationState = $this->contributing_artist_id ? 1 : 0; + $artist = Artist::get($artistName); + + switch ($compilationState) { + case 1: // ALL, or forcing compilation status to be Yes + $isCompilation = true; + break; + case 2: // Keep current compilation status + $isCompilation = $this->album->artist_id === Artist::VARIOUS_ID; + break; + default: + $isCompilation = false; + break; } - $album = null; - - if ($compilationState === 0) { - // Not a compilation song - $this->contributing_artist_id = null; - $albumArtist = Artist::get($artistName); - $album = Album::get($albumArtist, $albumName, false); - } else { - $contributingArtist = Artist::get($artistName); - $this->contributing_artist_id = $contributingArtist->id; - $album = Album::get(Artist::getVarious(), $albumName, true); - } + $album = Album::get($artist, $albumName, $isCompilation); + $this->contributing_artist_id = $artist->id; $this->album_id = $album->id; $this->title = $title; $this->lyrics = $lyrics; @@ -207,12 +211,13 @@ class Song extends Model $this->save(); - // Get the updated record, with album and all. - $updatedSong = self::with('album', 'album.artist', 'contributingArtist')->find($this->id); - // Make sure lyrics is included in the returned JSON. - $updatedSong->makeVisible('lyrics'); + // Clean up unnecessary data from the object + unset($this->album); + unset($this->artist); + // and make sure the lyrics is shown + $this->makeVisible('lyrics'); - return $updatedSong; + return $this; } /** @@ -336,20 +341,6 @@ class Song extends Model return str_replace(["\r\n", "\r", "\n"], '
', $value); } - /** - * Get the correct artist of the song. - * If it's part of a compilation, that would be the contributing artist. - * Otherwise, it's the album artist. - * - * @return Artist - */ - public function getArtistAttribute() - { - return $this->contributing_artist_id - ? $this->contributingArtist - : $this->album->artist; - } - /** * Determine if the song is an AWS S3 Object. * diff --git a/app/Services/Media.php b/app/Services/Media.php index dd331759..948375e4 100644 --- a/app/Services/Media.php +++ b/app/Services/Media.php @@ -117,13 +117,15 @@ class Media */ public function gatherFiles($path) { - return Finder::create() - ->ignoreUnreadableDirs() - ->ignoreDotFiles((bool) config('koel.ignore_dot_files')) // https://github.com/phanan/koel/issues/450 - ->files() - ->followLinks() - ->name('/\.(mp3|ogg|m4a|flac)$/i') - ->in($path); + return iterator_to_array( + Finder::create() + ->ignoreUnreadableDirs() + ->ignoreDotFiles((bool) config('koel.ignore_dot_files')) // https://github.com/phanan/koel/issues/450 + ->files() + ->followLinks() + ->name('/\.(mp3|ogg|m4a|flac)$/i') + ->in($path) + ); } /** diff --git a/app/Traits/SupportsDeleteWhereIDsNotIn.php b/app/Traits/SupportsDeleteWhereIDsNotIn.php index 9f39a416..a49a5177 100644 --- a/app/Traits/SupportsDeleteWhereIDsNotIn.php +++ b/app/Traits/SupportsDeleteWhereIDsNotIn.php @@ -40,6 +40,8 @@ trait SupportsDeleteWhereIDsNotIn // If that's not possible (i.e. this array has more than 65535 elements, too) // then we'll delete chunk by chunk. static::deleteByChunk($ids, $key); + + return $whereInIDs; } /** diff --git a/composer.json b/composer.json index d86298cf..6fb08c58 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,8 @@ "tymon/jwt-auth": "^0.5.6", "aws/aws-sdk-php-laravel": "^3.1", "pusher/pusher-php-server": "^2.2", - "predis/predis": "~1.0" + "predis/predis": "~1.0", + "doctrine/dbal": "^2.5" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/composer.lock b/composer.lock index 5dbfb72c..14570fe8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "c7714d02ea086eb67d42299b88015650", + "content-hash": "b2aa59c478d7e97ce1a96c24405ca7c7", "packages": [ { "name": "aws/aws-sdk-php", @@ -142,6 +142,355 @@ ], "time": "2016-01-18T06:57:07+00:00" }, + { + "name": "doctrine/annotations", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", + "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-02-24T16:22:25+00:00" + }, + { + "name": "doctrine/cache", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2016-10-29T11:16:17+00:00" + }, + { + "name": "doctrine/collections", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", + "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2017-01-03T10:49:41+00:00" + }, + { + "name": "doctrine/common", + "version": "v2.7.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "930297026c8009a567ac051fd545bf6124150347" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347", + "reference": "930297026c8009a567ac051fd545bf6124150347", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.6|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2017-01-13T14:02:13+00:00" + }, + { + "name": "doctrine/dbal", + "version": "v2.5.12", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/7b9e911f9d8b30d43b96853dab26898c710d8f44", + "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.8-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*||^3.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2017-02-08T12:53:47+00:00" + }, { "name": "doctrine/inflector", "version": "v1.1.0", @@ -209,6 +558,60 @@ ], "time": "2015-11-06T14:35:42+00:00" }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09T13:34:57+00:00" + }, { "name": "erusev/parsedown", "version": "1.6.2", diff --git a/database/migrations/2017_04_21_092159_copy_artist_to_contributing_artist.php b/database/migrations/2017_04_21_092159_copy_artist_to_contributing_artist.php new file mode 100644 index 00000000..4af36511 --- /dev/null +++ b/database/migrations/2017_04_21_092159_copy_artist_to_contributing_artist.php @@ -0,0 +1,34 @@ +get()->each(function (Song $song) { + if (!$song->contributing_artist_id) { + $song->contributing_artist_id = $song->album->artist->id; + $song->save(); + } + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/database/migrations/2017_04_22_161504_drop_is_complication_from_albums.php b/database/migrations/2017_04_22_161504_drop_is_complication_from_albums.php new file mode 100644 index 00000000..207136bb --- /dev/null +++ b/database/migrations/2017_04_22_161504_drop_is_complication_from_albums.php @@ -0,0 +1,30 @@ +dropColumn('is_compilation'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/resources/assets/js/components/main-wrapper/main-content/album.vue b/resources/assets/js/components/main-wrapper/main-content/album.vue index c2dbec2f..bc9737f5 100644 --- a/resources/assets/js/components/main-wrapper/main-content/album.vue +++ b/resources/assets/js/components/main-wrapper/main-content/album.vue @@ -6,14 +6,14 @@ {{ album.name }} - + by {{ album.artist.name }} {{ album.artist.name }} • - {{ meta.songCount | pluralize('song') }} + {{ album.songs.length | pluralize('song') }} • - {{ meta.totalLength }} + {{ fmtLength }}