NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name MusicBrainz: Import from Amazon // @namespace https://github.com/Goram/MusicBrainz-Import-from-Amazon // @include *://www.amazon.* // @version 0.98.5 // @grant none // @author Gore (based on https://github.com/dufferzafar/Userscripts) // @description Import releases from Amazon // @require https://code.jquery.com/jquery-2.2.3.min.js // @require https://code.jquery.com/ui/1.11.4/jquery-ui.min.js // @require https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js // @run-at document-end // ==/UserScript== 'use strict'; var jquery = {}; jquery = jQuery.noConflict(true); if (jquery('#rightCol').length) { jquery('#rightCol').prepend( '<data-gore-mbifa-bootstrap data-ng-app="goreMbifa" data-ng-controller="mbifaController">' ); } else if (jquery('.buyingDetailsGrid').length) { jquery('.buyingDetailsGrid').prepend( '<tr><td><data-gore-mbifa-bootstrap data-ng-app="goreMbifa" data-ng-controller="mbifaController"></td></tr>' ); } var goreMbifa = angular.module('goreMbifa', []); goreMbifa.constant('config', { 'sites': { 'www.amazon.de': { 'languages': { 'de': { 'months': { 'Januar': '1', 'Februar': '2', 'März': '3', 'April': '4', 'Mai': '5', 'Juni': '6', 'Juli': '7', 'August': '8', 'September': '9', 'Oktober': '10', 'November': '11', 'Dezember': '12' }, 'disc': 'Disk', 'releaseDateOrder': [0, 1, 2], 'regexReleaseDate': /Audio CD \((.*)\)/, 'regexNumDiscs': /Anzahl Disks\/Tonträger: (.*)/, 'regexReleaseLabel': /Label: (.*)/ } } }, 'www.amazon.com': { 'languages': { 'en': { 'months': { 'January': '1', 'February': '2', 'March': '3', 'April': '4', 'May': '5', 'June': '6', 'July': '7', 'August': '8', 'September': '9', 'October': '10', 'November': '11', 'December': '12' }, 'disc': 'Disc', 'releaseDateOrder': [1, 0, 2], 'regexReleaseDate': /Audio CD \((.*)\)/, 'regexNumDiscs': /Number of Discs: (.*)/, 'regexReleaseLabel': /Label: (.*)/ } } }, 'www.amazon.co.uk': { 'languages': { 'en': { 'months': { 'Jan.': '1', 'Feb.': '2', 'March': '3', 'April': '4', 'May': '5', 'June': '6', 'July': '7', 'Aug.': '8', 'Sept.': '9', 'Oct.': '10', 'Nov.': '11', 'Dec': '12' }, 'disc': 'Disc', 'releaseDateOrder': [0, 1, 2], 'regexReleaseDate': /Audio CD \((.*)\)/, 'regexNumDiscs': /Number of Discs: (.*)/, 'regexReleaseLabel': /Label: (.*)/ } } }, 'www.amazon.ca': { 'languages': { 'en': { 'months': { 'Jan': '1', 'Feb': '2', 'March': '3', 'April': '4', 'May': '5', 'June': '6', 'July': '7', 'Aug': '8', 'Sept': '9', 'Oct': '10', 'Nov': '11', 'Dec': '12' }, 'disc': 'Disc', 'releaseDateOrder': [1, 0, 2], 'regexReleaseDate': /Audio CD \((.*)\)/, 'regexNumDiscs': /Number of Discs: (.*)/, 'regexReleaseLabel': /Label: (.*)/ }, 'fr': { 'months': { 'janvier': '1', 'février': '2', 'mars': '3', 'avril': '4', 'mai': '5', 'juin': '6', 'juillet': '7', 'août': '8', 'septembre': '9', 'octobre': '10', 'novembre': '11', 'décembre': '12' }, 'disc': 'Disc', 'releaseDateOrder': [0, 1, 2], 'regexReleaseDate': /Audio CD \((.*)\)/, 'regexNumDiscs': /Quantité de disques : (.*)/, 'regexReleaseLabel': /Étiquette : (.*)/ } } }, 'www.amazon.fr': { 'languages': { 'fr': { 'months': { 'janvier': '1', 'février': '2', 'mars': '3', 'avril': '4', 'mai': '5', 'juin': '6', 'juillet': '7', 'août': '8', 'septembre': '9', 'octobre': '10', 'novembre': '11', 'décembre': '12' }, 'disc': 'Disc', 'releaseDateOrder': [0, 1, 2], 'regexReleaseDate': /CD \((.*)\)/, 'regexNumDiscs': /Nombre de disques: (.*)/, 'regexReleaseLabel': /Label: (.*)/ } } } }, 'form': { 'method': 'post', 'action': 'https://musicbrainz.org/release/add', 'acceptCharset': 'UTF-8', 'primaryType': { 'name': 'type', 'types': [ {'key': '', 'value': 'Primary Type'}, {'key': 'Album', 'value': 'Album'}, {'key': 'Single', 'value': 'Single'}, {'key': 'EP', 'value': 'EP'}, {'key': 'Broadcast', 'value': 'Broadcast'}, {'key': 'Other', 'value': 'Other'} ] }, 'editNode': 'Release added using the MB-Import-From-Amazon userscript from page: ' }, 'link': { 'href': 'https://musicbrainz.org/search', 'type': 'release', 'limit': '25', 'method': 'direct' } }); goreMbifa.config(function ($sceDelegateProvider) { $sceDelegateProvider.resourceUrlWhitelist([ 'https://musicbrainz.org/**' ]); }); goreMbifa.directive('goreMbifaBootstrap', function () { return { template: `<style> #gorembifa-app { border: 1px solid #ddd; border-radius: 4px; padding: 14px 18px 12px 18px; margin: 0px 0px 12px 0px; } #gorembifa-app select { width: 100%; border: 1px solid #ddd; border-radius: 4px; padding: 3px; } #gorembifa-app form { width: 100%; display: block; margin-bottom: 10px; } #gorembifa-app input[type="submit"] { width: 100%; height: 24px; border: 1px solid #ddd; border-radius: 3px; background: linear-gradient(to bottom, #f7f8fa, #e7e9ec); margin: 10px 0px 0px 0px; font-family: Arial, sans-serif; } #gorembifa-app #search-direct, #search-indexed { width: 100%; text-align: center; } #gorembifa-app #search-indexed div { text-align: left; margin: 10px 0px 0px 0px; } </style> <div id="gorembifa-app"> <form method="{{ form.method }}" action="{{ form.action }}" accept-charset="{{ form.acceptCharset }}"> <select name="{{ form.primaryType.name }}" data-ng-model="form.primaryType.selectedType" data-ng-options="type.value for type in form.primaryType.types track by type.key"/> <input type="hidden" name="name" value="{{ form.title }}"/> <input type="hidden" name="artist_credit.names.0.name" value="{{ form.artist }}"/> <input type="hidden" name="status" value="{{ form.status }}"/> <input type="hidden" name="events.0.date.day" value="{{ form.releaseDate[0] }}"/> <input type="hidden" name="events.0.date.month" value="{{ form.releaseDate[1] }}"/> <input type="hidden" name="events.0.date.year" value="{{ form.releaseDate[2] }}"/> <input type="hidden" name="labels.0.name" value="{{ form.label }}"/> <input type="hidden" name="urls.0.link_type" value="{{ form.externalLinkType }}"/> <input type="hidden" name="urls.0.url" value="{{ form.asin }}"/> <discs data-ng-repeat="disc in form.tracklist.discs track by $index"> <input type="hidden" name="mediums.{{ $index }}.format" value="CD"/> <tracks data-ng-repeat="track in disc.tracks" track by $index> <input type="hidden" name="mediums.{{ $parent.$index }}.track.{{ $index }}.number" value="{{ track.number }}"/> <input type="hidden" name="mediums.{{ $parent.$index }}.track.{{ $index }}.name" value="{{ track.title }}"/> <input type="hidden" name="mediums.{{ $parent.$index }}.track.{{ $index }}.length" value="{{ track.length }}"/> </tracks> </discs> <input type="hidden" name="edit_note" value="{{ form.editNote }}"/> <input type="submit" value="Export to MusicBrainz"/> </form> <div id="search-direct"> <a href="{{ link.href }}?query={{ link.query }}&type={{ link.type }}&limit={{ link.limit }}&method={{ link.method }}" title="Direct search uses exact matches only, but provides up to the minute correct results. If the result is empty, modify your search or try the indexed one."> Direct search on MusicBrainz </a> </div> <div id="search-indexed"> <a href="" title=" Search indexes are updated every 3 hours.">Indexed search results</a> <div> <ul data-ng-if="searchIndexed.error === false" data-ng-repeat="release in searchIndexed.releases"> <li> {{ release['artist-credit'][0].artist.name }} - {{ release.title }}<br/> Score: {{ release.score }}<br/> Format: <media data-ng-repeat="media in release.media">{{ media.format }} </media> </li> </ul> <span data-ng-if="searchIndexed.error == true">The indexed search was not available.</span> </div> </div> </div>` }; }); goreMbifa.controller('mbifaController', function ($scope, $http, config, dataCollectorService) { var data = dataCollectorService.collectData(); var documentLocationHref = document.location.href.split('?')[0] $scope.form = { 'method': config.form.method, 'action': config.form.action, 'acceptCharset': config.form.acceptCharset, 'primaryType': { 'name': config.form.primaryType.name, 'types': config.form.primaryType.types, 'selectedType': config.form.primaryType.types[0] }, 'title': data['title'], 'artist': data['artist'], 'status': 'official', 'releaseDate': data['releaseDate'], 'label': data['label'], 'externalLinkType': '77', 'asin': documentLocationHref, 'tracklist': data['tracklist'], 'editNote': config.form.editNode + documentLocationHref }; $scope.link = { 'href': config.link.href, 'query': encodeURIComponent(data['title']), 'type': config.link.type, 'limit': config.link.limit, 'method': config.link.method }; $scope.searchIndexed = { 'releases':{}, 'error': false } $http({ method: 'GET', url: 'https://musicbrainz.org/ws/2/release?query=' + encodeURIComponent(data['title']) + '&fmt=json' }).then( function (response) { $scope.searchIndexed.releases = response.data.releases; }, function (response) { $scope.searchIndexed.error = true; }); jquery('#search-indexed').accordion({ heightStyle: 'content', active: false, collapsible: true }); }); goreMbifa.service('dataCollectorService', function (config, siteLookupService, languageLookupService) { this.collectData = function () { var siteSpecificConfig = config.sites[siteLookupService].languages[languageLookupService] var title = ''; if (jquery('#productTitle').length) { title = jquery('#productTitle').text().trim(); } else if (jquery('.buying').length) { title = jquery('#btAsinTitle span').clone().find('span').remove().end().html().trim(); } var artist = ''; if (jquery('.author a').length) { artist = jquery('.author a').text().trim(); } else if (jquery('.buying').length) { artist = jquery('.buying span a').html().trim() } return { 'title': title, 'artist': artist, 'releaseDate': this.collectReleaseDate(siteSpecificConfig), 'label': this.collectLabel(siteSpecificConfig), 'tracklist': this.collectTracklist(siteSpecificConfig) }; }; this.collectReleaseDate = function (siteSpecificConfig) { var releaseDate = []; var releaseDateElement = jquery('#productDetailsTable li').filter(function () { return siteSpecificConfig.regexReleaseDate.test(jquery(this).text()); }); var releaseDateMatch = siteSpecificConfig.regexReleaseDate.exec(releaseDateElement.text()); if (releaseDateMatch) { var releaseDateParts = releaseDateMatch[1].replace(/[.,]/g, '').split(' '); for (var i = 0; i < siteSpecificConfig.releaseDateOrder.length; i++) { if (i == 1) { releaseDate.push(siteSpecificConfig.months[releaseDateParts[siteSpecificConfig.releaseDateOrder[i]]]); continue; } releaseDate.push(releaseDateParts[siteSpecificConfig.releaseDateOrder[i]]); } } return releaseDate; } this.collectLabel = function (siteSpecificConfig) { var label = ''; var labelElement = jquery('#productDetailsTable li').filter(function () { return siteSpecificConfig.regexReleaseLabel.test(jquery(this).text()); }); var labelMatch = siteSpecificConfig.regexReleaseLabel.exec(labelElement.text()); if (labelMatch) { label = labelMatch[1]; } return label; } this.collectTracklist = function (siteSpecificConfig) { var tracklist = { 'discs': [] }; var disc = 0; var tracks = { 'tracks': [] }; // Amazon has more than one tracklist... if (jquery('#dmusic_tracklist_content').length) { /* Tracklists: One Disk: http://www.amazon.de/01-Fl%C3%BCsterer-Remastered-Gabriel-Burns/dp/B00N29D69I Multiple Disks: http://www.amazon.de/Book-Souls-limited-Deluxe/dp/B00ZVFYVMM */ var tracklistContent = jquery('#dmusic_tracklist_content tr'); for (var i = 1; i < tracklistContent.length; i++) { if (tracklistContent[i].id == 'dmusic_tracklist_player_disc_' + (disc + 2)) { tracklist['discs'][disc] = tracks; disc++; var tracks = { 'tracks': [] }; continue; } var trackDetails = tracklistContent[i].getElementsByTagName('td'); if (trackDetails[0].getElementsByClassName('TrackNumber')[0]) { tracks['tracks'].push({ 'number': trackDetails[0].getElementsByClassName('TrackNumber')[0].textContent.trim(), 'title': trackDetails[1].getElementsByClassName('TitleLink')[0].textContent.trim(), 'length': trackDetails[2].getElementsByTagName("span")[0].textContent.trim() }); } } } else if (jquery('#dmusic_tracklist_player').length) { /* Tracklists: One Disk: http://www.amazon.de/Seventh-Son-Iron-Maiden/dp/B0000251W3 Multiple Disks: http://www.amazon.de/Deceiver-Gods-Amon-Amarth/dp/B00CEJ2H6K */ var tracklistContent = jquery('#dmusic_tracklist_player .a-row'); for (var i = 1; i < tracklistContent.length; i++) { if (tracklistContent[i].textContent.trim() == siteSpecificConfig.disc + ": " + (disc + 2)) { tracklist['discs'][disc] = tracks; disc++; var tracks = { 'tracks': [] }; continue; } var trackDetails = tracklistContent[i].textContent.split('. '); tracks['tracks'].push({ 'number': trackDetails[0].trim(), 'title': trackDetails[1].trim() }); } } else if (jquery('#musicTracksFeature').length) { /* Tracklists: One Disk: http://www.amazon.ca/gp/product/B00062PWOQ Multiple Disks: http://www.amazon.ca/The-Book-Souls-Deluxe-Hardcover/dp/B00ZVFYVMM */ var tracklistContent = jquery('#musicTracksFeature tr'); for (var i = 0; i < tracklistContent.length; i++) { if (tracklistContent[i].classList.contains('sampleTracksHeader')) { if (i == 0) { continue; } tracklist['discs'][disc] = tracks; disc++; var tracks = { 'tracks': [] }; continue; } if (tracklistContent[i].hasAttribute('class')) { var trackDetails = tracklistContent[i].getElementsByTagName('td')[0].textContent.split('. '); tracks['tracks'].push({ 'number': trackDetails[0].trim(), 'title': trackDetails[1].trim() }); } } } tracklist['discs'][disc] = tracks; return tracklist; } }); goreMbifa.factory('languageLookupService', function () { var navLocale = jquery('header').attr('class').split(' ')[1]; return navLocale.substring(9, 11); }); goreMbifa.value('siteLookupService', document.domain);