diff options
Diffstat (limited to 'subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata')
6 files changed, 888 insertions, 0 deletions
diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/DefaultMetaDataParser.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/DefaultMetaDataParser.java new file mode 100644 index 00000000..897f39d4 --- /dev/null +++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/DefaultMetaDataParser.java @@ -0,0 +1,74 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.service.metadata; + +import java.io.File; + +import net.sourceforge.subsonic.domain.MediaFile; + +/** + * Parses meta data by guessing artist, album and song title based on the path of the file. + * + * @author Sindre Mehus + */ +public class DefaultMetaDataParser extends MetaDataParser { + + /** + * Parses meta data for the given file. No guessing or reformatting is done. + * + * @param file The file to parse. + * @return Meta data for the file. + */ + public MetaData getRawMetaData(File file) { + MetaData metaData = new MetaData(); + metaData.setArtist(guessArtist(file)); + metaData.setAlbumName(guessAlbum(file, metaData.getArtist())); + metaData.setTitle(guessTitle(file)); + return metaData; + } + + /** + * Updates the given file with the given meta data. + * This method has no effect. + * + * @param file The file to update. + * @param metaData The new meta data. + */ + public void setMetaData(MediaFile file, MetaData metaData) { + } + + /** + * Returns whether this parser supports tag editing (using the {@link #setMetaData} method). + * + * @return Always false. + */ + public boolean isEditingSupported() { + return false; + } + + /** + * Returns whether this parser is applicable to the given file. + * + * @param file The file in question. + * @return Whether this parser is applicable to the given file. + */ + public boolean isApplicable(File file) { + return file.isFile(); + } +}
\ No newline at end of file diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/FFmpegParser.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/FFmpegParser.java new file mode 100644 index 00000000..60ae1750 --- /dev/null +++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/FFmpegParser.java @@ -0,0 +1,170 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.service.metadata; + +import net.sourceforge.subsonic.Logger; +import net.sourceforge.subsonic.domain.MediaFile; +import net.sourceforge.subsonic.io.InputStreamReaderThread; +import net.sourceforge.subsonic.service.ServiceLocator; +import net.sourceforge.subsonic.service.TranscodingService; +import net.sourceforge.subsonic.util.StringUtil; +import org.apache.commons.io.FilenameUtils; + +import java.io.File; +import java.io.InputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parses meta data from video files using FFmpeg (http://ffmpeg.org/). + * <p/> + * Currently duration, bitrate and dimension are supported. + * + * @author Sindre Mehus + */ +public class FFmpegParser extends MetaDataParser { + + private static final Logger LOG = Logger.getLogger(FFmpegParser.class); + private static final Pattern DURATION_PATTERN = Pattern.compile("Duration: (\\d+):(\\d+):(\\d+).(\\d+)"); + private static final Pattern BITRATE_PATTERN = Pattern.compile("bitrate: (\\d+) kb/s"); + private static final Pattern DIMENSION_PATTERN = Pattern.compile("Video.*?, (\\d+)x(\\d+)"); + private static final Pattern PAR_PATTERN = Pattern.compile("PAR (\\d+):(\\d+)"); + + private TranscodingService transcodingService; + + /** + * Parses meta data for the given music file. No guessing or reformatting is done. + * + * + * @param file The music file to parse. + * @return Meta data for the file. + */ + @Override + public MetaData getRawMetaData(File file) { + + MetaData metaData = new MetaData(); + + try { + + File ffmpeg = new File(transcodingService.getTranscodeDirectory(), "ffmpeg"); + + String[] command = new String[]{ffmpeg.getAbsolutePath(), "-i", file.getAbsolutePath()}; + Process process = Runtime.getRuntime().exec(command); + InputStream stdout = process.getInputStream(); + InputStream stderr = process.getErrorStream(); + + // Consume stdout, we're not interested in that. + new InputStreamReaderThread(stdout, "ffmpeg", true).start(); + + // Read everything from stderr. It will contain text similar to: + // Input #0, avi, from 'foo.avi': + // Duration: 00:00:33.90, start: 0.000000, bitrate: 2225 kb/s + // Stream #0.0: Video: mpeg4, yuv420p, 352x240 [PAR 1:1 DAR 22:15], 29.97 fps, 29.97 tbr, 29.97 tbn, 30k tbc + // Stream #0.1: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s + String[] lines = StringUtil.readLines(stderr); + + Integer width = null; + Integer height = null; + Double par = 1.0; + for (String line : lines) { + + Matcher matcher = DURATION_PATTERN.matcher(line); + if (matcher.find()) { + int hours = Integer.parseInt(matcher.group(1)); + int minutes = Integer.parseInt(matcher.group(2)); + int seconds = Integer.parseInt(matcher.group(3)); + metaData.setDurationSeconds(hours * 3600 + minutes * 60 + seconds); + } + + matcher = BITRATE_PATTERN.matcher(line); + if (matcher.find()) { + metaData.setBitRate(Integer.valueOf(matcher.group(1))); + } + + matcher = DIMENSION_PATTERN.matcher(line); + if (matcher.find()) { + width = Integer.valueOf(matcher.group(1)); + height = Integer.valueOf(matcher.group(2)); + } + + // PAR = Pixel Aspect Rate + matcher = PAR_PATTERN.matcher(line); + if (matcher.find()) { + int a = Integer.parseInt(matcher.group(1)); + int b = Integer.parseInt(matcher.group(2)); + if (a > 0 && b > 0) { + par = (double) a / (double) b; + } + } + } + + if (width != null && height != null) { + width = (int) Math.round(width.doubleValue() * par); + metaData.setWidth(width); + metaData.setHeight(height); + } + + + } catch (Throwable x) { + LOG.warn("Error when parsing metadata in " + file, x); + } + + return metaData; + } + + /** + * Not supported. + */ + @Override + public void setMetaData(MediaFile file, MetaData metaData) { + throw new RuntimeException("setMetaData() not supported in " + getClass().getSimpleName()); + } + + /** + * Returns whether this parser supports tag editing (using the {@link #setMetaData} method). + * + * @return Always false. + */ + @Override + public boolean isEditingSupported() { + return false; + } + + /** + * Returns whether this parser is applicable to the given file. + * + * @param file The file in question. + * @return Whether this parser is applicable to the given file. + */ + @Override + public boolean isApplicable(File file) { + String format = FilenameUtils.getExtension(file.getName()).toLowerCase(); + + for (String s : ServiceLocator.getSettingsService().getVideoFileTypesAsArray()) { + if (format.equals(s)) { + return true; + } + } + return false; + } + + public void setTranscodingService(TranscodingService transcodingService) { + this.transcodingService = transcodingService; + } +}
\ No newline at end of file diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/JaudiotaggerParser.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/JaudiotaggerParser.java new file mode 100644 index 00000000..8fa7659a --- /dev/null +++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/JaudiotaggerParser.java @@ -0,0 +1,296 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.service.metadata; + +import net.sourceforge.subsonic.Logger; +import net.sourceforge.subsonic.domain.MediaFile; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; +import org.jaudiotagger.audio.AudioFile; +import org.jaudiotagger.audio.AudioFileIO; +import org.jaudiotagger.audio.AudioHeader; +import org.jaudiotagger.tag.FieldKey; +import org.jaudiotagger.tag.Tag; +import org.jaudiotagger.tag.datatype.Artwork; +import org.jaudiotagger.tag.reference.GenreTypes; + +import java.io.File; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.logging.LogManager; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Parses meta data from audio files using the Jaudiotagger library + * (http://www.jthink.net/jaudiotagger/) + * + * @author Sindre Mehus + */ +public class JaudiotaggerParser extends MetaDataParser { + + private static final Logger LOG = Logger.getLogger(JaudiotaggerParser.class); + private static final Pattern GENRE_PATTERN = Pattern.compile("\\((\\d+)\\).*"); + private static final Pattern TRACK_NUMBER_PATTERN = Pattern.compile("(\\d+)/\\d+"); + + static { + try { + LogManager.getLogManager().reset(); + } catch (Throwable x) { + LOG.warn("Failed to turn off logging from Jaudiotagger.", x); + } + } + + /** + * Parses meta data for the given music file. No guessing or reformatting is done. + * + * + * @param file The music file to parse. + * @return Meta data for the file. + */ + @Override + public MetaData getRawMetaData(File file) { + + MetaData metaData = new MetaData(); + + try { + AudioFile audioFile = AudioFileIO.read(file); + Tag tag = audioFile.getTag(); + if (tag != null) { + metaData.setAlbumName(getTagField(tag, FieldKey.ALBUM)); + metaData.setTitle(getTagField(tag, FieldKey.TITLE)); + metaData.setYear(parseInteger(getTagField(tag, FieldKey.YEAR))); + metaData.setGenre(mapGenre(getTagField(tag, FieldKey.GENRE))); + metaData.setDiscNumber(parseInteger(getTagField(tag, FieldKey.DISC_NO))); + metaData.setTrackNumber(parseTrackNumber(getTagField(tag, FieldKey.TRACK))); + + String songArtist = getTagField(tag, FieldKey.ARTIST); + String albumArtist = getTagField(tag, FieldKey.ALBUM_ARTIST); + metaData.setArtist(StringUtils.isBlank(albumArtist) ? songArtist : albumArtist); + } + + AudioHeader audioHeader = audioFile.getAudioHeader(); + if (audioHeader != null) { + metaData.setVariableBitRate(audioHeader.isVariableBitRate()); + metaData.setBitRate((int) audioHeader.getBitRateAsNumber()); + metaData.setDurationSeconds(audioHeader.getTrackLength()); + } + + + } catch (Throwable x) { + LOG.warn("Error when parsing tags in " + file, x); + } + + return metaData; + } + + private String getTagField(Tag tag, FieldKey fieldKey) { + try { + return StringUtils.trimToNull(tag.getFirst(fieldKey)); + } catch (Exception x) { + // Ignored. + return null; + } + } + + /** + * Returns all tags supported by id3v1. + */ + public static SortedSet<String> getID3V1Genres() { + return new TreeSet<String>(GenreTypes.getInstanceOf().getAlphabeticalValueList()); + } + + /** + * Sometimes the genre is returned as "(17)" or "(17)Rock", instead of "Rock". This method + * maps the genre ID to the corresponding text. + */ + private String mapGenre(String genre) { + if (genre == null) { + return null; + } + Matcher matcher = GENRE_PATTERN.matcher(genre); + if (matcher.matches()) { + int genreId = Integer.parseInt(matcher.group(1)); + if (genreId >= 0 && genreId < GenreTypes.getInstanceOf().getSize()) { + return GenreTypes.getInstanceOf().getValueForId(genreId); + } + } + return genre; + } + + /** + * Parses the track number from the given string. Also supports + * track numbers on the form "4/12". + */ + private Integer parseTrackNumber(String trackNumber) { + if (trackNumber == null) { + return null; + } + + Integer result = null; + + try { + result = new Integer(trackNumber); + } catch (NumberFormatException x) { + Matcher matcher = TRACK_NUMBER_PATTERN.matcher(trackNumber); + if (matcher.matches()) { + try { + result = Integer.valueOf(matcher.group(1)); + } catch (NumberFormatException e) { + return null; + } + } + } + + if (Integer.valueOf(0).equals(result)) { + return null; + } + return result; + } + + private Integer parseInteger(String s) { + s = StringUtils.trimToNull(s); + if (s == null) { + return null; + } + try { + Integer result = Integer.valueOf(s); + if (Integer.valueOf(0).equals(result)) { + return null; + } + return result; + } catch (NumberFormatException x) { + return null; + } + } + + /** + * Updates the given file with the given meta data. + * + * @param file The music file to update. + * @param metaData The new meta data. + */ + @Override + public void setMetaData(MediaFile file, MetaData metaData) { + + try { + AudioFile audioFile = AudioFileIO.read(file.getFile()); + Tag tag = audioFile.getTagOrCreateAndSetDefault(); + + tag.setField(FieldKey.ARTIST, StringUtils.trimToEmpty(metaData.getArtist())); + tag.setField(FieldKey.ALBUM_ARTIST, StringUtils.trimToEmpty(metaData.getArtist())); + tag.setField(FieldKey.ALBUM, StringUtils.trimToEmpty(metaData.getAlbumName())); + tag.setField(FieldKey.TITLE, StringUtils.trimToEmpty(metaData.getTitle())); + tag.setField(FieldKey.GENRE, StringUtils.trimToEmpty(metaData.getGenre())); + + Integer track = metaData.getTrackNumber(); + if (track == null) { + tag.deleteField(FieldKey.TRACK); + } else { + tag.setField(FieldKey.TRACK, String.valueOf(track)); + } + + Integer year = metaData.getYear(); + if (year == null) { + tag.deleteField(FieldKey.YEAR); + } else { + tag.setField(FieldKey.YEAR, String.valueOf(year)); + } + + audioFile.commit(); + + } catch (Throwable x) { + LOG.warn("Failed to update tags for file " + file, x); + throw new RuntimeException("Failed to update tags for file " + file + ". " + x.getMessage(), x); + } + } + + /** + * Returns whether this parser supports tag editing (using the {@link #setMetaData} method). + * + * @return Always true. + */ + @Override + public boolean isEditingSupported() { + return true; + } + + /** + * Returns whether this parser is applicable to the given file. + * + * @param file The music file in question. + * @return Whether this parser is applicable to the given file. + */ + @Override + public boolean isApplicable(File file) { + if (!file.isFile()) { + return false; + } + + String format = FilenameUtils.getExtension(file.getName()).toLowerCase(); + + return format.equals("mp3") || + format.equals("m4a") || + format.equals("aac") || + format.equals("ogg") || + format.equals("flac") || + format.equals("wav") || + format.equals("mpc") || + format.equals("mp+") || + format.equals("ape") || + format.equals("wma"); + } + + /** + * Returns whether cover art image data is available in the given file. + * + * @param file The music file. + * @return Whether cover art image data is available. + */ + public boolean isImageAvailable(MediaFile file) { + try { + return getArtwork(file) != null; + } catch (Throwable x) { + LOG.warn("Failed to find cover art tag in " + file, x); + return false; + } + } + + /** + * Returns the cover art image data embedded in the given file. + * + * @param file The music file. + * @return The embedded cover art image data, or <code>null</code> if not available. + */ + public byte[] getImageData(MediaFile file) { + try { + return getArtwork(file).getBinaryData(); + } catch (Throwable x) { + LOG.warn("Failed to find cover art tag in " + file, x); + return null; + } + } + + private Artwork getArtwork(MediaFile file) throws Exception { + AudioFile audioFile = AudioFileIO.read(file.getFile()); + Tag tag = audioFile.getTag(); + return tag == null ? null : tag.getFirstArtwork(); + } +}
\ No newline at end of file diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaData.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaData.java new file mode 100644 index 00000000..d3fa08a0 --- /dev/null +++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaData.java @@ -0,0 +1,135 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.service.metadata; + +/** + * Contains meta-data (song title, artist, album etc) for a music file. + * @author Sindre Mehus + */ +public class MetaData { + + private Integer discNumber; + private Integer trackNumber; + private String title; + private String artist; + private String albumName; + private String genre; + private Integer year; + private Integer bitRate; + private boolean variableBitRate; + private Integer durationSeconds; + private Integer width; + private Integer height; + + public Integer getDiscNumber() { + return discNumber; + } + + public void setDiscNumber(Integer discNumber) { + this.discNumber = discNumber; + } + + public Integer getTrackNumber() { + return trackNumber; + } + + public void setTrackNumber(Integer trackNumber) { + this.trackNumber = trackNumber; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getArtist() { + return artist; + } + + public void setArtist(String artist) { + this.artist = artist; + } + + public String getAlbumName() { + return albumName; + } + + public void setAlbumName(String albumName) { + this.albumName = albumName; + } + + public String getGenre() { + return genre; + } + + public void setGenre(String genre) { + this.genre = genre; + } + + public Integer getYear() { + return year; + } + + public void setYear(Integer year) { + this.year = year; + } + + public Integer getBitRate() { + return bitRate; + } + + public void setBitRate(Integer bitRate) { + this.bitRate = bitRate; + } + + public boolean getVariableBitRate() { + return variableBitRate; + } + + public void setVariableBitRate(boolean variableBitRate) { + this.variableBitRate = variableBitRate; + } + + public Integer getDurationSeconds() { + return durationSeconds; + } + + public void setDurationSeconds(Integer durationSeconds) { + this.durationSeconds = durationSeconds; + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } +} diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaDataParser.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaDataParser.java new file mode 100644 index 00000000..2ed70acc --- /dev/null +++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaDataParser.java @@ -0,0 +1,162 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.service.metadata; + +import java.io.File; +import java.util.List; + +import org.apache.commons.io.FilenameUtils; + +import net.sourceforge.subsonic.domain.MediaFile; +import net.sourceforge.subsonic.domain.MusicFolder; +import net.sourceforge.subsonic.service.ServiceLocator; +import net.sourceforge.subsonic.service.SettingsService; + + +/** + * Parses meta data from media files. + * + * @author Sindre Mehus + */ +public abstract class MetaDataParser { + + /** + * Parses meta data for the given file. + * + * @param file The file to parse. + * @return Meta data for the file, never null. + */ + public MetaData getMetaData(File file) { + + MetaData metaData = getRawMetaData(file); + String artist = metaData.getArtist(); + String album = metaData.getAlbumName(); + String title = metaData.getTitle(); + + if (artist == null) { + artist = guessArtist(file); + } + if (album == null) { + album = guessAlbum(file, artist); + } + if (title == null) { + title = guessTitle(file); + } + + title = removeTrackNumberFromTitle(title, metaData.getTrackNumber()); + metaData.setArtist(artist); + metaData.setAlbumName(album); + metaData.setTitle(title); + + return metaData; + } + + /** + * Parses meta data for the given file. No guessing or reformatting is done. + * + * + * @param file The file to parse. + * @return Meta data for the file. + */ + public abstract MetaData getRawMetaData(File file); + + /** + * Updates the given file with the given meta data. + * + * @param file The file to update. + * @param metaData The new meta data. + */ + public abstract void setMetaData(MediaFile file, MetaData metaData); + + /** + * Returns whether this parser is applicable to the given file. + * + * @param file The file in question. + * @return Whether this parser is applicable to the given file. + */ + public abstract boolean isApplicable(File file); + + /** + * Returns whether this parser supports tag editing (using the {@link #setMetaData} method). + * + * @return Whether tag editing is supported. + */ + public abstract boolean isEditingSupported(); + + /** + * Guesses the artist for the given file. + */ + public String guessArtist(File file) { + File parent = file.getParentFile(); + if (isRoot(parent)) { + return null; + } + File grandParent = parent.getParentFile(); + return isRoot(grandParent) ? null : grandParent.getName(); + } + + /** + * Guesses the album for the given file. + */ + public String guessAlbum(File file, String artist) { + File parent = file.getParentFile(); + String album = isRoot(parent) ? null : parent.getName(); + if (artist != null && album != null) { + album = album.replace(artist + " - ", ""); + } + return album; + } + + /** + * Guesses the title for the given file. + */ + public String guessTitle(File file) { + return removeTrackNumberFromTitle(FilenameUtils.getBaseName(file.getPath()), null); + } + + private boolean isRoot(File file) { + SettingsService settings = ServiceLocator.getSettingsService(); + List<MusicFolder> folders = settings.getAllMusicFolders(false, true); + for (MusicFolder folder : folders) { + if (file.equals(folder.getPath())) { + return true; + } + } + return false; + } + + /** + * Removes any prefixed track number from the given title string. + * + * @param title The title with or without a prefixed track number, e.g., "02 - Back In Black". + * @param trackNumber If specified, this is the "true" track number. + * @return The title with the track number removed, e.g., "Back In Black". + */ + protected String removeTrackNumberFromTitle(String title, Integer trackNumber) { + title = title.trim(); + + // Don't remove numbers if true track number is given, and title does not start with it. + if (trackNumber != null && !title.matches("0?" + trackNumber + "[\\.\\- ].*")) { + return title; + } + + String result = title.replaceFirst("^\\d{2}[\\.\\- ]+", ""); + return result.length() == 0 ? title : result; + } +}
\ No newline at end of file diff --git a/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaDataParserFactory.java b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaDataParserFactory.java new file mode 100644 index 00000000..31b56be4 --- /dev/null +++ b/subsonic-main/src/main/java/net/sourceforge/subsonic/service/metadata/MetaDataParserFactory.java @@ -0,0 +1,51 @@ +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see <http://www.gnu.org/licenses/>. + + Copyright 2009 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.service.metadata; + +import java.io.File; +import java.util.List; + +/** + * Factory for creating meta-data parsers. + * + * @author Sindre Mehus + */ +public class MetaDataParserFactory { + + private List<MetaDataParser> parsers; + + public void setParsers(List<MetaDataParser> parsers) { + this.parsers = parsers; + } + + /** + * Returns a meta-data parser for the given file. + * + * @param file The file in question. + * @return An applicable parser, or <code>null</code> if no parser is found. + */ + public MetaDataParser getParser(File file) { + for (MetaDataParser parser : parsers) { + if (parser.isApplicable(file)) { + return parser; + } + } + return null; + } +} |