mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-22 15:58:52 -05:00
* 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) {
|
public BindingException(String binding, String innerMessage, Throwable cause) {
|
||||||
this(String.format("BindingError: \"%s\": %s", binding, innerMessage), 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.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.SortedMap;
|
|
||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
import net.sf.ehcache.Cache;
|
import net.sf.ehcache.Cache;
|
||||||
@ -81,7 +80,7 @@ public class EpisodeFormatBindingBean {
|
|||||||
@Define("hi")
|
@Define("hi")
|
||||||
public String getHeightAndInterlacement() {
|
public String getHeightAndInterlacement() {
|
||||||
String height = getMediaInfo(StreamKind.Video, 0, "Height");
|
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)
|
if (height == null || interlacement == null)
|
||||||
return null;
|
return null;
|
||||||
@ -120,37 +119,37 @@ public class EpisodeFormatBindingBean {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Define("general")
|
@Define("general")
|
||||||
public SortedMap<String, String> getGeneralMediaInfo() {
|
public Object getGeneralMediaInfo() {
|
||||||
return getMediaInfo().snapshot(StreamKind.General, 0);
|
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.General, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Define("video")
|
@Define("video")
|
||||||
public SortedMap<String, String> getVideoInfo() {
|
public Object getVideoInfo() {
|
||||||
return getMediaInfo().snapshot(StreamKind.Video, 0);
|
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Video, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Define("audio")
|
@Define("audio")
|
||||||
public SortedMap<String, String> getAudioInfo() {
|
public Object getAudioInfo() {
|
||||||
return getMediaInfo().snapshot(StreamKind.Audio, 0);
|
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() {
|
public synchronized MediaInfo getMediaInfo() {
|
||||||
if (mediaFile == null) {
|
if (mediaFile == null) {
|
||||||
|
@ -6,10 +6,9 @@ import java.io.Closeable;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import com.sun.jna.WString;
|
import com.sun.jna.WString;
|
||||||
@ -80,14 +79,14 @@ public class MediaInfo implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Map<StreamKind, List<SortedMap<String, String>>> snapshot() {
|
public Map<StreamKind, List<Map<String, String>>> snapshot() {
|
||||||
Map<StreamKind, List<SortedMap<String, String>>> mediaInfo = new EnumMap<StreamKind, List<SortedMap<String, String>>>(StreamKind.class);
|
Map<StreamKind, List<Map<String, String>>> mediaInfo = new EnumMap<StreamKind, List<Map<String, String>>>(StreamKind.class);
|
||||||
|
|
||||||
for (StreamKind streamKind : StreamKind.values()) {
|
for (StreamKind streamKind : StreamKind.values()) {
|
||||||
int streamCount = streamCount(streamKind);
|
int streamCount = streamCount(streamKind);
|
||||||
|
|
||||||
if (streamCount > 0) {
|
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++) {
|
for (int i = 0; i < streamCount; i++) {
|
||||||
streamInfoList.add(snapshot(streamKind, i));
|
streamInfoList.add(snapshot(streamKind, i));
|
||||||
@ -101,8 +100,8 @@ public class MediaInfo implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SortedMap<String, String> snapshot(StreamKind streamKind, int streamNumber) {
|
public Map<String, String> snapshot(StreamKind streamKind, int streamNumber) {
|
||||||
TreeMap<String, String> streamInfo = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
|
Map<String, String> streamInfo = new LinkedHashMap<String, String>();
|
||||||
|
|
||||||
for (int i = 0, count = parameterCount(streamKind, streamNumber); i < count; i++) {
|
for (int i = 0, count = parameterCount(streamKind, streamNumber); i < count; i++) {
|
||||||
String value = get(streamKind, streamNumber, i, InfoKind.Text);
|
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(progressIndicator, "pos 1al 0al, hidemode 3");
|
||||||
header.add(title, "wrap unrel:push");
|
header.add(title, "wrap unrel:push");
|
||||||
header.add(preview, "gap indent, hidemode 3, wmax 90%");
|
header.add(preview, "gap indent, hidemode 3, wmax 90%");
|
||||||
header.add(errorMessage, "gap indent, hidemode 3, newline");
|
header.add(errorMessage, "gap indent, hidemode 3, wmax 90%, newline");
|
||||||
header.add(warningMessage, "gap indent, hidemode 3, newline");
|
header.add(warningMessage, "gap indent, hidemode 3, wmax 90%, newline");
|
||||||
|
|
||||||
JPanel content = new JPanel(new MigLayout("insets dialog, nogrid, fill"));
|
JPanel content = new JPanel(new MigLayout("insets dialog, nogrid, fill"));
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ public class EpisodeFormatDialog extends JDialog {
|
|||||||
} catch (LinkageError e) {
|
} catch (LinkageError e) {
|
||||||
// MediaInfo native library is missing -> notify user
|
// MediaInfo native library is missing -> notify user
|
||||||
Logger.getLogger("ui").log(Level.SEVERE, e.getMessage(), e);
|
Logger.getLogger("ui").log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
|
||||||
// rethrow error
|
// rethrow error
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -353,7 +353,7 @@ public class EpisodeFormatDialog extends JDialog {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMessage.setText(error != null ? error.getCause().getMessage() : null);
|
errorMessage.setText(error != null ? ExceptionUtilities.getRootCauseMessage(error) : null);
|
||||||
errorMessage.setVisible(error != null);
|
errorMessage.setVisible(error != null);
|
||||||
|
|
||||||
warningMessage.setText(warning != null ? warning.getCause().getMessage() : null);
|
warningMessage.setText(warning != null ? warning.getCause().getMessage() : null);
|
||||||
|
@ -9,7 +9,6 @@ import java.io.File;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedMap;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
@ -29,16 +28,24 @@ import net.sourceforge.tuned.ui.TunedUtilities;
|
|||||||
|
|
||||||
public class MediaInfoComponent extends JTabbedPane {
|
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);
|
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
|
// create tabs for all streams
|
||||||
for (Entry<StreamKind, List<SortedMap<String, String>>> entry : mediaInfo.entrySet()) {
|
for (Entry<StreamKind, List<Map<String, String>>> entry : mediaInfo.entrySet()) {
|
||||||
for (SortedMap<String, String> parameters : entry.getValue()) {
|
for (Map<String, String> parameters : entry.getValue()) {
|
||||||
addTab(entry.getKey().toString(), new JScrollPane(new JTable(new ParameterTableModel(parameters))));
|
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 {
|
protected static class ParameterTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private final List<Entry<?, ?>> data;
|
private final List<Entry<String, String>> data;
|
||||||
|
|
||||||
|
|
||||||
public ParameterTableModel(Map<?, ?> data) {
|
public ParameterTableModel(Map<String, String> data) {
|
||||||
this.data = new ArrayList<Entry<?, ?>>(data.entrySet());
|
this.data = new ArrayList<Entry<String, String>>(data.entrySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user