/*
* 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.ui.thumbnails;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import org.jajuk.base.Album;
import org.jajuk.base.AlbumManager;
import org.jajuk.events.JajukEvent;
import org.jajuk.events.JajukEvents;
import org.jajuk.events.ObservationManager;
import org.jajuk.services.core.SessionService;
import org.jajuk.util.Const;
import org.jajuk.util.IconLoader;
import org.jajuk.util.JajukIcons;
import org.jajuk.util.UtilGUI;
import org.jajuk.util.UtilSystem;
import org.jajuk.util.error.JajukRuntimeException;
import org.jajuk.util.log.Log;
/**
* Manage thumbnails.
*/
public final class ThumbnailManager {
/**
* No instances.
*/
private ThumbnailManager() {
}
/**
* Delete all thumbs for a given size.
*
* @param size size, eg: Const.THUMBNAIL_SIZE_150x150
*/
public static void cleanThumbs(String size) {
File fThumb = SessionService.getConfFileByPath(Const.FILE_THUMBS + '/' + size);
if (fThumb.exists()) {
File[] files = fThumb.listFiles();
for (File file : files) {
if (!file.getAbsolutePath().matches(".*" + Const.FILE_THUMB_NO_COVER)) {
try {
UtilSystem.deleteFile(file);
} catch (IOException e) {
Log.error(e);
}
}
}
// Refresh default cover
File fDefault = SessionService.getConfFileByPath(Const.FILE_THUMBS + "/" + size + "/"
+ Const.FILE_THUMB_NO_COVER);
if (fDefault.exists() && !fDefault.delete()) {
Log.warn("Could not delete " + fDefault.toString());
}
try {
int iSize = Integer.parseInt(new StringTokenizer(size, "x").nextToken());
createThumbnail(IconLoader.getIcon(JajukIcons.NO_COVER), fDefault, iSize);
} catch (Exception e) {
Log.error(e);
}
}
// Reset all thumbs cache
for (Album album : AlbumManager.getInstance().getAlbums()) {
cleanThumbs(album);
}
}
/**
* Delete all thumbs for a given album.
*
* @param album
*/
public static void cleanThumbs(Album album) {
// Now delete thumb files
for (int size = 50; size <= 300; size += 50) {
File fThumb = ThumbnailManager.getThumbBySize(album, size);
if (fThumb.exists()) {
boolean out = fThumb.delete();
if (!out) {
Log.warn("Cannot delete thumb for album: " + album);
}
}
album.setAvailableThumb(size, false);
}
}
/**
* Reads an image in a file and creates a thumbnail in another file. Will be
* created if necessary. the thumbnail must be maxDim pixels or less. Thanks
* Marco Schmidt
* http://schmidt.devlib.org/java/save-jpeg-thumbnail.html#source
*
* @param orig source image
* @param thumb destination file
* @param maxDim required size
* @throws InterruptedException the interrupted exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void createThumbnail(final File orig, final File thumb, final int maxDim)
throws InterruptedException, IOException {
// do not use URL object has it can corrupt special paths
ImageIcon ii = new ImageIcon(orig.getAbsolutePath());
if (ii.getImageLoadStatus() != MediaTracker.COMPLETE) {
throw new JajukRuntimeException("Cannot load image: " + orig.getAbsolutePath()
+ ", load status is: " + ii.getImageLoadStatus());
}
createThumbnail(ii, thumb, maxDim);
}
/**
* Reads an image in a file and creates a thumbnail in another file. Use this
* method to get thumbs from images inside jar files, some bugs in URL
* encoding makes impossible to create the image from a file. Will be created
* if necessary. the thumbnail must be maxDim pixels or less. Thanks Marco
* Schmidt http://schmidt.devlib.org/java/save-jpeg-thumbnail.html#source
*
* @param ii
* @param thumb destination file (jpg)
* @param maxDim required size
* @throws InterruptedException the interrupted exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void createThumbnail(final ImageIcon ii, final File thumb, final int maxDim)
throws InterruptedException, IOException {
// Synchronize the file to avoid any concurrency between several threads refreshing the thumb
// like the catalog view and the artist view.
// Don't lock the thumb file itself because we have to write in in this method and
// Windows doesn't support share mode for locks but only exclusive
File thumbLock = new File(thumb.getAbsolutePath() + ".lock");
thumbLock.createNewFile();
synchronized (thumbLock.getAbsolutePath().intern()) {
// Note that at this point, the image is fully loaded (done in the ImageIcon constructor)
final Image image = ii.getImage();
// determine thumbnail size from WIDTH and HEIGHT
int thumbWidth = maxDim;
int thumbHeight = maxDim;
final double thumbRatio = (double) thumbWidth / (double) thumbHeight;
final int imageWidth = image.getWidth(null);
final int imageHeight = image.getHeight(null);
final double imageRatio = (double) imageWidth / (double) imageHeight;
if (thumbRatio < imageRatio) {
thumbHeight = (int) (thumbWidth / imageRatio);
} else {
thumbWidth = (int) (thumbHeight * imageRatio);
}
// draw original image to thumbnail image object and
// scale it to the new size on-the-fly
final BufferedImage thumbImage = UtilGUI.toBufferedImage(image, thumbWidth, thumbHeight);
// save thumbnail image to OUTFILE
ImageIO.write(thumbImage, UtilSystem.getExtension(thumb), thumb);
// Free thumb memory
thumbImage.flush();
}
}
/**
* Check all thumbs existence for performance reasons.
*
* @param size size of thumbs to be checked
*/
public static void populateCache(final int size) {
for (Album album : AlbumManager.getInstance().getAlbums()) {
File fThumb = ThumbnailManager.getThumbBySize(album, size);
album.setAvailableThumb(size, fThumb.exists() && fThumb.length() > 0);
}
}
/**
* Make thumbnail file exists (album id.jpg or.gif or .png) in thumbs
* directory if it doesn't exist yet
*
* @param album
* @param size
*
* @return whether a new cover has been created
*/
public static boolean refreshThumbnail(final Album album, final int size) {
// Check if the thumb is known in cache
if (album.isThumbAvailable(size)) {
return false;
}
final File fThumb = getThumbBySize(album, size);
final File fCover = album.findCover();
if (fCover != null) {
try {
createThumbnail(fCover, fThumb, size);
// Update thumb availability
album.setAvailableThumb(size, true);
// Notify the thumb creation
Properties details = new Properties();
details.put(Const.DETAIL_CONTENT, album);
ObservationManager.notify(new JajukEvent(JajukEvents.THUMB_CREATED, details));
return true;
} catch (final Exception e) {
Log.error(e);
}
}
return false; // thumb already exists or source file cannot be read (an exception occurred)
}
/**
* Return thumb file by album and size.
*
* @param album the album
* @param size the size (like 50)
*
* @return thumb file by album and size
*/
public static File getThumbBySize(Album album, int size) {
StringBuilder thumb = new StringBuilder(Const.FILE_THUMBS).append('/').append(size).append('x')
.append(size).append('/').append(album.getID()).append('.').append(Const.EXT_THUMB);
return SessionService.getConfFileByPath(thumb.toString());
}
}