NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name MusicBrainz: Sort artists // @description Allows you to change the order of artist names in any of the multiple artists editors. NOTICE: This will remove any artist lookup data already present in the editor. You have to assign this manually again. // @supportURL https://github.com/JensBee/userscripts // @namespace http://www.jens-bertram.net/userscripts/sort-artists // @icon https://wiki.musicbrainz.org/-/images/3/39/MusicBrainz_Logo_Square_Transparent.png // @license MIT // @version 0.1.1beta // // @require https://greasyfork.org/scripts/5140-musicbrainz-function-library/code/MusicBrainz%20function%20library.js?version=21997 // // @grant none // @include *://musicbrainz.org/recording/*/edit // @include *://*.musicbrainz.org/recording/*/edit // @include *://musicbrainz.org/recording/create // @include *://*.musicbrainz.org/recording/create // @include *://musicbrainz.org/release/*/edit // @include *://*.musicbrainz.org/release/*/edit // @include *://musicbrainz.org/release-group/*/edit // @include *://*.musicbrainz.org/release-group/*/edit // @include *://musicbrainz.org/release/add // @include *://*.musicbrainz.org/release/add // @include *://musicbrainz.org/artist/*/split // @include *://*.musicbrainz.org/artist/*/split // ==/UserScript== // Hackish solution to re-sort artist credits. There's currently no (no known to // me) option to preserve already associated artist data. Sorting is done by // first removing all credits, resorting them and adding them again. //**************************************************************************// var mbz = mbz || {}; mbz.artist_sort = { btn: { down: '<button class="icon track-down artist_sort MBZ-ArtistSort" ' + 'type="button"></button>', up: '<button class="icon track-up artist_sort MBZ-ArtistSort" ' + 'type="button"></button>' }, notice: '<b>Notice:</b> When sorting artists you\'ll have ' + 'to lookup all associations already made again.' }; mbz.artist_sort.BubbleEditor = function() {}; mbz.artist_sort.BubbleEditor.prototype = { /** * Executes the sorting. */ _doSort: function(api, idx, dir) { this.rewriteRows(api, this.moveRow(this._scanRowData(api), idx, dir)); }, /** * Extract data from each artist credit table row. */ _scanRowData: function(api) { var rowsData = []; var rows = api.getCreditRows(); if (rows.length > 1) { $.each(rows, function(idx) { // collect row data var inputs = api.getCreditInputs($(this)); if (inputs.length == 3) { // mb-artist, artist-credit, join phrase rowsData.push([inputs[0].val(), inputs[1].val(), inputs[2].val()]); } else { console.err("Error scanning artist credits: inputs not found."); } }); } return rowsData; }, /** * Re-writes row data to move a data row up or down. * @rowsData current row data array * @idx Row index to move * @dir >0 move down, <0 move up * @return new row data array */ moveRow: function(rowsData, idx, dir) { var newRowData = null; if (dir > 0 && idx < rowsData.length -1) { // down newRowData = this.swapRows(rowsData, idx, idx + 1); } else if (dir < 0 && idx > 0){ // up newRowData = this.swapRows(rowsData, idx, idx - 1); } if (newRowData && newRowData.length > 0) { return newRowData; } return []; }, /** * Removes all rows from the editor and add all new ones with the updated * data. * @bubbleApi MBZ Bubble API to use for calls * @rowData new row data to write */ rewriteRows: function(bubbleApi, rowData) { if (rowData.length > 0) { // get current rows.. var rows = bubbleApi.getCreditRows(); // add a new empty one, that will survive, else any data may be still set bubbleApi.addArtist("", true); // remove all previous credits, but the last one will survive $.each(bubbleApi.getCreditRows(), function(){ bubbleApi.removeArtist(this); // remove by row }); // add as many rows as we need for (var i=0; i < rowData.length; i++) { bubbleApi.addArtist(rowData[i], true); } } }, /** * Swap position of two rows. * @rowsData current row data array * @indexA first row to swap * @indexB second row to swap * @return row data array with the two rows position swapped */ swapRows: function(rowsData, idxA, idxB) { var newRowData = []; for (var idx in rowsData) { if (idx == idxA) { newRowData[idx] = rowsData[idxB]; } else if (idx == idxB) { newRowData[idx] = rowsData[idxA]; } else { newRowData[idx] = rowsData[idx]; } } // switch join phrases var joinA = newRowData[idxA][2]; var joinB = newRowData[idxB][2]; newRowData[idxA][2] = joinB; newRowData[idxB][2] = joinA; return newRowData; } }; /** * Access the artists bubble editor. */ mbz.artist_sort.ArtistBubbleEditor = function() { var b = null; var initialized = false; var self = this; /** * Executes the sorting. */ function doSort(idx, dir) { self._doSort.call(self, MBZ.BubbleEditor.ArtistCredits, idx, dir); }; this.init = function(bubble) { if (initialized) { return; } if (bubble.length == 0) { console.debug("Credits bubble not found."); return; } initialized = true; b = bubble; b.on('click', 'button.MBZ-ArtistSort', function(){ var btn = $(this); var idx = btn.data('idx'); if (typeof idx !== 'undefined' && idx != null) { if (btn.hasClass('track-up')) { doSort(idx, -1); return false; } else if (btn.hasClass('track-down')) { doSort(idx, 1); return false; } } }); var notice = $(b.find('p').get(0)); notice.after('<p>' + mbz.artist_sort.notice + '</p>'); }; /** * Attach sort buttons to each data row. */ this.attachSortButtons = function() { var rows = MBZ.BubbleEditor.ArtistCredits.getCreditRows(); if (rows.length > 1) { var self = this; $.each(rows, function(idx) { var cell = $(this).children('td').first().find('label'); if (cell.find('button.artist_sort').length == 0) { var btnUp = $(mbz.artist_sort.btn.up); var btnDown = $(mbz.artist_sort.btn.down); btnUp.data('idx', idx); btnDown.data('idx', idx); cell.prepend(btnUp); cell.prepend(btnDown); } }); } }; MBZ.BubbleEditor.ArtistCredits.onAppear({cb: self.init}); MBZ.BubbleEditor.ArtistCredits.onContentChange({cb: self.attachSortButtons}); }; mbz.artist_sort.ArtistBubbleEditor.prototype = new mbz.artist_sort.BubbleEditor(); /** * Access the track artists bubble editor. */ mbz.artist_sort.TrackBubbleEditor = function() { var b = $('#track-ac-bubble'); var rowsData = []; var initialized = false; var self = this; /** * Executes the sorting. */ function doSort(idx, dir) { self._doSort.call(self, MBZ.BubbleEditor.TrackArtistCredits, idx, dir); }; /** * Initialize the component. */ function init() { if (initialized) { return; } initialized = true; b.find('thead').prepend('<tr><td colspan="4" style="padding-bottom:1em;">' + mbz.artist_sort.notice + '</td></tr>'); b.on('click', 'button.MBZ-ArtistSort', function(){ var btn = $(this); var idx = btn.data('idx'); if (typeof idx !== 'undefined' && idx != null) { if (btn.hasClass('track-up')) { doSort(idx, -1); return false; } else if (btn.hasClass('track-down')) { doSort(idx, 1); return false; } } }); }; /** * Attach sort buttons to each data row. */ this.attachSortButtons = function() { var rows = MBZ.BubbleEditor.TrackArtistCredits.getCreditRows(); if (rows.length > 1) { $.each(rows, function(idx) { var cell = $($(this).find('td').get(3)); if (cell.length == 1 && cell.find('button.MBZ-ArtistSort').length == 0) { var btnUp = $(mbz.artist_sort.btn.up); var btnDown = $(mbz.artist_sort.btn.down); btnUp.data('idx', idx); btnDown.data('idx', idx); cell.prepend(btnUp); cell.prepend(btnDown); } }); } }; MBZ.BubbleEditor.TrackArtistCredits.onContentChange({ cb: this.attachSortButtons}); init(); }; mbz.artist_sort.TrackBubbleEditor.prototype = new mbz.artist_sort.BubbleEditor(); /** * Initialize all required components. */ mbz.artist_sort.init = function() { var path = window.location.pathname; // artist bubble editor is always there new mbz.artist_sort.ArtistBubbleEditor(); // release page has also a track bubble editor if (path.contains('/release/')) { // resize bubble slightly to make space for sort buttons MBZ.Html.addStyle('#release-editor #track-ac-bubble {width:61%;}'); // track artist bubble editor new mbz.artist_sort.TrackBubbleEditor(); } } mbz.artist_sort.init();