Thesunfei / Pandora music replay and download

Thank you very much for the continued update of the script.

Please update the code to the following to allow for more control of the filename creation. It will allow for the filtering out of invalid characters and give more control over what replaces the invalid characters.

The following is a unified diff of the changes I propose.


diff --git a/Pandora_music_replay_and_download.user.js b/Pandora_music_replay_and_download.user.js
index 172b988..79ee36c 100644
--- a/Pandora_music_replay_and_download.user.js
+++ b/Pandora_music_replay_and_download.user.js
@@ -1,6 +1,6 @@
 // ==UserScript==
 // @name         Pandora music replay and download
-// @version      1.2.1
+// @version      1.2.2
 // @require https://code.jquery.com/jquery-3.2.1.min.js
 // @author       Thesunfei
 // @grant        none
@@ -10,6 +10,7 @@
 // ==/UserScript==
 
 // ==Revision History==
+// 1.2.2 Added additional control over filename creation
 // 1.0 Added playmode switch and now-playing title
 // 0.8 Added album name between Title and Artist for reference
 // ==/Revision History==
@@ -345,7 +346,7 @@ padding:0;
 <div class="audiotrack">
 <div class="audioposition"></div>
 </div>
-<a class="audiodownload" download="${artist+' - '+(album?(album+' - '):'')+title}.m4a"></a>
+<a class="audiodownload" download="${getFormatedSongFilename()}"></a>
 </div>
 </div>
 <audio preload class='audiocloned'>
@@ -407,8 +408,11 @@ padding:0;
       xhr.onreadystatechange = function () {
         if (this.status == 200 && this.readyState == 4) {
           audio = this.response;
+          //Get the url of the audio object
           audiourl = URL.createObjectURL(audio);
+          //Set the audio element with the url to get it
           $("audio", ele).prop("src", audiourl);
+          //Set the download link
           $("a", ele).prop("href", audiourl);
         }else if (this.status!=200){
             ele.remove();
@@ -417,6 +421,39 @@ padding:0;
       xhr.send();
     }
 
+    //Get the filename used for downloading based on the variables that exist
+    function getFormatedSongFilename() {
+      //What separates artist, album, and track in the filename
+      var downloadElementSeparator = " - ";
+      //Include a spot for an album, if missing, in the download filename.
+      var includeAlbumPlaceholder = true;
+
+      var filename = sanitizeString(downloadElementSeparator, artist) + downloadElementSeparator;           //Add the artist
+      if (album) {                                                                                          //See if we have an album to add
+        filename = filename + sanitizeString(downloadElementSeparator, album) + downloadElementSeparator;   //  Album object exists so add it
+      }
+      else if (includeAlbumPlaceholder == true) {                                                           //  Album object does not exist, see if we need to add an album placeholder
+        filename = filename + downloadElementSeparator;                                                     //    Add album placeholder by just adding another separator
+      }
+      filename = filename + sanitizeString(downloadElementSeparator, title) + ".m4a";                       //Add title and extension
+
+      return filename;
+    }
+
+    //make sure the string does not contain the downloadElementSeparator, nor any OS filename restrictions
+    function sanitizeString(dirtyString) {
+      //Remove any illegal characters based on the operating system.
+      dirtyString = dirtyString.replace(/[*?"|]/g, ""  );         //windows filename restrictions -> replace with space        * ? |
+      dirtyString = dirtyString.replace(/["]/g,    "''");         //windows filename restrictions -> replace with ''           "
+      dirtyString = dirtyString.replace(/[<>]/g,   "_" );         //windows filename restrictions -> replace with underscore   < >
+      dirtyString = dirtyString.replace(/[\\\/]/g, "," );         //windows filename restrictions -> replace with comma        \ /
+      dirtyString = dirtyString.replace(/[:]/g,    ";" );         //windows filename restrictions -> replace with semicolon    :
+
+      var sepRegEx = new RegExp(downloadElementSeparator, "g");   //create RegExp object to find downloadElementSeparator
+      dirtyString = dirtyString.replace(sepRegEx, "-");           //downloadElementSeparator      -> replace with dash         -
+      return dirtyString;
+    }
+
     function updateView() {
       $(".audioposition", ele).css("left", currenttime / totaltime * 100 + "%");
       $(ele).removeClass("onplaying onpaused onloading");