/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.services.covers;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Scanner;
import javax.swing.ImageIcon;
import org.jajuk.services.core.SessionService;
import org.jajuk.ui.windows.JajukMainWindow;
import org.jajuk.util.Conf;
import org.jajuk.util.Const;
import org.jajuk.util.DownloadManager;
import org.jajuk.util.UtilGUI;
import org.jajuk.util.error.JajukException;
import org.jajuk.util.log.Log;
/**
* A cover, encapsulates URL, files and manages cover priority to display.
*/
public class Cover implements Comparable<Cover>, Const {
/**
* Covers type enumeration.
* <p>
* NO_COVER : default jajuk cover displayed when no other is available
* </p>
* <p>
* LOCAL_COVER : cover located on the disk and not a standard one
* </p>
* <p>
* TAG_COVER : tag cover located inside the audio files as a tag
* </p>
* <p>
* STANDARD COVER : cover located on the disk with a obvious name (cover.png,
* front.png...)
* </p>
* <p>
* REMOTE_COVER : cover from the web (HTTP protocol)
* </p>
* <p>
* SELECTED_COVER : local or tag cover selected by user as the default local cover to
* display in thumbs..
* </p>
*/
public enum CoverType {
NO_COVER, REMOTE_COVER, LOCAL_COVER,
// cover stored in the tag of a file
TAG_COVER, STANDARD_COVER, SELECTED_COVER
}
/** Cover URL*. */
private final URL url;
/** Cover Type. */
private final CoverType type;
/** Associated file. */
private File file;
/** Default cover image. */
private static final ImageIcon DEFAULT_COVER_ICON = UtilGUI.getImage(IMAGES_SPLASHSCREEN);
/**
* Constructor for remote covers.
*
* @param url
* @param type
*/
public Cover(final URL url, final CoverType type) {
this.url = url;
this.type = type;
// only remote and no_cover are created by URL (file:// for no_cover, the
// image is inside the jajuk jar)
if (type == CoverType.REMOTE_COVER || type == CoverType.NO_COVER) {
this.file = SessionService.getCachePath(url);
}
}
/**
* Constructor for local covers.
*
* @param localFile
* @param type
* @throws IOException Signals that an I/O exception has occurred.
*/
public Cover(final File localFile, final CoverType type) throws IOException {
this.type = type;
this.file = localFile;
this.url = new URL("file://" + file.getAbsolutePath());
}
/*
* (non-Javadoc) The priority order is : SELECTED > STANDARD > TAG > LOCAL > REMOTE > NO_COVER
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(Cover cOther) {
// should be able to handle null
if (cOther == null) {
return -1;
}
// We leverage the enum ordering for comparison
// If both covers are standard covers, order according to FILE_DEFAULT_COVER values order
int comparison = getType().ordinal() - cOther.getType().ordinal();
if (comparison != 0 || getType() != CoverType.STANDARD_COVER) {
return comparison;
} else {
// Compute for the current file and the other the associated index in the FILE_DEFAULT_COVER
// pattern list
String fileName = file.getName();
String fileNameNoExtension = fileName.substring(0, fileName.lastIndexOf('.'));
int fileIndex = 0;
String fileNameOther = cOther.getFile().getName();
String fileNameOtherNoExtension = fileNameOther.substring(0, fileNameOther.lastIndexOf('.'));
int fileOtherIndex = 0;
Scanner s = new Scanner(Conf.getString(Const.FILE_DEFAULT_COVER)).useDelimiter(";");
for (int i = 0; s.hasNext(); i++) {
String pattern = s.next();
if (fileNameNoExtension.matches(".*" + pattern + ".*")) {
fileIndex = i;
// We keep the index of the first found matching pattern if the file matches
// several file patterns (like xxx_font_jajuk.jpg)
break;
}
}
s = new Scanner(Conf.getString(Const.FILE_DEFAULT_COVER)).useDelimiter(";");
for (int i = 0; s.hasNext(); i++) {
String pattern = s.next();
if (fileNameOtherNoExtension.matches(".*" + pattern + ".*")) {
fileOtherIndex = i;
break;
}
}
return fileOtherIndex - fileIndex;
}
}
/**
* Gets the type.
*
* @return Returns the type.
*/
public CoverType getType() {
return type;
}
/**
* Gets the url.
*
* @return Returns the sURL.
*/
public URL getURL() {
return url;
}
/**
* Return cover image size in kilobyte.
*
* @return the size
*/
public String getSize() {
int iSize = (int) (Math.ceil(((double) file.length()) / 1024));
return Integer.toString(iSize);
}
/**
* Gets the image.
*
* @return Returns the image.
* @throws IOException Signals that an I/O exception has occurred.
* @throws InterruptedException the interrupted exception
* @throws JajukException the jajuk exception
*/
public Image getImage() throws IOException, InterruptedException, JajukException {
// default cover image is cached in memory for perfs
if (getType() == CoverType.NO_COVER) {
return DEFAULT_COVER_ICON.getImage();
}
long l = System.currentTimeMillis();
if (!file.exists() || file.length() == 0) {
this.file = DownloadManager.downloadToCache(url);
}
Image image = null;
synchronized (Cover.class) {
image = Toolkit.getDefaultToolkit().getImage(getFile().getAbsolutePath());
MediaTracker tracker = new MediaTracker(JajukMainWindow.getInstance());
tracker.addImage(image, 1);
tracker.waitForAll();
tracker.removeImage(image);
// If image cannot be correctly loaded, throw an exception
if (tracker.getErrorsAny() != null && tracker.getErrorsAny().length > 0) {
throw new JajukException(9, getFile().getAbsolutePath());
}
}
Log.debug("Loaded {{" + url + "}} in " + (System.currentTimeMillis() - l) + " ms");
return image;
}
/**
* toString method.
*
* @return the string
*/
@Override
public String toString() {
return "Type=" + type + " URL={{" + url + "}}";
}
/**
* Equals needed for consistency for sorting.
*
* @param o
*
* @return true, if equals
*/
@Override
public boolean equals(Object o) {
// this also handles null by definition
if (!(o instanceof Cover)) {
return false;
}
// we have an item of type Cover, so we can cast it safely
Cover cOther = (Cover) o;
return url.toString().equals(cOther.getURL().toString());
}
/**
* Hash code.
*
* @return object hashcode
*/
@Override
public int hashCode() {
try {
return this.url.toURI().hashCode() + type.ordinal();
} catch (URISyntaxException e) {
Log.warn("Found invalid URL: {{" + url.toString() + "}}");
return 0;
}
}
/**
* Gets the file.
*
* @return the file
*/
public File getFile() {
return this.file;
}
}