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);
}
}
}
}