com.limegroup.gnutella.metadata.MP3DataEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.limegroup.gnutella.metadata.MP3DataEditor.java

Source

package com.limegroup.gnutella.metadata;

import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.limegroup.gnutella.util.FileUtils;
import com.limegroup.gnutella.xml.LimeXMLReplyCollection;
import com.limegroup.gnutella.xml.LimeXMLUtils;

import de.vdheide.mp3.ID3v2;
import de.vdheide.mp3.ID3v2DecompressionException;
import de.vdheide.mp3.ID3v2Exception;
import de.vdheide.mp3.ID3v2Frame;
import de.vdheide.mp3.NoID3v2TagException;

/**
 * an editor specifically for mp3 files with id3 tags
 */
public class MP3DataEditor extends AudioMetaDataEditor {

    private static final Log LOG = LogFactory.getLog(MP3DataEditor.class);

    private static final String ISO_LATIN_1 = "8859_1";
    private static final String UNICODE = "Unicode";

    static final String TITLE_ID = "TIT2";
    static final String ARTIST_ID = "TPE1";
    static final String ALBUM_ID = "TALB";
    static final String YEAR_ID = "TYER";
    static final String TRACK_ID = "TRCK";
    static final String COMMENT_ID = "COMM";
    static final String GENRE_ID = "TCON";
    static final String LICENSE_ID = "TCOP";

    /**
     * Actually writes the ID3 tags out to the ID3V3 section of the mp3 file
     */
    private int writeID3V2DataToDisk(File file) throws IOException, ID3v2Exception {
        ID3v2 id3Handler = new ID3v2(file);
        Vector frames = null;
        try {
            frames = (Vector) id3Handler.getFrames().clone();
        } catch (NoID3v2TagException ex) {//there are no ID3v2 tags in the file
            //fall thro' we'll deal with it later -- frames will be null
        }

        List framesToUpdate = new ArrayList();
        addAllNeededFrames(framesToUpdate);
        if (framesToUpdate.size() == 0) //we have nothing to update
            return LimeXMLReplyCollection.NORMAL;
        if (frames != null) { //old frames present, update the differnt ones 
            for (Iterator iter = frames.iterator(); iter.hasNext();) {
                ID3v2Frame oldFrame = (ID3v2Frame) iter.next();
                //note: equality of ID3v2Frame based on value of id
                int index = framesToUpdate.indexOf(oldFrame);
                ID3v2Frame newFrame = null;
                if (index >= 0) {
                    newFrame = (ID3v2Frame) framesToUpdate.remove(index);
                    if (Arrays.equals(oldFrame.getContent(), newFrame.getContent()))
                        continue;//no need to update, skip this frame
                }
                //we are either going to replace it if it was changed, or remove
                //it since there is no equivalent frame in the ones we need to
                //update, this means the user probably removed it
                id3Handler.removeFrame(oldFrame);
                if (newFrame != null)
                    id3Handler.addFrame(newFrame);
            }
        }
        //now we are left with the ones we need to add only, if there were no
        //old tags this will be all the frames that need to get updated
        for (Iterator iter = framesToUpdate.iterator(); iter.hasNext();) {
            ID3v2Frame frame = (ID3v2Frame) iter.next();
            id3Handler.addFrame(frame);
        }

        id3Handler.update();
        //No Exceptions? We are home
        return LimeXMLReplyCollection.NORMAL;
    }

    private void addAllNeededFrames(List updateList) {
        add(updateList, title_, TITLE_ID);
        add(updateList, artist_, ARTIST_ID);
        add(updateList, album_, ALBUM_ID);
        add(updateList, year_, YEAR_ID);
        add(updateList, track_, TRACK_ID);
        add(updateList, comment_, COMMENT_ID);
        add(updateList, genre_, GENRE_ID);
        add(updateList, license_, LICENSE_ID);
    }

    private void add(List list, String data, String id) {
        if (data != null && !data.equals("")) {
            // genre needs to be updated.
            if (id == GENRE_ID && getGenreByte() > -1)
                data = "(" + getGenreByte() + ")" + data;

            ID3v2Frame frame = makeFrame(id, data);
            if (frame != null)
                list.add(frame);
        }
    }

