* intuitive access for all map bindings in episode expression format (e.g. video.width or video['width'])
* added AssociativeScriptObject * enable table sorting in MediaInfoComponent
This commit is contained in:
parent
912bf0464f
commit
e6b785df63
|
@ -0,0 +1,169 @@
|
|||
|
||||
package net.sourceforge.filebot.format;
|
||||
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import sun.org.mozilla.javascript.internal.Scriptable;
|
||||
|
||||
|
||||
class AssociativeScriptObject implements Scriptable {
|
||||
|
||||
/**
|
||||
* Map allowing look-up of values by a fault-tolerant key as specified by the defining key.
|
||||
*
|
||||
* @see {@link #definingKey(String)}
|
||||
*/
|
||||
protected final TreeMap<String, Object> properties = new TreeMap<String, Object>(new Comparator<String>() {
|
||||
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
return definingKey(s1).compareTo(definingKey(s2));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* The Java constructor
|
||||
*/
|
||||
public AssociativeScriptObject(Map<String, ?> properties) {
|
||||
this.properties.putAll(properties);
|
||||
}
|
||||
|
||||
|
||||
protected String definingKey(String s) {
|
||||
// letters and digits are defining, everything else will be ignored
|
||||
return s.replaceAll("[^\\p{Alnum}]", "").toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines properties available by name.
|
||||
*
|
||||
* @param name the name of the property
|
||||
* @param start the object where lookup began
|
||||
*/
|
||||
public boolean has(String name, Scriptable start) {
|
||||
return properties.containsKey(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the property with the given name.
|
||||
*
|
||||
* @param name the property name
|
||||
* @param start the object where the lookup began
|
||||
*/
|
||||
public Object get(String name, Scriptable start) {
|
||||
Object value = properties.get(name);
|
||||
|
||||
if (value == null)
|
||||
throw new BindingException(name, "undefined");
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines properties available by index.
|
||||
*
|
||||
* @param index the index of the property
|
||||
* @param start the object where lookup began
|
||||
*/
|
||||
public boolean has(int index, Scriptable start) {
|
||||
// get property by index not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get property by index.
|
||||
*
|
||||
* @param index the index of the property
|
||||
* @param start the object where the lookup began
|
||||
*/
|
||||
public Object get(int index, Scriptable start) {
|
||||
// get property by index not supported
|
||||
throw new BindingException(String.valueOf(index), "undefined");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get property names.
|
||||
*/
|
||||
public Object[] getIds() {
|
||||
return properties.keySet().toArray();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of this JavaScript class.
|
||||
*/
|
||||
public String getClassName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the string value of this object.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object getDefaultValue(Class typeHint) {
|
||||
return this.toString();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClassName() + properties.entrySet().toString();
|
||||
}
|
||||
|
||||
|
||||
public void put(String name, Scriptable start, Object value) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public void put(int index, Scriptable start, Object value) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public void delete(String id) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public void delete(int index) {
|
||||
// ignore, object is immutable
|
||||
}
|
||||
|
||||
|
||||
public Scriptable getPrototype() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void setPrototype(Scriptable prototype) {
|
||||
// ignore, don't care about prototype
|
||||
}
|
||||
|
||||
|
||||
public Scriptable getParentScope() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void setParentScope(Scriptable parent) {
|
||||
// ignore, don't care about scope
|
||||
}
|
||||
|
||||
|
||||
public boolean hasInstance(Scriptable value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,11 @@ public class BindingException extends RuntimeException {
|
|||
}
|
||||
|
||||
|
||||
public BindingException(String binding, String innerMessage) {
|
||||
this(binding, innerMessage, null);
|
||||
}
|
||||
|
||||
|
||||
public BindingException(String binding, String innerMessage, Throwable cause) {
|
||||
this(String.format("BindingError: \"%s\": %s", binding, innerMessage), cause);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.SortedMap;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
|
@ -81,7 +80,7 @@ public class EpisodeFormatBindingBean {
|
|||
@Define("hi")
|
||||
public String getHeightAndInterlacement() {
|
||||
String height = getMediaInfo(StreamKind.Video, 0, "Height");
|
||||
String interlacement = getMediaInfo(StreamKind.Video, 0, "Interlacement/String");
|
||||
String interlacement = getMediaInfo(StreamKind.Video, 0, "Interlacement");
|
||||
|
||||
if (height == null || interlacement == null)
|
||||
return null;
|
||||
|
@ -120,37 +119,37 @@ public class EpisodeFormatBindingBean {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Define("general")
|
||||
public SortedMap<String, String> getGeneralMediaInfo() {
|
||||
return getMediaInfo().snapshot(StreamKind.General, 0);
|
||||
public Object getGeneralMediaInfo() {
|
||||
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.General, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Define("video")
|
||||
public SortedMap<String, String> getVideoInfo() {
|
||||
return getMediaInfo().snapshot(StreamKind.Video, 0);
|
||||
public Object getVideoInfo() {
|
||||
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Video, 0));
|
||||
}
|
||||
|
||||
|
||||
@Define("audio")
|
||||
public SortedMap<String, String> getAudioInfo() {
|
||||
return getMediaInfo().snapshot(StreamKind.Audio, 0);
|
||||
public Object getAudioInfo() {
|
||||
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Audio, 0));
|
||||
}
|
||||
|
||||
|
||||
@Define("text")
|
||||
public SortedMap<String, String> getTextInfo() {
|
||||
return getMediaInfo().snapshot(StreamKind.Text, 0);
|
||||
}
|
||||
|
||||
|
||||
@Define("image")
|
||||
public SortedMap<String, String> getImageInfo() {
|
||||
return getMediaInfo().snapshot(StreamKind.Image, 0);
|
||||
}
|
||||
|
||||
@Define("text")
|
||||
public Object getTextInfo() {
|
||||
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Text, 0));
|
||||
}
|
||||
|
||||
|
||||
@Define("image")
|
||||
public Object getImageInfo() {
|
||||
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Image, 0));
|
||||
}
|
||||
|
||||
|
||||
public synchronized MediaInfo getMediaInfo() {
|
||||
if (mediaFile == null) {
|
||||
|
|
|
@ -6,10 +6,9 @@ import java.io.Closeable;
|
|||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import com.sun.jna.Pointer;
|
||||
import com.sun.jna.WString;
|
||||
|
@ -80,14 +79,14 @@ public class MediaInfo implements Closeable {
|
|||
}
|
||||
|
||||
|
||||
public Map<StreamKind, List<SortedMap<String, String>>> snapshot() {
|
||||
Map<StreamKind, List<SortedMap<String, String>>> mediaInfo = new EnumMap<StreamKind, List<SortedMap<String, String>>>(StreamKind.class);
|
||||
public Map<StreamKind, List<Map<String, String>>> snapshot() {
|
||||
Map<StreamKind, List<Map<String, String>>> mediaInfo = new EnumMap<StreamKind, List<Map<String, String>>>(StreamKind.class);
|
||||
|
||||
for (StreamKind streamKind : StreamKind.values()) {
|
||||
int streamCount = streamCount(streamKind);
|
||||
|
||||
if (streamCount > 0) {
|
||||
List<SortedMap<String, String>> streamInfoList = new ArrayList<SortedMap<String, String>>(streamCount);
|
||||
List<Map<String, String>> streamInfoList = new ArrayList<Map<String, String>>(streamCount);
|
||||
|
||||
for (int i = 0; i < streamCount; i++) {
|
||||
streamInfoList.add(snapshot(streamKind, i));
|
||||
|
@ -101,8 +100,8 @@ public class MediaInfo implements Closeable {
|
|||
}
|
||||
|
||||
|
||||
public SortedMap<String, String> snapshot(StreamKind streamKind, int streamNumber) {
|
||||
TreeMap<String, String> streamInfo = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
||||
public Map<String, String> snapshot(StreamKind streamKind, int streamNumber) {
|
||||
Map<String, String> streamInfo = new LinkedHashMap<String, String>();
|
||||
|
||||
for (int i = 0, count = parameterCount(streamKind, streamNumber); i < count; i++) {
|
||||
String value = get(streamKind, streamNumber, i, InfoKind.Text);
|
||||
|
|
|
@ -109,8 +109,8 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
header.add(progressIndicator, "pos 1al 0al, hidemode 3");
|
||||
header.add(title, "wrap unrel:push");
|
||||
header.add(preview, "gap indent, hidemode 3, wmax 90%");
|
||||
header.add(errorMessage, "gap indent, hidemode 3, newline");
|
||||
header.add(warningMessage, "gap indent, hidemode 3, newline");
|
||||
header.add(errorMessage, "gap indent, hidemode 3, wmax 90%, newline");
|
||||
header.add(warningMessage, "gap indent, hidemode 3, wmax 90%, newline");
|
||||
|
||||
JPanel content = new JPanel(new MigLayout("insets dialog, nogrid, fill"));
|
||||
|
||||
|
@ -208,7 +208,7 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
} catch (LinkageError e) {
|
||||
// MediaInfo native library is missing -> notify user
|
||||
Logger.getLogger("ui").log(Level.SEVERE, e.getMessage(), e);
|
||||
|
||||
|
||||
// rethrow error
|
||||
throw e;
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
error = e;
|
||||
}
|
||||
|
||||
errorMessage.setText(error != null ? error.getCause().getMessage() : null);
|
||||
errorMessage.setText(error != null ? ExceptionUtilities.getRootCauseMessage(error) : null);
|
||||
errorMessage.setVisible(error != null);
|
||||
|
||||
warningMessage.setText(warning != null ? warning.getCause().getMessage() : null);
|
||||
|
|
|
@ -9,7 +9,6 @@ import java.io.File;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
|
@ -29,16 +28,24 @@ import net.sourceforge.tuned.ui.TunedUtilities;
|
|||
|
||||
public class MediaInfoComponent extends JTabbedPane {
|
||||
|
||||
public MediaInfoComponent(Map<StreamKind, List<SortedMap<String, String>>> mediaInfo) {
|
||||
public MediaInfoComponent(Map<StreamKind, List<Map<String, String>>> mediaInfo) {
|
||||
insert(mediaInfo);
|
||||
}
|
||||
|
||||
|
||||
public void insert(Map<StreamKind, List<SortedMap<String, String>>> mediaInfo) {
|
||||
public void insert(Map<StreamKind, List<Map<String, String>>> mediaInfo) {
|
||||
// create tabs for all streams
|
||||
for (Entry<StreamKind, List<SortedMap<String, String>>> entry : mediaInfo.entrySet()) {
|
||||
for (SortedMap<String, String> parameters : entry.getValue()) {
|
||||
addTab(entry.getKey().toString(), new JScrollPane(new JTable(new ParameterTableModel(parameters))));
|
||||
for (Entry<StreamKind, List<Map<String, String>>> entry : mediaInfo.entrySet()) {
|
||||
for (Map<String, String> parameters : entry.getValue()) {
|
||||
JTable table = new JTable(new ParameterTableModel(parameters));
|
||||
|
||||
// allow sorting
|
||||
table.setAutoCreateRowSorter(true);
|
||||
|
||||
// sort by parameter name
|
||||
table.getRowSorter().toggleSortOrder(0);
|
||||
|
||||
addTab(entry.getKey().toString(), new JScrollPane(table));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,11 +82,11 @@ public class MediaInfoComponent extends JTabbedPane {
|
|||
|
||||
protected static class ParameterTableModel extends AbstractTableModel {
|
||||
|
||||
private final List<Entry<?, ?>> data;
|
||||
private final List<Entry<String, String>> data;
|
||||
|
||||
|
||||
public ParameterTableModel(Map<?, ?> data) {
|
||||
this.data = new ArrayList<Entry<?, ?>>(data.entrySet());
|
||||
public ParameterTableModel(Map<String, String> data) {
|
||||
this.data = new ArrayList<Entry<String, String>>(data.entrySet());
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue