home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / components / sbDownloadDeviceHelper.js < prev    next >
Text File  |  2012-06-08  |  18KB  |  509 lines

  1. /*
  2. //
  3. // BEGIN SONGBIRD GPL
  4. //
  5. // This file is part of the Songbird web player.
  6. //
  7. // Copyright(c) 2005-2008 POTI, Inc.
  8. // http://songbirdnest.com
  9. //
  10. // This file may be licensed under the terms of of the
  11. // GNU General Public License Version 2 (the "GPL").
  12. //
  13. // Software distributed under the License is distributed
  14. // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
  15. // express or implied. See the GPL for the specific language
  16. // governing rights and limitations.
  17. //
  18. // You should have received a copy of the GPL along with this
  19. // program. If not, go to http://www.gnu.org/licenses/gpl.html
  20. // or write to the Free Software Foundation, Inc.,
  21. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. //
  23. // END SONGBIRD GPL
  24. //
  25. */
  26.  
  27. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  28. Components.utils.import("resource://app/jsmodules/sbProperties.jsm");
  29. Components.utils.import("resource://app/jsmodules/ArrayConverter.jsm");
  30. Components.utils.import("resource://app/jsmodules/FolderUtils.jsm");
  31.  
  32. const Ci = Components.interfaces;
  33. const Cc = Components.classes;
  34. const Cr = Components.results;
  35. const Cu = Components.utils;
  36. const CE = Components.Exception;
  37.  
  38. const PREF_DOWNLOAD_FOLDER       = {audio: "songbird.download.music.folder",
  39.                                     video: "songbird.download.video.folder"};
  40. const PREF_DOWNLOAD_ALWAYSPROMPT = {audio: "songbird.download.music.alwaysPrompt",
  41.                                     video: "songbird.download.video.alwaysPrompt"};
  42. const _MFM_GET_MANAGED_PATH_OPTIONS = Ci.sbIMediaFileManager.MANAGE_MOVE |
  43.                                       Ci.sbIMediaFileManager.MANAGE_COPY |
  44.                                       Ci.sbIMediaFileManager.MANAGE_RENAME;
  45.  
  46. /**
  47.  * \brief Get the name of the platform we are running on.
  48.  * \return The name of the OS platform. (ie. Windows).
  49.  * \retval Windows_NT Running under Windows.
  50.  * \retval Darwin Running under Darwin/OS X.
  51.  * \retval Linux Running under Linux.
  52.  * \retval SunOS Running under Solaris.
  53.  */
  54. function getPlatformString() 
  55. {
  56.   try {
  57.     var sysInfo =
  58.       Components.classes["@mozilla.org/system-info;1"]
  59.                 .getService(Components.interfaces.nsIPropertyBag2);
  60.     return sysInfo.getProperty("name");
  61.   }
  62.   catch (e) {
  63.     dump("System-info not available, trying the user agent string.\n");
  64.     var user_agent = navigator.userAgent;
  65.     if (user_agent.indexOf("Windows") != -1)
  66.       return "Windows_NT";
  67.     else if (user_agent.indexOf("Mac OS X") != -1)
  68.       return "Darwin";
  69.     else if (user_agent.indexOf("Linux") != -1)
  70.       return "Linux";
  71.     else if (user_agent.indexOf("SunOS") != -1)
  72.       return "SunOS";
  73.     return "";
  74.   }
  75. }
  76.  
  77. /**
  78.  * Our super-smart heuristic for determining if the download folder is valid.
  79.  */
  80. function folderIsValid(folder) {
  81.   try {
  82.     return folder && folder.isDirectory();
  83.   }
  84.   catch (e) {
  85.   }
  86.   return false;
  87. }
  88.  
  89. /**
  90.  * Returns an nsILocalFile or null if the path is bad.
  91.  */
  92. function makeFile(path) {
  93.   var file = Cc["@mozilla.org/file/local;1"].
  94.              createInstance(Ci.nsILocalFile);
  95.  
  96.   // Ensure all paths are lowercase for Windows.
  97.   // See bug #7178.
  98.   var actualPath = path;
  99.   if(getPlatformString() == "Windows_NT") {
  100.     actualPath = actualPath.toLowerCase();
  101.   }
  102.  
  103.   try {
  104.     file.initWithPath(actualPath);
  105.   }
  106.   catch (e) {
  107.     return null;
  108.   }
  109.   return file;
  110. }
  111.  
  112. /**
  113.  * Returns a file url for the path.
  114.  */
  115. function makeFileURL(path)
  116. {
  117.   // Can't use the IOService because we have callers off of the main thread...
  118.   // Super lame hack instead.
  119.  
  120.   // Ensure all paths are lowercase for Windows.
  121.   // See bug #7178.
  122.   var actualPath = path;
  123.   if(getPlatformString() == "Windows_NT") {
  124.     actualPath = actualPath.toLowerCase();
  125.   }
  126.   
  127.   return "file://" + actualPath;
  128. }
  129.  
  130. function sbDownloadDeviceHelper()
  131. {
  132.   this._mainLibrary = Cc["@songbirdnest.com/Songbird/library/Manager;1"]
  133.                         .getService(Ci.sbILibraryManager).mainLibrary;
  134. }
  135.  
  136. sbDownloadDeviceHelper.prototype =
  137. {
  138.   classDescription: "Songbird Download Device Helper",
  139.   classID:          Components.ID("{576b6833-15d8-483a-84d6-2fbd329c82e1}"),
  140.   contractID:       "@songbirdnest.com/Songbird/DownloadDeviceHelper;1",
  141.   QueryInterface:   XPCOMUtils.generateQI([Ci.sbIDownloadDeviceHelper])
  142. }
  143.  
  144. sbDownloadDeviceHelper.prototype.downloadItem =
  145. function sbDownloadDeviceHelper_downloadItem(aMediaItem)
  146. {
  147.   var downloadFolder = this._getDownloadFolder_nothrow(aMediaItem.contentType);
  148.   if (!downloadFolder) {
  149.     return;
  150.   }
  151.  
  152.   let uriArray           = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
  153.                              .createInstance(Ci.nsIMutableArray);
  154.   let propertyArrayArray = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
  155.                              .createInstance(Ci.nsIMutableArray);
  156.  
  157.   let item;
  158.   if (aMediaItem.library.equals(this._mainLibrary)) {
  159.     item = aMediaItem;
  160.   }
  161.   else {
  162.     this._addItemToArrays(aMediaItem, uriArray, propertyArrayArray);
  163.     let items = this._mainLibrary.batchCreateMediaItems(uriArray,
  164.                                                         propertyArrayArray,
  165.                                                         true);
  166.     item = items.queryElementAt(0, Ci.sbIMediaItem);
  167.   }
  168.  
  169.   // Pass the downloadFolder as the 'media-folder' option for the
  170.   // Media File Manager.  The MFM will then put the file in the
  171.   // user's preferred folder (or possibly a subfolder thereof)
  172.   this._setDownloadDestinationIfNotSet([item],
  173.                                        {'media-folder': downloadFolder});
  174.   this._checkAndRemoveExistingItem(item);
  175.   this.getDownloadMediaList().add(item);
  176. }
  177.  
  178. sbDownloadDeviceHelper.prototype.downloadSome =
  179. function sbDownloadDeviceHelper_downloadSome(aMediaItems)
  180. {
  181.   let uriArray           = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
  182.                              .createInstance(Ci.nsIMutableArray);
  183.   let propertyArrayArray = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
  184.                              .createInstance(Ci.nsIMutableArray);
  185.  
  186.   var items = [];
  187.   var foundTypes = {};
  188.   while (aMediaItems.hasMoreElements()) {
  189.     let item = aMediaItems.getNext();
  190.     if (item.library.equals(this._mainLibrary)) {
  191.       items.push(item);
  192.     }
  193.     else {
  194.       this._checkAndRemoveExistingItem(item);
  195.       this._addItemToArrays(item, uriArray, propertyArrayArray);
  196.     }
  197.     // Keep track of which media types are present.  This
  198.     // will be used below to get the download folders for
  199.     // these media types:
  200.     foundTypes[item.contentType] = true;
  201.   }
  202.  
  203.   // Get the user's preferred download folder for each media type
  204.   // in the item list and set up Media File Manager options with
  205.   // those folders.  The MFM will then sort the files into the
  206.   // respective folders (or possibly subfolders thereof).  Do this
  207.   // before creating the media items in case the user is prompted
  208.   // to choose a folder and clicks cancel:
  209.   mfmFolders = {};
  210.   try {
  211.     for (let contentType in foundTypes) {
  212.       let folder = this.getDownloadFolder(contentType);
  213.       if (folder) {
  214.         mfmFolders["media-folder:" + contentType] = folder;
  215.       }
  216.     }
  217.   }
  218.   catch (e if e.result == Cr.NS_ERROR_ABORT) {
  219.     return;
  220.   }
  221.  
  222.   if (uriArray.length > 0) {
  223.     let addedItems = this._mainLibrary.batchCreateMediaItems(uriArray,
  224.                                                              propertyArrayArray,
  225.                                                              true);
  226.     for (let i = 0; i < addedItems.length; i++) {
  227.       items.push(addedItems.queryElementAt(i, Ci.sbIMediaItem));
  228.     }
  229.   }
  230.  
  231.   this._setDownloadDestinationIfNotSet(items, mfmFolders);
  232.   this.getDownloadMediaList().addSome(ArrayConverter.enumerator(items));
  233. }
  234.  
  235. sbDownloadDeviceHelper.prototype.downloadAll =
  236. function sbDownloadDeviceHelper_downloadAll(aMediaList)
  237. {
  238.   let uriArray           = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
  239.                              .createInstance(Ci.nsIMutableArray);
  240.   let propertyArrayArray = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
  241.                              .createInstance(Ci.nsIMutableArray);
  242.  
  243.   var items = [];
  244.   var foundTypes = {};
  245.   var isForeign = aMediaList.library.equals(this._mainLibrary);
  246.   for (let i = 0; i < aMediaList.length; i++) {
  247.     var item = aMediaList.getItemByIndex(i);
  248.     if (isForeign) {
  249.       this._checkAndRemoveExistingItem(item);      
  250.       this._addItemToArrays(item, uriArray, propertyArrayArray);
  251.     }
  252.     else {
  253.       items.push(item);
  254.     }
  255.     // Keep track of which media types are present.  This
  256.     // will be used below to get the download folders for
  257.     // these media types:
  258.     foundTypes[item.contentType] = true;
  259.   }
  260.  
  261.   // Get the user's preferred download folder for each media type
  262.   // in the item list and set up Media File Manager options with
  263.   // those folders.  The MFM will then sort the files into the
  264.   // respective folders (or possibly subfolders thereof).  Do this
  265.   // before creating the media items in case the user is prompted
  266.   // to choose a folder and clicks cancel.
  267.   mfmFolders = {};
  268.   try {
  269.     for (let contentType in foundTypes) {
  270.       let folder = this.getDownloadFolder(contentType);
  271.       if (folder) {
  272.         mfmFolders["media-folder:" + contentType] = folder;
  273.       }
  274.     }
  275.   }
  276.   catch (e if e.result == Cr.NS_ERROR_ABORT) {
  277.     return;
  278.   }
  279.  
  280.   if (uriArray.length > 0) {
  281.     let addedItems = this._mainLibrary.batchCreateMediaItems(uriArray,
  282.                                                              propertyArrayArray,
  283.                                                              true);
  284.     for (let i = 0; i < addedItems.length; i++) {
  285.       items.push(addedItems.queryElementAt(i, Ci.sbIMediaItem));
  286.     }
  287.   }
  288.  
  289.   this._setDownloadDestinationIfNotSet(items, mfmFolders);
  290.   this.getDownloadMediaList().addSome(ArrayConverter.enumerator(items));
  291. }
  292.  
  293. sbDownloadDeviceHelper.prototype.getDownloadMediaList =
  294. function sbDownloadDeviceHelper_getDownloadMediaList()
  295. {
  296.   if (!this._downloadDevice) {
  297.     var devMgr = Cc["@songbirdnest.com/Songbird/DeviceManager;1"]
  298.                    .getService(Ci.sbIDeviceManager);
  299.     if (devMgr) {
  300.       var downloadCat = "Songbird Download Device";
  301.       if (devMgr.hasDeviceForCategory(downloadCat)) {
  302.         this._downloadDevice = devMgr.getDeviceByCategory(downloadCat)
  303.                                      .QueryInterface(Ci.sbIDownloadDevice);
  304.       }
  305.     }
  306.   }
  307.   return this._downloadDevice ? this._downloadDevice.downloadMediaList : null;
  308. }
  309.  
  310. sbDownloadDeviceHelper.prototype.getDefaultDownloadFolder =
  311. function sbDownloadDeviceHelper_getDefaultDownloadFolder(aContentType)
  312. {
  313.   var mediaDir = FolderUtils.getDownloadFolder(aContentType);
  314.   // We should never get something bad here, but just in case...
  315.   if (!folderIsValid(mediaDir)) {
  316.     Cu.reportError("Desktop directory is not a directory!");
  317.     throw Cr.NS_ERROR_FILE_NOT_DIRECTORY;
  318.   }
  319.  
  320.   return mediaDir;
  321. }
  322.  
  323. sbDownloadDeviceHelper.prototype.getDownloadFolder =
  324. function sbDownloadDeviceHelper_getDownloadFolder(aContentType)
  325. {
  326.   const Application = Cc["@mozilla.org/fuel/application;1"].
  327.                       getService(Ci.fuelIApplication);
  328.   const prefs = Application.prefs;
  329.  
  330.   var downloadPath;
  331.   if (prefs.has(PREF_DOWNLOAD_FOLDER[aContentType])) {
  332.     downloadPath = prefs.get(PREF_DOWNLOAD_FOLDER[aContentType]).value;
  333.   }
  334.  
  335.   var downloadFolder;
  336.   if (downloadPath) {
  337.     downloadFolder = makeFile(downloadPath);
  338.   }
  339.   else {
  340.     // The pref was empty. Use (and write) the default.
  341.     downloadFolder = this.getDefaultDownloadFolder(aContentType);
  342.     if (downloadFolder) {
  343.       prefs.setValue(PREF_DOWNLOAD_FOLDER[aContentType], downloadFolder.path);
  344.     }
  345.   }
  346.  
  347.   const alwaysPrompt = prefs.getValue(PREF_DOWNLOAD_ALWAYSPROMPT[aContentType], false);
  348.   if (folderIsValid(downloadFolder) && !alwaysPrompt) {
  349.     return downloadFolder;
  350.   }
  351.  
  352.   const sbs = Cc["@mozilla.org/intl/stringbundle;1"].
  353.               getService(Ci.nsIStringBundleService);
  354.   const strings =
  355.     sbs.createBundle("chrome://songbird/locale/songbird.properties");
  356.   const title = strings.GetStringFromName(
  357.                 "prefs.main.downloads." + aContentType + ".chooseTitle");
  358.  
  359.   const wm = Cc["@mozilla.org/appshell/window-mediator;1"].
  360.              getService(Ci.nsIWindowMediator);
  361.   const mainWin = wm.getMostRecentWindow("Songbird:Main");
  362.  
  363.   // Need to prompt, launch the filepicker.
  364.   const folderPicker = Cc["@mozilla.org/filepicker;1"].
  365.                        createInstance(Ci.nsIFilePicker);
  366.   folderPicker.init(mainWin, title, Ci.nsIFilePicker.modeGetFolder);
  367.   folderPicker.displayDirectory = downloadFolder;
  368.  
  369.   if (folderPicker.show() != Ci.nsIFilePicker.returnOK) {
  370.     // This is our signal that the user cancelled the dialog. Some folks won't
  371.     // care, but most will.
  372.     throw new CE("User canceled the download dialog.", Cr.NS_ERROR_ABORT);
  373.   }
  374.  
  375.   downloadFolder = folderPicker.file;
  376.  
  377.   prefs.setValue(PREF_DOWNLOAD_FOLDER[aContentType], downloadFolder.path);
  378.   return downloadFolder;
  379. }
  380.  
  381. sbDownloadDeviceHelper.prototype._checkAndRemoveExistingItem =
  382. function sbDownloadDeviceHelper__checkAndRemoveExistingItem(aMediaItem)
  383. {
  384.   var downloadMediaList = this.getDownloadMediaList();
  385.   if (downloadMediaList) {
  386.     var itemOriginUrl = aMediaItem.getProperty(SBProperties.originURL);
  387.  
  388.     var listEnumerationListener = {
  389.       onEnumerationBegin: function() {
  390.       },
  391.       onEnumeratedItem: function(list, item) {
  392.         downloadMediaList.remove(item);
  393.       },
  394.       onEnumerationEnd: function() {
  395.       },
  396.     };
  397.     
  398.     if ( (itemOriginUrl != "") &&
  399.          (itemOriginUrl != null) ) {
  400.       downloadMediaList.enumerateItemsByProperty(SBProperties.originURL,
  401.                                                  itemOriginUrl,
  402.                                                  listEnumerationListener);
  403.     }
  404.   }
  405. }
  406.  
  407. sbDownloadDeviceHelper.prototype._getDownloadFolder_nothrow =
  408. function sbDownloadDeviceHelper__getDownloadFolder_nothrow(aContentType)
  409. {
  410.   // This function returns null if the user cancels the dialog.
  411.   try {
  412.     return this.getDownloadFolder(aContentType);
  413.   }
  414.   catch (e if e.result == Cr.NS_ERROR_ABORT) {
  415.   }
  416.   return null;
  417. }
  418.  
  419. sbDownloadDeviceHelper.prototype._addItemToArrays =
  420. function sbDownloadDeviceHelper__addItemToArrays(aMediaItem,
  421.                                                  aURIArray,
  422.                                                  aPropertyArrayArray)
  423. {
  424.   aURIArray.appendElement(aMediaItem.contentSrc, false);
  425.  
  426.   var dest   = SBProperties.createArray();
  427.   var source = aMediaItem.getProperties();
  428.   for (let i = 0; i < source.length; i++) {
  429.     var prop = source.getPropertyAt(i);
  430.     // Filter our enableAutoDownload, as the Auto Downloader
  431.     // should ignore these items.  Their download is already
  432.     // imminent:
  433.     if (prop.id != SBProperties.contentSrc &&
  434.         prop.id != SBProperties.enableAutoDownload)
  435.     {
  436.       dest.appendElement(prop, false);
  437.     }
  438.   }
  439.  
  440.   var target = aMediaItem.library.guid + "," + aMediaItem.guid;
  441.   dest.appendProperty(SBProperties.downloadStatusTarget, target);
  442.  
  443.   aPropertyArrayArray.appendElement(dest, false);
  444. }
  445.  
  446. sbDownloadDeviceHelper.prototype._setDownloadDestinationIfNotSet =
  447. function sbDownloadDeviceHelper__setDownloadDestinationIfNotSet(
  448.          aItems,
  449.          aMediaFileManagerOptions)
  450. {
  451.   // Copy the Media File Manager options to an nsIPropertyBag
  452.   var bag = Cc["@mozilla.org/hash-property-bag;1"]
  453.               .createInstance(Ci.nsIWritablePropertyBag);
  454.   for (prop in aMediaFileManagerOptions) {
  455.     bag.setProperty(prop, aMediaFileManagerOptions[prop]);
  456.   }
  457.   
  458.   // Use the Media File Manager to organize files into subfolders.
  459.   // The caller will designate the root folder for each media type
  460.   // in the MFM options bag (based on user preferences):
  461.   var mediaFileMgr = Cc["@songbirdnest.com/Songbird/media-manager/file;1"]
  462.                      .createInstance(Ci.sbIMediaFileManager);
  463.   mediaFileMgr.init(bag);
  464.   
  465.   for each (var item in aItems) {
  466.     try {
  467.       var curDestination = item.getProperty(SBProperties.destination);
  468.       if (!curDestination) {
  469.         let destFile = mediaFileMgr.getManagedPath(
  470.                                     item,
  471.                                     _MFM_GET_MANAGED_PATH_OPTIONS);
  472.         let path = destFile.path;
  473.         
  474.         /// @todo Video items tend to have meaningless file names, because
  475.         ///       they lack a trackName property.  For now, strip off the leaf
  476.         ///       name of unbetitled items, and
  477.         //        sbDownloadSession::CompleteTransfer() will construct a
  478.         //        file name from the source URL:
  479.         if (!item.getProperty(SBProperties.trackName)) {
  480.           // Leave a trailing delimiter so the path parses as a directory:
  481.           path = path.replace(/[^/\\]*$/, "");
  482.         }
  483.         
  484.         // Ensure all paths are lowercase for Windows.
  485.         // See bug #7178.
  486.         let actualDestination = makeFileURL(path);
  487.         if(getPlatformString() == "Windows_NT") {
  488.           actualDestination = actualDestination.toLowerCase();
  489.         }
  490.         
  491.         item.setProperty(SBProperties.destination, actualDestination);
  492.       }
  493.     }
  494.     catch (e) {
  495.       // We're not allowed to set the download destination on remote media items
  496.       // so that call may fail. The remoteAPI should unwrap all items and lists
  497.       // *before* handing them to us, so throw the error with a slightly more
  498.       // helpful message.
  499.       throw new CE("Download destination could not be set on this media item:\n"
  500.                    + "  " + item + "\nIs it actually a remote media item?",
  501.                    e.result);
  502.     }
  503.   }
  504. }
  505.  
  506. function NSGetModule(compMgr, fileSpec) {
  507.   return XPCOMUtils.generateModule([sbDownloadDeviceHelper]);
  508. }
  509.