2012-03-28 22:16:10 +00:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MusicBrainz Import helper functions
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/ *
2015-01-18 12:34:10 +00:00
* How to use this module ?
*
* - First build a release object ( see expected format below ) that you ' ll fill in from source of data
* - Call as follows , e . g . :
2015-06-17 22:29:08 +00:00
* var parameters = MBImport . buildFormParameters ( parsedRelease , optionalEditNote ) ;
2015-01-18 12:34:10 +00:00
* - Then build the HTML that you ' ll inject into source site page :
2015-06-17 22:29:08 +00:00
* var formHtml = MBImport . buildFormHTML ( parameters ) ;
2015-01-18 12:34:10 +00:00
* - Addinionally , you can inject a search link to verify that the release is not already known by MusicBrainz :
2015-06-17 22:29:08 +00:00
* var linkHtml = MBImport . buildSearchLink ( parsedRelease ) ;
2015-01-18 12:34:10 +00:00
*
* Expected format of release object :
*
* release = {
* title ,
* artist _credit ,
* type ,
* status ,
* language ,
* script ,
* packaging ,
* country ,
* year ,
* month ,
* day ,
* labels = [ { name , mbid , catno } , ... ] ,
* barcode ,
* urls = [ { url , link _type } , ... ] ,
* discs = [
* {
* title ,
* format ,
* tracks = [
* { number , title , duration , artist _credit } ,
* ...
* ]
* } ,
* ...
* ] ,
* }
*
* where 'artist_credit' has the following format :
*
* artist _credit = [
* {
* credited _name ,
* artist _name ,
* artist _mbid ,
* joinphrase
* } ,
* ...
* ]
*
* /
2012-03-28 22:16:10 +00:00
2015-06-17 22:29:08 +00:00
var MBImport = ( function ( ) {
2012-03-28 22:16:10 +00:00
// --------------------------------------- publics ----------------------------------------- //
2015-06-12 19:28:24 +00:00
var url _types = {
purchase _for _download : 74 ,
download _for _free : 75 ,
2015-06-12 22:15:20 +00:00
discogs : 76 ,
purchase _for _mail _order : 79 ,
2015-06-12 22:30:38 +00:00
other _databases : 82 ,
2015-06-12 19:28:24 +00:00
stream _for _free : 85 ,
2015-06-12 22:15:20 +00:00
license : 301
2015-06-12 19:28:24 +00:00
}
2012-03-28 22:16:10 +00:00
// compute HTML of search link
2014-02-21 17:42:25 +00:00
function fnBuildSearchLink ( release ) {
2015-06-16 13:34:02 +00:00
var parameters = searchParams ( release ) ;
var url _params = [ ] ;
parameters . forEach ( function ( parameter ) {
var value = parameter . value + "" ;
url _params . push ( encodeURI ( parameter . name + '=' + value ) ) ;
} ) ;
return '<a class="musicbrainz_import" href="//musicbrainz.org/search?' + url _params . join ( '&' ) + '">Search in MusicBrainz</a>' ;
}
2012-03-28 22:16:10 +00:00
2015-06-16 13:34:02 +00:00
// compute HTML of search button
function fnBuildSearchButton ( release ) {
var parameters = searchParams ( release ) ;
var html = '<form class="musicbrainz_import musicbrainz_import_search" action="//musicbrainz.org/search" method="get" target="_blank" accept-charset="UTF-8" charset="' + document . characterSet + '">' ;
parameters . forEach ( function ( parameter ) {
var value = parameter . value + "" ;
html += "<input type='hidden' value='" + value . replace ( /'/g , "'" ) + "' name='" + parameter . name + "'/>" ;
} ) ;
html += '<button type="submit" title="Search for this release in MusicBrainz (open a new tab)"><span>Search in MB</span></button>' ;
html += '</form>' ;
return html ;
2012-03-28 22:16:10 +00:00
}
2015-06-14 19:57:57 +00:00
function fnSearchUrlFor ( type , what ) {
type = type . replace ( '-' , '_' ) ;
var params = [
'query=' + luceneEscape ( what ) ,
'type=' + type ,
'indexed=1'
] ;
return '//musicbrainz.org/search?' + params . join ( '&' ) ;
}
2012-03-28 22:16:10 +00:00
// compute HTML of import form
2014-02-21 17:42:25 +00:00
function fnBuildFormHTML ( parameters ) {
2012-03-28 22:16:10 +00:00
2014-02-21 17:46:29 +00:00
// Build form
2015-06-16 13:34:02 +00:00
var innerHTML = '<form class="musicbrainz_import musicbrainz_import_add" action="//musicbrainz.org/release/add" method="post" target="_blank" accept-charset="UTF-8" charset="' + document . characterSet + '">' ;
2014-02-21 17:46:29 +00:00
parameters . forEach ( function ( parameter ) {
2012-03-28 22:16:10 +00:00
var value = parameter . value + "" ;
2014-02-21 17:46:29 +00:00
innerHTML += "<input type='hidden' value='" + value . replace ( /'/g , "'" ) + "' name='" + parameter . name + "'/>" ;
} ) ;
2012-03-28 22:16:10 +00:00
2015-06-16 13:34:02 +00:00
innerHTML += '<button type="submit" title="Import this release into MusicBrainz (open a new tab)"><img src="//musicbrainz.org/favicon.ico" /><span>Import into MB</span></button>' ;
2014-02-21 17:46:29 +00:00
innerHTML += '</form>' ;
2012-03-28 22:16:10 +00:00
return innerHTML ;
}
// build form POST parameters that MB is waiting
2014-02-21 17:42:25 +00:00
function fnBuildFormParameters ( release , edit _note ) {
2014-02-21 17:46:29 +00:00
// Form parameters
var parameters = new Array ( ) ;
appendParameter ( parameters , 'name' , release . title ) ;
2012-03-28 22:16:10 +00:00
2014-02-21 17:46:29 +00:00
// Release Artist credits
buildArtistCreditsFormParameters ( parameters , "" , release . artist _credit ) ;
2012-03-28 22:16:10 +00:00
2014-08-14 23:55:30 +00:00
if ( release [ "secondary_types" ] ) {
for ( var i = 0 ; i < release . secondary _types . length ; i ++ ) {
appendParameter ( parameters , 'type' , release . secondary _types [ i ] ) ;
}
}
2014-02-21 17:46:29 +00:00
appendParameter ( parameters , 'status' , release . status ) ;
appendParameter ( parameters , 'language' , release . language ) ;
appendParameter ( parameters , 'script' , release . script ) ;
appendParameter ( parameters , 'packaging' , release . packaging ) ;
2012-03-28 22:16:10 +00:00
2015-06-07 19:48:00 +00:00
// ReleaseGroup
appendParameter ( parameters , 'release_group' , release . release _group _mbid ) ;
2014-02-21 17:46:29 +00:00
// Date + country
appendParameter ( parameters , 'country' , release . country ) ;
if ( ! isNaN ( release . year ) && release . year != 0 ) { appendParameter ( parameters , 'date.year' , release . year ) ; } ;
if ( ! isNaN ( release . month ) && release . month != 0 ) { appendParameter ( parameters , 'date.month' , release . month ) ; } ;
if ( ! isNaN ( release . day ) && release . day != 0 ) { appendParameter ( parameters , 'date.day' , release . day ) ; } ;
2012-03-28 22:16:10 +00:00
// Barcode
2014-02-21 18:21:53 +00:00
appendParameter ( parameters , 'barcode' , release . barcode ) ;
2012-03-28 22:16:10 +00:00
2014-02-21 17:46:29 +00:00
// Label + catnos
2012-03-28 22:16:10 +00:00
for ( var i = 0 ; i < release . labels . length ; i ++ ) {
var label = release . labels [ i ] ;
appendParameter ( parameters , 'labels.' + i + '.name' , label . name ) ;
appendParameter ( parameters , 'labels.' + i + '.mbid' , label . mbid ) ;
2014-02-21 17:46:29 +00:00
if ( label . catno != "none" ) {
2012-03-28 22:16:10 +00:00
appendParameter ( parameters , 'labels.' + i + '.catalog_number' , label . catno ) ;
}
}
2014-02-21 17:42:25 +00:00
2014-02-21 19:04:38 +00:00
// URLs
for ( var i = 0 ; i < release . urls . length ; i ++ ) {
var url = release . urls [ i ] ;
appendParameter ( parameters , 'urls.' + i + '.url' , url . url ) ;
appendParameter ( parameters , 'urls.' + i + '.link_type' , url . link _type ) ;
}
2014-02-21 17:46:29 +00:00
// Mediums
2015-06-13 02:25:31 +00:00
var total _tracks = 0 ;
var total _tracks _with _duration = 0 ;
var total _duration = 0 ;
2014-02-21 17:46:29 +00:00
for ( var i = 0 ; i < release . discs . length ; i ++ ) {
var disc = release . discs [ i ] ;
appendParameter ( parameters , 'mediums.' + i + '.format' , disc . format ) ;
appendParameter ( parameters , 'mediums.' + i + '.name' , disc . title ) ;
// Tracks
for ( var j = 0 ; j < disc . tracks . length ; j ++ ) {
var track = disc . tracks [ j ] ;
2015-06-13 02:25:31 +00:00
total _tracks ++ ;
2014-02-21 17:46:29 +00:00
appendParameter ( parameters , 'mediums.' + i + '.track.' + j + '.number' , track . number ) ;
appendParameter ( parameters , 'mediums.' + i + '.track.' + j + '.name' , track . title ) ;
2015-06-13 02:25:31 +00:00
var tracklength = "?:??" ;
var duration _ms = hmsToMilliSeconds ( track . duration ) ;
if ( ! isNaN ( duration _ms ) ) {
tracklength = duration _ms ;
total _tracks _with _duration ++ ;
total _duration += duration _ms ;
}
2014-02-21 17:46:29 +00:00
appendParameter ( parameters , 'mediums.' + i + '.track.' + j + '.length' , tracklength ) ;
buildArtistCreditsFormParameters ( parameters , 'mediums.' + i + '.track.' + j + '.' , track . artist _credit ) ;
}
}
2012-03-28 22:16:10 +00:00
2015-06-13 02:25:31 +00:00
// Guess release type if not given
if ( ! release . type && release . title && total _tracks == total _tracks _with _duration ) {
release . type = fnGuessReleaseType ( release . title , total _tracks , total _duration ) ;
}
appendParameter ( parameters , 'type' , release . type ) ;
2014-02-21 17:46:29 +00:00
// Add Edit note parameter
appendParameter ( parameters , 'edit_note' , edit _note ) ;
2014-02-21 17:42:25 +00:00
2014-02-21 17:46:29 +00:00
return parameters ;
2012-03-28 22:16:10 +00:00
}
2015-06-12 20:01:06 +00:00
// Convert a list of artists to a list of artist credits with joinphrases
function fnArtistCredits ( artists _list ) {
var artists = artists _list . map ( function ( item ) { return { artist _name : item } ; } ) ;
if ( artists . length > 2 ) {
var last = artists . pop ( ) ;
last . joinphrase = '' ;
var prev = artists . pop ( ) ;
prev . joinphrase = ' & ' ;
for ( var i = 0 ; i < artists . length ; i ++ ) {
artists [ i ] . joinphrase = ', ' ;
}
artists . push ( prev ) ;
artists . push ( last ) ;
} else if ( artists . length == 2 ) {
artists [ 0 ] . joinphrase = ' & ' ;
}
2015-06-13 13:18:04 +00:00
var credits = [ ] ;
// re-split artists if featuring or vs
artists . map ( function ( item ) {
var c = item . artist _name . replace ( /\s*(?:feat\.?|ft\.?|featuring)\s+/gi , ' feat. ' ) ;
c = c . replace ( /\s*\(( feat. )([^\)]+)\)/g , '$1$2' ) ;
c = c . replace ( /\s+(?:versus|vs\.?)\s+/gi , ' vs. ' ) ;
c = c . replace ( /\s+/g , ' ' ) ;
var splitted = c . split ( /( feat\. | vs\. )/ ) ;
if ( splitted . length == 1 ) {
credits . push ( item ) ; // nothing to split
} else {
var new _items = [ ] ;
var n = 0 ;
for ( var i = 0 ; i < splitted . length ; i ++ ) {
if ( n && ( splitted [ i ] == ' feat. ' || splitted [ i ] == ' vs. ' ) ) {
new _items [ n - 1 ] . joinphrase = splitted [ i ] ;
} else {
new _items [ n ++ ] = {
artist _name : splitted [ i ] . trim ( ) ,
joinphrase : ''
} ;
}
}
new _items [ n - 1 ] . joinphrase = item . joinphrase ;
new _items . map ( function ( newit ) {
credits . push ( newit )
} ) ;
}
} ) ;
2015-06-15 15:23:24 +00:00
return credits . map ( function ( item ) {
2015-06-17 13:04:38 +00:00
var mappings = {
'[unknown]' : '125ec42a-7229-4250-afc5-e057484327fe' ,
'Various Artists' : '89ad4ac3-39f7-470e-963a-56509c546377'
} ;
if ( ! item . mbid && item . artist _name in mappings ) {
item . mbid = mappings [ item . artist _name ] ;
2015-06-15 15:23:24 +00:00
}
return item ;
} ) ;
2015-06-12 20:01:06 +00:00
}
2015-06-13 02:25:31 +00:00
// Try to guess release type using number of tracks, title and total duration (in millisecs)
function fnGuessReleaseType ( title , num _tracks , duration _ms ) {
if ( num _tracks < 1 ) return '' ;
2015-06-12 20:40:40 +00:00
var has _single = ! ! title . match ( /\bsingle\b/i ) ;
var has _EP = ! ! title . match ( /\bEP\b/i ) ;
if ( has _single && has _EP ) {
has _single = false ;
has _EP = false ;
}
2015-06-13 02:25:31 +00:00
var perhaps _single = ( ( has _single && num _tracks <= 4 ) || num _tracks <= 2 ) ;
var perhaps _EP = has _EP || ( num _tracks > 2 && num _tracks <= 6 ) ;
var perhaps _album = ( num _tracks > 8 ) ;
if ( isNaN ( duration _ms ) ) {
// no duration, try to guess with title and number of tracks
if ( perhaps _single && ! perhaps _EP && ! perhaps _album ) return 'single' ;
if ( ! perhaps _single && perhaps _EP && ! perhaps _album ) return 'EP' ;
if ( ! perhaps _single && ! perhaps _EP && perhaps _album ) return 'album' ;
return '' ;
}
var duration _mn = duration _ms / ( 60 * 1000 ) ;
if ( perhaps _single && duration _mn >= 1 && duration _mn < 7 ) return 'single' ;
if ( perhaps _EP && duration _mn > 7 && duration _mn <= 30 ) return 'EP' ;
if ( perhaps _album && duration _mn > 30 ) return 'album' ;
2015-06-12 20:40:40 +00:00
return '' ;
}
2015-06-13 02:25:31 +00:00
// convert HH:MM:SS or MM:SS to milliseconds
function hmsToMilliSeconds ( str ) {
2015-06-15 07:50:40 +00:00
if ( typeof str == 'undefined' || str === null || str === NaN || str === '' ) return NaN ;
2015-06-13 02:25:31 +00:00
if ( typeof str == 'number' ) return str ;
var t = str . split ( ':' ) ;
var s = 0 ;
var m = 1 ;
while ( t . length > 0 ) {
s += m * parseInt ( t . pop ( ) , 10 ) ;
m *= 60 ;
}
return s * 1000 ;
}
2015-06-15 11:59:09 +00:00
// convert ISO8601 duration (limited to hours/minutes/seconds) to milliseconds
// format looks like PT1H45M5.789S (note: floats can be used)
// https://en.wikipedia.org/wiki/ISO_8601#Durations
function fnISO8601toMilliSeconds ( str ) {
var regex = /^PT(?:(\d*\.?\d*)H)?(?:(\d*\.?\d*)M)?(?:(\d*\.?\d*)S)?$/ ,
m = str . replace ( ',' , '.' ) . match ( regex ) ;
if ( ! m ) return NaN ;
return ( 3600 * parseFloat ( m [ 1 ] || 0 ) + 60 * parseFloat ( m [ 2 ] || 0 ) + parseFloat ( m [ 3 ] || 0 ) ) * 1000 ;
}
2015-06-17 09:51:51 +00:00
function fnMakeEditNote ( release _url , importer _name , format ) {
var home = 'https://github.com/murdos/musicbrainz-userscripts' ;
return 'Imported from ' + release _url + ( format ? '(' + format + ')' : '' ) + ' using ' + importer _name + ' import script from ' + home ;
}
2012-03-28 22:16:10 +00:00
// --------------------------------------- privates ----------------------------------------- //
function appendParameter ( parameters , paramName , paramValue ) {
if ( ! paramValue ) return ;
2014-02-21 17:46:29 +00:00
parameters . push ( { name : paramName , value : paramValue } ) ;
2012-03-28 22:16:10 +00:00
}
function luceneEscape ( text ) {
2015-06-16 13:29:40 +00:00
var newtext = text . replace ( /[-[\]{}()*+?~:\\^!"\/]/g , "\\$&" ) ;
2014-02-21 17:46:29 +00:00
return newtext . replace ( "&&" , "\&&" ) . replace ( "||" , "\||" ) ;
2012-03-28 22:16:10 +00:00
}
function buildArtistCreditsFormParameters ( parameters , paramPrefix , artist _credit ) {
if ( ! artist _credit ) return ;
for ( var i = 0 ; i < artist _credit . length ; i ++ ) {
var ac = artist _credit [ i ] ;
2014-02-21 17:46:29 +00:00
appendParameter ( parameters , paramPrefix + 'artist_credit.names.' + i + '.name' , ac . credited _name ) ;
2012-03-28 22:16:10 +00:00
appendParameter ( parameters , paramPrefix + 'artist_credit.names.' + i + '.artist.name' , ac . artist _name ) ;
2015-06-07 19:48:00 +00:00
appendParameter ( parameters , paramPrefix + 'artist_credit.names.' + i + '.mbid' , ac . mbid ) ;
2014-02-21 17:46:29 +00:00
if ( typeof ac . joinphrase != 'undefined' && ac . joinphrase != "" ) {
appendParameter ( parameters , paramPrefix + 'artist_credit.names.' + i + '.join_phrase' , ac . joinphrase ) ;
2012-03-28 22:16:10 +00:00
}
}
}
2015-06-16 13:34:02 +00:00
function searchParams ( release ) {
var params = [ ] ;
var totaltracks = 0 ;
for ( var i = 0 ; i < release . discs . length ; i ++ ) {
totaltracks += release . discs [ i ] . tracks . length ;
}
var release _artist = "" ;
for ( var i = 0 ; i < release . artist _credit . length ; i ++ ) {
var ac = release . artist _credit [ i ] ;
release _artist += ac . artist _name ;
if ( typeof ac . joinphrase != 'undefined' && ac . joinphrase != "" ) {
release _artist += ac . joinphrase ;
} else {
if ( i != release . artist _credit . length - 1 ) release _artist += ", " ;
}
}
var query = 'artist:(' + luceneEscape ( release _artist ) + ')'
+ ' release:(' + luceneEscape ( release . title ) + ')'
+ ' tracks:(' + totaltracks + ')'
+ ( release . country ? ' country:' + release . country : '' ) ;
appendParameter ( params , 'query' , query ) ;
appendParameter ( params , 'type' , 'release' ) ;
appendParameter ( params , 'advanced' , '1' ) ;
return params ;
}
2012-03-28 22:16:10 +00:00
// ---------------------------------- expose publics here ------------------------------------ //
2014-02-21 17:46:29 +00:00
return {
2015-06-12 19:28:24 +00:00
buildSearchLink : fnBuildSearchLink ,
2015-06-16 13:34:02 +00:00
buildSearchButton : fnBuildSearchButton ,
2015-06-12 19:28:24 +00:00
buildFormHTML : fnBuildFormHTML ,
buildFormParameters : fnBuildFormParameters ,
2015-06-12 20:01:06 +00:00
makeArtistCredits : fnArtistCredits ,
2015-06-12 20:40:40 +00:00
guessReleaseType : fnGuessReleaseType ,
2015-06-13 02:25:31 +00:00
hmsToMilliSeconds : hmsToMilliSeconds ,
2015-06-15 11:59:09 +00:00
ISO8601toMilliSeconds : fnISO8601toMilliSeconds ,
2015-06-17 09:51:51 +00:00
makeEditNote : fnMakeEditNote ,
2015-06-14 19:57:57 +00:00
searchUrlFor : fnSearchUrlFor ,
2015-06-12 19:28:24 +00:00
URL _TYPES : url _types
} ;
2012-03-28 22:16:10 +00:00
} ) ( ) ;