private void fillMetaInfo() { if (tag == null) { title = audioFile.getFile().getName(); artist = ""; album = ""; album_artist = ""; track = ""; composer = ""; year = ""; diskNumber = ""; genre = ""; } else { title = tag.getFirst(FieldKey.TITLE); if (title.equals("")) { title = audioFile.getFile().getName(); } artist = tag.getFirst(FieldKey.ARTIST); album = tag.getFirst(FieldKey.ALBUM); album_artist = tag.getFirst(FieldKey.ALBUM_ARTIST); track = tag.getFirst(FieldKey.TRACK); composer = tag.getFirst(FieldKey.COMPOSER); year = tag.getFirst(FieldKey.YEAR); diskNumber = tag.getFirst(FieldKey.DISC_NO); genre = tag.getFirst(FieldKey.GENRE); } }
/** * Prechecks before normal write * * <p> * * <ul> * <li>If the tag is actually empty, remove the tag * <li>if the file is not writable, throw exception * <li> * <li>If the file is too small to be a valid file, throw exception * <li> * </ul> * * @param af * @throws CannotWriteException */ private void precheckWrite(AudioFile af) throws CannotWriteException { // Preliminary checks try { if (af.getTag().isEmpty()) { delete(af); return; } } catch (CannotReadException re) { throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile().getPath())); } if (!af.getFile().canWrite()) { logger.severe(ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile().getPath())); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED.getMsg(af.getFile().getPath())); } if (af.getFile().length() <= MINIMUM_FILESIZE) { logger.severe( ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg( af.getFile().getPath())); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE_FILE_IS_TOO_SMALL.getMsg( af.getFile().getPath())); } }
/** * Update the GridFSDBFile in the associated DB with the key/values in updateKeys * * @param updateKeys Map of new tag data * @param file GridFSDBFile to update with tag data * @param db * @param songId ID of Song to update with tag data * @return */ public static boolean updateFile( Map<String, String> updateKeys, GridFSDBFile file, DB db, ObjectId songId) { // TODO updateKeys? File audioFile = null; try { audioFile = File.createTempFile("tmp", ".mp3"); } catch (IOException e) { log.error("tmp file not created", e); } audioFile.deleteOnExit(); AudioFile f = null; ObjectId id = (ObjectId) file.getId(); ObjectId oid = null; try { file.writeTo(audioFile); f = AudioFileIO.read(audioFile); Tag tag = f.getTagOrCreateAndSetDefault(); DBObject q = new BasicDBObject("_id", songId); DBObject o = new BasicDBObject("$set", new BasicDBObject(updateKeys)); if (updateKeys.get("artist") != null) { tag.setField(FieldKey.ARTIST, updateKeys.get("artist")); } if (updateKeys.get("album") != null) { tag.setField(FieldKey.ALBUM, updateKeys.get("album")); } if (updateKeys.get("title") != null) { tag.setField(FieldKey.TITLE, updateKeys.get("title")); } if (updateKeys.get("track") != null) { tag.setField(FieldKey.TRACK, updateKeys.get("track")); } if (updateKeys.get("year") != null) { tag.setField(FieldKey.YEAR, updateKeys.get("year")); } AudioFileIO.write(f); GridFS myFS = new GridFS(db); myFS.remove(id); GridFSInputFile inputFile = putSongFileInDB(f.getFile(), db, file.getContentType(), file.getFilename(), id); oid = (ObjectId) inputFile.getId(); if (oid.equals(id)) { db.getCollection("songs").update(q, o); } } catch (KeyNotFoundException knfe) { log.error("key not found", knfe); } catch (FieldDataInvalidException fdie) { log.error("tried to set field with invalid value", fdie); } catch (Exception e) { log.error("error reading/writing file", e); } return (oid.equals(id)); }
/** * This will write a custom ID3 tag (TXXX). This works only with MP3 files (Flac with ID3-Tag not * tested). * * @param description The description of the custom tag i.e. "catalognr" There can only be one * custom TXXX tag with that description in one MP3 file * @param text The actual text to be written into the new tag field * @return True if the tag has been properly written, false otherwise */ public static boolean setCustomTag(AudioFile audioFile, String description, String text) throws IOException { FrameBodyTXXX txxxBody = new FrameBodyTXXX(); txxxBody.setDescription(description); txxxBody.setText(text); // Get the tag from the audio file // If there is no ID3Tag create an ID3v2.3 tag Tag tag = audioFile.getTagOrCreateAndSetDefault(); if (tag instanceof AbstractID3Tag) { // If there is only a ID3v1 tag, copy data into new ID3v2.3 tag if (!(tag instanceof ID3v23Tag || tag instanceof ID3v24Tag)) { Tag newTagV23 = null; if (tag instanceof ID3v1Tag) { newTagV23 = new ID3v23Tag((ID3v1Tag) audioFile.getTag()); // Copy old tag data } if (tag instanceof ID3v22Tag) { newTagV23 = new ID3v23Tag((ID3v22Tag) audioFile.getTag()); // Copy old tag data } audioFile.setTag(newTagV23); tag = newTagV23; } AbstractID3v2Frame frame = null; if (tag instanceof ID3v23Tag) { if (((ID3v23Tag) audioFile.getTag()).getInvalidFrames() > 0) { throw new IOException("read some invalid frames!"); } frame = new ID3v23Frame("TXXX"); } else if (tag instanceof ID3v24Tag) { if (((ID3v24Tag) audioFile.getTag()).getInvalidFrames() > 0) { throw new IOException("read some invalid frames!"); } frame = new ID3v24Frame("TXXX"); } frame.setBody(txxxBody); try { tag.setField(frame); } catch (FieldDataInvalidException e) { Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, e); return false; } } else if (tag instanceof FlacTag) { try { ((FlacTag) tag).setField(description, text); } catch (KeyNotFoundException ex) { Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex); return false; } catch (FieldDataInvalidException ex) { return false; } } else if (tag instanceof Mp4Tag) { // TagField field = new Mp4TagTextField("----:com.apple.iTunes:"+description, text); TagField field; field = new Mp4TagReverseDnsField( Mp4TagReverseDnsField.IDENTIFIER + ":" + "com.apple.iTunes" + ":" + description, "com.apple.iTunes", description, text); // TagField field = new Mp4TagTextField(description, text); try { tag.setField(field); } catch (FieldDataInvalidException ex) { Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex); return false; } } else if (tag instanceof VorbisCommentTag) { try { ((VorbisCommentTag) tag).setField(description, text); } catch (KeyNotFoundException ex) { Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex); return false; } catch (FieldDataInvalidException ex) { Logger.getLogger(TrackAnalyzer.class.getName()).log(Level.SEVERE, null, ex); return false; } } else { // tag not implented Logger.getLogger(TrackAnalyzer.class.getName()) .log( Level.WARNING, "couldn't write key information for " + audioFile.getFile().getName() + " to tag, because this format is not supported."); return false; } // write changes in tag to file try { audioFile.commit(); } catch (CannotWriteException e) { e.printStackTrace(); return false; } return true; }
/** * Delete the tag (if any) present in the given file * * @param af The file to process * @throws CannotWriteException if anything went wrong */ public synchronized void delete(AudioFile af) throws CannotReadException, CannotWriteException { if (!af.getFile().canWrite()) { throw new CannotWriteException( ErrorMessage.GENERAL_DELETE_FAILED.getMsg(af.getFile().getPath())); } if (af.getFile().length() <= MINIMUM_FILESIZE) { throw new CannotWriteException( ErrorMessage.GENERAL_DELETE_FAILED.getMsg(af.getFile().getPath())); } RandomAccessFile raf = null; RandomAccessFile rafTemp = null; File tempF = null; // Will be set to true on VetoException, causing the finally block to discard the tempfile. boolean revert = false; try { tempF = File.createTempFile( af.getFile().getName().replace('.', '_'), TEMP_FILENAME_SUFFIX, af.getFile().getParentFile()); rafTemp = new RandomAccessFile(tempF, WRITE_MODE); raf = new RandomAccessFile(af.getFile(), WRITE_MODE); raf.seek(0); rafTemp.seek(0); try { if (this.modificationListener != null) { this.modificationListener.fileWillBeModified(af, true); } deleteTag(raf, rafTemp); if (this.modificationListener != null) { this.modificationListener.fileModified(af, tempF); } } catch (ModifyVetoException veto) { throw new CannotWriteException(veto); } } catch (Exception e) { revert = true; throw new CannotWriteException("\"" + af.getFile().getAbsolutePath() + "\" :" + e, e); } finally { // will be set to the remaining file. File result = af.getFile(); try { if (raf != null) { raf.close(); } if (rafTemp != null) { rafTemp.close(); } if (tempF.length() > 0 && !revert) { boolean deleteResult = af.getFile().delete(); if (deleteResult == false) { logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_ORIGINAL_FILE.getMsg( af.getFile().getPath(), tempF.getPath())); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_ORIGINAL_FILE.getMsg( af.getFile().getPath(), tempF.getPath())); } boolean renameResult = tempF.renameTo(af.getFile()); if (renameResult == false) { logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg( af.getFile().getPath(), tempF.getPath())); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg( af.getFile().getPath(), tempF.getPath())); } result = tempF; // If still exists we can now delete if (tempF.exists()) { if (!tempF.delete()) { // Non critical failed deletion logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg( tempF.getPath())); } } } else { // It was created but never used if (!tempF.delete()) { // Non critical failed deletion logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(tempF.getPath())); } } } catch (Exception ex) { logger.severe( "AudioFileWriter exception cleaning up delete:" + af.getFile().getPath() + " or" + tempF.getAbsolutePath() + ":" + ex); } // Notify listener if (this.modificationListener != null) { this.modificationListener.fileOperationFinished(result); } } }
// TODO Creates temp file in same folder as the original file, this is safe but would impose a // performance // overhead if the original file is on a networked drive public synchronized void write(AudioFile af) throws CannotWriteException { logger.info("Started writing tag data for file:" + af.getFile().getName()); // Prechecks precheckWrite(af); RandomAccessFile raf = null; RandomAccessFile rafTemp = null; File newFile = null; File result = null; // Create temporary File try { newFile = File.createTempFile( af.getFile().getName().replace('.', '_'), TEMP_FILENAME_SUFFIX, af.getFile().getParentFile()); } // Unable to create temporary file, can happen in Vista if have Create Files/Write Data set to // Deny catch (IOException ioe) { logger.log( Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_CREATE_TEMPORARY_FILE_IN_FOLDER.getMsg( af.getFile().getName(), af.getFile().getParentFile().getAbsolutePath()), ioe); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_TO_CREATE_TEMPORARY_FILE_IN_FOLDER.getMsg( af.getFile().getName(), af.getFile().getParentFile().getAbsolutePath())); } // Open temporary file and actual file for Editing try { rafTemp = new RandomAccessFile(newFile, WRITE_MODE); raf = new RandomAccessFile(af.getFile(), WRITE_MODE); } // Unable to write to writable file, can happen in Vista if have Create Folders/Append Data set // to Deny catch (IOException ioe) { logger.log( Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_OPEN_FILE_FOR_EDITING.getMsg( af.getFile().getAbsolutePath()), ioe); // If we managed to open either file, delete it. try { if (raf != null) { raf.close(); } if (rafTemp != null) { rafTemp.close(); } } catch (IOException ioe2) { // Warn but assume has worked okay logger.log( Level.WARNING, ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE.getMsg( af.getFile(), ioe.getMessage()), ioe2); } // Delete the temp file ( we cannot delet until closed correpsonding rafTemp) if (!newFile.delete()) { // Non critical failed deletion logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg( newFile.getAbsolutePath())); } throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_TO_OPEN_FILE_FOR_EDITING.getMsg( af.getFile().getAbsolutePath())); } // Write data to File try { raf.seek(0); rafTemp.seek(0); try { if (this.modificationListener != null) { this.modificationListener.fileWillBeModified(af, false); } writeTag(af.getTag(), raf, rafTemp); if (this.modificationListener != null) { this.modificationListener.fileModified(af, newFile); } } catch (ModifyVetoException veto) { throw new CannotWriteException(veto); } } catch (Exception e) { logger.log( Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(af.getFile(), e.getMessage()), e); try { if (raf != null) { raf.close(); } if (rafTemp != null) { rafTemp.close(); } } catch (IOException ioe) { // Warn but assume has worked okay logger.log( Level.WARNING, ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE.getMsg( af.getFile().getAbsolutePath(), ioe.getMessage()), ioe); } // Delete the temporary file because either it was never used so lets just tidy up or we did // start writing to it but // the write failed and we havent renamed it back to the original file so we can just delete // it. if (!newFile.delete()) { // Non critical failed deletion logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg( newFile.getAbsolutePath())); } throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_BECAUSE.getMsg(af.getFile(), e.getMessage())); } finally { try { if (raf != null) { raf.close(); } if (rafTemp != null) { rafTemp.close(); } } catch (IOException ioe) { // Warn but assume has worked okay logger.log( Level.WARNING, ErrorMessage.GENERAL_WRITE_PROBLEM_CLOSING_FILE_HANDLE.getMsg( af.getFile().getAbsolutePath(), ioe.getMessage()), ioe); } } // Result held in this file result = af.getFile(); // If the temporary file was used if (newFile.length() > 0) { // Rename Original File // Can fail on Vista if have Special Permission 'Delete' set Deny File originalFileBackup = new File( af.getFile().getParentFile().getPath(), AudioFile.getBaseFilename(af.getFile()) + ".old"); boolean renameResult = af.getFile().renameTo(originalFileBackup); if (renameResult == false) { logger.log( Level.SEVERE, ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP.getMsg( af.getFile().getPath(), originalFileBackup.getName())); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_FILE_TO_BACKUP.getMsg( af.getFile().getPath(), originalFileBackup.getName())); } // Rename Temp File to Original File renameResult = newFile.renameTo(af.getFile()); if (!renameResult) { // Renamed failed so lets do some checks rename the backup back to the original file // New File doesnt exist if (!newFile.exists()) { logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_NEW_FILE_DOESNT_EXIST.getMsg( newFile.getAbsolutePath())); } // Rename the backup back to the original if (!originalFileBackup.renameTo(af.getFile())) { // TODO now if this happens we are left with testfile.old instead of testfile.mp4 logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_ORIGINAL_BACKUP_TO_ORIGINAL.getMsg( originalFileBackup.getAbsolutePath(), af.getFile().getName())); } logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg( af.getFile().getAbsolutePath(), newFile.getName())); throw new CannotWriteException( ErrorMessage.GENERAL_WRITE_FAILED_TO_RENAME_TO_ORIGINAL_FILE.getMsg( af.getFile().getAbsolutePath(), newFile.getName())); } else { // Rename was okay so we can now delete the backup of the original boolean deleteResult = originalFileBackup.delete(); if (!deleteResult) { // Not a disaster but can't delete the backup so make a warning logger.warning( ErrorMessage.GENERAL_WRITE_WARNING_UNABLE_TO_DELETE_BACKUP_FILE.getMsg( originalFileBackup.getAbsolutePath())); } } // Delete the temporary file if still exists if (newFile.exists()) { if (!newFile.delete()) { // Non critical failed deletion logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(newFile.getPath())); } } } else { // Delete the temporary file that wasn't ever used if (!newFile.delete()) { // Non critical failed deletion logger.warning( ErrorMessage.GENERAL_WRITE_FAILED_TO_DELETE_TEMPORARY_FILE.getMsg(newFile.getPath())); } } if (this.modificationListener != null) { this.modificationListener.fileOperationFinished(result); } }