2012-03-28 22:49:43 +00:00
// ==UserScript==
2015-06-10 12:59:12 +00:00
// @name Import Bandcamp releases to MusicBrainz
2015-05-28 08:26:38 +00:00
// @description Add a button on Bandcamp's album pages to open MusicBrainz release editor with pre-filled data for the selected release
2022-05-06 13:56:08 +00:00
// @version 2022.5.6.1
2012-03-28 22:49:43 +00:00
// @namespace http://userscripts.org/users/22504
2014-02-22 09:33:26 +00:00
// @downloadURL https://raw.github.com/murdos/musicbrainz-userscripts/master/bandcamp_importer.user.js
// @updateURL https://raw.github.com/murdos/musicbrainz-userscripts/master/bandcamp_importer.user.js
2021-10-17 16:31:37 +00:00
// @include /^https?://[^/]+/(?:album|track)/[^/]+\/?$/
2021-11-13 15:48:51 +00:00
// @include /^https?://web\.archive\.org/web/\d+/https?://[^/]+/(?:album|track)/[^/]+\/?$/
2022-04-10 02:08:53 +00:00
// @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
2015-06-17 22:29:08 +00:00
// @require lib/mbimport.js
2015-06-05 09:13:42 +00:00
// @require lib/logger.js
// @require lib/mblinks.js
2015-06-15 07:52:59 +00:00
// @require lib/mbimportstyle.js
2015-07-10 12:27:31 +00:00
// @icon https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/assets/images/Musicbrainz_import_logo.png
2019-01-05 14:48:14 +00:00
// @grant unsafeWindow
2021-11-13 15:48:51 +00:00
// @run-at document-start
2012-03-28 22:49:43 +00:00
// ==/UserScript==
2015-06-05 08:39:00 +00:00
// prevent JQuery conflicts, see http://wiki.greasespot.net/@grant
this . $ = this . jQuery = jQuery . noConflict ( true ) ;
2021-12-03 15:31:45 +00:00
// eslint-disable-next-line no-global-assign
2015-05-25 16:13:40 +00:00
if ( ! unsafeWindow ) unsafeWindow = window ;
2015-05-16 12:41:23 +00:00
2020-04-05 14:01:21 +00:00
String . prototype . fix _bandcamp _url = function ( ) {
Bandcamp Importer: Use only https URLs
- Bandcamp now redirects all http URLs to https
- They didn't change URLs in DOM data yet, most are still http
- MB changed Bandcamp URLs normalization to use https
- MB didn't change old Bandcamp http URLs in the database yet
Basically this breaks some features of the importer, like detection of existing artists, labels, and releases (small icons)
Since the importer has no way to know if the url is stored as http or https, the result is quite random.
One solution would be to query for both, doubling the number of requests to the web service.
Another, much better, is to convert all old Bandcamp http URLs in MB database to https (all new URLs are normalized to https).
This patch only fix http URLs still appearing in pages, and look up for https URLS on the web service.
So, at the moment, the fix is incomplete.
2018-06-01 21:22:19 +00:00
return this . replace ( 'http://' , 'https://' ) ;
} ;
2021-12-03 15:31:45 +00:00
const BandcampImport = {
2018-11-20 22:18:49 +00:00
// Analyze Bandcamp data and return a release object
2020-04-05 14:01:21 +00:00
retrieveReleaseInfo : function ( ) {
2018-11-20 22:18:49 +00:00
let bandcampAlbumData = unsafeWindow . TralbumData ;
let bandcampEmbedData = unsafeWindow . EmbedData ;
let release = {
discs : [ ] ,
artist _credit : [ ] ,
2020-02-14 14:08:57 +00:00
barcode : '' ,
2018-11-20 22:18:49 +00:00
title : '' ,
year : 0 ,
month : 0 ,
day : 0 ,
parent _album _url : '' ,
labels : [ ] ,
format : 'Digital Media' ,
country : 'XW' ,
type : '' ,
status : 'official' ,
packaging : 'None' ,
language : 'eng' ,
script : 'Latn' ,
urls : [ ] ,
2020-04-05 14:01:21 +00:00
url : bandcampAlbumData . url . fix _bandcamp _url ( ) ,
2018-11-20 22:18:49 +00:00
} ;
2012-03-28 22:49:43 +00:00
2018-11-20 22:18:49 +00:00
// Grab release title
release . title = bandcampAlbumData . current . title ;
// Grab release event information
let date = this . convdate ( bandcampAlbumData . current . release _date ) ;
if ( date ) {
if ( ! ( date . year > 2008 || ( date . year == 2008 && date . month >= 9 ) ) ) {
// use publish date if release date is before Bandcamp launch (2008-09)
let pdate = this . convdate ( bandcampAlbumData . current . publish _date ) ;
if ( pdate ) {
date = pdate ;
}
}
release . year = date . year ;
release . month = date . month ;
release . day = date . day ;
2014-10-08 21:24:41 +00:00
}
2015-06-18 09:52:59 +00:00
2018-11-20 22:18:49 +00:00
// FIXME: implement a mapping between bandcamp release types and MB ones
if ( bandcampAlbumData . current . type == 'track' ) {
// map Bandcamp single tracks to singles
release . type = 'single' ;
// if track belongs to an album, get its url.
if ( bandcampEmbedData . album _embed _data ) {
release . parent _album _url = bandcampEmbedData . album _embed _data . linkback . fix _bandcamp _url ( ) ;
release . type = 'track' ; // <-- no import
}
2015-06-18 09:52:59 +00:00
}
2015-05-29 08:58:14 +00:00
2018-11-20 22:18:49 +00:00
// Tracks
let disc = {
tracks : [ ] ,
2020-04-05 14:01:21 +00:00
format : release . format ,
2015-05-29 08:58:14 +00:00
} ;
2018-11-20 22:18:49 +00:00
release . discs . push ( disc ) ;
// attempt to detect multiple artists tracks
// bandcamp formats them as 'artist - tracktitle'
// only set to true if ALL tracks are formatted like this
// and if string doesn't start with a number (ie. 02 - title)
let various _artists = true ;
2021-12-03 15:31:45 +00:00
for ( let i = 0 ; i < bandcampAlbumData . trackinfo . length ; i ++ ) {
2018-11-20 22:18:49 +00:00
if ( ! bandcampAlbumData . trackinfo [ i ] . title . match ( / - / ) || bandcampAlbumData . trackinfo [ i ] . title . match ( /^\d+ - / ) ) {
various _artists = false ;
break ;
}
}
2015-05-29 08:58:14 +00:00
2018-11-20 22:18:49 +00:00
// Release artist credit
if ( bandcampAlbumData . artist . match ( /^various(?: artists)?$/i ) && various _artists ) {
release . artist _credit = [ MBImport . specialArtist ( 'various_artists' ) ] ;
} else {
release . artist _credit = MBImport . makeArtistCredits ( [ bandcampAlbumData . artist ] ) ;
}
let tracks _streamable = 0 ;
2020-04-05 14:01:21 +00:00
$ . each ( bandcampAlbumData . trackinfo , function ( index , bctrack ) {
2018-11-20 22:18:49 +00:00
let title = bctrack . title ;
let artist = [ ] ;
if ( various _artists ) {
let m = bctrack . title . match ( /^(.+) - (.+)$/ ) ;
if ( m ) {
title = m [ 2 ] ;
artist = [ m [ 1 ] ] ;
}
}
if ( bctrack . file ) tracks _streamable ++ ;
let track = {
title : title ,
duration : Math . round ( bctrack . duration * 1000 ) ,
2020-04-05 14:01:21 +00:00
artist _credit : MBImport . makeArtistCredits ( artist ) ,
2018-11-20 22:18:49 +00:00
} ;
disc . tracks . push ( track ) ;
2015-05-25 16:13:40 +00:00
} ) ;
2015-06-12 21:54:01 +00:00
2018-11-20 22:18:49 +00:00
// Check for hidden tracks (more tracks in the download than shown for streaming ie.)
let showntracks = bandcampAlbumData . trackinfo . length ;
let numtracks = - 1 ;
let nostream = false ;
// album description indicates number of tracks in the download
2022-04-10 02:08:53 +00:00
let match = /^\d+ track album$/ . exec ( document . querySelector ( 'meta[property="og:description"]' ) . getAttribute ( 'content' ) ) ;
2018-11-20 22:18:49 +00:00
if ( match ) {
numtracks = parseInt ( match , 10 ) ;
}
if ( numtracks > 0 && numtracks > showntracks ) {
// display a warning if tracks in download differs from tracks shown
$ ( 'h2.trackTitle' ) . append (
2022-04-10 02:08:53 +00:00
` <p style="font-size: 70%; font-style: italic; margin: 0.1em 0;">Warning: ${ numtracks } vs ${ showntracks } tracks</p> `
2018-11-20 22:18:49 +00:00
) ;
// append unknown tracks to the release
2021-12-03 15:31:45 +00:00
for ( let i = 0 ; i < numtracks - showntracks ; i ++ ) {
2018-11-20 22:18:49 +00:00
let track = {
title : '[unknown]' ,
duration : null ,
2020-04-05 14:01:21 +00:00
artist _credit : [ ] ,
2018-11-20 22:18:49 +00:00
} ;
disc . tracks . push ( track ) ;
}
// disable stream link as only part of the album can be streamed
nostream = true ;
}
2012-03-28 22:49:43 +00:00
2018-11-20 22:18:49 +00:00
// URLs
let link _type = MBImport . URL _TYPES ;
// Download for free vs. for purchase
if ( bandcampAlbumData . current . download _pref !== null ) {
if (
bandcampAlbumData . freeDownloadPage !== null ||
bandcampAlbumData . current . download _pref === 1 ||
( bandcampAlbumData . current . download _pref === 2 && bandcampAlbumData . current . minimum _price === 0 )
) {
release . urls . push ( {
url : release . url ,
2020-04-05 14:01:21 +00:00
link _type : link _type . download _for _free ,
2018-11-20 22:18:49 +00:00
} ) ;
}
if ( bandcampAlbumData . current . download _pref === 2 ) {
release . urls . push ( {
url : release . url ,
2020-04-05 14:01:21 +00:00
link _type : link _type . purchase _for _download ,
2018-11-20 22:18:49 +00:00
} ) ;
}
}
// Check if the release is streamable
if ( bandcampAlbumData . hasAudio && ! nostream && disc . tracks . length > 0 && disc . tracks . length == tracks _streamable ) {
release . urls . push ( {
url : release . url ,
2020-04-05 14:01:21 +00:00
link _type : link _type . stream _for _free ,
2018-11-20 22:18:49 +00:00
} ) ;
}
// Check if release is Creative Commons licensed
2022-04-10 02:08:53 +00:00
const ccIcons = document . querySelector ( 'div#license a.cc-icons' ) ;
if ( ccIcons ) {
2018-11-20 22:18:49 +00:00
release . urls . push ( {
2022-04-10 02:08:53 +00:00
url : ccIcons . getAttribute ( 'href' ) ,
2020-04-05 14:01:21 +00:00
link _type : link _type . license ,
2018-11-20 22:18:49 +00:00
} ) ;
}
// Check if album has a back link to a label
2019-12-21 11:24:35 +00:00
let label = this . getlabelname ( ) ;
2018-11-20 22:18:49 +00:00
if ( label ) {
release . labels . push ( {
name : label ,
mbid : '' ,
2020-04-05 14:01:21 +00:00
catno : 'none' ,
2018-11-20 22:18:49 +00:00
} ) ;
}
2015-06-16 13:34:02 +00:00
2020-09-01 14:17:10 +00:00
// UPCs generally apply to physical releases so set the barcode when
// digital download is the only available medium
let mediums = bandcampAlbumData . packages ;
2020-06-08 17:58:37 +00:00
let upc = bandcampAlbumData . current . upc ;
2020-09-01 14:17:10 +00:00
if ( ( mediums === null || mediums . length === 0 ) && upc !== null ) {
2020-06-08 17:58:37 +00:00
release . barcode = upc ;
}
2018-11-20 22:18:49 +00:00
return release ;
} ,
2015-05-25 16:13:40 +00:00
2018-11-20 22:18:49 +00:00
// Insert links in page
2020-04-05 14:01:21 +00:00
insertLink : function ( release ) {
2018-11-20 22:18:49 +00:00
if ( release . type == 'track' ) {
// only import album or single, tracks belong to an album
return false ;
}
// Form parameters
let edit _note = MBImport . makeEditNote ( release . url , 'Bandcamp' ) ;
let parameters = MBImport . buildFormParameters ( release , edit _note ) ;
// Build form
let mbUI = $ ( ` <div id="mb_buttons"> ${ MBImport . buildFormHTML ( parameters ) } ${ MBImport . buildSearchButton ( release ) } </div> ` ) . hide ( ) ;
// Append MB import link
$ ( '#name-section' ) . append ( mbUI ) ;
2022-04-10 02:08:53 +00:00
document . querySelector ( '#mb_buttons' ) . style . marginTop = '6px' ;
2022-05-06 13:56:08 +00:00
document . querySelectorAll ( 'form.musicbrainz_import' ) . forEach ( form => ( form . style . display = 'inline-block' ) ) ;
2018-11-20 22:18:49 +00:00
mbUI . slideDown ( ) ;
} ,
// helper to convert bandcamp date to MB date
2020-04-05 14:01:21 +00:00
convdate : function ( date ) {
2018-11-20 22:18:49 +00:00
if ( typeof date != 'undefined' && date !== '' ) {
let d = new Date ( date ) ;
return {
year : d . getUTCFullYear ( ) ,
month : d . getUTCMonth ( ) + 1 ,
2020-04-05 14:01:21 +00:00
day : d . getUTCDate ( ) ,
2018-11-20 22:18:49 +00:00
} ;
}
return false ;
2019-12-21 11:24:35 +00:00
} ,
// get label name from back link if possible
2020-04-05 14:01:21 +00:00
getlabelname : function ( ) {
let label = $ ( 'a.back-to-label-link span.back-link-text' ) . contents ( ) . get ( 2 ) ;
2019-12-21 11:24:35 +00:00
if ( typeof label == 'undefined' ) {
return '' ;
}
2019-12-21 12:33:39 +00:00
return label . textContent ;
2020-04-05 14:01:21 +00:00
} ,
2015-05-25 16:13:40 +00:00
} ;
2021-11-13 15:48:51 +00:00
if ( window . location . hostname === 'web.archive.org' ) {
window . addEventListener ( 'beforescriptexecute' , function ( e ) {
let prev = e . target . previousElementSibling ;
if ( ! prev || ! prev . src ) {
return ;
}
let patchproc = function ( ) {
if ( ! window . _WBWombat ) {
return ;
}
// Patch the Wombat options to exclude Musicbrainz URLs
let oldWombat = window . _WBWombat ;
window . _WBWombat = function ( wbwindow , wbinfo ) {
wbinfo . wombat _opts . no _rewrite _prefixes . push ( 'https://musicbrainz.org/' ) ;
wbinfo . wombat _opts . no _rewrite _prefixes . push ( 'http://musicbrainz.org/' ) ;
wbinfo . wombat _opts . no _rewrite _prefixes . push ( '//musicbrainz.org/' ) ;
return oldWombat ( wbwindow , wbinfo ) ;
} ;
} ;
// Insert our payload after Wombat's been loaded, but before its initialised
if ( prev . src . search ( /\/_static\/js\/wombat.js/ ) != - 1 ) {
window . removeEventListener ( 'beforescriptexecute' , arguments . callee ) ;
let script = document . createElement ( 'script' ) ;
script . type = 'text/javascript' ;
script . text = ` ( ${ patchproc } )() ` ;
prev . parentNode . insertBefore ( script , e . target ) ;
}
} ) ;
}
2020-04-05 14:01:21 +00:00
$ ( document ) . ready ( function ( ) {
2018-11-20 22:18:49 +00:00
/ * k e e p t h e f o l l o w i n g l i n e a s f i r s t , i t i s r e q u i r e d t o s k i p
* pages which aren ' t actually a bandcamp page , since we support
* bandcamp pages under third - party domains .
* see @ include
* /
if ( ! unsafeWindow . TralbumData ) return ;
/***/
2015-06-16 19:33:26 +00:00
2018-11-20 22:18:49 +00:00
MBImportStyle ( ) ;
2015-05-25 16:13:40 +00:00
2018-11-20 22:18:49 +00:00
let mblinks = new MBLinks ( 'BCI_MBLINKS_CACHE' ) ;
2015-06-05 08:39:00 +00:00
2018-11-20 22:18:49 +00:00
let release = BandcampImport . retrieveReleaseInfo ( ) ;
2015-06-06 23:20:23 +00:00
2018-11-20 22:18:49 +00:00
// add MB artist link
2021-12-03 15:31:45 +00:00
let root _url = release . url . match ( /^(https?:\/\/[^/]+)/ ) [ 1 ] . split ( '?' ) [ 0 ] ;
2018-11-20 22:18:49 +00:00
let label _url = '' ;
2020-10-06 22:02:42 +00:00
2018-11-20 22:18:49 +00:00
mblinks . searchAndDisplayMbLink (
root _url ,
'label' ,
2020-04-05 14:01:21 +00:00
function ( link ) {
2018-11-20 22:18:49 +00:00
$ ( 'p#band-name-location span.title' ) . append ( link ) ;
} ,
` label: ${ root _url } `
) ;
2022-04-10 02:08:53 +00:00
const labelback = document . querySelector ( 'a.back-to-label-link' ) ;
2018-11-20 22:18:49 +00:00
if ( labelback ) {
2022-04-10 02:08:53 +00:00
const labelbacklink = labelback . getAttribute ( 'href' ) ;
2018-11-20 22:18:49 +00:00
if ( labelbacklink ) {
label _url = labelbacklink
2021-12-03 15:31:45 +00:00
. match ( /^(https?:\/\/[^/]+)/ ) [ 1 ]
2018-11-20 22:18:49 +00:00
. split ( '?' ) [ 0 ]
. fix _bandcamp _url ( ) ;
mblinks . searchAndDisplayMbLink (
label _url ,
'label' ,
2020-04-05 14:01:21 +00:00
function ( link ) {
2018-11-20 22:18:49 +00:00
$ ( 'a.back-to-label-link span.back-link-text' ) . append ( link ) ;
} ,
` label: ${ label _url } `
) ;
}
2017-09-05 08:34:13 +00:00
}
2015-06-05 08:39:00 +00:00
2018-11-20 22:18:49 +00:00
if ( release . artist _credit . length == 1 ) {
// try to get artist's mbid from cache
let artist _mbid = mblinks . resolveMBID ( root _url ) ;
if ( artist _mbid ) {
release . artist _credit [ 0 ] . mbid = artist _mbid ;
}
2015-06-16 19:24:01 +00:00
}
2015-07-10 08:14:01 +00:00
2018-11-20 22:18:49 +00:00
// try to get label mbid from cache
let label _mbid = '' ;
let label _name = '' ;
if ( label _url ) {
label _mbid = mblinks . resolveMBID ( ` label: ${ label _url } ` ) ;
2019-12-21 11:24:35 +00:00
label _name = BandcampImport . getlabelname ( ) ;
2018-11-20 22:18:49 +00:00
} else {
label _mbid = mblinks . resolveMBID ( ` label: ${ root _url } ` ) ;
2020-04-05 14:01:21 +00:00
if ( label _mbid ) label _name = $ ( 'p#band-name-location span.title' ) . text ( ) . trim ( ) ;
2018-11-20 22:18:49 +00:00
}
if ( label _mbid || label _name ) {
if ( release . labels . length == 0 ) {
release . labels . push ( {
name : '' ,
mbid : '' ,
2020-04-05 14:01:21 +00:00
catno : 'none' ,
2018-11-20 22:18:49 +00:00
} ) ;
}
release . labels [ 0 ] . name = label _name ;
release . labels [ 0 ] . mbid = label _mbid ;
2015-07-10 08:14:01 +00:00
}
2015-06-16 19:24:01 +00:00
2018-11-20 22:18:49 +00:00
BandcampImport . insertLink ( release ) ;
LOGGER . info ( 'Parsed release: ' , release ) ;
2015-06-05 08:39:00 +00:00
2018-11-20 22:18:49 +00:00
if ( release . type == 'track' ) {
2020-10-06 22:02:42 +00:00
mblinks . searchAndDisplayMbLink ( root _url , 'artist' , function ( link ) {
$ ( 'div#name-section h3 span:last' ) . before ( link ) ;
} ) ;
2018-11-20 22:18:49 +00:00
// add MB links to parent album
2020-04-05 14:01:21 +00:00
mblinks . searchAndDisplayMbLink ( release . parent _album _url , 'release' , function ( link ) {
2020-10-06 22:02:42 +00:00
$ ( 'div#name-section h3 span:first' ) . before ( link ) ;
2018-11-20 22:18:49 +00:00
} ) ;
} else {
2020-10-06 22:02:42 +00:00
mblinks . searchAndDisplayMbLink ( root _url , 'artist' , function ( link ) {
$ ( 'div#name-section h3 span:first' ) . before ( link ) ;
} ) ;
2018-11-20 22:18:49 +00:00
// add MB release links to album or single
2020-04-05 14:01:21 +00:00
mblinks . searchAndDisplayMbLink ( release . url , 'release' , function ( link ) {
2020-10-06 22:02:42 +00:00
$ ( 'div#name-section h3 span:first' ) . after ( link ) ;
2018-11-20 22:18:49 +00:00
} ) ;
}
2012-03-28 22:49:43 +00:00
2018-11-20 22:18:49 +00:00
// append a comma after each tag to ease cut'n'paste to MB
$ ( 'div.tralbum-tags a:not(:last-child).tag' ) . after ( ', ' ) ;
2015-06-01 10:01:08 +00:00
2018-11-20 22:18:49 +00:00
// append a link to the full size image
2022-04-10 02:08:53 +00:00
const tralbumArt = document . querySelector ( 'div#tralbumArt' ) ;
const fullsizeimageurl = tralbumArt . querySelector ( 'a' ) . getAttribute ( 'href' ) . replace ( '_10' , '_0' ) ;
tralbumArt . insertAdjacentHTML (
'afterend' ,
2018-11-20 22:18:49 +00:00
` <div id='bci_link'><a class='custom-color' href=' ${ fullsizeimageurl } ' title='Open original image in a new tab (Bandcamp importer)' target='_blank'>Original image</a></div> `
) ;
2015-05-25 16:13:40 +00:00
2022-04-10 02:08:53 +00:00
const bci _link = document . querySelector ( 'div#bci_link' ) ;
bci _link . style . paddingTop = '0.5em' ;
bci _link . style . textAlign = 'right' ;
bci _link . querySelector ( 'a' ) . style . fontWeight = 'bold' ;
const upc = unsafeWindow . TralbumData . current . upc ;
2020-02-14 14:08:57 +00:00
if ( typeof upc != 'undefined' && upc !== null ) {
2022-04-10 02:08:53 +00:00
document
. querySelector ( 'div #trackInfoInner' )
. insertAdjacentHTML (
'beforeend' ,
` <div id="mbimport_upc" style="margin-bottom: 2em; font-size: smaller;">UPC: <a href="https://atisket.pulsewidth.org.uk/?upc= ${ upc } "> ${ upc } </a></div> `
) ;
2020-02-14 14:08:57 +00:00
}
2015-05-25 16:13:40 +00:00
} ) ;