    private ID3v2Frame makeFrame(String frameID, String value) {

        boolean isISOLatin1 = true;

        // Basic/ISO-Latin-1: 0x0000 ... 0x00FF
        // Unicode: > 0x00FF ??? Even with 3byte chars?
        for (int i = 0; i < value.length(); i++) {
            if (value.charAt(i) > 0x00FF) {
                isISOLatin1 = false;
                break;
            }
        }

        try {
            return new ID3v2Frame(frameID, value.getBytes((isISOLatin1) ? ISO_LATIN_1 : UNICODE), true, //discard tag if it's altered/unrecognized
                    true, //discard tag if file altered/unrecognized
                    false, //read/write
                    ID3v2Frame.NO_COMPRESSION, //no compression
                    (byte) 0, //no encryption
                    (byte) 0, //no Group
                    isISOLatin1);
        } catch (ID3v2DecompressionException cx) {
            return null;
        } catch (UnsupportedEncodingException err) {
            return null;
        }
    }

    /**
     * Actually writes the ID3 tags out to the ID3V1 section of mp3 file.
     */
    private int writeID3V1DataToDisk(RandomAccessFile file) {
        byte[] buffer = new byte[30];//max buffer length...drop/pickup vehicle

        //see if there are ID3 Tags in the file
        String tag = "";
        try {
            file.readFully(buffer, 0, 3);
            tag = new String(buffer, 0, 3);
        } catch (EOFException e) {
            return LimeXMLReplyCollection.RW_ERROR;
        } catch (IOException e) {
            return LimeXMLReplyCollection.RW_ERROR;
        }
        //We are sure this is an MP3 file.Otherwise this method would never
        //be called.
        if (!tag.equals("TAG")) {
            //Write the TAG
            try {
                byte[] tagBytes = "TAG".getBytes();//has to be len 3
                file.seek(file.length() - 128);//reset the file-pointer
                file.write(tagBytes, 0, 3);//write these three bytes into the File
            } catch (IOException ioe) {
                return LimeXMLReplyCollection.BAD_ID3;
            }
        }
        LOG.debug("about to start writing to file");
        boolean b;
        b = toFile(title_, 30, file, buffer);
        if (!b)
            return LimeXMLReplyCollection.FAILED_TITLE;
        b = toFile(artist_, 30, file, buffer);
        if (!b)
            return LimeXMLReplyCollection.FAILED_ARTIST;
        b = toFile(album_, 30, file, buffer);
        if (!b)
            return LimeXMLReplyCollection.FAILED_ALBUM;
        b = toFile(year_, 4, file, buffer);
        if (!b)
            return LimeXMLReplyCollection.FAILED_YEAR;
        //comment and track (a little bit tricky)
        b = toFile(comment_, 28, file, buffer);//28 bytes for comment
        if (!b)
            return LimeXMLReplyCollection.FAILED_COMMENT;

        byte trackByte = (byte) -1;//initialize
        try {
            if (track_ == null || track_.equals(""))
                trackByte = (byte) 0;
            else
                trackByte = Byte.parseByte(track_);
        } catch (NumberFormatException nfe) {
            return LimeXMLReplyCollection.FAILED_TRACK;
        }

        try {
            file.write(0);//separator b/w comment and track(track is optional)
            file.write(trackByte);
        } catch (IOException e) {
            return LimeXMLReplyCollection.FAILED_TRACK;
        }

        //genre
        byte genreByte = getGenreByte();
        try {
            file.write(genreByte);
        } catch (IOException e) {
            return LimeXMLReplyCollection.FAILED_GENRE;
        }
        //come this far means we are OK.
        return LimeXMLReplyCollection.NORMAL;

    }

    private boolean toFile(String val, int maxLen, RandomAccessFile file, byte[] buffer) {
        if (LOG.isDebugEnabled())
            LOG.debug("writing value to file " + val);
        byte[] fromString;

        if (val == null || val.equals("")) {
            fromString = new byte[maxLen];
            Arrays.fill(fromString, 0, maxLen, (byte) 0);//fill it all with 0
        } else {
            try {
                fromString = val.getBytes(ISO_LATIN_1);
            } catch (UnsupportedEncodingException err) {
                // Should never happen
                return false;
            }
        }

        int len = fromString.length;
        if (len < maxLen) {
            System.arraycopy(fromString, 0, buffer, 0, len);
            Arrays.fill(buffer, len, maxLen, (byte) 0);//fill the rest with 0s
        } else//cut off the rest
            System.arraycopy(fromString, 0, buffer, 0, maxLen);

        try {
            file.write(buffer, 0, maxLen);
        } catch (IOException e) {
            return false;
        }

        return true;
    }

