sbPlaylistReaderManager.js
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 //
26 // sbIPlaylistReaderManager Object
27 //
28 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
29 
30 const Cc = Components.classes;
31 const Ci = Components.interfaces;
32 const Cr = Components.results;
33 
35 {
36  var catMan = Cc["@mozilla.org/categorymanager;1"]
37  .getService(Ci.nsICategoryManager);
38 
39  var readers = catMan.enumerateCategory("playlist-reader");
40  while (readers.hasMoreElements()) {
41  var entry = readers.getNext();
42  entry = entry.QueryInterface(Ci.nsISupportsCString);
43  var contractid = catMan.getCategoryEntry("playlist-reader", entry);
44 
45  try {
46  var aReader = Cc[contractid].createInstance(Ci.sbIPlaylistReader);
47  this.m_Readers.push(aReader);
48  }
49  catch(e) {
50  }
51  }
52 
53  // Cache the supported strings
54  for(var i in this.m_Readers)
55  {
56  var nExtensionsCount = {};
57  var aExts = this.m_Readers[i].supportedFileExtensions(nExtensionsCount);
58  this.m_Extensions = this.m_Extensions.concat(aExts);
59  var nMIMETypesCount = {};
60  var aMIMETypes = this.m_Readers[i].supportedMIMETypes(nMIMETypesCount);
61  this.m_MIMETypes = this.m_MIMETypes.concat(aMIMETypes);
62  }
63 
64  var obs = Cc["@mozilla.org/observer-service;1"]
65  .getService(Ci.nsIObserverService);
66  obs.addObserver(this, "quit-application", false);
67 }
68 
70 
72 {
73  classDescription: "Songbird Playlist Reader Manager Interface",
74  classID: Components.ID("{ced5902c-bd90-4099-acee-77487a5b1d13}"),
75  contractID: "@songbirdnest.com/Songbird/PlaylistReaderManager;1",
76 
77  originalURI: null,
78 
79  m_rootContractID: "@songbirdnest.com/Songbird/Playlist/Reader/",
80  m_interfaceID: Components.interfaces.sbIPlaylistReader,
81  m_Readers: new Array(),
82  m_Extensions: new Array(),
83  m_MIMETypes: new Array(),
84 
85  getTempFilename: function(aExtension)
86  {
87  var extension;
88  if (aExtension)
89  extension = aExtension;
90  else
91  extension = "tmp";
92  var file = Components.classes["@mozilla.org/file/directory_service;1"]
93  .getService(Components.interfaces.nsIProperties)
94  .get("TmpD", Components.interfaces.nsIFile);
95  file.append("songbird." + extension);
96  file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);
97 
98  return file.path;
99  },
100 
101  getFileExtension: function(aThing)
102  {
103  var name = "";
104  if (aThing instanceof Ci.nsIURL) {
105  if ("" != aThing.fileExtension) {
106  return aThing.fileExtension;
107  }
108  }
109  if (aThing instanceof Ci.nsIURI) {
110  name = aThing.path;
111  }
112  if (aThing instanceof Ci.nsIFile) {
113  name = aThing.leafName;
114  }
115  // find the file extension
116  var m = /\.([^\.\/]+)$/(name);
117  if (m) {
118  return m[1];
119  } else {
120  return null;
121  }
122  },
123 
124  //sbIPlaylistReaderManager
125  loadPlaylist: function(aURI, aMediaList, aContentType, aAddDistinctOnly, aPlaylistReaderListener)
126  {
127  const PlaylistReaderListener = new Components.Constructor("@songbirdnest.com/Songbird/PlaylistReaderListener;1", "sbIPlaylistReaderListener");
128 
129  var theExtension = this.getFileExtension(aURI);
130 
131  if (aURI instanceof Ci.nsIFileURL)
132  {
133  var file = aURI.QueryInterface(Ci.nsIFileURL).file;
134 
135  // Can't be a playlist if there is nothing in it
136  if (!file.exists() || file.fileSize == 0) {
137  return -1;
138  }
139 
140  // If we are trying to load highly generic
141  // content types, try to guess better ones
142  if (aContentType == null ||
143  aContentType == "" ||
144  aContentType == "text/html" ||
145  aContentType == "text/xml" ||
146  aContentType == "text/plain" ||
147  aContentType == "application/xhtml" ||
148  aContentType == "application/xhtml+xml" ||
149  aContentType == "application/xml") {
150  aContentType = this.guessMimeType(file);
151  if (!aContentType) {
152  return -1;
153  }
154  }
155 
156  if (!this.originalURI) {
157  this.originalURI = aURI;
158  }
159 
160  try {
161  this.read(file, aMediaList, aContentType, aAddDistinctOnly);
162  if (aPlaylistReaderListener && aPlaylistReaderListener.observer) {
163  aPlaylistReaderListener.observer.observe(aMediaList, "success", "");
164  }
165  return 1;
166  }
167  catch(e) {
168  if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
169  return -1
170  }
171  throw e;
172  }
173  finally {
174  this.originalURI = null;
175  }
176 
177  }
178  else
179  {
180  // Remember the original url.
181  this.originalURI = aURI;
182 
183  var browser = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
184  .createInstance(Ci.nsIWebBrowserPersist);
185 
186  if(!browser) return -1;
187 
188  // Create a local file to save the remote playlist to
189  var destFile = this.getTempFilename(theExtension);
190  var localFile = Cc["@mozilla.org/file/local;1"]
191  .createInstance(Ci.nsILocalFile);
192  localFile.initWithPath(destFile);
193 
194  var ios = Cc["@mozilla.org/network/io-service;1"]
195  .getService(Ci.nsIIOService);
196  var localFileUri = ios.newFileURI(localFile);
197 
198  var registerFileForDelete = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
199  .getService(Ci.nsPIExternalAppLauncher);
200  registerFileForDelete.deleteTemporaryFileOnExit(localFile);
201 
202  var prListener = null;
203  if(aPlaylistReaderListener)
204  {
205  prListener = aPlaylistReaderListener;
206  }
207  else
208  {
209  prListener = (new PlaylistReaderListener()).QueryInterface(Ci.sbIPlaylistReaderListener);
210  }
211 
212  prListener.originalURI = this.originalURI;
213  prListener.mediaList = aMediaList;
214  prListener.destinationURI = localFileUri;
215  prListener.addDistinctOnly = aAddDistinctOnly;
216 
217  // let the download decompress gzip as appropriate
218  browser.persistFlags &=
219  ~(Ci.nsIWebBrowserPersist.PERSIST_FLAGS_NO_CONVERSION);
220 
221  browser.progressListener = prListener;
222 
223  browser.saveURI(aURI, null, null, null, "", localFileUri);
224 
225  return 1;
226  }
227 
228  return 1;
229  },
230 
231  read: function(aFile, aMediaList, aContentType, aAddDistinctOnly, aPlaylistFormatType)
232  {
233  if (!this.originalURI) {
234  var ioService = Cc["@mozilla.org/network/io-service;1"]
235  .getService(Ci.nsIIOService);
236  this.originalURI = ioService.newFileURI(aFile);
237  }
238 
239  var theExtension = this.getFileExtension(aFile);
240  for (var r in this.m_Readers)
241  {
242  var aReader = this.m_Readers[r];
243  if(!aContentType)
244  {
245  var nExtensionsCount = {};
246  var theExtensions = aReader.supportedFileExtensions(nExtensionsCount);
247 
248  if (SB_ArrayContains(theExtensions, theExtension)) {
249 
250  // Handoff the original url
251  aReader.originalURI = this.originalURI;
252  this.originalURI = null;
253 
254  aReader.read(aFile, aMediaList, aAddDistinctOnly, aPlaylistFormatType);
255  return;
256  }
257  }
258  else
259  {
260  var nMIMTypeCount = {};
261  var theMIMETypes = aReader.supportedMIMETypes(nMIMTypeCount);
262 
263  if (SB_ArrayContains(theMIMETypes, aContentType)) {
264 
265  // Handoff the original url
266  aReader.originalURI = this.originalURI;
267  this.originalURI = null;
268 
269  aReader.read(aFile, aMediaList, aAddDistinctOnly, aPlaylistFormatType);
270  return;
271  }
272  }
273  }
274 
275  // Couldn't handle it so throw
276  throw Cr.NS_ERROR_NOT_AVAILABLE;
277  },
278 
279  supportedFileExtensions: function(nExtCount)
280  {
281  nExtCount.value = this.m_Extensions.length;
282  return this.m_Extensions;
283  },
284 
285  supportedMIMETypes: function(nMIMECount)
286  {
287  nMIMECount.value = this.m_MIMETypes.length;
288  return this.m_MIMETypes;
289  },
290 
291  guessMimeType: function(file)
292  {
293  // Read a few bytes from the file
294  var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
295  .createInstance(Components.interfaces.nsIFileInputStream);
296  var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
297  .createInstance(Components.interfaces.nsIScriptableInputStream);
298  fstream.init(file, -1, 0, 0);
299  sstream.init(fstream);
300  var str = sstream.read(4096);
301  sstream.close();
302  fstream.close();
303 
304  // This object literal maps mime types to regexps. If the content matches
305  // the given regexp, it is assigned the given mime type.
306  var regexps = [
307  {
308  // Note: "\s\S" matches *everything*, unlike "." which matches
309  // everything except system-specific variations on \n.
310  regexp: /<\?xml[\s\S]*<rss/,
311  mimeType: "application/rss+xml"
312  },
313  {
314  regexp: /<\?xml[\s\S]*xmlns="http:\/\/www\.w3\.org\/2005\/Atom"/,
315  mimeType: "application/atom+xml"
316  },
317  {
318  regexp: /^\[playlist\]/i,
319  mimeType: "audio/x-scpls"
320  },
321  {
322  regexp: /^#EXTM3U/i,
323  mimeType: "audio/mpegurl"
324  },
325  {
326  regexp: /<html/i,
327  mimeType: "text/html"
328  },
329  {
330  regexp: /^(http|mms|rtsp)/im,
331  mimeType: "audio/mpegurl"
332  },
333  {
334  regexp: /.*(mp3|ogg|flac|wav|m4a|wma|wmv|asx|asf|avi|mov|mpg|mp4)$/im,
335  mimeType: "audio/mpegurl"
336  },
337  {
338  regexp: /^<ASX/i,
339  mimeType: "video/x-ms-asf"
340  }
341  ];
342 
343  for(var i = 0; i < regexps.length; i++) {
344  var re = regexps[i].regexp;
345  if(re.test(str)) {
346  return regexps[i].mimeType;
347  }
348  }
349 
350  // Otherwise, we have no guess
351  return null;
352  },
353 
354  observe: function(aSubject, aTopic, aData) {
355  var obs = Cc["@mozilla.org/observer-service;1"]
356  .getService(Ci.nsIObserverService);
357  obs.removeObserver(this, "quit-application");
358 
359  for (let i in this.m_Readers) {
360  this.m_Readers[i] = null;
361  }
362  },
363 
364  QueryInterface: XPCOMUtils.generateQI([Ci.sbIPlaylistReaderManager])
365 };
366 
367 function SB_ArrayContains(a, v) {
368  return a.some(function(e) { return e == v; } );
369 }
370 
371 function NSGetModule(compMgr, fileSpec) {
372  return XPCOMUtils.generateModule([CPlaylistReaderManager]);
373 }
classDescription entry
Definition: FeedWriter.js:1427
function getTempFilename()
Get a temporary file name.
function NSGetModule(compMgr, fileSpec)
function SB_ArrayContains(a, v)
sbDeviceFirmwareAutoCheckForUpdate prototype contractID
sbOSDControlService prototype QueryInterface
sbDeviceFirmwareAutoCheckForUpdate prototype classDescription
var ioService
Element Properties html
return null
Definition: FeedWriter.js:1143
sbDeviceFirmwareAutoCheckForUpdate prototype classID
var ios
Definition: head_feeds.js:5
var browser
Definition: openLocation.js:42
_getSelectedPageStyle s i
function CPlaylistReaderManager()
_updateTextAndScrollDataForFrame aData
var file
sbDeviceFirmwareAutoCheckForUpdate prototype observe