Album art is used to provide a visual experience to a users media collection in a familar format. Users are use to having their media collection as Albums, Tapes, or Records. We call the different types Albums in general, and each Album has one or more images associated with it to make it easier for the user to identify.
Album Art comes in various types, and allows for each item to have multiple and even unique images. Meta data tags can have different limits and types of art allowed to be stored in the meta data. By default all images will be stored in a local file cache so that any items that we are not able to write images to will still be able to have images in Songbird. This will also allow for faster loads for the images in Songbird. The main property for images is the primaryImageURL which is a resource://sb-artwork/ url that points to the file in the album art cache.
Since we started with ID3 we will be using it as our base for what we support and how things are configured. The types of covers we will support is as follows:
| Type | Value | Hex Value |
| OTHER (Secondary) | 0 | 0x00 |
| FILEICON | 1 | 0x01 |
| OTHERFILEICON | 2 | 0x02 |
| FRONTCOVER (Primary) | 3 | 0x03 |
| BACKCOVER | 4 | 0x04 |
| LEAFLETPAGE | 5 | 0x05 |
| MEDIA | 6 | 0x06 |
| LEADARTIST | 7 | 0x07 |
| ARTIST | 8 | 0x08 |
| CONDUCTOR | 9 | 0x09 |
| BAND | 10 | 0x0A |
| COMPOSER | 11 | 0x0B |
| LYRICIST | 12 | 0x0C |
| RECORDINGLOCATION | 13 | 0x0D |
| DURINGRECORDING | 14 | 0x0E |
| DURINGPERFORMANCE | 15 | 0x0F |
| MOVIESCREENCAPTURE | 16 | 0x10 |
| COLOUREDFISH | 17 | 0x11 |
| ILLUSTRATION | 18 | 0x12 |
| BANDLOGO | 19 | 0x13 |
| PUBLISHERLOGO | 20 | 0x14 |
http://www.id3.org/
We use TAGLib for suporting the ID3 tags. The artwork is retrieved from the APIC frames with the following structure:
<Header for 'Attached picture', ID: "APIC">
Text encoding $xx
MIME type <text string> $00
Picture type $xx
Description <text string according to encoding> $00 (00)
Picture data <binary data>
http://www.xiph.org/vorbis/doc/v-comment.html
Currently images are not properly supported in the ogg header specification, there have been hacks at this but the only current implementation is to put a url to the image file in the header. We will default back to only storing the image on the filesystem and in the database.
http://wiki.hydrogenaudio.org/index...._specification
Currently images are not properly supported in the APEv2 header specification. We will default back to only storing the image on the filesystem and in the database.
The primary image for display is stored as an uri in the primaryImageURL property, the uri points either to a cached image stored in the profile folder or other file/http/etc location. It is not recommended that the images are stored as web uris as a network connection may not be available.
Images that are cached on the file system, generally for images pasted, copied or retrieved from meta data, are stored in the profile folder for quicker access to the image. The cached images are stored under the artwork folder in the profile folder.
/Users/[username]/App Data/Local/Songbird2/Profiles/[profile]/artwork
/home/[username]/.songbird2/[profile]/artwork
/Users/[username]/Library/Application Data/Songbird2/Profiles/[profile]/artwork
Fetchers are individual components that follow the same catagory "songbird-album-art-fetcher". The current fetchers are:
Other fetchers can be created that will retrieve artwork from online services or other devices. The fetchers are based off the sbIAlbumArtFetcher component defined in sbIAlbumArtFetcher.idl.
/root
/chrome/
chrome.manifest
/locale/en-US
[fetchername]-fetcher.properties
/components
sb[FetcherName]ArtFetcher.js
/defaults/preferences
prefs.js
install.rdf
locale [fetchername]-fetcher en-US chrome/locale/en-US/
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#"
xmlns:songbird="http://www.songbirdnest.com/2007/addon-metadata-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>{bac3da27-34f4-4085-9da4-84595ef36a39}</em:id>
<em:type>2</em:type>
<em:name>[Fetcher Name] Album Art Fetcher</em:name>
<em:version>1.0.0.0</em:version>
<em:creator></em:creator>
<em:description>Fetch album art from [Fetcher Name].</em:description>
<em:targetApplication>
<Description>
<em:id>songbird@songbirdnest.com</em:id>
<em:minVersion>1.1.0</em:minVersion>
<em:maxVersion>1.2.0</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>
extensions.albumart.[fetchername].name=[Fetcher Name] extensions.albumart.[fetchername].description=A New Fetcher
// Album Art Fetching preferences
// Dump status to console
pref("extensions.albumart.[fetchername].debug", false);
// Enable the Fetcher
pref("extensions.albumart.[fetchername].enabled", true);
// Priority of the Fetcher. Use a number between 1 and 999
// A lower number is higher priority.
pref("extensions.albumart.[fetchername].priority", XXX);
/*
//
// BEGIN SONGBIRD GPL
//
// This file is part of the Songbird web player.
//
// Copyright(c) 2005-2009 POTI, Inc.
// http://songbirdnest.com
//
// This file may be licensed under the terms of of the
// GNU General Public License Version 2 (the "GPL").
//
// Software distributed under the License is distributed
// on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
// express or implied. See the GPL for the specific language
// governing rights and limitations.
//
// You should have received a copy of the GPL along with this
// program. If not, go to http://www.gnu.org/licenses/gpl.html
// or write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// END SONGBIRD GPL
//
*/
/* This is a XPCOM component for the Album Art Fetcher interface.
*/
const Cc = Components.classes;
const CC = Components.Constructor;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
// The root of our preferences branch
const PREF_BRANCH = "extensions.albumart.[fetchername].";
// The string bundle location
const STRING_BUNDLE = "chrome://[fetchername]-fetcher/locale/[fetchername]-fetcher.properties";
// Importing helper modules
Cu.import('resource://app/jsmodules/sbProperties.jsm');
Cu.import("resource://app/jsmodules/StringUtils.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
/**
* Since we can't use the FUEL components until after all other components have
* been loaded we define a lazy getter here for when we need it.
*/
__defineGetter__("Application", function() {
delete this.Application;
return this.Application = Cc["@mozilla.org/fuel/application;1"]
.getService(Ci.fuelIApplication);
});
/**
* Album Art Fetcher - sb[FetcherName]AlbumArtFetcher
*/
function sb[FetcherName]AlbumArtFetcher() {
var stringBundleService = Cc["@mozilla.org/intl/stringbundle;1"]
.getService(Ci.nsIStringBundleService);
this._bundle = stringBundleService.createBundle(STRING_BUNDLE);
this.DEBUG = Application.prefs.getValue(PREF_BRANCH + "debug", false);
if (this.DEBUG) {
this._consoleService = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
}
this._debug("Starting [FetcherName] Album Art Fetcher Component");
};
sb[FetcherName]AlbumArtFetcher.prototype = {
// XPCOM Magic
className: 'sb[FetcherName]AlbumArtFetcher',
classDescription: '[FetcherName] Album Cover Fetcher',
classID: Components.ID('{}'), // Use uuidgen to get an ID
contractID: '@songbirdnest.com/Songbird/album-art/[fetchername]-fetcher;1',
_xpcom_categories: [{
category: "songbird-album-art-fetcher"
}],
// Variables
DEBUG: false, // Debug flag
_shutdown: false, // Flag to shutdown
_albumArtSourceList: null, // Source list
_bundle: null, // String bundle
_shortName: "[fetchername]",
// Services
_consoleService: null,
/**
* Internal debugging functions
*/
/**
* \brief Dumps out a message if the DEBUG flag is enabled with
* the className pre-appended.
* \param message String to print out.
*/
_debug: function (message)
{
if (!this.DEBUG) return;
try {
dump(this.className + ": " + message + "\n");
this._consoleService.logStringMessage(this.className + ": " + message);
} catch (err) {
// We do not want to throw an exception here
}
},
/**
* \brief Dumps out an error message with the className + ": [ERROR]"
* pre-appended, and will report the error so it will appear in the
* error console.
* \param message String to print out.
*/
_logError: function (message)
{
try {
dump(this.className + ": [ERROR] - " + message + "\n");
Cu.reportError(this.className + ": [ERROR] - " + message);
} catch (err) {
// We do not want to thow an exception here
}
},
/*********************************
* sbIAlbumArtFetcher
********************************/
// These are a bunch of getters for attributes in the sbIAlbumArtFetcher.idl
get shortName() {
this._debug("Getting shortName: " + this._shortName);
return this._shortName;
},
// These next few use the .properties file to get the information
get name() {
var mName = SBString(PREF_BRANCH + "name", null, this._bundle);
this._debug("Getting Name: " + mName);
return mName;
},
get description() {
var mDescription = SBString(PREF_BRANCH + "description", null, this._bundle);
this._debug("Getting Description: " + mDescription);
return mDescription;
},
get isLocal() {
var mLocal = Application.prefs.getValue(PREF_BRANCH + "islocal", true);
this._debug("Getting isLocal: " + mLocal);
return mLocal;
},
// These are preference settings
get priority() {
var priority = parseInt(Application.prefs.getValue(PREF_BRANCH + "priority", 15), 10);
this._debug("Getting Priority: " + priority);
return priority;
},
set priority(aNewVal) {
this._debug("Setting Priority: " + aNewVal);
return Application.prefs.setValue(PREF_BRANCH + "priority", aNewVal);
},
get isEnabled() {
return Application.prefs.getValue(PREF_BRANCH + "enabled", false);
},
set isEnabled(aNewVal) {
this._debug("Setting isEnabled: " + aNewVal);
return Application.prefs.setValue(PREF_BRANCH + "enabled", aNewVal);
},
get albumArtSourceList() {
this._debug("Getting albumArtSourceList: " +
this._albumArtSourceList.Length() +
" items");
return this._albumArtSourceList;
},
set albumArtSourceList(aNewVal) {
if (aNewVal) {
this._debug("Setting albumArtSourceList: " + aNewVal.Length() + " items");
} else {
this._debug("Setting albumArtSourceList: NULL");
}
this._albumArtSourceList = aNewVal;
},
fetchAlbumArtForAlbum: function (aMediaItems, aListener) {
this._debug("fetchAlbumArtForAlbum called.");
// Do what you need to do to fetch artwork for an album.
// Then if successful call aListener.onAlbumResult(artworkURL, aMediaItems);
// You can also call aListener.onTrackResult(artworkURL, aMediaItem); for each
// one you find an artwork for.
// If you do not find artwork you do not need to call either of those functions.
// Finally call aListener.onAlbumComplete(aMediaItems);
},
fetchAlbumArtForTrack: function (aMediaItem, aListener) {
this._debug("fetchAlbumArtForTrack called.");
// Do what you need to do to fetch artwork for a single track.
// Then if successful call aListener.onTrackResult(artworkURL, aMediaItem);
// If not successful then you do not need to call anything.
// Finally call aListener.onAlbumComplete(items);
// A helpful piece of code for this is
// var items = Cc["@songbirdnest.com/moz/xpcom/threadsafe-array;1"]
// .createInstance(Ci.nsIMutableArray);
// items.appendElement(aMediaItem, false);
},
shutdown: function () {
this._debug("Shutdown called.");
},
/*********************************
* nsISupports
********************************/
QueryInterface: XPCOMUtils.generateQI([Ci.sbIAlbumArtFetcher])
}
// This is for XPCOM to register this Fetcher as a module
function NSGetModule(compMgr, fileSpec) {
return XPCOMUtils.generateModule([sb[FetcherName]AlbumArtFetcher]);
}
If you wish to test the extension you can install the Developer Tools [here] and select the "Link Extension in Test Mode...".
http://wiki.xiph.org/VorbisComment#Cover_art