    private byte getGenreByte() {
        if (genre_ == null)
            return -1;
        else if (genre_.equals("Blues"))
            return 0;
        else if (genre_.equals("Classic Rock"))
            return 1;
        else if (genre_.equals("Country"))
            return 2;
        else if (genre_.equals("Dance"))
            return 3;
        else if (genre_.equals("Disco"))
            return 4;
        else if (genre_.equals("Funk"))
            return 5;
        else if (genre_.equals("Grunge"))
            return 6;
        else if (genre_.equals("Hop"))
            return 7;
        else if (genre_.equals("Jazz"))
            return 8;
        else if (genre_.equals("Metal"))
            return 9;
        else if (genre_.equals("New Age"))
            return 10;
        else if (genre_.equals("Oldies"))
            return 11;
        else if (genre_.equals("Other"))
            return 12;
        else if (genre_.equals("Pop"))
            return 13;
        else if (genre_.equals("R &amp; B"))
            return 14;
        else if (genre_.equals("Rap"))
            return 15;
        else if (genre_.equals("Reggae"))
            return 16;
        else if (genre_.equals("Rock"))
            return 17;
        else if (genre_.equals("Techno"))
            return 17;
        else if (genre_.equals("Industrial"))
            return 19;
        else if (genre_.equals("Alternative"))
            return 20;
        else if (genre_.equals("Ska"))
            return 21;
        else if (genre_.equals("Metal"))
            return 22;
        else if (genre_.equals("Pranks"))
            return 23;
        else if (genre_.equals("Soundtrack"))
            return 24;
        else if (genre_.equals("Euro-Techno"))
            return 25;
        else if (genre_.equals("Ambient"))
            return 26;
        else if (genre_.equals("Trip-Hop"))
            return 27;
        else if (genre_.equals("Vocal"))
            return 28;
        else if (genre_.equals("Jazz+Funk"))
            return 29;
        else if (genre_.equals("Fusion"))
            return 30;
        else if (genre_.equals("Trance"))
            return 31;
        else if (genre_.equals("Classical"))
            return 32;
        else if (genre_.equals("Instrumental"))
            return 33;
        else if (genre_.equals("Acid"))
            return 34;
        else if (genre_.equals("House"))
            return 35;
        else if (genre_.equals("Game"))
            return 36;
        else if (genre_.equals("Sound Clip"))
            return 37;
        else if (genre_.equals("Gospel"))
            return 38;
        else if (genre_.equals("Noise"))
            return 39;
        else if (genre_.equals("AlternRock"))
            return 40;
        else if (genre_.equals("Bass"))
            return 41;
        else if (genre_.equals("Soul"))
            return 42;
        else if (genre_.equals("Punk"))
            return 43;
        else if (genre_.equals("Space"))
            return 44;
        else if (genre_.equals("Meditative"))
            return 45;
        else if (genre_.equals("Instrumental Pop"))
            return 46;
        else if (genre_.equals("Instrumental Rock"))
            return 47;
        else if (genre_.equals("Ethnic"))
            return 48;
        else if (genre_.equals("Gothic"))
            return 49;
        else if (genre_.equals("Darkwave"))
            return 50;
        else if (genre_.equals("Techno-Industrial"))
            return 51;
        else if (genre_.equals("Electronic"))
            return 52;
        else if (genre_.equals("Pop-Folk"))
            return 53;
        else if (genre_.equals("Eurodance"))
            return 54;
        else if (genre_.equals("Dream"))
            return 55;
        else if (genre_.equals("Southern Rock"))
            return 56;
        else if (genre_.equals("Comedy"))
            return 57;
        else if (genre_.equals("Cult"))
            return 58;
        else if (genre_.equals("Gangsta"))
            return 59;
        else if (genre_.equals("Top 40"))
            return 60;
        else if (genre_.equals("Christian Rap"))
            return 61;
        else if (genre_.equals("Pop/Funk"))
            return 62;
        else if (genre_.equals("Jungle"))
            return 63;
        else if (genre_.equals("Native American"))
            return 64;
        else if (genre_.equals("Cabaret"))
            return 65;
        else if (genre_.equals("New Wave"))
            return 66;
        else if (genre_.equals("Psychadelic"))
            return 67;
        else if (genre_.equals("Rave"))
            return 68;
        else if (genre_.equals("Showtunes"))
            return 69;
        else if (genre_.equals("Trailer"))
            return 70;
        else if (genre_.equals("Lo-Fi"))
            return 71;
        else if (genre_.equals("Tribal"))
            return 72;
        else if (genre_.equals("Acid Punk"))
            return 73;
        else if (genre_.equals("Acid Jazz"))
            return 74;
        else if (genre_.equals("Polka"))
            return 75;
        else if (genre_.equals("Retro"))
            return 76;
        else if (genre_.equals("Musical"))
            return 77;
        else if (genre_.equals("Rock &amp; Roll"))
            return 78;
        else if (genre_.equals("Hard Rock"))
            return 79;
        else if (genre_.equals("Folk"))
            return 80;
        else if (genre_.equals("Folk-Rock"))
            return 81;
        else if (genre_.equals("National Folk"))
            return 82;
        else if (genre_.equals("Swing"))
            return 83;
        else if (genre_.equals("Fast Fusion"))
            return 84;
        else if (genre_.equals("Bebob"))
            return 85;
        else if (genre_.equals("Latin"))
            return 86;
        else if (genre_.equals("Revival"))
            return 87;
        else if (genre_.equals("Celtic"))
            return 88;
        else if (genre_.equals("Bluegrass"))
            return 89;
        else if (genre_.equals("Avantgarde"))
            return 90;
        else if (genre_.equals("Gothic Rock"))
            return 91;
        else if (genre_.equals("Progressive Rock"))
            return 92;
        else if (genre_.equals("Psychedelic Rock"))
            return 93;
        else if (genre_.equals("Symphonic Rock"))
            return 94;
        else if (genre_.equals("Slow Rock"))
            return 95;
        else if (genre_.equals("Big Band"))
            return 96;
        else if (genre_.equals("Chorus"))
            return 97;
        else if (genre_.equals("Easy Listening"))
            return 98;
        else if (genre_.equals("Acoustic"))
            return 99;
        else if (genre_.equals("Humour"))
            return 100;
        else if (genre_.equals("Speech"))
            return 101;
        else if (genre_.equals("Chanson"))
            return 102;
        else if (genre_.equals("Opera"))
            return 103;
        else if (genre_.equals("Chamber Music"))
            return 104;
        else if (genre_.equals("Sonata"))
            return 105;
        else if (genre_.equals("Symphony"))
            return 106;
        else if (genre_.equals("Booty Bass"))
            return 107;
        else if (genre_.equals("Primus"))
            return 108;
        else if (genre_.equals("Porn Groove"))
            return 109;
        else if (genre_.equals("Satire"))
            return 110;
        else if (genre_.equals("Slow Jam"))
            return 111;
        else if (genre_.equals("Club"))
            return 112;
        else if (genre_.equals("Tango"))
            return 113;
        else if (genre_.equals("Samba"))
            return 114;
        else if (genre_.equals("Folklore"))
            return 115;
        else if (genre_.equals("Ballad"))
            return 116;
        else if (genre_.equals("Power Ballad"))
            return 117;
        else if (genre_.equals("Rhythmic Soul"))
            return 118;
        else if (genre_.equals("Freestyle"))
            return 119;
        else if (genre_.equals("Duet"))
            return 120;
        else if (genre_.equals("Punk Rock"))
            return 121;
        else if (genre_.equals("Drum Solo"))
            return 122;
        else if (genre_.equals("A capella"))
            return 123;
        else if (genre_.equals("Euro-House"))
            return 124;
        else if (genre_.equals("Dance Hall"))
            return 125;
        else
            return -1;
    }

