header={ "chef": "BeatRig", "recipe_version": "1.275", "title": "Convert to MP3", "description": "Convert (almost any file) to MP3", "spice": "BQ==:G+Hy36kJcnQQYYavb/JV2Il6eCYr6h4hRJHCF9rRbhX5BcazL6cuTgUMdz0xA50VWA5PmmV0Q7aWkiUtNFtbk991l+t0e8IBXMReMIT1GNjnZZDv7n2YRAo0xcSQbY0ixjG6LLH4wGKnspKAoA/v69iLn6N2IH0XzIZwTGdHSo4=", "flavour": "rYBh+nhgieggDZx2gcETNujYfSW0GS/gdkAbaFiAKAfG9WqtQCVnKTX/q5chkMtEQjA00FGHo7CGEf38tSozFlxMfqPogyb+GkYXlVy+Tq1eb2ydRRM9oIHlaxNzPzPgqAjG589975ebp43HjLaP7/IFh/LArgfIBfTfDrdqbC0=", "time": 1723837145, "magnetron_version": "1.0.317", "tags": "audio", "core_version": "0.6.4", "dependencies": "ffmpeg,ffprobe", "type": "demo", "os": "windows,macOS", "functions": "main,onConfig,onAbout", "uuid": "9ec596d71c384d62825a4a5c14162139", "palette": "Clean Slate", "instructions": "Drop file(s) here" }; // ============================================================================= // GLOBALS var files = []; // GLOBAL LIST OF FILES IN THE FILELIST var settings = {}; // SETTINGS var settingsFile = ""; // LOCATION OF THE SETTINGS FILE var has_sox = false; // CHECK IF FFMPEG HAS SOX RESAMPLER BUILT IN, WILL BE TESTET THE FIRST TIME NEEDED var duration = 0; // duration of the current processing file for progress bar var curFileCount = 0; // current file count for progress bar // CONTANT BITRATE OPTIONS var bitrate_cbr = { "320" : "320K: High Quality", "256" : "256K", "224" : "224K", "192" : "192K: Default Quality", "160" : "160K", "128" : "128K", "112" : "112K", "96" : "96K", "80" : "80K", "64" : "64K", "56" : "56K", "48" : "48K", "40" : "40K", "32" : "32K: Low Quality", }; // VARIABLE BITRATE OPTIONS var bitrate_vbr = { "0" : "0 - High Quality (220-260k)", "1" : "1 - High Quality (190-250k)", "2" : "2 - High Quality (170-210k)", "3" : "3 - Medium Quality (150-195k)", "4" : "4 - Medium Quality (140-185k)", "5" : "5 - Medium Quality (120-150k)", "6" : "6 - Low Quality (100-130k)", "7" : "7 - Low Quality (80-120k)", "8" : "8 - Low Quality (70-105k)", "9" : "9 - Low Quality (45-85k)", }; // ============================================================================= // SET DEFAULT VALUES function init() { // LOCATION OF THE SETTINGS FILE settingsFile = folders.content+gvar.pss+'default.txt'; // SETTINGS settings = { "normalise" : 0, "normalise_val" : -14, "normalise_type" : 'LUFS', "bitrate_mode" : 'Variable', "bitrate_cbr" : '192', "bitrate_vbr" : '4', "samplerate" : 'source', "customFolder" : false, // Move fixed files to a custom folder "customFolder_path" : folders.home, // By default this is the users home folder "nametag" : false, // Tag added to fixed file "nametag_val" : '-MP3', "subFolder" : true, // Move fixed files to a subfolder "subFolder_val" : 'mp3', }; // CHECK IF FFMPEG HAS SOX RESAMPLER BUILT IN // WILL BE TESTET THE FIRST TIME NEEDED has_sox = "NOTDEFINED"; } // ============================================================================= // MAIN function main() { // CHECK IF FFMPEG & FFPROBE ARE AVAILABLE FOR TRUE PEAK LIMITING if(checkDependencies("ffprobe,ffmpeg")){ abort("FFMPEG and/or FFPROBE is not available"); } // ----------------------------- // DEFAULT SETTINGS init(); // ----------------------------- // LOAD CUSTOM SETTINGS loadSettings(); // ----------------------------- // START PROGRESS setProgress(101); // ----------------------------- // GET ALL FILES FROM THE APP files = getFiles(); // ----------------------------- if( files.length == 0) abort("Add some files first!"); // ----------------------------- // CLEAN UP THE FILE LIST for (i=0;i 0) { fProp(i, 'file_icon', 'CheckSquare'); fProp(i, 'file_icon_color', 'FFFF0000'); fProp(i, 'file_status', 'Conversion failed!'); // REMOVE // fProp(i, 'done', 1); // REMOVE FILE IF FAILED if(fileExists(outfile)) deleteFile(outfile); } else { fProp(i, 'file_icon', 'CheckSquare'); fProp(i, 'file_icon_color', 'FF008800'); fProp(i, 'file_status', 'Done'); // fProp(i, 'done', 1); } } // ALL DONE setFiles(files); setProgress(100); setMainMessage("All done"); } // ============================================================================= // CMD CALLBACK function cmd_callback(cmd_name, cmd_output) { if(cmd_name == "ffmpeg" && cmd_output.length > 0 && duration > 0.) { var progress = searchRegEx(cmd_output , "(?:time=)(.*?)(?:\ bitrate=)", "i", 1); progress = progress[progress.length-1].split(':'); progress = (parseFloat(progress[0]) * 60. * 60.) + (parseFloat(progress[1])*60.) + parseFloat(progress[2]); progress = (progress/files.length) * curFileCount; progress = Math.round( (progress/ duration) * 100. ); setProgress( progress ); } } // ============================================================================= // ON CONFIG, SHOW DIALOG AND SAVE SETTINGS function onConfig() { // DEFAULT SETTINGS init(); // LOAD CUSTOM SETTINGS loadSettings(); // SHOW DIALOG var r = settings_dial(); // ----------------------------- // IF RESET IS PUSHED if(r.returns === -1){ deleteFile(settingsFile); // DELETE THE SETTINGS FILE init(); // RESET TO DEFAULT SETTINGS onConfig(); // OPEN THIS CONFIG FUNCTION AGAIN } // ----------------------------- // IF SAVED WAS PUSHED else if(r.returns === 1){ // STORE CONSTANT BITRATE KEY, NOT VALUE for (var i = 0; i < getObjectSize(bitrate_cbr); i++) { if(getObjectValue(bitrate_cbr,i) == settings['bitrate_cbr']) { settings['bitrate_cbr'] = getObjectKey(bitrate_cbr,i); break; } } // STORE VARIABLE BITRATE KEY, NOT VALUE for (var i = 0; i < getObjectSize(bitrate_vbr); i++) { if(getObjectValue(bitrate_vbr,i) == settings['bitrate_vbr']) { settings['bitrate_vbr'] = getObjectKey(bitrate_vbr,i); break; } } // SUBFOLDER SHOULD START WITH A SLASH AND NO OTHER SLASHES ARE ALLOWED settings.subFolder_val = replaceInString( settings.subFolder_val, gvar.pss, "" ); writeFile( settingsFile, objectToString(settings) ); } // ----------------------------- } // ============================================================================= // ON ABOUT, SHOW POPUP WITH BUTTON TO OPEN WEBSITE function onAbout() { var args = { "info": { "type": "texteditor", "bounds" : { "y" : 70, "w" : 400, "h" : 150 }, "visible": true, "multiline" : true, "wordwrap" : true, "default" : header.description+'\n\nMore information on the recipes page on our website.', "enabled" : false }, "showpage" : { "type" : "button", "label" : "show page", "returns" : 1 }, "close" : { "type" : "button", "label" : "close", "returns" : 0 } }; var r = dialog(header.title, "" , "", args); if (r.showpage == 1) launchInBrowser('https://magnetron.app/recipes/uuid/'+header.uuid+'/'); } // ============================================================================= // LOAD DEFAULT VALUES function loadSettings(){ // ADD/UPDATE SETTINGS if ( fileExists(settingsFile) ){ // GET FILE var t = stringToObject( readFile(settingsFile) ); // REPLACE VALUES ONE-BY-ONE for (var j = 0; j < getObjectSize(t); j++) { settings[getObjectKey(t, j)] = getObjectValue(t, j); } } } // ============================================================================ // DIALOG CALLBACK function dialog_callback(props) { // ---------------- // UPDATE SETTINGS if (props["name"] == "bitrate_mode") { settings['bitrate_mode'] = props["text"]; } if (props["name"] == "bitrate_cbr") { settings['bitrate_cbr'] = props["text"]; } if (props["name"] == "bitrate_vbr") { settings['bitrate_vbr'] = props["text"]; } if (props["name"] == "samplerate") { settings['samplerate'] = props["text"]; } if (props["name"] == "normalise") { settings['normalise'] = props["toggle"]; } if (props["name"] == "normalise_val") { settings['normalise_val'] = props["value"]; } if (props["name"] == "normalise_type") { settings['normalise_type'] = props["text"]; } if (props["name"] == "customFolder") { settings['customFolder'] = props["toggle"]; } if (props["name"] == "customFolder_path") { settings['customFolder_path'] = props["path"]; } if (props["name"] == "nametag") { settings['nametag'] = props["toggle"]; } if (props["name"] == "nametag_val") { settings['nametag_val'] = props["text"]; } if (props["name"] == "subFolder") { settings['subFolder'] = props["toggle"]; } if (props["name"] == "subFolder_val") { settings['subFolder_val'] = props["text"]; } // ---------------- // UPDATE DIALOG dialog({ "bitrate_cbr" : { "visible" : settings['bitrate_mode'] == 'Constant', "default": settings['bitrate_cbr'] }, "bitrate_vbr" : { "visible" : settings['bitrate_mode'] == 'Variable', "default": settings['bitrate_vbr'] }, "normalise_val" : { "visible" : settings['normalise'] }, "normalise_type" : { "visible" : settings['normalise'], "default": settings['normalise_type'] }, "customFolder_path" : { "visible" : settings['customFolder'] }, "subFolder_val" : { "visible" : settings['subFolder'] }, "nametag_val" : { "visible" : settings['nametag'] }, }); // ---------------- } // ============================================================================= // HELPER FUNCTION FOR UPDATING THE FILELIST AND CHANGING THE FILES ARRAY AT THE // SAME TIME (THE FILE SHOULD BE PASSED AS OBJECT AND IS TREATED AS REFRENCE) function fProp(i, key, data) { var path = files[i]['path']; var index = files[i]['index']; if(typeof(data) === 'undefined'){ removeFileProperty(path, index, key); removeProperty(files[i], key); } else{ setFileProperty(path, index, key, data); files[i][key] = data; } } // ============================================================================= // GENERATE FILENAME function filename(source) { // AUDIO AND VIDEO PATH INFO var source = getPathInfo(source); // var name = source.basename; var ext = 'mp3'; // FILE EXTENSION var path = source.folder; var full = ''; // FULL PATH var inc = 0; // FILE INCREMENTAL FOR EXISTING FILES // CUSTOM FOLDER if(settings.customFolder){ path = settings.customFolder_path; } // SUB FOLDER if(settings.subFolder){ path = path+gvar.pss+settings.subFolder_val; } // NAME TAG if(settings.nametag){ name = name+settings.nametag_val; } // IF FILE EXISTS, ADD INCREMENTAL while (true) { // ADD INC. TO END OF THE NAME, INITIALY EMPTY var temp_name = name+(inc > 0 ? ' ['+inc+']' : ''); // FULL PATH INCL. FILENAME full = path+gvar.pss+temp_name+'.'+ext; // CHECK IF OUTPUT FILENAME IS UNIQUE var unique = true; for (var i = 0; i < cue.length; i++) { if( cue[i].output_file == full ){ unique = false; } } // IF FILE DOES NOT EXIST CONTINUE if(unique && !fileExists(full) ){ break; } // IF FILE EXISTS ADD INC. NUMBER inc++; } // RETURN THE FULL PATH return full; } // ============================================================================= // CHECK IF FOLDER EXISTS OR CREATE RECURSIVE function makeFolderRecursive(path) { // ----------------------------- path = path.split(gvar.pss); // path to array path.remove(""); // remove empty entries // ----------------------------- var test = gvar.isWindows ? path[0] : ''; // ----------------------------- var j = gvar.isWindows ? 1 : 0; for (; j < path.length; j++) { test = test + gvar.pss + path[j]; if(fileExists(test)){ continue; } makeFolder( test ); } // ----------------------------- } // ============================================================================= // PROCESS THE AUDIO function processAudio(path, outfile) { // ----------------------------- // OPTIONS var args = []; // INPUT FILE args.push("-i"); args.push(path.path); // REMOVE ALL VIDEO args.push("-vn"); // ----------------------------- // keep in mind that due to the filtering in mp3's the // peak levels will be an approx not exactly! if(settings.normalise) { // var levels = -99.; var adjust = 0.; // FFMPEG CALL var levels = cmd("ffmpeg", [ '-i',path.path, '-nostats', '-af','ebur128=peak='+(settings.normalise_type == 'dBTP' ? "true" : "sample")+':framelog=verbose', '-f','null', '-', ]); if(settings.normalise_type == 'dB'){ levels = searchRegEx(levels, "(?:Peak:)(.*)(?=dBFS)", "i", 1); levels = parseFloat(levels[levels.length-1]); } else if (settings.normalise_type == 'dBTP'){ levels = searchRegEx(levels, "(?:Peak:)(.*)(?=dBFS)", "i", 1); levels = parseFloat(levels[levels.length-1]); } else if(settings.normalise_type == 'LUFS'){ levels = searchRegEx(levels, "(?:I:)(.*)(?=LUFS)", "i", 0); levels = parseFloat(levels[levels.length-1]); } adjust = settings.normalise_val - levels; adjust = (Math.round(adjust*100)/100); args.push("-filter:a"); args.push("volume="+adjust+"dB"); } // ----------------------------- // SAMPLE RATE CONVERSION if(settings.samplerate != "source") { // TEST IF FFMPEG HAS SOX BUILT IN if(has_sox == "NOTDEFINED"){ var has_sox = cmd("ffmpeg", ['-version']); has_sox = containsString(ffmpeg, '--enable-libsoxr') ? true : false; } // if(has_sox){ args.push("-resampler"); args.push("soxr"); } args.push("-ar"); if(settings.samplerate == '32kHz') { args.push( '32000' ); } else if(settings.samplerate == '44.1kHz') { args.push( '44100' ); } else if(settings.samplerate == '48kHz') { args.push( '48000' ); } } // ----------------------------- // BITRATE if(settings.bitrate == 'Constant'){ args.push("-b:a"); args.push(settings.bitrate_cbr); } else{ args.push("-q:a"); args.push(settings.bitrate_vbr); } // OUTPUT args.push(outfile); // OVERWRITE SHOULD NOT BE POSSIBLE BUT JUST IN CASE args.push("-y"); // FFMPEG CALL var ffmpeg = cmd("ffmpeg", args); // CHECK FFMPEG OUTPUT FOR ERRORS return searchRegEx(ffmpeg, "error", "i", 0); } // ============================================================================= function analyseAudioFile(path) { // GET DURATION var ffprobe = cmd("ffprobe", [ '-v','error', '-select_streams','a', '-show_entries','stream=codec_type :format=duration', '-of','default=noprint_wrappers=1', '-i',path ]); // RETURN VALUES var duration = parseFloat(searchRegEx(ffprobe, "(?:duration=)(.*?)(?=[\n\r])", "o", 1)[0]); var streamCount = searchRegEx(ffprobe, "(?:codec_type=audio)(.*?)(?=[\n\r])", "o", 1); var hasAudio = parseInt(streamCount.length) === 1; return { 'duration' : duration, 'hasAudio' : hasAudio, }; } // ============================================================================= // SETTINGS DIALOG function settings_dial(){ // ----------------------------- // bitrate_cbr var bitrate_cbr_options = ""; var bitrate_cbr_default = ""; for (var i = 0; i < getObjectSize(bitrate_cbr); i++) { // ADD TO OPTIONS bitrate_cbr_options += (bitrate_cbr_options != "" ? ',' : '')+getObjectValue(bitrate_cbr,i); // CURRENT VAULE if(getObjectKey(bitrate_cbr,i) == settings.bitrate_cbr) { bitrate_cbr_default = getObjectValue(bitrate_cbr,i); } } // ----------------------------- // bitrate_vbr var bitrate_vbr_options = ""; var bitrate_vbr_default = ""; for (var i = 0; i < getObjectSize(bitrate_vbr); i++) { // ADD TO OPTIONS bitrate_vbr_options += (bitrate_vbr_options != "" ? ',' : '')+getObjectValue(bitrate_vbr,i); // CURRENT VAULE if(getObjectKey(bitrate_vbr,i) == settings.bitrate_vbr) { bitrate_vbr_default = getObjectValue(bitrate_vbr,i); } } // ----------------------------- return dialog({ // -------------------------------- "spacer0": { "type": "text", "label": "","just": "c", "bounds": { "x": 0, "w": 600, "h": 20 }, }, // -------------------------------- "mode_label" : { "type" : "text", "label" : "Bitrate mode", "just" : "l", "bounds" : { "x": 20, "w" : 160, }, }, "bitrate_mode" : { "type" : "combobox", "default" : settings.bitrate_mode, "items" : "Constant,Variable", "bounds" : { "y" : -1 , "x" : 180, "w" : 120 }, }, "bitrate_cbr" : { "type" : "combobox", "default" : bitrate_cbr_default, "items" : bitrate_cbr_options, "bounds" : { "y": -1, "x" : 310, "w" : 210 }, "visible" : true, }, "bitrate_vbr" : { "type" : "combobox", "default" : bitrate_vbr_default, "items" : bitrate_vbr_options, "bounds" : { "y": -1, "x" : 310, "w" : 210 }, "visible" : false, }, // SAMPLE RATE "samplerate_label" : { "type" : "text", "label" : "Samplerate", "just" : "l", "bounds" : { "x" : 20, "w" : 120 }, }, "samplerate" : { "type" : "combobox", "default" : settings.samplerate, "items" : "source,48kHz,44.1kHz,32kHz", "label" : " ", "bounds" : { "y": -1, "x" : 180, "w" : 340 }, }, // -------------------------------- "spacer1": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "normalise_label" : { "type" : "text", "label" : "Normalise", "bounds" : { "x": 20, "w" : 160, }, }, "normalise": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.normalise }, "normalise_val": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": -99, "max" : 0, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.normalise_val }, "normalise_type" : { "type" : "combobox", "default" : settings.normalise_type, "items" : "dB,dBTP,LUFS", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- "spacer2": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "customFolder_label" : { "type" : "text", "label" : "Custom folder", "just" : "l", "bounds" : { "x": 20, "w" : 160, }, }, "customFolder": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.customFolder }, "customFolder_path" : { "type" : "fileselect", "path" : settings.customFolder_path, "editable" : false, "dir" : true, "saving" : true, "label" : "empty", "bounds" : { "y": -1, "x": 180, "w" : 400, }, "visible": false, }, // -------------------------------- "subFolder_label" : { "type" : "text", "label" : "Sub folder", "just" : "l", "bounds" : { "x": 20, "w" : 160, }, }, "subFolder": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.subFolder }, "subFolder_val": { "type": "texteditor", "bounds" : { "y": -1, "x" : 180, "w" : 400, }, "multiline" : false, "wordwrap" : false, "default" : replaceInString( settings.subFolder_val, gvar.pss, "" ), "visible": true, }, // -------------------------------- "nametag_val_label" : { "type" : "text", "label" : "Name tag", "just" : "l", "bounds" : { "x": 20, "w" : 160, }, }, "nametag": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.nametag }, "nametag_val": { "type": "texteditor", "bounds" : { "y": -1, "x" : 180, "w" : 400, }, "multiline" : false, "wordwrap" : false, "default" : settings.nametag_val, "visible": false, }, // -------------------------------- "spacer3": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "reset" : { "type" : "button", "label" : "reset", "bounds" : { "x": 330, "w" : 80 }, "returns" : -1 }, "cancel" : { "type" : "button", "label" : "cancel", "bounds" : { "y": -1, "x": 415, "w" : 80 }, "returns" : 0 }, "save" : { "type" : "button", "label" : "save", "bounds" : { "y": -1, "x": 500, "w" : 80 }, "returns" : 1 }, // -------------------------------- }); }