added support for song track numbers as well as subsorting song lists

with a second sort key. track numbers are also editable via the song
edit modal interface.
This commit is contained in:
alex-phillips 2016-03-20 08:58:14 -04:00
parent 8c7dae6a32
commit 940cd1a914
7 changed files with 57 additions and 7 deletions

View file

@ -133,10 +133,12 @@ class Song extends Model
continue; continue;
} }
// If we're updating only one song, take into account the title and lyrics. // If we're updating only one song, take into account the title and lyrics
// and track number.
if (count($ids) === 1) { if (count($ids) === 1) {
$song->title = trim($data['title']) ?: $song->title; $song->title = trim($data['title']) ?: $song->title;
$song->lyrics = trim($data['lyrics']); $song->lyrics = trim($data['lyrics']);
$song->track = trim($data['track']);
} }
// If "newArtist" is provided, we'll see if such an artist name is found in our database. // If "newArtist" is provided, we'll see if such an artist name is found in our database.

View file

@ -229,11 +229,19 @@ class Media
return; return;
} }
$track = array_get($info, 'comments.track_number', [0])[0];
if (preg_match('#(\d+)/#', $track, $matches)) {
$track = $matches[1];
} elseif ((int) $track) {
$track = (int) $track;
}
$props = [ $props = [
'artist' => '', 'artist' => '',
'album' => '', 'album' => '',
'title' => '', 'title' => '',
'length' => $info['playtime_seconds'], 'length' => $info['playtime_seconds'],
'track' => $track,
'lyrics' => '', 'lyrics' => '',
'cover' => array_get($info, 'comments.picture', [null])[0], 'cover' => array_get($info, 'comments.picture', [null])[0],
'path' => $file->getPathname(), 'path' => $file->getPathname(),

View file

@ -35,6 +35,7 @@ $factory->define(App\Models\Song::class, function ($faker) {
return [ return [
'title' => $faker->sentence, 'title' => $faker->sentence,
'length' => $faker->randomFloat(2, 10, 500), 'length' => $faker->randomFloat(2, 10, 500),
'track' => $faker->randomNumber(),
'lyrics' => $faker->paragraph(), 'lyrics' => $faker->paragraph(),
'path' => '/tmp/'.uniqid().'.mp3', 'path' => '/tmp/'.uniqid().'.mp3',
'mtime' => time(), 'mtime' => time(),

View file

@ -17,6 +17,7 @@ class CreateSongsTable extends Migration
$table->integer('album_id')->unsigned(); $table->integer('album_id')->unsigned();
$table->string('title'); $table->string('title');
$table->float('length'); $table->float('length');
$table->integer('track');
$table->text('lyrics'); $table->text('lyrics');
$table->text('path'); $table->text('path');
$table->integer('mtime'); $table->integer('mtime');

View file

@ -280,7 +280,7 @@
* *
* @source https://github.com/vuejs/vue/blob/dev/src/filters/array-filters.js * @source https://github.com/vuejs/vue/blob/dev/src/filters/array-filters.js
*/ */
Vue.filter('caseInsensitiveOrderBy', (arr, sortKey, reverse) => { Vue.filter('caseInsensitiveOrderBy', (arr, sortKey, reverse, subSortKey) => {
if (!sortKey) { if (!sortKey) {
return arr; return arr;
} }
@ -289,17 +289,45 @@
// sort on a copy to avoid mutating original array // sort on a copy to avoid mutating original array
return arr.slice().sort((a, b) => { return arr.slice().sort((a, b) => {
let aSub = subSortKey ? Vue.util.isObject(a) ? Vue.parsers.path.getPath(a, subSortKey) : 0 : 0;
let bSub = subSortKey ? Vue.util.isObject(b) ? Vue.parsers.path.getPath(b, subSortKey) : 0 : 0;
a = Vue.util.isObject(a) ? Vue.parsers.path.getPath(a, sortKey) : a; a = Vue.util.isObject(a) ? Vue.parsers.path.getPath(a, sortKey) : a;
b = Vue.util.isObject(b) ? Vue.parsers.path.getPath(b, sortKey) : b; b = Vue.util.isObject(b) ? Vue.parsers.path.getPath(b, sortKey) : b;
if (_.isNumber(a) && _.isNumber(b)) { if (_.isNumber(a) && _.isNumber(b)) {
return a === b ? 0 : a > b ? order : -order; if (a === b) {
if (_.isNumber(aSub) && _.isNumber(bSub)) {
return aSub === bSub ? 0 : aSub > bSub ? order : -order;
} else if (aSub !== undefined && bSub !== undefined) {
aSub = aSub.toLowerCase();
bSub = bSub.toLowerCase();
return aSub === bSub ? 0 : aSub > bSub ? order : -order;
}
return 0;
}
return a > b ? order : -order;
} }
a = a === undefined ? a : a.toLowerCase(); a = a === undefined ? a : a.toLowerCase();
b = b === undefined ? b : b.toLowerCase(); b = b === undefined ? b : b.toLowerCase();
return a === b ? 0 : a > b ? order : -order; if (a === b) {
if (_.isNumber(aSub) && _.isNumber(bSub)) {
return aSub === bSub ? 0 : aSub > bSub ? order : -order;
} else if (aSub !== undefined && bSub !== undefined) {
aSub = aSub.toLowerCase();
bSub = bSub.toLowerCase();
return aSub === bSub ? 0 : aSub > bSub ? order : -order;
}
return 0;
}
return a > b ? order : -order;
}); });
}); });

View file

@ -44,6 +44,10 @@
:options="albumTypeaheadOptions" :options="albumTypeaheadOptions"
:value.sync="formData.albumName"></typeahead> :value.sync="formData.albumName"></typeahead>
</div> </div>
<div class="form-row" v-show="editSingle">
<label>Track</label>
<input type="text" v-model="formData.track">
</div>
</div> </div>
<div v-show="currentView === 'lyrics' && editSingle"> <div v-show="currentView === 'lyrics' && editSingle">
<div class="form-row"> <div class="form-row">
@ -106,6 +110,7 @@
albumName: '', albumName: '',
artistName: '', artistName: '',
lyrics: '', lyrics: '',
track: '',
}, },
}; };
}, },
@ -197,9 +202,11 @@
songStore.getInfo(this.songs[0], () => { songStore.getInfo(this.songs[0], () => {
this.loading = false; this.loading = false;
this.formData.lyrics = utils.br2nl(this.songs[0].lyrics); this.formData.lyrics = utils.br2nl(this.songs[0].lyrics);
this.formData.track = this.songs[0].track;
}); });
} else { } else {
this.formData.lyrics = utils.br2nl(this.songs[0].lyrics); this.formData.lyrics = utils.br2nl(this.songs[0].lyrics);
this.formData.track = this.songs[0].track;
} }
} else { } else {
this.formData.albumName = this.inSameAlbum ? this.songs[0].album.name : ''; this.formData.albumName = this.inSameAlbum ? this.songs[0].album.name : '';

View file

@ -18,7 +18,7 @@
<i class="fa fa-angle-down" v-show="sortKey === 'album.artist.name' && order > 0"></i> <i class="fa fa-angle-down" v-show="sortKey === 'album.artist.name' && order > 0"></i>
<i class="fa fa-angle-up" v-show="sortKey === 'album.artist.name' && order < 0"></i> <i class="fa fa-angle-up" v-show="sortKey === 'album.artist.name' && order < 0"></i>
</th> </th>
<th @click="sort('album.name')">Album <th @click="sort('album.name', 'track')">Album
<i class="fa fa-angle-down" v-show="sortKey === 'album.name' && order > 0"></i> <i class="fa fa-angle-down" v-show="sortKey === 'album.name' && order > 0"></i>
<i class="fa fa-angle-up" v-show="sortKey === 'album.name' && order < 0"></i> <i class="fa fa-angle-up" v-show="sortKey === 'album.name' && order < 0"></i>
</th> </th>
@ -33,10 +33,11 @@
<tbody> <tbody>
<tr <tr
v-for="item in items v-for="item in items
| caseInsensitiveOrderBy sortKey order | caseInsensitiveOrderBy sortKey order subSortKey
| filterSongBy q | filterSongBy q
| limitBy numOfItems" | limitBy numOfItems"
is="song-item" is="song-item"
data-track="{{ item.track }}"
data-song-id="{{ item.id }}" data-song-id="{{ item.id }}"
track-by="id" track-by="id"
:song="item" :song="item"
@ -81,6 +82,7 @@
lastSelectedRow: null, lastSelectedRow: null,
q: '', // The filter query q: '', // The filter query
sortKey: this.type === 'top-songs' ? 'playCount' : '', sortKey: this.type === 'top-songs' ? 'playCount' : '',
subSortKey: 0,
order: this.type === 'top-songs' ? -1 : 1, order: this.type === 'top-songs' ? -1 : 1,
componentCache: {}, componentCache: {},
}; };
@ -109,12 +111,13 @@
* *
* @param {String} key The sort key. Can be 'title', 'album', 'artist', or 'fmtLength' * @param {String} key The sort key. Can be 'title', 'album', 'artist', or 'fmtLength'
*/ */
sort(key) { sort(key, subSortKey) {
if (this.sortable === false) { if (this.sortable === false) {
return; return;
} }
this.sortKey = key; this.sortKey = key;
this.subSortKey = subSortKey ? subSortKey : 0;
this.order = 0 - this.order; this.order = 0 - this.order;
}, },