// ==UserScript==
// @name Bandcamp Tools
// @namespace http://violentmonkey.net/
// @version 1.0
// @description Adds QoL improvements to Bandcamp.
// @author InariOkami
// @match *://*.bandcamp.com/*
// @grant none
// @icon https://cdn-icons-png.flaticon.com/512/21/21496.png
// ==/UserScript==
(function() {
'use strict';
var app = {
id: "bcp-sp"
};
app.debug = false;
var cls = {
price: app.id + '-price',
handled: app.id + '-handled'
};
var selectors = {
product: 'li[data-trackid]:not(.' + cls.handled + ')'
};
function findOne(selector, context, dontYell) {
context = context || document;
var item = context.querySelector(selector);
if (item && app.debug) {
console.log(app.id, ': found element matching "' + selector + '"');
} else if (!item && !dontYell) {
console.warn(app.id, ': found no element for selector "' + selector + '"');
}
return item;
}
function findFirst(selector, context) {
return findAll(selector, context)[0];
}
function findAll(selector, context, dontYell) {
if (!selector || !selector.length || selector.length === 1) {
console.error(app.id, ': incorrect selector : ', selector);
}
context = context || document;
var items = Array.prototype.slice.call(context.querySelectorAll(selector));
if (items.length && app.debug) {
console.log(app.id, ': found', items.length, 'elements matching "' + selector + '"');
} else if (!items.length && !dontYell) {
console.warn(app.id, ': found no elements for selector "' + selector + '"');
}
return items;
}
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this;
var args = arguments;
var later = function later() {
timeout = null;
if (!immediate) {
func.apply(context, args);
}
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
func.apply(context, args);
}
};
}
function cleanPrevious() {
findAll('[class^="' + cls.price + '"]', document, true).forEach(function (node) {
return node.remove();
});
}
function displayPrice(product, price) {
var tag = document.createElement('div');
tag.innerHTML = price.value + ' <small>' + price.currency + '</small>';
tag.style = 'position: absolute; top: 0; right: 0; background-color: green; color: white;';
tag.classList.add(cls.price, 'col-edit-box');
product.appendChild(tag);
if (price.value > 2) {
product.style.filter = 'grayscale(1) opacity(.5)';
}
product.classList.add(cls.handled);
}
function displayPrices() {
findAll(selectors.product, document, true).forEach(function (product) {
var trackid = parseInt(product.getAttribute('data-trackid'));
if (trackid) {
if (app.debug) {
console.log(app.id, ': adding price for', trackid);
}
if (!app.tracks.hasOwnProperty(trackid)) {
throw new Error('failed at gettting track price');
}
var price = app.tracks[trackid];
displayPrice(product, price);
}
});
}
function setTracksFromList(list) {
if (!app.tracks) {
app.tracks = {};
}
var added = 0;
list.map(function (track) {
var trackid = track.track_id;
if (!app.tracks.hasOwnProperty(trackid)) {
app.tracks[trackid] = {
value: Math.round(track.price),
currency: track.currency
};
added++;
}
});
console.log(app.id, ': added', added, 'tracks to local db :D');
}
function getDataFromApi() {
fetch('https://bandcamp.com/api/fancollection/1/wishlist_items', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
fan_id: app.userid,
older_than_token: app.token
})
}).then(function (json) {
return json.json();
}).then(function (data) {
app.token = data.last_token;
setTracksFromList(data.track_list);
if (data.more_available) {
getDataFromApi();
}
});
}
function getDataFromPage() {
var dataEl = findOne('#pagedata');
var data = JSON.parse(dataEl.getAttribute('data-blob'));
setTracksFromList(data.track_list);
app.token = data.wishlist_data.last_token;
app.userid = data.fan_data.fan_id;
}
function process() {
displayPrices();
}
function init() {
console.log(app.id, ': init !');
cleanPrevious();
getDataFromPage();
getDataFromApi();
process();
}
init();
var processDebounced = debounce(process, 500);
document.addEventListener('scroll', processDebounced);
})();
(function() {
'use strict';
var table = $('#track_table tbody tr')
, tdata = table ? table.each(function(){}) : []
, adata = window.TralbumData || false;
if (table.length > 1 && adata) {
for(var i = 0; adata.trackinfo[i]; i++) {
var p = $($('tr td .dl_link')[i]), link = document.createElement("a"), track = adata.trackinfo[i];
link.href = track.file["mp3-128"];
link.title = link.download = track.track_num + " - " + track.title + ".mp3";
link.alt = 'If left clicking opens song, right click to download.';
link.innerHTML = 'Download!';
p.html(link);
}
} else {
$('.inline_player').append('<br /><strong><a href="#" class="font-size: 18px;" onclick="location.href=TralbumData.trackinfo[0].file["mp3-128"]">Download Now! (128kb MP3)</a></strong><br />');
}
alert("jestem");
})();
(function() {
'use strict';
const DBG = false;
let log = function(s) {
return (DBG && console.log(s));
},
qS = function(el, scope) {
scope = (typeof scope === 'object') ? scope : document;
return scope.querySelector(el) || false;
},
qSall = function(els, scope) {
scope = (typeof scope === 'object') ? scope : document;
return scope.querySelectorAll(els) || false;
},
hidden, visibilityChange, state,
tabFocused = function(evt) {
log("tab has focus!");
if (document !== evt.target) {
log("warning, document not equal to document. it is ");
log(evt.target); // should be document
}
if (evt.target.body !== document.activeElement) {
log("warning, document.activeElement not equal to document.body. it is ");
log(evt.target.body); // should be document.body
}
},
elmTarget = qS("#trackInfoInner > div.inline_player > table > tbody > tr:nth-child(1) > td.play_cell > a > div");
if (!elmTarget) {
log('main play button not found, exiting');
return;
}
if (typeof document.hidden !== "undefined") { // Opera 12.10 and Firefox 18 and later support
hidden = "hidden";
visibilityChange = "visibilitychange";
state = "visibilityState";
} else if (typeof document.mozHidden !== "undefined") {
hidden = "mozHidden";
visibilityChange = "mozvisibilitychange";
state = "mozVisibilityState";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
state = "msVisibilityState";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
state = "webkitVisibilityState";
}
if ('undefined' === typeof hidden) {
log('document.hidden not found, exiting');
return;
}
document.addEventListener(visibilityChange, function(e) {
return (false === document[hidden]) && tabFocused(e);
});
window.addEventListener('keydown', function(e) {
log("in keydown");
if(e.key === " " && e.target === document.body) {
log("keydown ok");
e.preventDefault();
}
});
qS('body').addEventListener("keyup", function(e) {
log("in keyup");
if (e.key === " " && e.target === document.body) {
e.preventDefault();
elmTarget.focus();
elmTarget.click();
elmTarget.blur();
log("keyup ok");
}
});
})();
var gen = document.querySelector("meta[name=generator]");
if(!gen || gen.content != "Bandcamp") {
return;
}
var style = document.createElement("style");
style.textContent = ".volumeControl{align-items:center;display:flex;height:52px;margin-top:1em}.volumeControl .thumb{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.volumeControl>.speaker{background:#fff;border:1px solid #d9d9d9;border-radius:2px;color:#000;cursor:pointer;font-size:32px;height:54px;line-height:54px;position:relative;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:54px}.volumeControl>.speaker>svg{margin:11px}";
document.head.appendChild(style);
var dragWidth = 226;
var dragging = false;
var dragPos = 0;
var percentage = parseFloat(localStorage.getItem("volume")) || 0.5;
var speaker, volumeInner, audio, volume;
function onLoad() {
audio = document.getElementsByTagName("audio")[0];
updateVolume();
var container = document.createElement("div");
container.classList.add("volumeControl");
speaker = document.createElement("i");
speaker.classList.add("speaker");
speaker.addEventListener("click", function () {
audio.muted = !audio.muted;
updateHtml();
});
container.appendChild(speaker);
var volume = document.createElement("div");
volume.classList.add("progbar");
container.appendChild(volume);
var fill = document.createElement("div");
fill.classList.add("progbar_empty");
fill.style.width = "248px";
volume.appendChild(fill);
volumeInner = document.createElement("div");
volumeInner.classList.add("thumb");
volumeInner.addEventListener("mousedown", function (e) {
dragging = true;
dragPos = e.offsetX;
});
fill.appendChild(volumeInner);
document.querySelector(".inline_player").appendChild(container);
updateHtml();
document.addEventListener("mouseup", function () {
if (dragging) {
localStorage.setItem("volume", percentage);
dragging = false;
}
});
document.addEventListener("mousemove", function (e) {
if (dragging) {
var pos = volume.getBoundingClientRect();
audio.muted = false;
percentage = clamp(((e.pageX - pos.left) - dragPos) / dragWidth, 0, 1);
updateVolume();
updateHtml();
}
});
}
if (document.readyState == 'complete') {
onLoad();
} else {
window.addEventListener("load", onLoad);
}
function updateVolume() {
audio.volume = (Math.exp(percentage) - 1) / (Math.E - 1);
}
function updateHtml() {
// svgs from https://www.material.io/resources/icons
if (audio.muted) {
speaker.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 24 24" width="32"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
volumeInner.style.left = "0%";
} else {
if (percentage <= 0) {
speaker.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 24 24" width="32"><path d="M7 9v6h4l5 5V4l-5 5H7z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
} else if (percentage < 0.5) {
speaker.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 24 24" width="32"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
} else {
speaker.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="32" viewBox="0 0 24 24" width="32"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/><path d="M0 0h24v24H0z" fill="none"/></svg>';
}
volumeInner.style.left = dragWidth * percentage + 'px';
}
}
function clamp(num, min, max) {
return num <= min ? min : num >= max ? max : num;
}