package com.limegroup.gnutella.metadata; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import com.limegroup.gnutella.ByteOrder; import com.limegroup.gnutella.util.IOUtils; public class OGMMetaData extends VideoMetaData { public static final String TITLE_TAG = "title"; public static final String COMMENT_TAG = "comment"; public static final String LICENSE_TAG = "license"; private static final String DATE_TAG = "date"; private static final String LANGUAGE_TAG = "language"; public OGMMetaData(File f) throws IOException { super(f); } protected void parseFile(File file) throws IOException { InputStream is = null; try { is = new FileInputStream(file); DataInputStream dis = new DataInputStream(is); Set set = readMetaData(dis); parseMetaData(set); } finally { IOUtils.close(is); } } /** * reads the first pages of the Ogg container, extracts all Vorbis comments * * @param dis * a DataInputStream * @return Set of String containing Vorbis comments * @throws IOException */ private Set readMetaData(DataInputStream dis) throws IOException { Set set = new HashSet(); boolean shouldStop = false; do { int pageSize = readHeader(dis); shouldStop = parseCommentBlock(pageSize, dis, set); } while (!shouldStop); return set; } /** * Reads the header of an Ogg page * * @param dis * the DataInputStream to read from * @return size of the rest of the page. * @throws IOException */ private int readHeader(DataInputStream dis) throws IOException { // read pageHeader if (dis.readByte() != 'O') throw new IOException("not an ogg file"); if (dis.readByte() != 'g') throw new IOException("not an ogg file"); if (dis.readByte() != 'g') throw new IOException("not an ogg file"); if (dis.readByte() != 'S') throw new IOException("not an ogg file"); // boring data IOUtils.ensureSkip(dis, 22); // number of page segments int segments = dis.readUnsignedByte(); int size = 0; for (int i = 0; i < segments; i++) { size += dis.readUnsignedByte(); } return size; } /* * parse what we hope is a comment block. If that's not the case, we mostly * skip the data. */ private boolean parseCommentBlock(int pageSize, DataInputStream dis, Set comments) throws IOException { int type = dis.readByte(); pageSize--; if ((type & 1) != 1) { // we are reading a data block, stop. IOUtils.ensureSkip(dis, pageSize); return true; } else if (type != 3) { IOUtils.ensureSkip(dis, pageSize); // reading some header block return false; } byte[] vorbis = new byte[6]; dis.readFully(vorbis); pageSize -= 6; if (vorbis[0] != 'v' || vorbis[1] != 'o' || vorbis[2] != 'r' || vorbis[3] != 'b' || vorbis[4] != 'i' || vorbis[5] != 's') { // not a vorbis comment IOUtils.ensureSkip(dis, pageSize); return true; } // read size of vendor string byte[] dword = new byte[4]; dis.readFully(dword); int vendorStringSize = ByteOrder.leb2int(dword, 0); // read vendor string byte[] vendorString = new byte[vendorStringSize]; dis.readFully(vendorString); // read number of comments dis.readFully(dword); int numComments = ByteOrder.leb2int(dword, 0); // read comments for (int i = 0; i < numComments; i++) { dis.readFully(dword); int commentSize = ByteOrder.leb2int(dword, 0); byte[] comment = new byte[commentSize]; dis.readFully(comment); comments.add(new String(comment, "UTF-8")); } // last bit marker missing -> error if ((dis.readByte() & 1) != 1) return true; return false; } /** * extracts usable information from a Set of Vorbis comments * * @param data * a Set of String containing Vorbis comments */ private void parseMetaData(Set data) { for (Iterator iter = data.iterator(); iter.hasNext();) { String comment = iter.next().toString(); int index = comment.indexOf('='); if (index <= 0) continue; String key = comment.substring(0, index); String value = comment.substring(index + 1); if (key.equalsIgnoreCase(COMMENT_TAG)) { if(getComment() != null) setComment(getComment() + "\n" + value); else setComment(value); } else if (key.equalsIgnoreCase(LANGUAGE_TAG)) { if(getLanguage() != null) setLanguage(getLanguage() + ";" + value); else setLanguage(value); } else if (key.equalsIgnoreCase(LICENSE_TAG)) { setLicense(value); } else if (key.equalsIgnoreCase(TITLE_TAG)) { setTitle(value); } else if (key.equalsIgnoreCase(DATE_TAG)) { setYear(value); } } } }