+ Integrated Music Mode with UI and cmdline interface
This commit is contained in:
parent
b8802bb2d3
commit
ac372ca2cd
|
@ -20,8 +20,8 @@ import java.util.concurrent.Future;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sourceforge.filebot.audio.AcoustID;
|
||||
import net.sourceforge.filebot.media.MediaDetection;
|
||||
import net.sourceforge.filebot.web.AcoustID;
|
||||
import net.sourceforge.filebot.web.AnidbClient;
|
||||
import net.sourceforge.filebot.web.EpisodeListProvider;
|
||||
import net.sourceforge.filebot.web.FanartTV;
|
||||
|
|
|
@ -66,6 +66,8 @@ import net.sourceforge.filebot.similarity.SimilarityMetric;
|
|||
import net.sourceforge.filebot.subtitle.SubtitleFormat;
|
||||
import net.sourceforge.filebot.ui.Language;
|
||||
import net.sourceforge.filebot.vfs.MemoryFile;
|
||||
import net.sourceforge.filebot.web.AcoustID;
|
||||
import net.sourceforge.filebot.web.AudioTrack;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.filebot.web.EpisodeFormat;
|
||||
import net.sourceforge.filebot.web.EpisodeListProvider;
|
||||
|
@ -101,6 +103,11 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
return renameMovie(files, action, conflictAction, outputDir, format, getMovieIdentificationService(db), query, locale, strict);
|
||||
}
|
||||
|
||||
if (WebServices.AcoustID.getName().equalsIgnoreCase(db) || containsOnly(files, AUDIO_FILES)) {
|
||||
// music mode
|
||||
return renameMusic(files, action, conflictAction, outputDir, format, WebServices.AcoustID);
|
||||
}
|
||||
|
||||
// auto-determine mode
|
||||
List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES);
|
||||
double max = mediaFiles.size();
|
||||
|
@ -498,6 +505,25 @@ public class CmdlineOperations implements CmdlineInterface {
|
|||
}
|
||||
|
||||
|
||||
private List<File> renameMusic(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, AcoustID service) throws Exception {
|
||||
// map old files to new paths by applying formatting and validating filenames
|
||||
Map<File, File> renameMap = new LinkedHashMap<File, File>();
|
||||
|
||||
// check audio files against acoustid
|
||||
for (Entry<File, AudioTrack> match : service.lookup(filter(files, AUDIO_FILES)).entrySet()) {
|
||||
File file = match.getKey();
|
||||
AudioTrack music = match.getValue();
|
||||
String newName = (format != null) ? format.format(new MediaBindingBean(music, file)) : validateFileName(music.toString());
|
||||
|
||||
renameMap.put(file, getDestinationFile(file, newName, outputDir));
|
||||
}
|
||||
|
||||
// rename movies
|
||||
Analytics.trackEvent("CLI", "Rename", "Music", renameMap.size());
|
||||
return renameAll(renameMap, renameAction, conflictAction);
|
||||
}
|
||||
|
||||
|
||||
private File getDestinationFile(File original, String newName, File outputDir) {
|
||||
String extension = getExtension(original);
|
||||
File newFile = new File(extension != null ? newName + '.' + extension : newName);
|
||||
|
|
|
@ -32,6 +32,7 @@ import net.sourceforge.filebot.hash.HashType;
|
|||
import net.sourceforge.filebot.media.MetaAttributes;
|
||||
import net.sourceforge.filebot.mediainfo.MediaInfo;
|
||||
import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind;
|
||||
import net.sourceforge.filebot.web.AudioTrack;
|
||||
import net.sourceforge.filebot.web.Date;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.filebot.web.Movie;
|
||||
|
@ -68,6 +69,8 @@ public class MediaBindingBean {
|
|||
return getEpisode().getSeriesName();
|
||||
if (infoObject instanceof Movie)
|
||||
return getMovie().getName();
|
||||
if (infoObject instanceof AudioTrack)
|
||||
return getMusic().getArtist();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -120,6 +123,10 @@ public class MediaBindingBean {
|
|||
|
||||
@Define("t")
|
||||
public String getTitle() {
|
||||
if (infoObject instanceof AudioTrack) {
|
||||
return getMusic().getTitle();
|
||||
}
|
||||
|
||||
// single episode format
|
||||
if (getEpisodes().size() == 1) {
|
||||
return getEpisode().getTitle();
|
||||
|
@ -510,6 +517,24 @@ public class MediaBindingBean {
|
|||
}
|
||||
|
||||
|
||||
@Define("artist")
|
||||
public String getArtist() {
|
||||
return getMusic().getArtist();
|
||||
}
|
||||
|
||||
|
||||
@Define("title")
|
||||
public String getSongTitle() {
|
||||
return getMusic().getTitle();
|
||||
}
|
||||
|
||||
|
||||
@Define("album")
|
||||
public String getAlbum() {
|
||||
return getMusic().getAlbum();
|
||||
}
|
||||
|
||||
|
||||
@Define("episode")
|
||||
public Episode getEpisode() {
|
||||
return (Episode) infoObject;
|
||||
|
@ -528,6 +553,12 @@ public class MediaBindingBean {
|
|||
}
|
||||
|
||||
|
||||
@Define("music")
|
||||
public AudioTrack getMusic() {
|
||||
return (AudioTrack) infoObject;
|
||||
}
|
||||
|
||||
|
||||
@Define("pi")
|
||||
public Integer getPart() {
|
||||
return ((MoviePart) infoObject).getPartIndex();
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
package net.sourceforge.filebot.ui.rename;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.MediaTypes.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.web.AcoustID;
|
||||
import net.sourceforge.filebot.web.AudioTrack;
|
||||
import net.sourceforge.filebot.web.SortOrder;
|
||||
|
||||
|
||||
class AudioFingerprintMatcher implements AutoCompleteMatcher {
|
||||
|
||||
private AcoustID service;
|
||||
|
||||
|
||||
public AudioFingerprintMatcher(AcoustID service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Match<File, ?>> match(List<File> files, SortOrder order, Locale locale, boolean autodetection, Component parent) throws Exception {
|
||||
List<Match<File, ?>> matches = new ArrayList<Match<File, ?>>();
|
||||
|
||||
// check audio files against acoustid
|
||||
for (Entry<File, AudioTrack> it : service.lookup(filter(files, AUDIO_FILES)).entrySet()) {
|
||||
matches.add(new Match<File, AudioTrack>(it.getKey(), it.getValue()));
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
}
|
|
@ -64,6 +64,7 @@ import net.sourceforge.filebot.Settings;
|
|||
import net.sourceforge.filebot.format.BindingException;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.format.MediaBindingBean;
|
||||
import net.sourceforge.filebot.web.AudioTrackFormat;
|
||||
import net.sourceforge.filebot.web.EpisodeFormat;
|
||||
import net.sourceforge.filebot.web.MovieFormat;
|
||||
import net.sourceforge.tuned.DefaultThreadFactory;
|
||||
|
@ -103,7 +104,7 @@ class FormatDialog extends JDialog {
|
|||
|
||||
|
||||
public enum Mode {
|
||||
Episode, Movie;
|
||||
Episode, Movie, Music;
|
||||
|
||||
public Mode next() {
|
||||
if (ordinal() < values().length - 1)
|
||||
|
@ -122,8 +123,10 @@ class FormatDialog extends JDialog {
|
|||
switch (this) {
|
||||
case Episode:
|
||||
return new EpisodeFormat(true, true);
|
||||
default: // case Movie
|
||||
case Movie: // case Movie
|
||||
return new MovieFormat(true, true, false);
|
||||
default:
|
||||
return new AudioTrackFormat();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +231,7 @@ class FormatDialog extends JDialog {
|
|||
title.setText(this.getTitle());
|
||||
status.setVisible(false);
|
||||
|
||||
switchEditModeAction.putValue(Action.NAME, String.format("%s Format", mode.next()));
|
||||
switchEditModeAction.putValue(Action.NAME, String.format("Switch to %s Format", mode.next()));
|
||||
updateHelpPanel(mode);
|
||||
|
||||
// update preview to current format
|
||||
|
|
|
@ -2,9 +2,11 @@ help.url = http://filebot.sourceforge.net/naming.html
|
|||
|
||||
episode.syntax: <html><b>{</b> <b>}</b> \u2026 expression, <b>n</b> \u2026 name, <b>s</b> \u2026 season, <b>e</b> \u2026 episode, <b>t</b> \u2026 title</html>
|
||||
movie.syntax: <html><b>{</b> <b>}</b> \u2026 expression, <b>n</b> \u2026 name, <b>y</b> \u2026 year</html>
|
||||
music.syntax: <html><b>{</b> <b>}</b> \u2026 expression, <b>n</b> \u2026 artist, <b>t</b> \u2026 title, <b>album</b> \u2026 album</html>
|
||||
|
||||
episode.sample: Dark Angel - 3x01 - Labyrinth [2009-06-01]
|
||||
movie.sample: Avatar (2009) Part 1
|
||||
music.sample: Leona Lewis - I See You
|
||||
|
||||
# basic 1.01
|
||||
episode.example[0]: {n} - {s}.{e} - {t}
|
||||
|
@ -23,3 +25,12 @@ movie.example[1]: {n} ({y}, {director}) {vf} {af}
|
|||
movie.example[2]: {n} {[y, certification, rating]}
|
||||
# normalized scene name
|
||||
movie.example[3]: {n.space('.')}.{y}{'.'+source}.{vc}
|
||||
|
||||
# simple artist - title
|
||||
music.example[0]: {n} - {t}
|
||||
# simple artist - album - title
|
||||
music.example[1]: {n} - {album} - {t}
|
||||
# artist - title [crc32]
|
||||
music.example[2]: {n} - {t} {[crc32]}
|
||||
# artist - title [2ch, 128000]
|
||||
music.example[3]: {n} - {t} {[af, audio.BitRate]}
|
||||
|
|
|
@ -55,6 +55,8 @@ import net.sourceforge.filebot.WebServices;
|
|||
import net.sourceforge.filebot.similarity.Match;
|
||||
import net.sourceforge.filebot.ui.Language;
|
||||
import net.sourceforge.filebot.ui.rename.RenameModel.FormattedFuture;
|
||||
import net.sourceforge.filebot.web.AudioTrack;
|
||||
import net.sourceforge.filebot.web.AudioTrackFormat;
|
||||
import net.sourceforge.filebot.web.Episode;
|
||||
import net.sourceforge.filebot.web.EpisodeFormat;
|
||||
import net.sourceforge.filebot.web.EpisodeListProvider;
|
||||
|
@ -83,6 +85,7 @@ public class RenamePanel extends JComponent {
|
|||
|
||||
private static final PreferencesEntry<String> persistentEpisodeFormat = Settings.forPackage(RenamePanel.class).entry("rename.format.episode");
|
||||
private static final PreferencesEntry<String> persistentMovieFormat = Settings.forPackage(RenamePanel.class).entry("rename.format.movie");
|
||||
private static final PreferencesEntry<String> persistentMusicFormat = Settings.forPackage(RenamePanel.class).entry("rename.format.music");
|
||||
private static final PreferencesEntry<String> persistentPreferredLanguage = Settings.forPackage(RenamePanel.class).entry("rename.language").defaultValue("en");
|
||||
private static final PreferencesEntry<String> persistentPreferredEpisodeOrder = Settings.forPackage(RenamePanel.class).entry("rename.episode.order").defaultValue("Airdate");
|
||||
|
||||
|
@ -258,7 +261,7 @@ public class RenamePanel extends JComponent {
|
|||
|
||||
actionPopup.addSeparator();
|
||||
actionPopup.addDescription(new JLabel("Music Mode:"));
|
||||
actionPopup.add(new AutoCompleteAction("AcoustID", ResourceManager.getIcon("search.acoustid"), new AudioFingerprintMatcher(WebServices.AcoustID)));
|
||||
actionPopup.add(new AutoCompleteAction(WebServices.AcoustID.getName(), WebServices.AcoustID.getIcon(), new AudioFingerprintMatcher(WebServices.AcoustID)));
|
||||
|
||||
actionPopup.addSeparator();
|
||||
actionPopup.addDescription(new JLabel("Options:"));
|
||||
|
@ -281,6 +284,10 @@ public class RenamePanel extends JComponent {
|
|||
renameModel.useFormatter(Movie.class, new ExpressionFormatter(dialog.getFormat().getExpression(), MovieFormat.NameYear, Movie.class));
|
||||
persistentMovieFormat.setValue(dialog.getFormat().getExpression());
|
||||
break;
|
||||
case Music:
|
||||
renameModel.useFormatter(AudioTrack.class, new ExpressionFormatter(dialog.getFormat().getExpression(), new AudioTrackFormat(), AudioTrack.class));
|
||||
persistentMusicFormat.setValue(dialog.getFormat().getExpression());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.web.WebRequest.*;
|
||||
import static net.sourceforge.tuned.FileUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import net.sourceforge.filebot.Cache;
|
||||
import net.sourceforge.filebot.ResourceManager;
|
||||
|
||||
import com.cedarsoftware.util.io.JsonReader;
|
||||
|
||||
|
||||
public class AcoustID {
|
||||
|
||||
private static final FloodLimit REQUEST_LIMIT = new FloodLimit(3, 1, TimeUnit.SECONDS);
|
||||
|
||||
private String apikey;
|
||||
|
||||
|
||||
public AcoustID(String apikey) {
|
||||
this.apikey = apikey;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return "AcoustID";
|
||||
}
|
||||
|
||||
|
||||
public Icon getIcon() {
|
||||
return ResourceManager.getIcon("search.acoustid");
|
||||
}
|
||||
|
||||
|
||||
public Map<File, AudioTrack> lookup(Iterable<File> files) throws Exception {
|
||||
Map<File, AudioTrack> results = new LinkedHashMap<File, AudioTrack>();
|
||||
|
||||
for (Map<String, String> fp : fpcalc(files)) {
|
||||
results.put(new File(fp.get("FILE")), lookup(fp.get("DURATION"), fp.get("FINGERPRINT")));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
public AudioTrack lookup(String duration, String fingerprint) throws IOException, InterruptedException {
|
||||
// http://api.acoustid.org/v2/lookup?client=8XaBELgH&meta=recordings+releasegroups+compress&duration=641&fingerprint=AQABz0qUkZK4oOfhL-CPc4e5C_wW2H2QH9uDL4cvoT8UNQ-eHtsE8cceeFJx-LiiHT-aPzhxoc-Opj_eI5d2hOFyMJRzfDk-QSsu7fBxqZDMHcfxPfDIoPWxv9C1o3yg44d_3Df2GJaUQeeR-cb2HfaPNsdxHj2PJnpwPMN3aPcEMzd-_MeB_Ej4D_CLP8ghHjkJv_jh_UDuQ8xnILwunPg6hF2R8HgzvLhxHVYP_ziJX0eKPnIE1UePMByDJyg7wz_6yELsB8n4oDmDa0Gv40hf6D3CE3_wH6HFaxCPUD9-hNeF5MfWEP3SCGym4-SxnXiGs0mRjEXD6fgl4LmKWrSChzzC33ge9PB3otyJMk-IVC6R8MTNwD9qKQ_CC8kPv4THzEGZS8GPI3x0iGVUxC1hRSizC5VzoamYDi-uR7iKPhGSI82PkiWeB_eHijvsaIWfBCWH5AjjCfVxZ1TQ3CvCTclGnEMfHbnZFA8pjD6KXwd__Cn-Y8e_I9cq6CR-4S9KLXqQcsxxoWh3eMxiHI6TIzyPv0M43YHz4yte-Cv-4D16Hv9F9C9SPUdyGtZRHV-OHEeeGD--BKcjVLOK_NCDXMfx44dzHEiOZ0Z44Rf6DH5R3uiPj4d_PKolJNyRJzyu4_CTD2WOvzjKH9GPb4cUP1Av9EuQd8fGCFee4JlRHi18xQh96NLxkCgfWFKOH6WGeoe4I3za4c5hTscTPEZTES1x8kE-9MQPjT8a8gh5fPgQZtqCFj9MDvp6fDx6NCd07bjx7MLR9AhtnFnQ70GjOcV0opmm4zpY3SOa7HiwdTtyHa6NC4e-HN-OfC5-OP_gLe2QDxfUCz_0w9l65HiPAz9-IaGOUA7-4MZ5CWFOlIfe4yUa6AiZGxf6w0fFxsjTOdC6Itbh4mGD63iPH9-RFy909XAMj7mC5_BvlDyO6kGTZKJxHUd4NDwuZUffw_5RMsde5CWkJAgXnDReNEaP6DTOQ65yaD88HoeX8fge-DSeHo9Qa8cTHc80I-_RoHxx_UHeBxrJw62Q34Kd7MEfpCcu6BLeB1ePw6OO4sOF_sHhmB504WWDZiEu8sKPpkcfCT9xfej0o0lr4T5yNJeOvjmu40w-TDmqHXmYgfFhFy_M7tD1o0cO_B2ms2j-ACEEQgQgAIwzTgAGmBIKIImNQAABwgQATAlhDGCCEIGIIM4BaBgwQBogEBIOESEIA8ARI5xAhxEFmAGAMCKAURKQQpQzRAAkCCBQEAKkQYIYIQQxCixCDADCABMAE0gpJIgyxhEDiCKCCIGAEIgJIQByAhFgGACCACMRQEyBAoxQiHiCBCFOECQFAIgAABR2QAgFjCDMA0AUMIoAIMChQghChASGEGeYEAIAIhgBSErnJPPEGWYAMgw05AhiiGHiBBBGGSCQcQgwRYJwhDDhgCSCSSEIQYwILoyAjAIigBFEUQK8gAYAQ5BCAAjkjCCAEEMZAUQAZQCjCCkpCgFMCCiIcVIAZZgilAQAiSHQECOcQAQIc4QClAHAjDDGkAGAMUoBgyhihgEChFCAAWEIEYwIJYwViAAlHCBIGEIEAEIQAoBwwgwiEBAEEEOoEwBY4wRwxAhBgAcKAESIQAwwIowRFhoBhAE
|
||||
URL url = new URL("http://api.acoustid.org/v2/lookup?client=" + apikey + "&meta=recordings+releasegroups+compress&duration=" + duration + "&fingerprint=" + fingerprint);
|
||||
|
||||
Cache cache = Cache.getCache("web-datasource");
|
||||
AudioTrack audioTrack = cache.get(url, AudioTrack.class);
|
||||
if (audioTrack != null)
|
||||
return audioTrack;
|
||||
|
||||
// respect rate limit
|
||||
REQUEST_LIMIT.acquirePermit();
|
||||
|
||||
String response = readAll(getReader(url.openConnection()));
|
||||
Map<?, ?> data = JsonReader.jsonToMaps(response);
|
||||
|
||||
if (!data.get("status").equals("ok")) {
|
||||
throw new IOException("acoustid responded with error: " + data.get("status"));
|
||||
}
|
||||
|
||||
Map<?, ?> recording = (Map<?, ?>) ((List<?>) ((Map<?, ?>) ((List<?>) data.get("results")).get(0)).get("recordings")).get(0);
|
||||
String artist = (String) ((Map<?, ?>) ((List<?>) recording.get("artists")).get(0)).get("name");
|
||||
String title = (String) recording.get("title");
|
||||
String album = (String) ((Map<?, ?>) ((List<?>) recording.get("releasegroups")).get(0)).get("title");
|
||||
audioTrack = new AudioTrack(artist, title, album);
|
||||
|
||||
cache.put(url, audioTrack);
|
||||
return audioTrack;
|
||||
}
|
||||
|
||||
|
||||
public List<Map<String, String>> fpcalc(Iterable<File> files) throws IOException {
|
||||
List<String> command = new ArrayList<String>();
|
||||
command.add("fpcalc");
|
||||
for (File f : files) {
|
||||
command.add(f.toString());
|
||||
}
|
||||
|
||||
Process process = null;
|
||||
try {
|
||||
process = new ProcessBuilder(command).start();
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Failed to exec fpcalc: " + e.getMessage());
|
||||
}
|
||||
|
||||
Scanner scanner = new Scanner(process.getInputStream());
|
||||
LinkedList<Map<String, String>> results = new LinkedList<Map<String, String>>();
|
||||
|
||||
try {
|
||||
while (scanner.hasNextLine()) {
|
||||
String[] value = scanner.nextLine().split("=", 2);
|
||||
if (value.length != 2)
|
||||
continue;
|
||||
|
||||
if (results.isEmpty() || results.getLast().containsKey(value[0])) {
|
||||
results.addLast(new HashMap<String, String>(3));
|
||||
}
|
||||
results.getLast().put(value[0], value[1]);
|
||||
}
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
public class AudioTrack implements Serializable {
|
||||
|
||||
private String artist;
|
||||
private String title;
|
||||
private String album;
|
||||
|
||||
|
||||
public AudioTrack(String artist, String title, String album) {
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s - %s", getArtist(), getTitle());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
package net.sourceforge.filebot.web;
|
||||
|
||||
|
||||
import java.text.FieldPosition;
|
||||
import java.text.Format;
|
||||
import java.text.ParsePosition;
|
||||
|
||||
|
||||
public class AudioTrackFormat extends Format {
|
||||
|
||||
@Override
|
||||
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
|
||||
return sb.append(obj.toString());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AudioTrack parseObject(String source, ParsePosition pos) {
|
||||
String[] s = source.split(" - ", 2);
|
||||
if (s.length == 2) {
|
||||
pos.setIndex(source.length());
|
||||
return new AudioTrack(s[0].trim(), s[1].trim(), "VA");
|
||||
} else {
|
||||
pos.setErrorIndex(0);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue