// ==UserScript==
// @name Mixcloud (by MixesDB)
// @author User:Martin@MixesDB (Subfader@GitHub)
// @version 2025.08.20.1
// @description Change the look and behaviour of certain DJ culture related websites to help contributing to MixesDB, e.g. add copy-paste ready tracklists in wiki syntax.
// @homepageURL https://www.mixesdb.com/w/Help:MixesDB_userscripts
// @supportURL https://discord.com/channels/1258107262833262603/1261652394799005858
// @updateURL https://cdn.rawgit.com/mixesdb/userscripts/refs/heads/main/Mixcloud/script.user.js
// @downloadURL https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/Mixcloud/script.user.js
// @require https://cdn.rawgit.com/mixesdb/userscripts/refs/heads/main/includes/jquery-3.7.1.min.js
// @require https://cdn.rawgit.com/mixesdb/userscripts/refs/heads/main/includes/waitForKeyElements.js
// @require https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/includes/global.js?v-Mixcloud_26
// @require https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/includes/toolkit.js?v-Mixcloud_193
// @include http*mixcloud.com*
// @icon https://www.google.com/s2/favicons?sz=64&domain=mixcloud.com
// @noframes
// @grant unsafeWindow
// @run-at document-end
// ==/UserScript==
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Load @ressource files with variables
* global.js URL needs to be changed manually
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var cacheVersion = 14,
scriptName = "Mixcloud";
loadRawCss( githubPath_raw + "includes/global.css?v-" + scriptName + "_" + cacheVersion );
loadRawCss( githubPath_raw + scriptName + "/script.css?v-" + cacheVersion );
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Basics
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Before anythings starts: Reload the page
* Firefox on macOS needs a tiny delay, otherwise there's constant reloading
*/
redirectOnUrlChange( 1000 );
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Funcs
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// createToggleApiArea
function createToggleApiArea( urlVar ) {
logFunc( "createToggleApiArea" );
logVar( "urlVar", urlVar );
$.get(urlVar, function( data ) {
waitForKeyElements('div[data-testid="playerHero"]', function( jNode ) {
var apiTextLinkified = linkify( data ),
toggleArea = '
'+ apiTextLinkified +'
';
jNode.next().append( toggleArea );
$("#toggleApiText").slideDown();
});
}, "text" );
}
// appendArtworkInfo
function appendArtworkInfo( artwork_max_url, imgWrapper ) {
logFunc( "appendArtworkInfo" );
var img = new Image();
img.onload = function(){
var imageWidth = this.width,
imageHeight = this.height,
artworkInfo = imageWidth +' x '+ imageHeight,
artworkInfo_link = ''+artworkInfo+'';
imgWrapper.after( ''+artworkInfo_link+'
' );
};
img.src = artwork_max_url;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* User pages
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Filter options
* @DRY
*/
logVar( "urlPath(2)", urlPath(2) );
if( urlPath(2) == "uploads" || urlPath(2).replace(/\?.+$/,"") == "" ) { // https://www.mixcloud.com/Groove_Mag/?hideUsed=true
// vars
var getHideUsed = getURLParameter("hideUsed") == "true" ? "true" : "false",
checkedUsed = "";
if( getHideUsed == "true" ) checkedUsed = 'checked';
// append filter section and param handling
waitForKeyElements('main > section > div > ul', function( jNode ) {
var userPageTabs = jNode,
userPageTabs_firstText = $("li:first-of-type a span", userPageTabs).text();
// is really user page?
if( userPageTabs_firstText == "Shows" ) {
var filterOptions = '';
filterOptions += 'Hide:';
filterOptions += '';
filterOptions += '
';
userPageTabs.before( filterOptions );
// reload
var windowLocation = window.location,
href = $(location).attr('href');
if( typeof href != "undefined" ) {
var url = href.replace(/\?.*$/g,"");
}
if( typeof url != "undefined" ) {
$("#hideUsed").change(function(){
if(!this.checked) {
windowLocation.href = url + "?hideUsed=false";
} else {
windowLocation.href = url + "?hideUsed=true";
}
});
}
}
});
}
// Hiding option: each used player
waitForKeyElements('button[data-testid="audiocard-play-button"]', function( jNode ) {
if( getHideUsed == "true" ) {
logFunc( "Hiding used players" );
var wrapper = jNode.parent("div").parent("div").parent("div").parent("div").parent("div"),
playerUrl = 'https://www.mixcloud.com' + $("a", wrapper).attr("href");
getToolkit( playerUrl, "hide if used", "lazy loading list", wrapper );
}
});
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Player pages
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Original artwork
*/
if( urlPath(2) != "" ) {
waitForKeyElements('div[data-testid="playerHero"] img[data-in-view="true"]:not(.processed)', function( jNode ) {
jNode.addClass("processed");
var artwork_thumb_url = jNode.attr("src"),
artwork_max_url = artwork_thumb_url.replace(/\/unsafe\/[0-9]+x[0-9]+\//, "/unsafe/0x0/"); /* https://community.metabrainz.org/t/is-there-a-native-optimal-size-for-cover-art-from-mixcloud/640075 */
logVar( "artwork_max_url", artwork_max_url );
appendArtworkInfo( artwork_max_url, jNode )
});
}
/*
* Action buttons
*/
if( urlPath(2) != "" ) {
waitForKeyElements('button[aria-label="Add To"]:not(.processed)', function( jNode ) {
jNode.addClass("processed");
var apiUrl = url.replace( /(www\.)?mixcloud\.com/, "api.mixcloud.com" );
// create wrappers to ensure prefered order of async created elements
jNode.after( '' );
// add api toggle link
var apiButton = 'API';
logVar( "apiUrl", apiUrl );
$(".mdb-apiLink-wrapper").after( apiButton );
/*
* Using API data
*/
$.get(apiUrl, function( data ) {
// add dur toggle
var dur_sec = data["audio_length"],
durToggleWrapper = getFileDetails_forToggle( dur_sec ),
dur = convertHMS( dur_sec ),
durToggleLink = ''+dur+'';
// add dur button
$(".mdb-durToggle-wrapper:not(.processed)").append( durToggleLink ).addClass("processed");
// append toggle wrapper
jNode.addClass("processed-dur");
jNode.closest("div").after( ''+durToggleWrapper+'
' );
// toggle dur
waitForKeyElements('.mdb-durToggleLink', function( jNode ) {
jNode.click(function(){
log("click");
$("#mdb-fileDetails").toggle();
$("#mdb-fileDetails textarea").select().focus();
});
});
}, "json" );
});
// api link on click
waitForKeyElements(".mdb-apiLink", function( jNode ) {
jNode.click(function(){
var apiUrl = jNode.attr("data-apiurl"),
apiToggleArea = $("#toggleApiText");
if( apiToggleArea.length == 0 ) {
createToggleApiArea( apiUrl );
} else {
( apiToggleArea.is(':visible') ) ? apiToggleArea.slideUp() : apiToggleArea.slideDown();
}
});
});
}
/*
* Toolkit
*/
waitForKeyElements('div[data-testid="playerHero"] + div + div:not(.mdb-processed-toolkit)', function( jNode ) {
var titleText = $("h1").text(),
embedUrl = location.href.replace(/\?.+$/, "");
getToolkit( embedUrl, "playerUrl", "detail page", jNode, "prepend", titleText, "", 1, embedUrl );
jNode.addClass("mdb-processed-toolkit");
});