/*** ** @(#) TradeCard.com 1.0 ** ** Copyright (c) 2010 TradeCard, Inc. All Rights Reserved. ** ** ** THIS COMPUTER SOFTWARE IS THE PROPERTY OF TradeCard, Inc. ** ** Permission is granted to use this software as specified by the TradeCard ** COMMERCIAL LICENSE AGREEMENT. You may use this software only for ** commercial purposes, as specified in the details of the license. ** TRADECARD SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY ** THE LICENSEE AS A RESULT OF USING OR MODIFYING THIS SOFTWARE IN ANY WAY. ** ** YOU MAY NOT DISTRIBUTE ANY SOURCE CODE OR OBJECT CODE FROM THE TradeCard.com ** TOOLKIT AT ANY TIME. VIOLATORS WILL BE PROSECUTED TO THE FULLEST EXTENT ** OF UNITED STATES LAW. ** ** @version 1.0 ** @author Copyright (c) 2010 TradeCard, Inc. All Rights Reserved. ** **/ package com.partydj.player; import java.io.*; import java.util.*; import java.util.concurrent.*; import java.util.regex.*; import org.cmc.music.metadata.*; import org.cmc.music.myid3.*; import com.partydj.server.*; import com.partydj.util.*; import com.partydj.util.json.*; /** * $MR TODO: make the metadata a local interface - implement with the 3rd party lib **/ public class MediaFile implements JSONSerializable { public static final String FILENAME = "fileName"; public static final String METADATA = "metadata"; public static final String ARTIST = "artist"; public static final String ALBUM = "album"; public static final String TITLE = "title"; public static final String TRACK = "track"; public static final String YEAR = "year"; public static final String LENGTH = "length"; private File file; Future<IMusicMetadata> parser; private IMusicMetadata metadata = null; private static final ExecutorService METADATA_PARSER_POOL = Executors.newFixedThreadPool(5, NamedThreadFactory.createDaemonFactory("Metadata Parser")); private static Map<String, MediaFile> CACHE = new HashMap(); public static final Comparator<MediaFile> SORT_BY_ARTIST_ALBUM_TITLE = new Comparator<MediaFile>() { @Override public int compare(MediaFile o1, MediaFile o2) { String o1Comparable = o1.getMetadata() != null ? o1.getSimpleName() : null; String o2Comparable = o2.getMetadata() != null ? o2.getSimpleName() : null; if (o1Comparable != null && o2Comparable != null) { return o1Comparable.compareTo(o2Comparable); } else if (o1Comparable == null && o2Comparable != null) { return 1; } else if (o1Comparable != null && o2Comparable == null) { return -1; } return 0; } }; public static final Comparator<MediaFile> SORT_BY_TITLE = new Comparator<MediaFile>() { @Override public int compare(MediaFile o1, MediaFile o2) { String o1Title = o1.getMetadata() != null ? o1.getMetadata().getSongTitle() : null; String o2Title = o2.getMetadata() != null ? o2.getMetadata().getSongTitle() : null; if (o1Title != null && o2Title != null) { return o1Title.compareTo(o2Title); } else if (o1Title == null && o2Title != null) { return 1; } else if (o1Title != null && o2Title == null) { return -1; } return 0; } }; static class MetadataParser implements Callable<IMusicMetadata> { File source; MetadataParser(File source) { this.source = source; } @Override public IMusicMetadata call() throws Exception { IMusicMetadata metadata; try { MusicMetadataSet metadataSet = new MyID3().read(source); metadata = metadataSet.getSimplified(); } catch (Exception e) { metadata = new MusicMetadata(source.getName()); try { File file = source; String fileName = file.getName(); fileName = file.getName().substring(0, fileName.lastIndexOf(".")); String[] parts = fileName.split("\\s*[-]\\s*"); metadata.setSongTitle(parts[parts.length - 1]); int index = 0; if (parts.length > index + 1) { if (parts[index].matches("\\d+")) { String num = parts[index]; if (num.charAt(0) == '0') { num = num.substring(1); } metadata.setTrackNumber(Integer.valueOf(num), ""); } if (parts.length > index + 1) { metadata.setArtist(parts[index++]); } } if (parts.length > index + 1) { metadata.setArtist(parts[index++]); } } catch (Exception ignore) {} } return metadata; } } public static MediaFile create(String source) { if (CACHE.containsKey(source)) { return CACHE.get(source); } return source != null ? create(new File(source)) : null; } public static MediaFile create(File source) { if (source != null && source.exists()) { String cacheKey = source.getAbsolutePath(); if (CACHE.containsKey(cacheKey)) { return CACHE.get(cacheKey); } Future<IMusicMetadata> parser = METADATA_PARSER_POOL.submit(new MetadataParser(source)); MediaFile file = new MediaFile(source, parser); CACHE.put(cacheKey, file); return file; } return null; } public static MediaFile fromJSON(JSONObject json) { String fileName = json.getString(FILENAME); return create(fileName); } private MediaFile(File file, Future<IMusicMetadata> parser) { this.file = file; this.parser = parser; } public File getFile() { return file; } public IMusicMetadata getMetadata() { if (metadata == null) { try { metadata = parser.get(); } catch (Exception e) { metadata = new MusicMetadata(getFile().getName()); } ensureMetadata(); } return metadata; } public static final Pattern TITLE_AND_TRACK = Pattern.compile("^0*(\\d+)\\s(\\s*-\\s*)?(.*)$"); public static final Pattern TRACK_NUM = Pattern.compile("^0*(\\d\\d+)"); private void ensureMetadata() { try { File file = getFile(); String fileName = file.getName(); fileName = file.getName().substring(0, fileName.lastIndexOf(".")); String[] parts = fileName.split("\\s*[-]\\s*"); if (metadata.getSongTitle() == null) { String title = parts[parts.length - 1]; Matcher m = TITLE_AND_TRACK.matcher(title); if (m.find()) { try { title = m.group(3); } catch (Exception ignore) {} //ignore the track } metadata.setSongTitle(title); } if (metadata.getArtist() == null) { String poolFile = Config.config().getProperty(ConfigKeys.MUSIC_POOL); String[] path = file.getAbsolutePath().substring(poolFile.length()).split("[\\\\/]"); if (path.length > 3) { metadata.setArtist(path[path.length - 3]); if (metadata.getAlbum() == null) { metadata.setAlbum(path[path.length - 2]); } } else if (path.length > 2) { metadata.setArtist(path[path.length - 2]); } } int offset = 0; if (parts.length > 1 && TRACK_NUM.matcher(parts[0]).find()) { offset++; } if (metadata.getArtist() == null && parts.length > offset+1) { metadata.setArtist(parts[offset]); } if (metadata.getAlbum() == null && parts.length > offset+2) { metadata.setAlbum(parts[offset+1]); } } catch (Exception ignore) {} } public String getLengthDisplay() { Number seconds = getMetadata().getDurationSeconds(); if (seconds != null) { return Etc.getTimeDurationDisplay(seconds.intValue()); } return null; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((file == null) ? 0 : file.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; MediaFile other = (MediaFile)obj; if (file == null) { if (other.file != null) return false; } else if (!file.equals(other.file)) return false; return true; } @Override public String toString() { return getFile().getAbsolutePath(); } public String getSimpleName() { return getMetadata() != null ? getMetadata().getArtist() + " - " + getMetadata().getAlbum() + " - " + getMetadata().getSongTitle() : "".intern(); } public JSONObject toJSON() { JSONObject json = new JSONObject(); json.put(FILENAME, getFile().getAbsolutePath()); IMusicMetadata md = getMetadata(); JSONObject metadata = new JSONObject(); metadata.put(ARTIST, md.getArtist()); metadata.put(ALBUM, md.getAlbum()); String title = md.getSongTitle(); if (title == null) { title = getFile().getName(); } metadata.put(TITLE, title); metadata.put(TRACK, md.getPartOfSetIndex()); metadata.put(YEAR, md.getYear()); metadata.put(LENGTH, getLengthDisplay()); json.put(METADATA, metadata); return json; } }