GP SoftwareTwitter
Opus FAQsManualCommandsObjects

Audiobook M3U Generator

My script will split an audiobook every Nth file, creating a new m3u playlist until all audio files are counted. You can choose to split the audiobook every 2 to 10 files from the dialog prompt. The m3u file name is based on the current folder name with a _CD number at the end. If you have any album art images in the audiobook folder they are ignored. Only audio files will be added to the playlist (mp3, wav, ogg, m4v, flac,wma).

I have 74 mp3 files in the audiobookTitleHere folder. Lets say one mp3 file is 5 minutes in length. If I wanted to listen for 50 minutes, I'd create a playlist every 10 files. You can lower this number to listen for less time.

[imgur link is now broken]

1. Right click any toolbar
2. Click Customize...
3. Copy and paste directly to your toolbar

<?xml version="1.0"?>
<button backcol="none" display="label" label_pos="right" textcol="none">
	<label>Audiobook M3U Generator</label>
	<tip>Splits the audiobook every nth file (2-10)</tip>
	<function type="script">
		<instruction>@script jscript</instruction>
		<instruction>function zeroPad(num)</instruction>
		<instruction>  var numZeropad = num + &apos;&apos;;</instruction>
		<instruction>  while(numZeropad.length &lt; 2){</instruction>
		<instruction>  numZeropad = &quot;0&quot; + numZeropad;</instruction>
		<instruction>return numZeropad;</instruction>
		<instruction>function numberCDS(CD){</instruction>
		<instruction>  var getCDNumber = zeroPad(CD);</instruction>
		<instruction>return getCDNumber;</instruction>
		<instruction>function isAudio(extension)</instruction>
		<instruction>  switch (extension.toLowerCase()){</instruction>
		<instruction>  case &apos;.mp3&apos;:</instruction>
		<instruction>  case &apos;.wav&apos;:</instruction>
		<instruction>  case &apos;.ogg&apos;:</instruction>
		<instruction>  case &apos;.m4v&apos;:</instruction>
		<instruction>  case &apos;.flac&apos;:</instruction>
		<instruction>  case &apos;.wma&apos;:</instruction>
		<instruction>      return true;</instruction>
		<instruction>    }</instruction>
		<instruction>  return false;</instruction>
		<instruction>function OnClick(ClickData)</instruction>
		<instruction>      var objCmd = ClickData.Func.Command;</instruction>
		<instruction>      var allFiles = objCmd.sourcetab.files;</instruction>
		<instruction>      var objEnum = new Enumerator(allFiles);</instruction>
		<instruction>      if (!objEnum.atEnd())</instruction>
		<instruction>      {</instruction>
		<instruction>              // build an array of the files so we can sort it</instruction>
		<instruction>              var objFiles = new Array();</instruction>
		<instruction>              i = 0;</instruction>
		<instruction>              while (!objEnum.atEnd())</instruction>
		<instruction>              {</instruction>
		<instruction>                    objFiles[i] = objEnum.item();</instruction>
		<instruction>                    if (isAudio(objFiles[i].ext) == false)</instruction>
		<instruction>                    {</instruction>
		<instruction>                      objFiles.splice (i, 1);</instruction>
		<instruction>                    }</instruction>
		<instruction>                    ++i;</instruction>
		<instruction>                    objEnum.moveNext();</instruction>
		<instruction>              }</instruction>
		<instruction>      objFiles.sort();</instruction>
		<instruction>      ClickData.func.command.ClearFiles();</instruction>
		<instruction>      audiobookFolderName = objCmd.sourcetab.path.filepart;</instruction>
		<instruction>      var audiobookFolderPath = objCmd.sourcetab.path;</instruction>
		<instruction>      var fso, f1;</instruction>
		<instruction>      var ForWriting = 2;</instruction>
		<instruction>      var totalNumberOfFiles = objCmd.sourcetab.stats.files;</instruction>
		<instruction>      var Dopus = DOpus.CreateCommand;</instruction>
		<instruction>      var dlg = Dopus.Dlg;</instruction>
		<instruction>      dlg.window = DOpus.Listers(0);</instruction>
		<instruction>      dlg.message = &quot;Split audiobook every nth file:&quot;;</instruction>
		<instruction>      dlg.title = &quot;Audiobook M3U Generator&quot;;</instruction>
		<instruction>      dlg.buttons = &quot;OK|Cancel&quot;;</instruction>
		<instruction>      dlg.choices = DOpus.NewVector(&quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot;, &quot;6&quot;, &quot;7&quot;, &quot;8&quot;, &quot;9&quot;, &quot;10&quot;)</instruction>
		<instruction>      dlg.selection = 0</instruction>
		<instruction>      ret =;</instruction>
		<instruction>      if (ret == 0)</instruction>
		<instruction>      {</instruction>
		<instruction>           return;</instruction>
		<instruction>      }</instruction>
		<instruction>      var chosenOutput = dlg.choices(dlg.selection);</instruction>
		<instruction>      switch (chosenOutput){</instruction>
		<instruction>      case &apos;2&apos;:</instruction>
		<instruction>      var chosenNumber = 2;</instruction>
		<instruction>      break; </instruction>
		<instruction>      case &apos;3&apos;:</instruction>
		<instruction>      var chosenNumber = 3; </instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;4&apos;:</instruction>
		<instruction>      var chosenNumber = 4; </instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;5&apos;:</instruction>
		<instruction>      var chosenNumber = 5;</instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;6&apos;:</instruction>
		<instruction>      var chosenNumber = 6;</instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;7&apos;:</instruction>
		<instruction>      var chosenNumber = 7;</instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;8&apos;:</instruction>
		<instruction>      var chosenNumber = 8;</instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;9&apos;:</instruction>
		<instruction>      var chosenNumber = 9;</instruction>
		<instruction>      break;</instruction>
		<instruction>      case &apos;10&apos;:</instruction>
		<instruction>      var chosenNumber = 10;</instruction>
		<instruction>      break;</instruction>
		<instruction>      }</instruction>
		<instruction>      var x = totalNumberOfFiles / chosenNumber;</instruction>
		<instruction>      var totalCDs = Math.floor(x);</instruction>
		<instruction>      if ((totalNumberOfFiles % chosenNumber) !== 0)</instruction>
		<instruction>      {</instruction>
		<instruction>        totalCDs = Math.floor(x) + 1;</instruction>
		<instruction>        totalNumberOfFiles = totalNumberOfFiles + 10;</instruction>
		<instruction>      }</instruction>
		<instruction>      var m3uName;</instruction>
		<instruction>      function createCD(currentCDNumber)</instruction>
		<instruction>      {</instruction>
		<instruction>        var getCdNumber = numberCDS(currentCDNumber);</instruction>
		<instruction>          if (currentCDNumber &lt;= totalCDs)</instruction>
		<instruction>          {</instruction>
		<instruction>            m3uName = audiobookFolderName + &quot;_CD&quot; + getCdNumber + &quot;.m3u&quot;;</instruction>
		<instruction>            return m3uName;</instruction>
		<instruction>          }</instruction>
		<instruction>      }</instruction>
		<instruction>        fso = new ActiveXObject(&quot;Scripting.FileSystemObject&quot;);</instruction>
		<instruction>        var i = 0;</instruction>
		<instruction>        var k = 1;</instruction>
		<instruction>        do {</instruction>
		<instruction>            for(incrementbyChosen = 0; incrementbyChosen &lt;= totalNumberOfFiles; incrementbyChosen += chosenNumber)</instruction>
		<instruction>            {</instruction>
		<instruction>              createCD(k);</instruction>
		<instruction>              if (k * chosenNumber == incrementbyChosen)</instruction>
		<instruction>              {</instruction>
		<instruction>                if (typeof m3uName !== &quot;undefined&quot;)</instruction>
		<instruction>                {</instruction>
		<instruction>                f1 = fso.CreateTextFile(audiobookFolderPath + &quot;\\&quot; + m3uName, ForWriting);</instruction>
		<instruction>                }</instruction>
		<instruction>                for (i; i &lt; objFiles.length &amp;&amp; i &lt; incrementbyChosen; i++)</instruction>
		<instruction>                {</instruction>
		<instruction>                    if (objFiles[i] != null)</instruction>
		<instruction>                    {</instruction>
		<instruction>                         f1.Write((objFiles[i].name) + &apos;\r&apos;);</instruction>
		<instruction>                    }</instruction>
		<instruction>                }</instruction>
		<instruction>              }</instruction>
		<instruction>            }</instruction>
		<instruction>            k++;</instruction>
		<instruction>        } while (k &lt;= totalCDs);</instruction>
		<instruction>      {</instruction>
		<instruction>      delete objFiles;</instruction>
		<instruction>      }</instruction>
		<instruction>   delete objEnum;</instruction>
		<instruction>   }</instruction>

This was my very first script using JavaScript. Feedback is welcome.

I'm trying to modify this script to work for multiple cases.

First I didn't want to have split playlist so I got rid of that part.

Next I wanted it to work in the case of selecting some files and folders, but I'm at a loss for how to process the selected directories?

  1. Nothing selected (use sourcetab and process it)
  2. Something Selected
  3. Process Each Folder, Get all files
  4. Make a M3U with all the selected files

The trouble I'm having is the FolderEnum vs Enumerator syntax. One uses atEnd() and another complete

How Can I make it work for all the situations described?

See my code as of now:

function isAudio(extension) {
  switch (extension.toLowerCase()) {
    case '.mp3':
    case '.wav':
    case '.ogg':
    case '.flac':
    case '.wma':
    case '.m4v':
    case '.m4a':
    case '.m4b':
      return true;
  return false;

function collectAudioFiles(objEnum) {
  var objFiles = new Array();
  var objT;
  var i = 0;
  while (!objEnum.atEnd()) {
    objT = objEnum.item();
    if (isAudio(objT.ext) == true) {
  return objFiles;

function genM3U(objEnum, bAbsolute) {
  if (typeof bAbsolute === 'undefined') {
    bAbsolute = true;
  // Original code here
  if (objEnum.atEnd()) {
    DOpus.Output("No Items Found");
  // build an array of the files so we can sort it
  var objFiles = collectAudioFiles(objEnum);

  //DOpus.Output("Length: "+objFiles.length);
  //DOpus.Output("Files: "+objFiles);
  if (objFiles.length < 1) {
    DOpus.Output("No Audio Files Found");
  var audiobookFolderName = objFiles[0].path.filepart+".m3u";
  var audiobookFolderPath = objFiles[0].path;
  var m3uName = audiobookFolderPath + '\\' + audiobookFolderName;

  var fso, f1;
  var ForWriting = 2;
  //var Dopus = DOpus.CreateCommand;
  fso = new ActiveXObject("Scripting.FileSystemObject");
  var i = 0;
  if (typeof m3uName !== "undefined") {
    DOpus.Output("Creating M3U File with "+objFiles.length+" entries.");
    f1 = fso.CreateTextFile(m3uName, ForWriting);
  for (i; i < objFiles.length; i++) {
    if (objFiles[i] != null) {
      if (bAbsolute) {
        f1.Write((objFiles[i].RealPath) + '\r');
      else {
        f1.Write((objFiles[i].name) + '\r');
  delete objFiles;
  DOpus.Output("Created M3U File:"+m3uName); 
  delete objEnum;

function filesFromDir(path) {


function OnClick(clickData) {
  var objCmd = clickData.Func.Command;
  // How to check if SHIFT was pressed? and set the absolute value to false
  // mine
  if (clickData.func.sourcetab.selected.count == 0) {
    DOpus.Output("Nothing Selected, Processing current directory");
    genM3U(new Enumerator(objCmd.sourcetab.files)); // call original code to create m3u for currently selected tab
  else {
    genM3U(new Enumerator(objCmd.sourcetab.selected_files));
    //genM3U(new Enumerator(objCmd.sourcetab.selected_dirs)); //This doesn't work

    for (var eSel = new Enumerator(objCmd.sourcetab.selected_dirs); !eSel.atEnd(); eSel.moveNext()) {
      // collect files in a collection
      var objFiles = []
      var objT;
      var eFolEnum = DOpus.FSUtil.ReadDir(eSel.item().RealPath)
      while(!eFolEnum.complete) {
        objT = eFolEnum.Next()
        if (!objT.is_dir) {
      genM3U(new Enumerator(objFiles));

Actually I was able to get it to work by doing something dumb. Loading all the files into memory and then pushing it onto the enumerator.

Sigh. Any feedback is welcome, feels too hackish right now, but will work for all my cases right now.
Download the button below.
Create M3U(s).dcf (6.7 KB)