';
jNode.closest(".audio-stream-box").append( mdbPlayerAndToolkit );
jNode.hide();
}
if( playerUrl ) {
// toolkit output
waitForKeyElements(".mdb-player-audiostream:not(.mdb-processed-toolkit)", function( jNode ) {
getToolkit( playerUrl, "playerUrl", "detail page", jNode, "after", titleText, "link", 1 );
jNode.addClass("mdb-processed-toolkit");
});
}
}
// embed player
waitForKeyElements(".request-summary img.artwork", function( jNode ) {
var playerUrl = jNode.closest("a").attr("href"),
heading = $(".MuiGrid-container .MuiGrid-grid-xs-12 p.MuiTypography-body1").first(),
titleText = normalizeTitleForSearch( heading.text() );
logVar( "playerUrl", playerUrl );
if( url != "" ) {
funcTidPlayers( jNode, playerUrl, titleText );
}
});
/*
* Compare page creation date to MixesDB last edit date
* only on positive usage results
*/
waitForKeyElements(".mdb-mixesdbLink.lastEdit", function( jNode ) {
var pageCreationTimestamp = $(".audio-stream-box > div > div > .MuiBox-root:nth-of-type(5) div + div p.MuiTypography-body1").text()
.trim()
// d/M/yyyy
// 1/3/2025, 9:54:50 AM
.replace(/ (AM|PM)$/i, "" )
.replace(/(\d+)\/(\d+)\/(\d+), (\d+:\d+:\d+)$/, "$3-$1-$2T$4Z" )
// m+d.M.yyyy
// 21.1.2025, 13:05:04
.replace(/(\d+)\.(\d+)\.(\d+), (\d+:\d+:\d+)$/, "$3-$2-$1T$4Z" ) // 18.1.2025, 10:43:21
// pad M-d
// 2025-1-3T9:54:50Z
.replace(/(\d{4})-(\d)-/, "$1-0$2-" )
.replace(/(\d{4})-(\d{2})-(\d)T/, "$1-$2-0$3T" )
;
var lastEditTimestamp = jNode.attr("data-lastedittimestamp"); // 2025-01-28T20:26:13Z
pageCreated_vs_lastEdit( pageCreationTimestamp, lastEditTimestamp );
});
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Tables
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* Tracklist table
* via table .mdb-tid-table
*/
// waitForKeyElements
waitForKeyElements(".mdb-tid-table:not('.tlEditor-processed')", function( jNode ) {
logFunc( "funcTidTracklist" );
var tlWrapper = jNode;
tlWrapper.addClass("tlEditor-processed");
// hide banner
$(".MuiAlert-root.MuiAlert-standardInfo").hide();
$(".MuiGrid-container.request-summary").css("margin-top", "0");
var heading = $(".MuiGrid-container .MuiGrid-grid-xs-12 p.MuiTypography-body1").first(),
mixTitle = heading.text(),
totalDur = $("p.MuiTypography-body1:contains('Duration')").closest("div").next(".MuiGrid-item").text(),
totalDur_Sec = durToSec(totalDur);
log(mixTitle);
logVar( "totalDur", totalDur);
// iterate
var tl = "",
li = $("tr", tlWrapper),
i = 1;
logVar("li.length", li.length);
li.each(function () {
var thisTrack = "";
var thisTitle = $(".title", this).text().replace(/\s*\n\s*/g, ' ').trim();
logVar( "title before replacing", thisTitle );
var artist = $(".artist", this).text()
.replace(/\s*\n\s*/g, ' ') // https://trackid.net/audiostreams/nature-one-2024-opening-gayphoriastage
.replace(/([A-Z0-9]),([A-Z0-9])/i, "$1, $2") // https://trackid.net/audiostreams/calvo-at-nature-one-2o17-we-call-it-home
,
title = thisTitle
.replace(/\s*-\s*(?:feat(?:\.|uring)?)\s+(.+?)(?=$|\s*\()/i, ' (featuring $1)') // Scared Of My Heart - featuring E.R. Thorpe (Andre Lodemann Remix) https://trackid.net/audiostreams/balance-selections-215-james-harcourt#google_vignette
.replace(/ \(\d+ - Remaster\)$/, "") // Foo (Nutt Mix - Remastered 2021)
.replace(/^(.+) \((.+) - (?:\d+ )?Remaster(?:ed|ing)?(?: \d+)?\)/g, "$1 ($2)") // All Night (I Can Do It Right) (2016 - Remaster) https://trackid.net/audiostreams/subfader-the-ghetto-funk-show-20090216-mix-2 | Run before below stuff
.replace(/^(.+) \(Remaster(ed|ing|is[ée])?( En)? '?\d{2,4}\)/gi, "$1") // (Remasterisé En 2002)
.replace(/(.+) - (.+ (?:Remix|Mix|Version))/g, "$1 ($2)")
.replace(/^\((.+)\)$/g, "$1") // avoid "[000] Inland [Systemscan]" https://trackid.net/audiostreams/shed-josey-rebelle-sven-von-thulen-txl-berlin-recordings-chapter-7-arte-concert
.replace(/^(.+)-\d{4,5}$/g, "$1") // numbers as suffix, e.g. "Track Title-24070" https://trackid.net/audiostreams/alex-kvitta-sonderspur-pod-011281213
.replace(/^(.+) - (.+)$/g, "$1 ($2)") // "Track Title - Some Version" https://trackid.net/audiostreams/dj-hell-mayday-1999-soundtropolis
.replace(/(.+) \((\d+ )?Remaster(ed|ing)?( \d+)?\)$/g, "$1") // "Track Title - (Remaster)" etc
.replace(/(.+) \((\d+ )?([A-Za-z]+ )?(\s*Re-?master(ed|ing|;)?)(\s*(Mix|Version|Edition))?\)$/gi, "$1") // "Track Title - (2013 Japan Remaster; Remastered)"
.replace(/\s+\(Mixed\)/i, "") // remove " (Mixed)" https://trackid.net/audiostreams/balance-selections-234-sinca
,
label = $(".label", this).text()
.replace(/\s*\n\s*/g, ' ')
.replace("Records (Distribution)", "Records")
.replace(/[\[\]]/g,"")
.fixTidLabelnames()
.removeMajorLabels()
,
startTime = $(".startTime", this).text(),
startTime_Sec = durToSec(startTime),
endTime = $(".endTime", this).text(),
endTime_Sec = durToSec(endTime),
previousTrack = $(".MuiDataGrid-row:eq(" + (i - 2) + ")"), // eq starts at 0
endTimePrevious = $(".MuiDataGrid-cell[data-field='endTime']", previousTrack).text(),
endTimePrevious_Sec = durToSec(endTimePrevious),
nextTrack = $(".MuiDataGrid-row:eq(" + (i) + ")"), // eq starts at 0
startTimeNext = $(".MuiDataGrid-cell[data-field='startTime']", nextTrack).text(),
startTimeNext_Sec = durToSec(startTimeNext);
artist = stripCountryCodes( artist );
title = removePointlessVersions( title );
title = removeDuplicateBracketedText( title );
//logVar( "artist", artist );
//logVar( "title", title );
// remove label when its actually the artist repeated
if (label == artist) {
label = "";
}
// dur
if (startTime !== "") {
// first track is gap?
if (i == 1) {
// start tl with gap when first dur is larger than 120(?)
if (startTime_Sec > 120) {
tl += '[0:00:00] ?\n...\n';
}
}
thisTrack += '[' + startTime + '] ';
}
// catch title (feat. artist2) and add to artist
// should actually be catched by TLE (issue#525)
var match = title.match(/\s*\(feat\. [^)]+\)/i);
if (match) {
var featPart = match[0].trim(); // e.g., "(feat. Foo)"
title = title.replace(match[0], '').trim();
artist += ' ' + featPart;
}
// artist - title
if (artist && title !== "") {
thisTrack += artist + ' - ' + title;
}
// label
if (label !== "") {
thisTrack += ' [' + label + ']';
}
tl += thisTrack;
//logVar( "thisTrack", thisTrack );
// gaps
// add "..." row if gap is too laarge
if( !$(this).is(':last-child') ) {
// not last track
var gapSec = startTimeNext_Sec - endTime_Sec;
//log( "-------------------------------" );
//log( "> startTime: " + startTime );
//log( "> startTime_Sec: " + startTime_Sec );
//log( "> endTime: " + endTime );
//log( "> next startTime: " + startTimeNext );
//log( "> gapSec: " + gapSec );
// TID end times sometimes before start time
// https://trackid.net/audiostreams/b5096745-56ad-4d4d-af61-8d16e32e0521
// don't create next "[dur] ?" tracks then
if( endTime_Sec < startTime_Sec ) {
log( "Negative gapSec!" );
tl += "\n...";
} else {
if( gapSec > 30 ) {
tl += "\n[" + endTime + "] ?";
if (gapSec > 180) {
tl += "\n...";
}
}
}
tl += "\n";
} else {
// last track
//log( "> last track" );
//log( "> lastTrack_gap: " + lastTrack_gap );
var lastTrack_gap = totalDur_Sec - endTime_Sec;
//log( "> lastTrack_gap: " + lastTrack_gap );
// is the last track close to end or possible gap?
if (lastTrack_gap > 70) {
tl += "\n[" + endTime + "] ?";
}
if (lastTrack_gap > 240) {
tl += "\n...";
}
}
i++;
});
// API
tl = tl.trim();
log("tl before API:\n" + tl);
if (tl !== "") {
var res = apiTracklist( tl, "trackidNet" ),
tlApi = res.text;
log( 'tlApi ("trackidNet"):\n' + tlApi );
if( tlApi ) {
var tl_arr = make_tlArr( tlApi ),
tl_arr_fixedCues = tidMarkFalseCues( addCueDiffs( tl_arr ) ),
tl_arr_noDupes = removeAdjacentDuplicateTracks( tl_arr_fixedCues ),
tl_fixedCues = arr_toTlText( tl_arr_noDupes );
log( "tl_fixedCues:\n" + tl_fixedCues );
var res_fixedCues = apiTracklist( tl_fixedCues, "trackidNet" ),
tlApi_fixedCues = res_fixedCues.text;
if( tlApi_fixedCues ) {
tlWrapper.before( ta );
$("#mixesdb-TLbox").addClass("mixesdb-TLbox")
.val( tlApi_fixedCues )
.attr( "data-tlcandidate", tlApi );
fixTLbox( res.feedback );
}
if( tlApi.split("\n").length != tlApi_fixedCues.split("\n").length ) {
var info_cuesRemoved = '
Possibly false "?" tracks have been removed due to short cue differences.';
info_cuesRemoved += ' ';
//info_cuesRemoved += ' Max gap: minutes';
info_cuesRemoved += '
');
});
// hide grid but keep page navigation
$(".MuiTablePagination-toolbar").insertAfter($(".mdb-tid-table"));
// on audio pages hide the play button doesn't work in the copied tracklist table
// but it is needed to generate the formatted tracklist textarea
// so we hide the table and add the youtube search icon to the existing grid.
if( urlPath(1) == "audiostreams" ) {
$(".mdb-tid-table").fadeOut(300); // needs to be visible shortly for tracklist textarea generation
grid.show();
// add youtube search icon to grid
// @TODO: What did I mena here?
} else {
// @TODO: Only show if opted in by new cookie option
if( !/audiostreams\?keywords.+/g.test( urlPath(1) ) ) {
grid.addClass('mdb-hide');
}
}
// remove empty th
//if( !addPlay ) $(".mdb-tid-table tbody th:first-of-type").remove();
}
//log("audiostreams: " + audiostreams);
//log("> length: " + audiostreams.length);
if (audiostreams.length > 0) {
var list = audiostreams.join(", "),
res = trackidnet_checked("trackidnet_checked_check_batch", list);
if (res !== null) {
$.each(res, function () {
var audiostream = $(this)[0].audiostream,
timestamp = $(this)[0].timestamp;
log(audiostream);
$("tr[data-audiostream='" + audiostream + "'] td.mixesdbPageCheck-status").html(checkIcon);
});
$(".mixesdbPageCheck-status-no").show();
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Submit request URLs
* https://trackid.net/submitrequest
* https://trackid.net/submitrequest?url=https://soundcloud.com/djrog/latin-vibes&keywords=foo%20bar
* Passing URL pramater requires the userscript "MixesDB Userscripts Helper (by MixesDB)"
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function on_submitrequest() {
logFunc( "on_submitrequest" );
// submitted URL, page preview pops up
// if exists, take user directly there without confirmation
// Test URL page exists: https://soundcloud.com/resident-advisor/ra944-tsvi
// Test URL page does not exist: https://soundcloud.com/djrog/latin-vibes
// buggy if this part comes after the requestUrl part
waitForKeyElements( ".audio-stream-box", submitrequest_pagePreview_wait);
function submitrequest_pagePreview_wait(jNode) {
log( "submitrequest_pagePreview_wait()" );
// if page exists or not: Does the next element contains the "VIEW TRACKLIST" button?
var firstButton = jNode.next(".MuiGrid-container").find("button:first"),
firstButton_text = firstButton.text().toLowerCase(); // "view tracklist"
logVar( "firstButton text", firstButton_text )
if( firstButton_text == "view tracklist" ) {
// page exists, send user directly there
// existing page might still be processing!
firstButton.trigger("click"); // We cannot catch that URL
} else {
// page does not exist
// stay cos user might want to use the option "Notify me when ready"
}
}
// if url was passed as parameter
var requestUrl = getURLParameter( "requestUrl" );
logVar( "requestUrl", requestUrl );
// Insert the requestUrl to the submit input
if( requestUrl && requestUrl !== "" ) {
var requestUrl_domain = new URL( requestUrl ).hostname.replace("www.",""),
keywords = getURLParameter("keywords");
logVar( "requestUrl_domain", requestUrl_domain );
logVar( "keywords", keywords );
// add URL to input and try to submit
waitForKeyElements( ".MuiGrid-grid-xs-12 .MuiFormControl-root input[type=text].MuiInputBase-input", function( jNode ) {
logFunc( "submitRequest_input_wait" );
// Submit notice cos we cannot just trigger a click on the the "VALIDATE" button
// For YouTube URLs it doesn't allow a blank after the URL...
var note_standard = create_note( "Press the SPACEBAR and ENTER to validate" ),
note_YouTube = create_note( "Press the SPACEBAR, BACKSPACE and ENTER" );
switch( requestUrl_domain ) {
case "youtube.com":
var submitNote = note_YouTube;
break;
case "youtu.be":
var submitNote = note_YouTube;
break;
default:
var submitNote = note_standard;
}
var input = create_input( requestUrl );
jNode.closest(".MuiGrid-container").before( input );
//var e = jQuery.Event( "keydown", { keyCode: 32 } );
jNode.select();
setTimeout(function () {
jNode.val( requestUrl );
//jNode.trigger( e );
jNode.closest(".MuiGrid-container").after( submitNote );
}, timeoutDelay);
});
// Add keywords to search input
waitForKeyElements( "#search-box", function( jNode ) {
logFunc( "submitRequest_searchInput_wait" );
var newSearch = '';
jNode.closest(".header-mid.MuiBox-root").replaceWith( newSearch );
});
// Click button "View Tracklist" when it appeas
waitForKeyElements( "button.MuiButton-root", function( jNode ) {
var buttonText = jNode.text();
if( buttonText == "View Tracklist" || buttonText == "Submit" ) {
jNode.click();
}
});
}
}