    public int commitMetaData(String filename) {
        if (LOG.isDebugEnabled())
            LOG.debug("committing mp3 file");
        if (!LimeXMLUtils.isMP3File(filename))
            return LimeXMLReplyCollection.INCORRECT_FILETYPE;
        File f = null;
        RandomAccessFile file = null;
        try {
            try {
                f = new File(filename);
                FileUtils.setWriteable(f);
                file = new RandomAccessFile(f, "rw");
            } catch (IOException e) {
                return LimeXMLReplyCollection.FILE_DEFECTIVE;
            }
            long length = 0;
            try {
                length = file.length();
                if (length < 128) //could not write - file too small
                    return LimeXMLReplyCollection.FILE_DEFECTIVE;
                file.seek(length - 128);
            } catch (IOException ee) {
                return LimeXMLReplyCollection.RW_ERROR;
            }
            //1. Try to write out the ID3v2 data first
            int ret = -1;
            try {
                ret = writeID3V2DataToDisk(f);
            } catch (IOException iox) {
                return LimeXMLReplyCollection.RW_ERROR;
            } catch (ID3v2Exception e) { //catches both ID3v2 related exceptions
                ret = writeID3V1DataToDisk(file);
            }
            return ret;
        } finally {
            if (file != null) {
                try {
                    file.close();
                } catch (IOException ignored) {
                }
            }
        }
    }

}