mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-13 08:26:15 +00:00
Merge remote-tracking branch 'upstream/develop' into double_click_activate_tile_view
Conflicts: ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTile.java ImageGallery/src/org/sleuthkit/autopsy/imagegallery/gui/drawableviews/DrawableTileBase.java
This commit is contained in:
commit
5c4bd438e8
@ -223,6 +223,10 @@
|
|||||||
<runtime-relative-path>ext/StixLib.jar</runtime-relative-path>
|
<runtime-relative-path>ext/StixLib.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/StixLib.jar</binary-origin>
|
<binary-origin>release/modules/ext/StixLib.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
|
<class-path-extension>
|
||||||
|
<runtime-relative-path>ext/opencv-248.jar</runtime-relative-path>
|
||||||
|
<binary-origin>release/modules/ext/opencv-248.jar</binary-origin>
|
||||||
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/sqlite-jdbc-3.7.15-M1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/sqlite-jdbc-3.7.15-M1.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/sqlite-jdbc-3.7.15-M1.jar</binary-origin>
|
<binary-origin>release/modules/ext/sqlite-jdbc-3.7.15-M1.jar</binary-origin>
|
||||||
|
Binary file not shown.
BIN
Core/release/modules/ext/opencv-248.jar
Normal file
BIN
Core/release/modules/ext/opencv-248.jar
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/amd64/opencv_ffmpeg248_64.dll
Normal file
BIN
Core/release/modules/lib/amd64/opencv_ffmpeg248_64.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/amd64/opencv_java248.dll
Normal file
BIN
Core/release/modules/lib/amd64/opencv_java248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/i386/opencv_ffmpeg248.dll
Normal file
BIN
Core/release/modules/lib/i386/opencv_ffmpeg248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/i386/opencv_java248.dll
Normal file
BIN
Core/release/modules/lib/i386/opencv_java248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/i586/opencv_ffmpeg248_64.dll
Normal file
BIN
Core/release/modules/lib/i586/opencv_ffmpeg248_64.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/i586/opencv_java248.dll
Normal file
BIN
Core/release/modules/lib/i586/opencv_java248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/i686/opencv_ffmpeg248_64.dll
Normal file
BIN
Core/release/modules/lib/i686/opencv_ffmpeg248_64.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/i686/opencv_java248.dll
Normal file
BIN
Core/release/modules/lib/i686/opencv_java248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/x86/opencv_ffmpeg248.dll
Normal file
BIN
Core/release/modules/lib/x86/opencv_ffmpeg248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/x86/opencv_java248.dll
Normal file
BIN
Core/release/modules/lib/x86/opencv_java248.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/x86_64/opencv_ffmpeg248_64.dll
Normal file
BIN
Core/release/modules/lib/x86_64/opencv_ffmpeg248_64.dll
Normal file
Binary file not shown.
BIN
Core/release/modules/lib/x86_64/opencv_java248.dll
Normal file
BIN
Core/release/modules/lib/x86_64/opencv_java248.dll
Normal file
Binary file not shown.
@ -21,9 +21,8 @@ package org.sleuthkit.autopsy.corecomponents;
|
|||||||
import java.awt.CardLayout;
|
import java.awt.CardLayout;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.util.Arrays;
|
import java.util.List;
|
||||||
import static java.util.Objects.nonNull;
|
import static java.util.Objects.nonNull;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -32,7 +31,6 @@ import org.openide.util.NbBundle;
|
|||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.openide.util.lookup.ServiceProviders;
|
import org.openide.util.lookup.ServiceProviders;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
@ -48,16 +46,18 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
|
|||||||
})
|
})
|
||||||
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
|
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
|
||||||
|
|
||||||
private static final Set<String> AUDIO_EXTENSIONS = new TreeSet<>(Arrays.asList(".mp3", ".wav", ".wma")); //NON-NLS
|
|
||||||
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
|
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
|
||||||
private AbstractFile lastFile;
|
private AbstractFile lastFile;
|
||||||
//UI
|
//UI
|
||||||
private final MediaViewVideoPanel videoPanel;
|
private final MediaViewVideoPanel videoPanel;
|
||||||
|
private final boolean videoPanelInited;
|
||||||
private final SortedSet<String> videoExtensions; // get them from the panel
|
private final SortedSet<String> videoExtensions; // get them from the panel
|
||||||
private final SortedSet<String> videoMimes;
|
private final SortedSet<String> videoMimes;
|
||||||
private final MediaViewImagePanel imagePanel;
|
private final MediaViewImagePanel imagePanel;
|
||||||
private final boolean videoPanelInited;
|
|
||||||
private final boolean imagePanelInited;
|
private final boolean imagePanelInited;
|
||||||
|
private final SortedSet<String> imageExtensions; // get them from the panel
|
||||||
|
private final SortedSet<String> imageMimes;
|
||||||
|
|
||||||
private static final String IMAGE_VIEWER_LAYER = "IMAGE"; //NON-NLS
|
private static final String IMAGE_VIEWER_LAYER = "IMAGE"; //NON-NLS
|
||||||
private static final String VIDEO_VIEWER_LAYER = "VIDEO"; //NON-NLS
|
private static final String VIDEO_VIEWER_LAYER = "VIDEO"; //NON-NLS
|
||||||
|
|
||||||
@ -71,14 +71,16 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
// get the right panel for our platform
|
// get the right panel for our platform
|
||||||
videoPanel = MediaViewVideoPanel.createVideoPanel();
|
videoPanel = MediaViewVideoPanel.createVideoPanel();
|
||||||
videoPanelInited = videoPanel.isInited();
|
videoPanelInited = videoPanel.isInited();
|
||||||
videoExtensions = new TreeSet<>(Arrays.asList(videoPanel.getExtensions()));
|
videoExtensions = new TreeSet<>(videoPanel.getExtensionsList());
|
||||||
videoMimes = new TreeSet<>(videoPanel.getMimeTypes());
|
videoMimes = new TreeSet<>(videoPanel.getMimeTypes());
|
||||||
|
|
||||||
imagePanel = new MediaViewImagePanel();
|
imagePanel = new MediaViewImagePanel();
|
||||||
imagePanelInited = imagePanel.isInited();
|
imagePanelInited = imagePanel.isInited();
|
||||||
|
imageExtensions = new TreeSet<>(imagePanel.getExtensionsList());
|
||||||
|
imageMimes = new TreeSet<>(imagePanel.getMimeTypes());
|
||||||
|
|
||||||
customizeComponents();
|
customizeComponents();
|
||||||
logger.log(Level.INFO, "Created MediaView instance: " + this); //NON-NLS
|
logger.log(Level.INFO, "Created MediaView instance: {0}", this); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
@ -125,12 +127,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
|
|
||||||
final Dimension dims = DataContentViewerMedia.this.getSize();
|
final Dimension dims = DataContentViewerMedia.this.getSize();
|
||||||
//logger.info("setting node on media viewer"); //NON-NLS
|
//logger.info("setting node on media viewer"); //NON-NLS
|
||||||
if (imagePanelInited && isImageSupported(file)) {
|
if (videoPanelInited && videoPanel.isSupported(file)) {
|
||||||
imagePanel.showImageFx(file, dims);
|
|
||||||
this.showVideoPanel(false);
|
|
||||||
} else if (videoPanelInited && isVideoSupported(file)) {
|
|
||||||
videoPanel.setupVideo(file, dims);
|
videoPanel.setupVideo(file, dims);
|
||||||
this.showVideoPanel(true);
|
this.showVideoPanel(true);
|
||||||
|
} else if (imagePanelInited && imagePanel.isSupported(file)) {
|
||||||
|
imagePanel.showImageFx(file, dims);
|
||||||
|
this.showVideoPanel(false);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
|
logger.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
|
||||||
@ -186,25 +188,11 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
* @return True if a video file that can be displayed
|
* @return True if a video file that can be displayed
|
||||||
*/
|
*/
|
||||||
private boolean isVideoSupported(AbstractFile file) {
|
private boolean isVideoSupported(AbstractFile file) {
|
||||||
String extension = file.getNameExtension();
|
if (null == file || file.getSize() == 0) {
|
||||||
|
|
||||||
//TODO: is this what we want, to require both extension and mimetype support?
|
|
||||||
if (AUDIO_EXTENSIONS.contains("." + extension) || videoExtensions.contains("." + extension)) {
|
|
||||||
try {
|
|
||||||
String mimeType = new FileTypeDetector().getFileType(file);
|
|
||||||
if (nonNull(mimeType)) {
|
|
||||||
return videoMimes.contains(mimeType);
|
|
||||||
}
|
|
||||||
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
|
||||||
if (!videoMimes.isEmpty() && file.isMimeType(videoMimes) == MimeMatchEnum.TRUE) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return videoPanel.isSupported(file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is the given file an image that we can display?
|
* is the given file an image that we can display?
|
||||||
@ -215,7 +203,11 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
*/
|
*/
|
||||||
private boolean isImageSupported(AbstractFile file) {
|
private boolean isImageSupported(AbstractFile file) {
|
||||||
|
|
||||||
return ImageUtils.thumbnailSupported(file);
|
if (null == file || file.getSize() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return imagePanel.isSupported(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -256,4 +248,43 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MediaViewPanel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return supported mime types
|
||||||
|
*/
|
||||||
|
List<String> getMimeTypes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns supported extensions (each starting with .)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<String> getExtensionsList();
|
||||||
|
|
||||||
|
default boolean isSupported(AbstractFile file) {
|
||||||
|
SortedSet<String> mimeTypes = new TreeSet<>(getMimeTypes());
|
||||||
|
try {
|
||||||
|
String mimeType = new FileTypeDetector().getFileType(file);
|
||||||
|
if (nonNull(mimeType)) {
|
||||||
|
return mimeTypes.contains(mimeType);
|
||||||
|
}
|
||||||
|
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
||||||
|
logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
||||||
|
if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == MimeMatchEnum.TRUE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String extension = file.getNameExtension();
|
||||||
|
|
||||||
|
if (getExtensionsList().contains("." + extension)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Component id="thumbnailScrollPanel" pref="642" max="32767" attributes="0"/>
|
<Group type="102" attributes="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" attributes="0">
|
<Group type="102" attributes="0">
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
@ -43,7 +44,14 @@
|
|||||||
<Component id="thumbnailSizeComboBox" min="-2" max="-2" attributes="0"/>
|
<Component id="thumbnailSizeComboBox" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace max="32767" attributes="0"/>
|
</Group>
|
||||||
|
<Group type="102" alignment="0" attributes="0">
|
||||||
|
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||||
|
<Component id="iconView" pref="563" max="32767" attributes="0"/>
|
||||||
|
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -66,8 +74,8 @@
|
|||||||
<Component id="thumbnailSizeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
<Component id="thumbnailSizeComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="thumbnailScrollPanel" max="32767" attributes="0"/>
|
<Component id="iconView" pref="330" max="32767" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="filePathLabel" min="-2" max="-2" attributes="0"/>
|
<Component id="filePathLabel" min="-2" max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
@ -75,18 +83,6 @@
|
|||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
<SubComponents>
|
<SubComponents>
|
||||||
<Container class="javax.swing.JScrollPane" name="thumbnailScrollPanel">
|
|
||||||
<Properties>
|
|
||||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
|
||||||
<Dimension value="[582, 348]"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
<AuxValues>
|
|
||||||
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new IconView();"/>
|
|
||||||
</AuxValues>
|
|
||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
|
||||||
</Container>
|
|
||||||
<Component class="javax.swing.JLabel" name="pageLabel">
|
<Component class="javax.swing.JLabel" name="pageLabel">
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
@ -213,5 +209,7 @@
|
|||||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="org.openide.explorer.view.IconView" name="iconView">
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -27,9 +27,6 @@ import java.util.Arrays;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import javax.swing.ListSelectionModel;
|
import javax.swing.ListSelectionModel;
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
@ -37,7 +34,6 @@ import org.netbeans.api.progress.ProgressHandleFactory;
|
|||||||
import org.openide.DialogDisplayer;
|
import org.openide.DialogDisplayer;
|
||||||
import org.openide.NotifyDescriptor;
|
import org.openide.NotifyDescriptor;
|
||||||
import org.openide.explorer.ExplorerManager;
|
import org.openide.explorer.ExplorerManager;
|
||||||
import org.openide.explorer.view.IconView;
|
|
||||||
import org.openide.nodes.AbstractNode;
|
import org.openide.nodes.AbstractNode;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
@ -45,9 +41,10 @@ import org.openide.nodes.NodeEvent;
|
|||||||
import org.openide.nodes.NodeListener;
|
import org.openide.nodes.NodeListener;
|
||||||
import org.openide.nodes.NodeMemberEvent;
|
import org.openide.nodes.NodeMemberEvent;
|
||||||
import org.openide.nodes.NodeReorderEvent;
|
import org.openide.nodes.NodeReorderEvent;
|
||||||
import org.openide.util.Exceptions;
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
@ -63,7 +60,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
// service provider when DataResultViewers can be made compatible with node
|
// service provider when DataResultViewers can be made compatible with node
|
||||||
// multi-selection actions.
|
// multi-selection actions.
|
||||||
//@ServiceProvider(service = DataResultViewer.class)
|
//@ServiceProvider(service = DataResultViewer.class)
|
||||||
final class DataResultViewerThumbnail extends AbstractDataResultViewer {
|
final class DataResultViewerThumbnail extends AbstractDataResultViewer {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName());
|
private static final Logger logger = Logger.getLogger(DataResultViewerThumbnail.class.getName());
|
||||||
//flag to keep track if images are being loaded
|
//flag to keep track if images are being loaded
|
||||||
@ -93,7 +90,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
private void initialize() {
|
private void initialize() {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
((IconView) thumbnailScrollPanel).setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
iconView.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||||
em.addPropertyChangeListener(new ExplorerManagerNodeSelectionListener());
|
em.addPropertyChangeListener(new ExplorerManagerNodeSelectionListener());
|
||||||
|
|
||||||
curPage = -1;
|
curPage = -1;
|
||||||
@ -110,7 +107,6 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
thumbnailScrollPanel = new IconView();
|
|
||||||
pageLabel = new javax.swing.JLabel();
|
pageLabel = new javax.swing.JLabel();
|
||||||
pagesLabel = new javax.swing.JLabel();
|
pagesLabel = new javax.swing.JLabel();
|
||||||
pagePrevButton = new javax.swing.JButton();
|
pagePrevButton = new javax.swing.JButton();
|
||||||
@ -122,32 +118,31 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
goToPageLabel = new javax.swing.JLabel();
|
goToPageLabel = new javax.swing.JLabel();
|
||||||
goToPageField = new javax.swing.JTextField();
|
goToPageField = new javax.swing.JTextField();
|
||||||
thumbnailSizeComboBox = new javax.swing.JComboBox<>();
|
thumbnailSizeComboBox = new javax.swing.JComboBox<>();
|
||||||
|
iconView = new org.openide.explorer.view.IconView();
|
||||||
thumbnailScrollPanel.setPreferredSize(new java.awt.Dimension(582, 348));
|
|
||||||
|
|
||||||
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageLabel.text")); // NOI18N
|
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageLabel.text")); // NOI18N
|
||||||
|
|
||||||
pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagesLabel.text")); // NOI18N
|
pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagesLabel.text")); // NOI18N
|
||||||
|
|
||||||
pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N NON-NLS
|
pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
|
||||||
pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagePrevButton.text")); // NOI18N
|
pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagePrevButton.text")); // NOI18N
|
||||||
pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N NON-NLS
|
pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
|
||||||
pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
|
pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
|
||||||
pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N NON-NLS
|
pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
|
||||||
pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
|
pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
pagePrevButtonActionPerformed(evt);
|
pagePrevButtonActionPerformed(evt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N NON-NLS
|
pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
|
||||||
pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNextButton.text")); // NOI18N
|
pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNextButton.text")); // NOI18N
|
||||||
pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N NON-NLS
|
pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
|
||||||
pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
|
||||||
pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
|
pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
|
||||||
pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
|
pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
|
||||||
pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N NON-NLS
|
pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
|
||||||
pageNextButton.addActionListener(new java.awt.event.ActionListener() {
|
pageNextButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
pageNextButtonActionPerformed(evt);
|
pageNextButtonActionPerformed(evt);
|
||||||
@ -171,10 +166,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
thumbnailSizeComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] {
|
thumbnailSizeComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { "Small Thumbnails", "Medium Thumbnails", "Large Thumbnails" }));
|
||||||
NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.comboBox.smallThumbnails"),
|
|
||||||
NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.comboBox.mediumThumbnails"),
|
|
||||||
NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.comboBox.largeThumbnails") }));
|
|
||||||
thumbnailSizeComboBox.addActionListener(new java.awt.event.ActionListener() {
|
thumbnailSizeComboBox.addActionListener(new java.awt.event.ActionListener() {
|
||||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||||
thumbnailSizeComboBoxActionPerformed(evt);
|
thumbnailSizeComboBoxActionPerformed(evt);
|
||||||
@ -185,7 +177,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 642, Short.MAX_VALUE)
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addContainerGap()
|
.addContainerGap()
|
||||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
@ -209,8 +202,12 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
.addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
|
||||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addGap(0, 0, 0)
|
||||||
|
.addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, 563, Short.MAX_VALUE)
|
||||||
|
.addGap(0, 0, 0)))
|
||||||
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
@ -228,8 +225,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
.addComponent(goToPageLabel)
|
.addComponent(goToPageLabel)
|
||||||
.addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
.addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
.addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
.addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
.addGap(0, 0, 0)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(thumbnailScrollPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
.addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, 330, Short.MAX_VALUE)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(filePathLabel))
|
.addComponent(filePathLabel))
|
||||||
);
|
);
|
||||||
@ -250,7 +247,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
private void thumbnailSizeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_thumbnailSizeComboBoxActionPerformed
|
private void thumbnailSizeComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_thumbnailSizeComboBoxActionPerformed
|
||||||
|
|
||||||
iconSize = ImageUtils.ICON_SIZE_MEDIUM; //default size
|
iconSize = ImageUtils.ICON_SIZE_MEDIUM; //default size
|
||||||
switch(thumbnailSizeComboBox.getSelectedIndex()) {
|
switch (thumbnailSizeComboBox.getSelectedIndex()) {
|
||||||
case 0:
|
case 0:
|
||||||
iconSize = ImageUtils.ICON_SIZE_SMALL;
|
iconSize = ImageUtils.ICON_SIZE_SMALL;
|
||||||
break;
|
break;
|
||||||
@ -260,13 +257,13 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
Node root = em.getRootContext();
|
Node root = em.getRootContext();
|
||||||
for (Children c : Arrays.asList(root.getChildren()) ) {
|
for (Children c : Arrays.asList(root.getChildren())) {
|
||||||
((ThumbnailViewChildren)c).setIconSize(iconSize);
|
((ThumbnailViewChildren) c).setIconSize(iconSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Node page : root.getChildren().getNodes()) {
|
for (Node page : root.getChildren().getNodes()) {
|
||||||
for (Node node : page.getChildren().getNodes()) {
|
for (Node node : page.getChildren().getNodes()) {
|
||||||
((ThumbnailViewNode)node).setIconSize(iconSize);
|
((ThumbnailViewNode) node).setIconSize(iconSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,6 +280,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
private javax.swing.JLabel filePathLabel;
|
private javax.swing.JLabel filePathLabel;
|
||||||
private javax.swing.JTextField goToPageField;
|
private javax.swing.JTextField goToPageField;
|
||||||
private javax.swing.JLabel goToPageLabel;
|
private javax.swing.JLabel goToPageLabel;
|
||||||
|
private org.openide.explorer.view.IconView iconView;
|
||||||
private javax.swing.JLabel imagesLabel;
|
private javax.swing.JLabel imagesLabel;
|
||||||
private javax.swing.JLabel imagesRangeLabel;
|
private javax.swing.JLabel imagesRangeLabel;
|
||||||
private javax.swing.JLabel pageLabel;
|
private javax.swing.JLabel pageLabel;
|
||||||
@ -290,7 +288,6 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
private javax.swing.JLabel pageNumLabel;
|
private javax.swing.JLabel pageNumLabel;
|
||||||
private javax.swing.JButton pagePrevButton;
|
private javax.swing.JButton pagePrevButton;
|
||||||
private javax.swing.JLabel pagesLabel;
|
private javax.swing.JLabel pagesLabel;
|
||||||
private javax.swing.JScrollPane thumbnailScrollPanel;
|
|
||||||
private javax.swing.JComboBox<String> thumbnailSizeComboBox;
|
private javax.swing.JComboBox<String> thumbnailSizeComboBox;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
@ -299,8 +296,15 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
if (selectedNode == null) {
|
if (selectedNode == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Children ch = selectedNode.getChildren();
|
||||||
|
for (Node n : ch.getNodes()) {
|
||||||
|
if (ThumbnailViewChildren.isSupported(n)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setNode(Node givenNode) {
|
public void setNode(Node givenNode) {
|
||||||
@ -318,8 +322,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
Node emptyNode = new AbstractNode(Children.LEAF);
|
Node emptyNode = new AbstractNode(Children.LEAF);
|
||||||
em.setRootContext(emptyNode); // make empty node
|
em.setRootContext(emptyNode); // make empty node
|
||||||
|
|
||||||
IconView iv = ((IconView) this.thumbnailScrollPanel);
|
iconView.setBackground(Color.BLACK);
|
||||||
iv.setBackground(Color.BLACK);
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.setCursor(null);
|
this.setCursor(null);
|
||||||
@ -348,8 +351,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearComponent() {
|
public void clearComponent() {
|
||||||
this.thumbnailScrollPanel.removeAll();
|
this.iconView.removeAll();
|
||||||
this.thumbnailScrollPanel = null;
|
this.iconView = null;
|
||||||
|
|
||||||
super.clearComponent();
|
super.clearComponent();
|
||||||
}
|
}
|
||||||
@ -374,8 +377,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
int newPage;
|
int newPage;
|
||||||
try {
|
try {
|
||||||
newPage = Integer.parseInt(pageNumText);
|
newPage = Integer.parseInt(pageNumText);
|
||||||
}
|
} catch (NumberFormatException e) {
|
||||||
catch (NumberFormatException e) {
|
|
||||||
//ignore input
|
//ignore input
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -433,8 +435,8 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
try {
|
try {
|
||||||
get();
|
get();
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
NotifyDescriptor d =
|
NotifyDescriptor d
|
||||||
new NotifyDescriptor.Message(
|
= new NotifyDescriptor.Message(
|
||||||
NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.switchPage.done.errMsg",
|
NbBundle.getMessage(this.getClass(), "DataResultViewerThumbnail.switchPage.done.errMsg",
|
||||||
ex.getMessage()),
|
ex.getMessage()),
|
||||||
NotifyDescriptor.ERROR_MESSAGE);
|
NotifyDescriptor.ERROR_MESSAGE);
|
||||||
@ -465,7 +467,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
|
|
||||||
pageNextButton.setEnabled(!(curPage == totalPages));
|
pageNextButton.setEnabled(!(curPage == totalPages));
|
||||||
pagePrevButton.setEnabled(!(curPage == 1));
|
pagePrevButton.setEnabled(!(curPage == 1));
|
||||||
goToPageField.setEnabled(totalPages>1);
|
goToPageField.setEnabled(totalPages > 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,7 +536,6 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
em.setExploredContext(pageNode);
|
em.setExploredContext(pageNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
updateControls();
|
updateControls();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -556,6 +557,7 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class ExplorerManagerNodeSelectionListener implements PropertyChangeListener {
|
private class ExplorerManagerNodeSelectionListener implements PropertyChangeListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
public void propertyChange(PropertyChangeEvent evt) {
|
||||||
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||||
@ -566,26 +568,23 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
AbstractFile af = selectedNodes[0].getLookup().lookup(AbstractFile.class);
|
AbstractFile af = selectedNodes[0].getLookup().lookup(AbstractFile.class);
|
||||||
if (af == null) {
|
if (af == null) {
|
||||||
filePathLabel.setText("");
|
filePathLabel.setText("");
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
try {
|
try {
|
||||||
String uPath = af.getUniquePath();
|
String uPath = af.getUniquePath();
|
||||||
filePathLabel.setText(uPath);
|
filePathLabel.setText(uPath);
|
||||||
filePathLabel.setToolTipText(uPath);
|
filePathLabel.setToolTipText(uPath);
|
||||||
}
|
} catch (TskCoreException e) {
|
||||||
catch (TskCoreException e){
|
|
||||||
logger.log(Level.WARNING, "Could not get unique path for content: {0}", af.getName()); //NON-NLS
|
logger.log(Level.WARNING, "Could not get unique path for content: {0}", af.getName()); //NON-NLS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
filePathLabel.setText("");
|
filePathLabel.setText("");
|
||||||
}
|
}
|
||||||
}
|
} finally {
|
||||||
finally {
|
|
||||||
setCursor(null);
|
setCursor(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,35 @@
|
|||||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
||||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
||||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
||||||
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
|
|
||||||
</AuxValues>
|
</AuxValues>
|
||||||
|
|
||||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
|
<Layout>
|
||||||
|
<DimensionLayout dim="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="jFXPanel" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
<DimensionLayout dim="1">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="jFXPanel" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
<SubComponents>
|
||||||
|
<Container class="javafx.embed.swing.JFXPanel" name="jFXPanel">
|
||||||
|
|
||||||
|
<Layout>
|
||||||
|
<DimensionLayout dim="0">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
<DimensionLayout dim="1">
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</DimensionLayout>
|
||||||
|
</Layout>
|
||||||
|
</Container>
|
||||||
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-15 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -18,7 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.corecomponents;
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
|
import com.google.common.io.Files;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -26,11 +28,8 @@ import java.util.List;
|
|||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.InvalidationListener;
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.concurrent.Task;
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.embed.swing.JFXPanel;
|
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
@ -55,17 +54,15 @@ import static javafx.scene.media.MediaPlayer.Status.STOPPED;
|
|||||||
import javafx.scene.media.MediaView;
|
import javafx.scene.media.MediaView;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.SwingWorker;
|
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
import org.openide.util.Cancellable;
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.openide.util.lookup.ServiceProviders;
|
import org.openide.util.lookup.ServiceProviders;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.core.Installer;
|
import org.sleuthkit.autopsy.core.Installer;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.VideoUtils;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
@ -83,51 +80,30 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
// for Javafx supported formats
|
// for Javafx supported formats
|
||||||
private static final String[] EXTENSIONS = new String[]{".m4v", ".fxm", ".flv", ".m3u8", ".mp4", ".aif", ".aiff", ".mp3", "m4a", ".wav"}; //NON-NLS
|
private static final String[] EXTENSIONS = new String[]{".m4v", ".fxm", ".flv", ".m3u8", ".mp4", ".aif", ".aiff", ".mp3", "m4a", ".wav"}; //NON-NLS
|
||||||
private static final List<String> MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
|
private static final List<String> MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
|
||||||
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(FXVideoPanel.class.getName());
|
||||||
|
|
||||||
private boolean fxInited = false;
|
private boolean fxInited = false;
|
||||||
|
|
||||||
// FX Components
|
|
||||||
private MediaPane mediaPane;
|
private MediaPane mediaPane;
|
||||||
|
|
||||||
// Current media content representations
|
|
||||||
private AbstractFile currentFile;
|
private AbstractFile currentFile;
|
||||||
|
|
||||||
// FX UI Components
|
|
||||||
private JFXPanel videoComponent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new form MediaViewVideoPanel
|
|
||||||
*/
|
|
||||||
public FXVideoPanel() {
|
public FXVideoPanel() {
|
||||||
fxInited = Installer.isJavaFxInited();
|
fxInited = Installer.isJavaFxInited();
|
||||||
initComponents();
|
initComponents();
|
||||||
if (fxInited) {
|
if (fxInited) {
|
||||||
setupFx();
|
Platform.runLater(() -> {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public JPanel getVideoPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupFx() {
|
|
||||||
Platform.runLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
videoComponent = new JFXPanel();
|
|
||||||
mediaPane = new MediaPane();
|
mediaPane = new MediaPane();
|
||||||
Scene fxScene = new Scene(mediaPane);
|
Scene fxScene = new Scene(mediaPane);
|
||||||
videoComponent.setScene(fxScene);
|
jFXPanel.setScene(fxScene);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
@Deprecated
|
||||||
@Override
|
public JPanel getVideoPanel() {
|
||||||
public void run() {
|
return this;
|
||||||
add(videoComponent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -148,50 +124,33 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
removeAll();
|
removeAll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mediaPane.setFit(dims);
|
||||||
|
|
||||||
String path = "";
|
String path = "";
|
||||||
try {
|
try {
|
||||||
path = file.getUniquePath();
|
path = file.getUniquePath();
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
logger.log(Level.SEVERE, "Cannot get unique path of video file"); //NON-NLS
|
logger.log(Level.SEVERE, "Cannot get unique path of video file", ex); //NON-NLS
|
||||||
}
|
}
|
||||||
mediaPane.setInfoLabelText(path);
|
mediaPane.setInfoLabelText(path);
|
||||||
mediaPane.setInfoLabelToolTipText(path);
|
mediaPane.setInfoLabelToolTipText(path);
|
||||||
|
|
||||||
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
final File tempFile = VideoUtils.getTempVideoFile(currentFile);
|
||||||
em.execute();
|
|
||||||
|
new Thread(mediaPane.new ExtractMedia(currentFile, tempFile)).start();
|
||||||
|
|
||||||
mediaPane.setFit(dims);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void reset() {
|
void reset() {
|
||||||
Platform.runLater(new Runnable() {
|
Platform.runLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (mediaPane != null) {
|
if (mediaPane != null) {
|
||||||
mediaPane.reset();
|
mediaPane.reset();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
currentFile = null;
|
currentFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private java.io.File getJFile(AbstractFile file) {
|
|
||||||
// Get the temp folder path of the case
|
|
||||||
String tempPath = Case.getCurrentCase().getTempDirectory();
|
|
||||||
String name = file.getName();
|
|
||||||
int extStart = name.lastIndexOf(".");
|
|
||||||
String ext = "";
|
|
||||||
if (extStart != -1) {
|
|
||||||
ext = name.substring(extStart, name.length()).toLowerCase();
|
|
||||||
}
|
|
||||||
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
|
|
||||||
|
|
||||||
java.io.File tempFile = new java.io.File(tempPath);
|
|
||||||
return tempFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* This method is called from within the constructor to initialize the form.
|
||||||
* WARNING: Do NOT modify this code. The content of this method is always
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
@ -201,10 +160,34 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
|
jFXPanel = new javafx.embed.swing.JFXPanel();
|
||||||
|
|
||||||
setBackground(new java.awt.Color(0, 0, 0));
|
setBackground(new java.awt.Color(0, 0, 0));
|
||||||
setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.LINE_AXIS));
|
|
||||||
|
javax.swing.GroupLayout jFXPanelLayout = new javax.swing.GroupLayout(jFXPanel);
|
||||||
|
jFXPanel.setLayout(jFXPanelLayout);
|
||||||
|
jFXPanelLayout.setHorizontalGroup(
|
||||||
|
jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGap(0, 400, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
jFXPanelLayout.setVerticalGroup(
|
||||||
|
jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGap(0, 300, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
|
this.setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javafx.embed.swing.JFXPanel jFXPanel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -212,134 +195,32 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
return fxInited;
|
return fxInited;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread that extracts Media from a Sleuthkit file representation to a
|
|
||||||
* Java file representation that the Media Player can take as input.
|
|
||||||
*/
|
|
||||||
private class ExtractMedia extends SwingWorker<Object, Void> {
|
|
||||||
|
|
||||||
private ProgressHandle progress;
|
|
||||||
|
|
||||||
boolean success = false;
|
|
||||||
|
|
||||||
private AbstractFile sFile;
|
|
||||||
|
|
||||||
private java.io.File jFile;
|
|
||||||
|
|
||||||
private long extractedBytes;
|
|
||||||
|
|
||||||
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
|
||||||
this.sFile = sFile;
|
|
||||||
this.jFile = jFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getExtractedBytes() {
|
|
||||||
return extractedBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the URI of the media file.
|
|
||||||
*
|
|
||||||
* @return the URI of the media file.
|
|
||||||
*/
|
|
||||||
public String getMediaUri() {
|
|
||||||
return Paths.get(jFile.getAbsolutePath()).toUri().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object doInBackground() throws Exception {
|
|
||||||
success = false;
|
|
||||||
progress = ProgressHandleFactory.createHandle(
|
|
||||||
NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingFile", sFile.getName()),
|
|
||||||
new Cancellable() {
|
|
||||||
@Override
|
|
||||||
public boolean cancel() {
|
|
||||||
return ExtractMedia.this.cancel(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mediaPane.setProgressLabelText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progressLabel.buffering"));
|
|
||||||
progress.start();
|
|
||||||
progress.switchToDeterminate(100);
|
|
||||||
try {
|
|
||||||
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
logger.log(Level.INFO, "Done buffering: " + jFile.getName()); //NON-NLS
|
|
||||||
success = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clean up or start the worker threads */
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
mediaPane.setProgressLabelText("");
|
|
||||||
try {
|
|
||||||
super.get(); //block and get all exceptions thrown while doInBackground()
|
|
||||||
} catch (CancellationException ex) {
|
|
||||||
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
|
||||||
mediaPane.setProgressLabelText(
|
|
||||||
NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingCancelled"));
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
|
|
||||||
mediaPane.setProgressLabelText(
|
|
||||||
NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingInterrupted"));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
|
||||||
mediaPane.setProgressLabelText(
|
|
||||||
NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.errorWritingVideoToDisk"));
|
|
||||||
} finally {
|
|
||||||
progress.finish();
|
|
||||||
if (!this.isCancelled()) {
|
|
||||||
logger.log(Level.INFO, "ExtractMedia in done: " + jFile.getName()); //NON-NLS
|
|
||||||
try {
|
|
||||||
Platform.runLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
mediaPane.prepareMedia(getMediaUri());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (MediaException e) {
|
|
||||||
logger.log(Level.WARNING, "something went wrong with javafx", e); //NON-NLS
|
|
||||||
reset();
|
|
||||||
mediaPane.setInfoLabelText(e.getMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The JavaFX Component that contains the Media and it's Controls.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private class MediaPane extends BorderPane {
|
private class MediaPane extends BorderPane {
|
||||||
|
|
||||||
private MediaPlayer mediaPlayer;
|
private MediaPlayer mediaPlayer;
|
||||||
|
|
||||||
private MediaView mediaView;
|
private final MediaView mediaView;
|
||||||
|
|
||||||
/** The Duration of the media. * */
|
/** The Duration of the media. * */
|
||||||
private Duration duration;
|
private Duration duration;
|
||||||
|
|
||||||
/** The container for the media controls. * */
|
/** The container for the media controls. * */
|
||||||
private HBox mediaTools;
|
private final HBox mediaTools;
|
||||||
|
|
||||||
/** The container for the media video output. * */
|
/** The container for the media video output. * */
|
||||||
private HBox mediaViewPane;
|
private final HBox mediaViewPane;
|
||||||
|
|
||||||
private VBox controlPanel;
|
private final VBox controlPanel;
|
||||||
|
|
||||||
private Slider progressSlider;
|
private final Slider progressSlider;
|
||||||
|
|
||||||
private Button pauseButton;
|
private final Button pauseButton;
|
||||||
|
|
||||||
private Button stopButton;
|
private final Button stopButton;
|
||||||
|
|
||||||
private Label progressLabel;
|
private final Label progressLabel;
|
||||||
|
|
||||||
private Label infoLabel;
|
private final Label infoLabel;
|
||||||
|
|
||||||
private int totalHours;
|
private int totalHours;
|
||||||
|
|
||||||
@ -347,22 +228,7 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
|
|
||||||
private int totalSeconds;
|
private int totalSeconds;
|
||||||
|
|
||||||
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
private final String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
||||||
|
|
||||||
/** The EventHandler for MediaPlayer.onReady(). * */
|
|
||||||
private final ReadyListener READY_LISTENER = new ReadyListener();
|
|
||||||
|
|
||||||
/** The EventHandler for MediaPlayer.onEndOfMedia(). * */
|
|
||||||
private final EndOfMediaListener END_LISTENER = new EndOfMediaListener();
|
|
||||||
|
|
||||||
/** The EventHandler for the CurrentTime property of the MediaPlayer. * */
|
|
||||||
private final TimeListener TIME_LISTENER = new TimeListener();
|
|
||||||
|
|
||||||
/** The EventHandler for MediaPlayer.onPause and MediaPlayer.onStop. * */
|
|
||||||
private final NotPlayListener NOT_PLAY_LISTENER = new NotPlayListener();
|
|
||||||
|
|
||||||
/** The EventHandler for MediaPlayer.onPlay. * */
|
|
||||||
private final PlayListener PLAY_LISTENER = new PlayListener();
|
|
||||||
|
|
||||||
private static final String PLAY_TEXT = "►";
|
private static final String PLAY_TEXT = "►";
|
||||||
|
|
||||||
@ -409,22 +275,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
setProgressActionListeners();
|
setProgressActionListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup the MediaPane for media playback. Run on the JavaFx Thread.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param mediaUri the URI of the media
|
|
||||||
*/
|
|
||||||
public void prepareMedia(String mediaUri) {
|
|
||||||
try {
|
|
||||||
mediaPlayer = createMediaPlayer(mediaUri);
|
|
||||||
mediaView.setMediaPlayer(mediaPlayer);
|
|
||||||
} catch (MediaException ex) {
|
|
||||||
this.setProgressLabelText("");
|
|
||||||
this.setInfoLabelText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.media.unsupportedFormat"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset this MediaPane.
|
* Reset this MediaPane.
|
||||||
*
|
*
|
||||||
@ -447,12 +297,9 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
* @param text
|
* @param text
|
||||||
*/
|
*/
|
||||||
public void setInfoLabelText(final String text) {
|
public void setInfoLabelText(final String text) {
|
||||||
logger.log(Level.INFO, "Setting Info Label Text: " + text); //NON-NLS
|
logger.log(Level.INFO, "Setting Info Label Text: {0}", text); //NON-NLS
|
||||||
Platform.runLater(new Runnable() {
|
Platform.runLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
infoLabel.setText(text);
|
infoLabel.setText(text);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,14 +309,11 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
* @param dims the current dimensions of the DataContentViewer
|
* @param dims the current dimensions of the DataContentViewer
|
||||||
*/
|
*/
|
||||||
public void setFit(final Dimension dims) {
|
public void setFit(final Dimension dims) {
|
||||||
Platform.runLater(new Runnable() {
|
Platform.runLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
setPrefSize(dims.getWidth(), dims.getHeight());
|
setPrefSize(dims.getWidth(), dims.getHeight());
|
||||||
// Set the Video output to fit the size allocated for it. give an
|
// Set the Video output to fit the size allocated for it. give an
|
||||||
// extra few px to ensure the info label will be shown
|
// extra few px to ensure the info label will be shown
|
||||||
mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight());
|
mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight());
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,7 +342,7 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
mediaPlayer.play();
|
mediaPlayer.play();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logger.log(Level.INFO, "MediaPlayer in unexpected state: " + status.toString()); //NON-NLS
|
logger.log(Level.INFO, "MediaPlayer in unexpected state: {0}", status.toString()); //NON-NLS
|
||||||
// If the MediaPlayer is in an unexpected state, stop playback.
|
// If the MediaPlayer is in an unexpected state, stop playback.
|
||||||
mediaPlayer.stop();
|
mediaPlayer.stop();
|
||||||
setInfoLabelText(NbBundle.getMessage(this.getClass(),
|
setInfoLabelText(NbBundle.getMessage(this.getClass(),
|
||||||
@ -508,20 +352,15 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stopButton.setOnAction(new EventHandler<ActionEvent>() {
|
stopButton.setOnAction((ActionEvent e) -> {
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent e) {
|
|
||||||
if (mediaPlayer == null) {
|
if (mediaPlayer == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaPlayer.stop();
|
mediaPlayer.stop();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
progressSlider.valueProperty().addListener(new InvalidationListener() {
|
progressSlider.valueProperty().addListener((Observable o) -> {
|
||||||
@Override
|
|
||||||
public void invalidated(Observable o) {
|
|
||||||
if (mediaPlayer == null) {
|
if (mediaPlayer == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -529,7 +368,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
if (progressSlider.isValueChanging()) {
|
if (progressSlider.isValueChanging()) {
|
||||||
mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
|
mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,13 +395,21 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
Media media = new Media(mediaUri);
|
Media media = new Media(mediaUri);
|
||||||
|
|
||||||
MediaPlayer player = new MediaPlayer(media);
|
MediaPlayer player = new MediaPlayer(media);
|
||||||
player.setOnReady(READY_LISTENER);
|
player.setOnReady(new ReadyListener());
|
||||||
player.setOnPaused(NOT_PLAY_LISTENER);
|
final Runnable pauseListener = () -> {
|
||||||
player.setOnStopped(NOT_PLAY_LISTENER);
|
pauseButton.setText(PLAY_TEXT);
|
||||||
player.setOnPlaying(PLAY_LISTENER);
|
};
|
||||||
player.setOnEndOfMedia(END_LISTENER);
|
player.setOnPaused(pauseListener);
|
||||||
|
player.setOnStopped(pauseListener);
|
||||||
|
player.setOnPlaying(() -> {
|
||||||
|
pauseButton.setText(PAUSE_TEXT);
|
||||||
|
});
|
||||||
|
player.setOnEndOfMedia(new EndOfMediaListener());
|
||||||
|
|
||||||
player.currentTimeProperty().addListener(TIME_LISTENER);
|
player.currentTimeProperty().addListener((observable, oldTime, newTime) -> {
|
||||||
|
updateSlider(newTime);
|
||||||
|
updateTime(newTime);
|
||||||
|
});
|
||||||
|
|
||||||
return player;
|
return player;
|
||||||
}
|
}
|
||||||
@ -616,29 +462,14 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
String durationStr = String.format(durationFormat,
|
String durationStr = String.format(durationFormat,
|
||||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
elapsedHours, elapsedMinutes, elapsedSeconds,
|
||||||
totalHours, totalMinutes, totalSeconds);
|
totalHours, totalMinutes, totalSeconds);
|
||||||
setProgressLabelText(durationStr);
|
Platform.runLater(() -> {
|
||||||
}
|
progressLabel.setText(durationStr);
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the progress label to show the text.
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
*/
|
|
||||||
private void setProgressLabelText(final String text) {
|
|
||||||
Platform.runLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
progressLabel.setText(text);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setInfoLabelToolTipText(final String text) {
|
private void setInfoLabelToolTipText(final String text) {
|
||||||
Platform.runLater(new Runnable() {
|
Platform.runLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
infoLabel.setTooltip(new Tooltip(text));
|
infoLabel.setTooltip(new Tooltip(text));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,38 +523,105 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responds to changes in the MediaPlayer currentTime property.
|
* Thread that extracts Media from a Sleuthkit file representation to a
|
||||||
|
* Java file representation that the Media Player can take as input.
|
||||||
|
*/
|
||||||
|
private class ExtractMedia extends Task<Long> {
|
||||||
|
|
||||||
|
private ProgressHandle progress;
|
||||||
|
|
||||||
|
private final AbstractFile sourceFile;
|
||||||
|
|
||||||
|
private final java.io.File tempFile;
|
||||||
|
|
||||||
|
ExtractMedia(AbstractFile sFile, java.io.File jFile) {
|
||||||
|
this.sourceFile = sFile;
|
||||||
|
this.tempFile = jFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the URI of the media file.
|
||||||
*
|
*
|
||||||
* Updates the progress slider and label with the current Time.
|
* @return the URI of the media file.
|
||||||
*/
|
*/
|
||||||
private class TimeListener implements ChangeListener<Duration> {
|
public String getMediaUri() {
|
||||||
|
return Paths.get(tempFile.getAbsolutePath()).toUri().toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
|
protected Long call() throws Exception {
|
||||||
updateSlider(newValue);
|
if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) {
|
||||||
updateTime(newValue);
|
progress = ProgressHandleFactory.createHandle(
|
||||||
}
|
NbBundle.getMessage(this.getClass(),
|
||||||
}
|
"FXVideoPanel.progress.bufferingFile",
|
||||||
|
sourceFile.getName()
|
||||||
|
),
|
||||||
|
() -> ExtractMedia.this.cancel(true));
|
||||||
|
|
||||||
/**
|
Platform.runLater(() -> {
|
||||||
* Triggered when MediaPlayer State changes to PAUSED or Stopped.
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progressLabel.buffering"));
|
||||||
*/
|
});
|
||||||
private class NotPlayListener implements Runnable {
|
|
||||||
|
progress.start(100);
|
||||||
|
try {
|
||||||
|
Files.createParentDirs(tempFile);
|
||||||
|
return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
||||||
|
return 0L;
|
||||||
|
} finally {
|
||||||
|
logger.log(Level.INFO, "Done buffering: {0}", tempFile.getName()); //NON-NLS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
protected void failed() {
|
||||||
pauseButton.setText(PLAY_TEXT);
|
super.failed();
|
||||||
|
onDone();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggered when MediaPlayer State changes to PLAYING.
|
|
||||||
*/
|
|
||||||
private class PlayListener implements Runnable {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
protected void succeeded() {
|
||||||
pauseButton.setText(PAUSE_TEXT);
|
super.succeeded();
|
||||||
|
onDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void cancelled() {
|
||||||
|
super.cancelled();
|
||||||
|
onDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDone() {
|
||||||
|
progressLabel.setText("");
|
||||||
|
try {
|
||||||
|
super.get(); //block and get all exceptions thrown while doInBackground()
|
||||||
|
} catch (CancellationException ex) {
|
||||||
|
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
||||||
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingCancelled"));
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
|
||||||
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingInterrupted"));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
||||||
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.errorWritingVideoToDisk"));
|
||||||
|
} finally {
|
||||||
|
if (null != progress) {
|
||||||
|
progress.finish();
|
||||||
|
}
|
||||||
|
if (!this.isCancelled()) {
|
||||||
|
logger.log(Level.INFO, "ExtractMedia is done: {0}", tempFile.getName()); //NON-NLS
|
||||||
|
try {
|
||||||
|
mediaPane.mediaPlayer = mediaPane.createMediaPlayer(getMediaUri());
|
||||||
|
mediaView.setMediaPlayer(mediaPane.mediaPlayer);
|
||||||
|
} catch (MediaException ex) {
|
||||||
|
progressLabel.setText("");
|
||||||
|
mediaPane.setInfoLabelText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.media.unsupportedFormat"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -739,114 +637,13 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
|
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
|
||||||
//
|
//What is/was the point of this method /interface.
|
||||||
// try {
|
|
||||||
// List<VideoFrame> frames = new ArrayList<>();
|
|
||||||
//
|
|
||||||
// FrameCapturer fc = new FrameCapturer(file);
|
|
||||||
// logger.log(Level.INFO, "Fc is null? " + (fc == null));
|
|
||||||
// frames = fc.getFrames(numFrames);
|
|
||||||
//
|
|
||||||
// return frames;
|
|
||||||
// }
|
|
||||||
// catch (NullPointerException e) {
|
|
||||||
// e.printStackTrace();
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private class FrameCapturer {
|
|
||||||
//
|
|
||||||
// private MediaPlayer mediaPlayer;
|
|
||||||
// private JFXPanel panel;
|
|
||||||
// private boolean isReady = false;
|
|
||||||
//
|
|
||||||
// FrameCapturer(java.io.File file) {
|
|
||||||
// initFx(file);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// boolean isReady() {
|
|
||||||
// return isReady;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private void initFx(final java.io.File file) {
|
|
||||||
// Platform.runAndWait(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public void run() {
|
|
||||||
// logger.log(Level.INFO, "In initFX.");
|
|
||||||
// // Create Media Player with no video output
|
|
||||||
// Media media = new Media(Paths.get(file.getAbsolutePath()).toUri().toString());
|
|
||||||
// mediaPlayer = new MediaPlayer(media);
|
|
||||||
// MediaView mediaView = new MediaView(mediaPlayer);
|
|
||||||
// mediaView.setStyle("-fx-background-color: black");
|
|
||||||
// Pane mediaViewPane = new Pane();
|
|
||||||
// mediaViewPane.getChildren().add(mediaView);
|
|
||||||
// Scene scene = new Scene(mediaViewPane);
|
|
||||||
// panel = new JFXPanel();
|
|
||||||
// panel.setScene(scene);
|
|
||||||
// isReady = true;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// List<VideoFrame> getFrames(int numFrames) {
|
|
||||||
// logger.log(Level.INFO, "in get frames");
|
|
||||||
// List<VideoFrame> frames = new ArrayList<VideoFrame>(0);
|
|
||||||
//
|
|
||||||
// if (mediaPlayer.getStatus() != Status.READY) {
|
|
||||||
// try {
|
|
||||||
// Thread.sleep(500);
|
|
||||||
// } catch (InterruptedException e) {
|
|
||||||
// return frames;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // get the duration of the video
|
|
||||||
// long myDurationMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
|
|
||||||
// if (myDurationMillis <= 0) {
|
|
||||||
// return frames;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // calculate the frame interval
|
|
||||||
// int numFramesToGet = numFrames;
|
|
||||||
// long frameInterval = (myDurationMillis - INTER_FRAME_PERIOD_MS) / numFrames;
|
|
||||||
// if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
|
|
||||||
// numFramesToGet = 1;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// final Object frameLock = new Object();
|
|
||||||
// BufferedImage frame;
|
|
||||||
// final int width = (int) panel.getSize().getWidth();
|
|
||||||
// final int height = (int) panel.getSize().getHeight();
|
|
||||||
// // for each timeStamp, grap a frame
|
|
||||||
// for (int i = 0; i < numFramesToGet; ++i) {
|
|
||||||
// frame = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
|
||||||
// logger.log(Level.INFO, "Grabbing a frame...");
|
|
||||||
// final long timeStamp = i * frameInterval + INTER_FRAME_PERIOD_MS;
|
|
||||||
//
|
|
||||||
// // Platform.runLater(new Runnable() {
|
|
||||||
// // @Override
|
|
||||||
// // public void run() {
|
|
||||||
// // synchronized (frameLock) {
|
|
||||||
// logger.log(Level.INFO, "seeking.");
|
|
||||||
// mediaPlayer.seek(new Duration(timeStamp));
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// // });
|
|
||||||
//
|
|
||||||
// synchronized (frameLock) {
|
|
||||||
// panel.paint(frame.createGraphics());
|
|
||||||
// logger.log(Level.INFO, "Adding image to frames");
|
|
||||||
// }
|
|
||||||
// frames.add(new VideoFrame(frame, timeStamp));
|
|
||||||
// }
|
|
||||||
// return frames;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getExtensions() {
|
public String[] getExtensions() {
|
||||||
return EXTENSIONS;
|
return EXTENSIONS.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-15 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.corecomponents;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -41,7 +42,6 @@ import javax.swing.JSlider;
|
|||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import org.gstreamer.ClockTime;
|
import org.gstreamer.ClockTime;
|
||||||
import org.gstreamer.Gst;
|
import org.gstreamer.Gst;
|
||||||
import org.gstreamer.GstException;
|
import org.gstreamer.GstException;
|
||||||
@ -52,23 +52,22 @@ import org.gstreamer.elements.RGBDataSink;
|
|||||||
import org.gstreamer.swing.VideoComponent;
|
import org.gstreamer.swing.VideoComponent;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
import org.openide.util.Cancellable;
|
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
import org.openide.util.lookup.ServiceProviders;
|
import org.openide.util.lookup.ServiceProviders;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.VideoUtils;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
|
|
||||||
@ServiceProviders(value = {
|
@ServiceProviders(value = {
|
||||||
@ServiceProvider(service = FrameCapture.class)
|
@ServiceProvider(service = FrameCapture.class)
|
||||||
})
|
})
|
||||||
public class GstVideoPanel extends MediaViewVideoPanel {
|
public class GstVideoPanel extends MediaViewVideoPanel {
|
||||||
|
|
||||||
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
|
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
|
||||||
private static final List<String> MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
|
private static final List<String> MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
|
||||||
|
|
||||||
@ -129,14 +128,12 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||||
progressSlider.setValue(0);
|
progressSlider.setValue(0);
|
||||||
|
|
||||||
progressSlider.addChangeListener(new ChangeListener() {
|
progressSlider.addChangeListener((ChangeEvent e) -> {
|
||||||
/**
|
/**
|
||||||
* Should always try to synchronize any call to
|
* Should always try to synchronize any call to
|
||||||
* progressSlider.setValue() to avoid a different thread
|
* progressSlider.setValue() to avoid a different thread
|
||||||
* changing playbin while stateChanged() is processing
|
* changing playbin while stateChanged() is processing
|
||||||
*/
|
*/
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
int time = progressSlider.getValue();
|
int time = progressSlider.getValue();
|
||||||
synchronized (playbinLock) {
|
synchronized (playbinLock) {
|
||||||
if (gstPlaybin2 != null && !autoTracking) {
|
if (gstPlaybin2 != null && !autoTracking) {
|
||||||
@ -154,7 +151,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
gstPlaybin2.setState(orig);
|
gstPlaybin2.setState(orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +203,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
pauseButton.setEnabled(true);
|
pauseButton.setEnabled(true);
|
||||||
progressSlider.setEnabled(true);
|
progressSlider.setEnabled(true);
|
||||||
|
|
||||||
java.io.File ioFile = getJFile(file);
|
java.io.File ioFile = VideoUtils.getTempVideoFile(file);
|
||||||
|
|
||||||
gstVideoComponent = new VideoComponent();
|
gstVideoComponent = new VideoComponent();
|
||||||
synchronized (playbinLock) {
|
synchronized (playbinLock) {
|
||||||
@ -219,7 +215,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
|
|
||||||
videoPanel.removeAll();
|
videoPanel.removeAll();
|
||||||
|
|
||||||
|
|
||||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||||
videoPanel.add(gstVideoComponent);
|
videoPanel.add(gstVideoComponent);
|
||||||
|
|
||||||
@ -239,11 +234,8 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
void reset() {
|
void reset() {
|
||||||
|
|
||||||
// reset the progress label text on the event dispatch thread
|
// reset the progress label text on the event dispatch thread
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
progressLabel.setText("");
|
progressLabel.setText("");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isInited()) {
|
if (!isInited()) {
|
||||||
@ -281,27 +273,13 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
currentFile = null;
|
currentFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private java.io.File getJFile(AbstractFile file) {
|
|
||||||
// Get the temp folder path of the case
|
|
||||||
String tempPath = Case.getCurrentCase().getTempDirectory();
|
|
||||||
String name = file.getName();
|
|
||||||
int extStart = name.lastIndexOf(".");
|
|
||||||
String ext = "";
|
|
||||||
if (extStart != -1) {
|
|
||||||
ext = name.substring(extStart, name.length()).toLowerCase();
|
|
||||||
}
|
|
||||||
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
|
|
||||||
|
|
||||||
java.io.File tempFile = new java.io.File(tempPath);
|
|
||||||
return tempFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param file a video file from which to capture frames
|
* @param file a video file from which to capture frames
|
||||||
* @param numFrames the number of frames to capture. These frames will be
|
* @param numFrames the number of frames to capture. These frames will be
|
||||||
* captured at successive intervals given by durationOfVideo/numFrames. If
|
* captured at successive intervals given by durationOfVideo/numFrames. If
|
||||||
* this frame interval is less than MIN_FRAME_INTERVAL_MILLIS, then only one
|
* this frame interval is less than MIN_FRAME_INTERVAL_MILLIS, then only one
|
||||||
* frame will be captured and returned.
|
* frame will be captured and returned.
|
||||||
|
*
|
||||||
* @return a List of VideoFrames representing the captured frames.
|
* @return a List of VideoFrames representing the captured frames.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -384,7 +362,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wait for FrameCaptureRGBListener to finish
|
// wait for FrameCaptureRGBListener to finish
|
||||||
synchronized(lock) {
|
synchronized (lock) {
|
||||||
try {
|
try {
|
||||||
lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
|
lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@ -557,9 +535,10 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (state.equals(State.READY)) {
|
} else if (state.equals(State.READY)) {
|
||||||
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
final File tempVideoFile = VideoUtils.getTempVideoFile(currentFile);
|
||||||
em.execute();
|
|
||||||
em.getExtractedBytes();
|
new ExtractMedia(currentFile, tempVideoFile).execute();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||||
@ -575,11 +554,10 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
|
|
||||||
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
||||||
|
|
||||||
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
private final String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
||||||
private long millisElapsed = 0;
|
private long millisElapsed = 0;
|
||||||
private final long INTER_FRAME_PERIOD_MS = 20;
|
private final long INTER_FRAME_PERIOD_MS = 20;
|
||||||
private final long END_TIME_MARGIN_MS = 50;
|
private final long END_TIME_MARGIN_MS = 50;
|
||||||
private boolean hadError = false;
|
|
||||||
|
|
||||||
private boolean isPlayBinReady() {
|
private boolean isPlayBinReady() {
|
||||||
synchronized (playbinLock) {
|
synchronized (playbinLock) {
|
||||||
@ -626,8 +604,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
// enable the slider
|
// enable the slider
|
||||||
progressSlider.setEnabled(true);
|
progressSlider.setEnabled(true);
|
||||||
|
|
||||||
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
|
ClockTime pos;
|
||||||
ClockTime pos = null;
|
|
||||||
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
||||||
|
|
||||||
synchronized (playbinLock) {
|
synchronized (playbinLock) {
|
||||||
@ -637,11 +614,11 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
|
|
||||||
// pick out the elapsed hours, minutes, seconds
|
// pick out the elapsed hours, minutes, seconds
|
||||||
long secondsElapsed = millisElapsed / 1000;
|
long secondsElapsed = millisElapsed / 1000;
|
||||||
elapsedHours = (int) secondsElapsed / 3600;
|
int elapsedHours = (int) secondsElapsed / 3600;
|
||||||
secondsElapsed -= elapsedHours * 3600;
|
secondsElapsed -= elapsedHours * 3600;
|
||||||
elapsedMinutes = (int) secondsElapsed / 60;
|
int elapsedMinutes = (int) secondsElapsed / 60;
|
||||||
secondsElapsed -= elapsedMinutes * 60;
|
secondsElapsed -= elapsedMinutes * 60;
|
||||||
elapsedSeconds = (int) secondsElapsed;
|
int elapsedSeconds = (int) secondsElapsed;
|
||||||
|
|
||||||
String durationStr = String.format(durationFormat,
|
String durationStr = String.format(durationFormat,
|
||||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
elapsedHours, elapsedMinutes, elapsedSeconds,
|
||||||
@ -667,7 +644,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void done() {
|
protected void done() {
|
||||||
// see if any exceptions were thrown
|
// see if any exceptions were thrown
|
||||||
@ -677,53 +653,38 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
logger.log(Level.WARNING, "Error updating video progress: " + ex.getMessage()); //NON-NLS
|
logger.log(Level.WARNING, "Error updating video progress: " + ex.getMessage()); //NON-NLS
|
||||||
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr",
|
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr",
|
||||||
ex.getMessage()));
|
ex.getMessage()));
|
||||||
|
} // catch and ignore if we were cancelled
|
||||||
|
catch (java.util.concurrent.CancellationException ex) {
|
||||||
}
|
}
|
||||||
// catch and ignore if we were cancelled
|
|
||||||
catch (java.util.concurrent.CancellationException ex ) { }
|
|
||||||
}
|
}
|
||||||
} //end class progress worker
|
} //end class progress worker
|
||||||
|
|
||||||
/* Thread that extracts and plays a file */
|
/* Thread that extracts and plays a file */
|
||||||
private class ExtractMedia extends SwingWorker<Object, Void> {
|
private class ExtractMedia extends SwingWorker<Long, Void> {
|
||||||
|
|
||||||
private ProgressHandle progress;
|
private ProgressHandle progress;
|
||||||
boolean success = false;
|
private final AbstractFile sourceFile;
|
||||||
private AbstractFile sFile;
|
private final java.io.File tempFile;
|
||||||
private java.io.File jFile;
|
|
||||||
private String duration;
|
|
||||||
private String position;
|
|
||||||
private long extractedBytes;
|
|
||||||
|
|
||||||
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
ExtractMedia(AbstractFile sFile, java.io.File jFile) {
|
||||||
this.sFile = sFile;
|
this.sourceFile = sFile;
|
||||||
this.jFile = jFile;
|
this.tempFile = jFile;
|
||||||
}
|
|
||||||
|
|
||||||
public long getExtractedBytes() {
|
|
||||||
return extractedBytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object doInBackground() throws Exception {
|
protected Long doInBackground() throws Exception {
|
||||||
success = false;
|
if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) {
|
||||||
progress = ProgressHandleFactory.createHandle(
|
progress = ProgressHandleFactory.createHandle(NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> ExtractMedia.this.cancel(true));
|
||||||
NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sFile.getName()),
|
|
||||||
new Cancellable() {
|
|
||||||
@Override
|
|
||||||
public boolean cancel() {
|
|
||||||
return ExtractMedia.this.cancel(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
|
||||||
progress.start();
|
progress.start(100);
|
||||||
progress.switchToDeterminate(100);
|
|
||||||
try {
|
try {
|
||||||
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
|
return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
||||||
|
return 0L;
|
||||||
}
|
}
|
||||||
success = true;
|
}
|
||||||
return null;
|
return 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clean up or start the worker threads */
|
/* clean up or start the worker threads */
|
||||||
@ -738,7 +699,9 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
||||||
} finally {
|
} finally {
|
||||||
|
if (progress != null) {
|
||||||
progress.finish();
|
progress.finish();
|
||||||
|
}
|
||||||
if (!this.isCancelled()) {
|
if (!this.isCancelled()) {
|
||||||
playMedia();
|
playMedia();
|
||||||
}
|
}
|
||||||
@ -746,11 +709,11 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void playMedia() {
|
void playMedia() {
|
||||||
if (jFile == null || !jFile.exists()) {
|
if (tempFile == null || !tempFile.exists()) {
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr"));
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClockTime dur = null;
|
ClockTime dur;
|
||||||
synchronized (playbinLock) {
|
synchronized (playbinLock) {
|
||||||
// must play, then pause and get state to get duration.
|
// must play, then pause and get state to get duration.
|
||||||
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
|
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
|
||||||
@ -766,7 +729,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
gstPlaybin2.getState();
|
gstPlaybin2.getState();
|
||||||
dur = gstPlaybin2.queryDuration();
|
dur = gstPlaybin2.queryDuration();
|
||||||
}
|
}
|
||||||
duration = dur.toString();
|
|
||||||
durationMillis = dur.toMillis();
|
durationMillis = dur.toMillis();
|
||||||
|
|
||||||
// pick out the total hours, minutes, seconds
|
// pick out the total hours, minutes, seconds
|
||||||
@ -777,9 +739,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
durationSeconds -= totalMinutes * 60;
|
durationSeconds -= totalMinutes * 60;
|
||||||
totalSeconds = (int) durationSeconds;
|
totalSeconds = (int) durationSeconds;
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
progressSlider.setMaximum((int) durationMillis);
|
progressSlider.setMaximum((int) durationMillis);
|
||||||
progressSlider.setMinimum(0);
|
progressSlider.setMinimum(0);
|
||||||
|
|
||||||
@ -792,18 +752,18 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
|||||||
pauseButton.setText("||");
|
pauseButton.setText("||");
|
||||||
videoProgressWorker = new VideoProgressWorker();
|
videoProgressWorker = new VideoProgressWorker();
|
||||||
videoProgressWorker.execute();
|
videoProgressWorker.execute();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getExtensions() {
|
public String[] getExtensions() {
|
||||||
return EXTENSIONS;
|
return EXTENSIONS.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getMimeTypes() {
|
public List<String> getMimeTypes() {
|
||||||
return MIMETYPES;
|
return MIMETYPES;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
|
|||||||
* Image viewer part of the Media View layered pane. Uses JavaFX to display the
|
* Image viewer part of the Media View layered pane. Uses JavaFX to display the
|
||||||
* image.
|
* image.
|
||||||
*/
|
*/
|
||||||
public class MediaViewImagePanel extends JPanel {
|
public class MediaViewImagePanel extends JPanel implements DataContentViewerMedia.MediaViewPanel {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName());
|
||||||
|
|
||||||
@ -64,20 +64,23 @@ public class MediaViewImagePanel extends JPanel {
|
|||||||
private ImageView fxImageView;
|
private ImageView fxImageView;
|
||||||
private BorderPane borderpane;
|
private BorderPane borderpane;
|
||||||
|
|
||||||
private final Label errorLabel = new Label("Could not load image file into media view.");
|
private final Label errorLabel = new Label("Could not load file into media view.");
|
||||||
private final Label tooLargeLabel = new Label("Could not load image file into media view (too large).");
|
private final Label tooLargeLabel = new Label("Could not load file into media view (too large).");
|
||||||
private final Label noReaderLabel = new Label("Image reader not found for file.");
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
ImageIO.scanForPlugins();
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* mime types we should be able to display. if the mimetype is unknown we
|
* mime types we should be able to display. if the mimetype is unknown we
|
||||||
* will fall back on extension and jpg/png header
|
* will fall back on extension and jpg/png header
|
||||||
*/
|
*/
|
||||||
static private final SortedSet<String> supportedMimes = ImageUtils.getSupportedMimeTypes();
|
static private final SortedSet<String> supportedMimes = ImageUtils.getSupportedImageMimeTypes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* extensions we should be able to display
|
* extensions we should be able to display
|
||||||
*/
|
*/
|
||||||
static private final List<String> supportedExtensions = ImageUtils.getSupportedExtensions().stream()
|
static private final List<String> supportedExtensions = ImageUtils.getSupportedImageExtensions().stream()
|
||||||
.map("."::concat)
|
.map("."::concat)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
@ -157,7 +160,7 @@ public class MediaViewImagePanel extends JPanel {
|
|||||||
BufferedImage bufferedImage = ImageIO.read(inputStream);
|
BufferedImage bufferedImage = ImageIO.read(inputStream);
|
||||||
if (bufferedImage == null) {
|
if (bufferedImage == null) {
|
||||||
LOGGER.log(Level.WARNING, "Image reader not found for file: {0}", file.getName()); //NON-NLS
|
LOGGER.log(Level.WARNING, "Image reader not found for file: {0}", file.getName()); //NON-NLS
|
||||||
borderpane.setCenter(noReaderLabel);
|
borderpane.setCenter(errorLabel);
|
||||||
} else {
|
} else {
|
||||||
Image fxImage = SwingFXUtils.toFXImage(bufferedImage, null);
|
Image fxImage = SwingFXUtils.toFXImage(bufferedImage, null);
|
||||||
if (fxImage.isError()) {
|
if (fxImage.isError()) {
|
||||||
@ -191,10 +194,21 @@ public class MediaViewImagePanel extends JPanel {
|
|||||||
/**
|
/**
|
||||||
* @return supported mime types
|
* @return supported mime types
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public List<String> getMimeTypes() {
|
public List<String> getMimeTypes() {
|
||||||
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
|
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns supported extensions (each starting with .)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<String> getExtensionsList() {
|
||||||
|
return getExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns supported extensions (each starting with .)
|
* returns supported extensions (each starting with .)
|
||||||
*
|
*
|
||||||
@ -204,6 +218,12 @@ public class MediaViewImagePanel extends JPanel {
|
|||||||
return Collections.unmodifiableList(supportedExtensions);
|
return Collections.unmodifiableList(supportedExtensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported(AbstractFile file) {
|
||||||
|
return DataContentViewerMedia.MediaViewPanel.super.isSupported(file)
|
||||||
|
|| ImageUtils.hasImageFileHeader(file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* This method is called from within the constructor to initialize the form.
|
||||||
* WARNING: Do NOT modify this code. The content of this method is always
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
@ -218,4 +238,5 @@ public class MediaViewImagePanel extends JPanel {
|
|||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,16 +21,20 @@ package org.sleuthkit.autopsy.corecomponents;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Video viewer part of the Media View layered pane.
|
* Video viewer part of the Media View layered pane.
|
||||||
* Uses different engines depending on platform.
|
* Uses different engines depending on platform.
|
||||||
*/
|
*/
|
||||||
public abstract class MediaViewVideoPanel extends JPanel implements FrameCapture {
|
public abstract class MediaViewVideoPanel extends JPanel implements FrameCapture, DataContentViewerMedia.MediaViewPanel {
|
||||||
|
|
||||||
|
private static final Set<String> AUDIO_EXTENSIONS = new TreeSet<>(Arrays.asList(".mp3", ".wav", ".wma")); //NON-NLS
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
||||||
|
|
||||||
@ -107,10 +111,30 @@ public abstract class MediaViewVideoPanel extends JPanel implements FrameCapture
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the extensions supported by this video panel.
|
* Return the extensions supported by this video panel.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
abstract public String[] getExtensions();
|
abstract public String[] getExtensions();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the MimeTypes supported by this video panel.
|
* Return the MimeTypes supported by this video panel.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
abstract public List<String> getMimeTypes();
|
abstract public List<String> getMimeTypes();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getExtensionsList() {
|
||||||
|
return Arrays.asList(getExtensions());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported(AbstractFile file) {
|
||||||
|
String extension = file.getNameExtension();
|
||||||
|
//TODO: is this what we want, to require both extension and mimetype support?
|
||||||
|
if (AUDIO_EXTENSIONS.contains("." + extension) || getExtensionsList().contains("." + extension)) {
|
||||||
|
return DataContentViewerMedia.MediaViewPanel.super.isSupported(file); //To change body of generated methods, choose Tools | Templates.
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2011 Basis Technology Corp.
|
* Copyright 2011-15 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -43,7 +43,7 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
|
|
||||||
static final int IMAGES_PER_PAGE = 200;
|
static final int IMAGES_PER_PAGE = 200;
|
||||||
private Node parent;
|
private Node parent;
|
||||||
private final HashMap<Integer, List<Node>> pages = new HashMap<Integer, List<Node>>();
|
private final HashMap<Integer, List<Node>> pages = new HashMap<>();
|
||||||
private int totalImages = 0;
|
private int totalImages = 0;
|
||||||
private int totalPages = 0;
|
private int totalPages = 0;
|
||||||
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
||||||
@ -57,13 +57,9 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
|
|
||||||
this.parent = arg;
|
this.parent = arg;
|
||||||
this.iconSize = iconSize;
|
this.iconSize = iconSize;
|
||||||
//
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
|
||||||
// protected Node copyNode(Node arg0) {
|
|
||||||
// return new ThumbnailViewNode(arg0);
|
|
||||||
// }
|
|
||||||
@Override
|
@Override
|
||||||
protected void addNotify() {
|
protected void addNotify() {
|
||||||
super.addNotify();
|
super.addNotify();
|
||||||
@ -85,7 +81,7 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
//TODO when lazy loading of original nodes is fixed
|
//TODO when lazy loading of original nodes is fixed
|
||||||
//we should be asking the datamodel for the children instead
|
//we should be asking the datamodel for the children instead
|
||||||
//and not counting the children nodes (which might not be preloaded at this point)
|
//and not counting the children nodes (which might not be preloaded at this point)
|
||||||
final List<Node> suppContent = new ArrayList<Node>();
|
final List<Node> suppContent = new ArrayList<>();
|
||||||
for (Node child : parent.getChildren().getNodes()) {
|
for (Node child : parent.getChildren().getNodes()) {
|
||||||
if (isSupported(child)) {
|
if (isSupported(child)) {
|
||||||
++totalImages;
|
++totalImages;
|
||||||
@ -122,8 +118,6 @@ class ThumbnailViewChildren extends Children.Keys<Integer> {
|
|||||||
pageNums[i] = i + 1;
|
pageNums[i] = i + 1;
|
||||||
}
|
}
|
||||||
setKeys(pageNums);
|
setKeys(pageNums);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,9 +19,17 @@
|
|||||||
package org.sleuthkit.autopsy.corecomponents;
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
import org.openide.nodes.FilterNode;
|
import org.openide.nodes.FilterNode;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
|
|
||||||
@ -31,9 +39,13 @@ import org.sleuthkit.datamodel.Content;
|
|||||||
*/
|
*/
|
||||||
class ThumbnailViewNode extends FilterNode {
|
class ThumbnailViewNode extends FilterNode {
|
||||||
|
|
||||||
|
static private final Image waitingIcon = Toolkit.getDefaultToolkit().createImage(ThumbnailViewNode.class.getResource("/org/sleuthkit/autopsy/images/working_spinner.gif"));
|
||||||
|
|
||||||
private SoftReference<Image> iconCache = null;
|
private SoftReference<Image> iconCache = null;
|
||||||
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
private int iconSize = ImageUtils.ICON_SIZE_MEDIUM;
|
||||||
//private final BufferedImage defaultIconBI;
|
|
||||||
|
private SwingWorker<Image, Object> swingWorker;
|
||||||
|
private Timer timer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the constructor
|
* the constructor
|
||||||
@ -60,24 +72,55 @@ class ThumbnailViewNode extends FilterNode {
|
|||||||
icon = iconCache.get();
|
icon = iconCache.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (icon == null) {
|
if (icon != null) {
|
||||||
Content content = this.getLookup().lookup(Content.class);
|
|
||||||
|
|
||||||
if (content != null) {
|
|
||||||
icon = ImageUtils.getThumbnail(content, iconSize);
|
|
||||||
} else {
|
|
||||||
icon = ImageUtils.getDefaultThumbnail();
|
|
||||||
}
|
|
||||||
|
|
||||||
iconCache = new SoftReference<>(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
return icon;
|
return icon;
|
||||||
|
} else {
|
||||||
|
final Content content = this.getLookup().lookup(Content.class);
|
||||||
|
if (content == null) {
|
||||||
|
return ImageUtils.getDefaultThumbnail();
|
||||||
|
}
|
||||||
|
if (swingWorker == null || swingWorker.isDone()) {
|
||||||
|
swingWorker = new SwingWorker<Image, Object>() {
|
||||||
|
final private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("generating thumbnail for video file " + content.getName());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Image doInBackground() throws Exception {
|
||||||
|
progressHandle.start();
|
||||||
|
return ImageUtils.getThumbnail(content, iconSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
super.done();
|
||||||
|
try {
|
||||||
|
iconCache = new SoftReference<>(super.get());
|
||||||
|
progressHandle.finish();
|
||||||
|
fireIconChange();
|
||||||
|
if (timer != null) {
|
||||||
|
timer.stop();
|
||||||
|
timer = null;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
Exceptions.printStackTrace(ex);
|
||||||
|
}
|
||||||
|
swingWorker = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
swingWorker.execute();
|
||||||
|
}
|
||||||
|
if (timer == null) {
|
||||||
|
timer = new Timer(100, (ActionEvent e) -> {
|
||||||
|
fireIconChange();
|
||||||
|
});
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
return waitingIcon;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIconSize(int iconSize) {
|
public void setIconSize(int iconSize) {
|
||||||
this.iconSize = iconSize;
|
this.iconSize = iconSize;
|
||||||
iconCache = null;
|
iconCache = null;
|
||||||
|
swingWorker = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -45,6 +46,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||||
|
import org.opencv.core.Core;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||||
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
|
||||||
@ -55,6 +57,7 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* Utilities for working with Images and creating thumbnails. Reuses thumbnails
|
* Utilities for working with Images and creating thumbnails. Reuses thumbnails
|
||||||
* by storing them in the case's cache directory.
|
* by storing them in the case's cache directory.
|
||||||
*/
|
*/
|
||||||
@ -69,46 +72,113 @@ public class ImageUtils {
|
|||||||
public static final int ICON_SIZE_MEDIUM = 100;
|
public static final int ICON_SIZE_MEDIUM = 100;
|
||||||
public static final int ICON_SIZE_LARGE = 200;
|
public static final int ICON_SIZE_LARGE = 200;
|
||||||
|
|
||||||
private static final Image DEFAULT_THUMBNAIL;
|
private static final Logger logger = LOGGER;
|
||||||
|
private static final BufferedImage DEFAULT_THUMBNAIL;
|
||||||
|
private static final TreeSet<String> SUPPORTED_MIME_TYPES = new TreeSet<>();
|
||||||
|
private static final List<String> SUPPORTED_EXTENSIONS = new ArrayList<>();
|
||||||
|
private static final List<String> SUPPORTED_IMAGE_EXTENSIONS;
|
||||||
|
private static final List<String> SUPPORTED_VIDEO_EXTENSIONS
|
||||||
|
= Arrays.asList("mov", "m4v", "flv", "mp4", "3gp", "avi", "mpg",
|
||||||
|
"mpeg", "asf", "divx", "rm", "moov", "wmv", "vob", "dat",
|
||||||
|
"m1v", "m2v", "m4v", "mkv", "mpe", "yop", "vqa", "xmv",
|
||||||
|
"mve", "wtv", "webm", "vivo", "vc1", "seq", "thp", "san",
|
||||||
|
"mjpg", "smk", "vmd", "sol", "cpk", "sdp", "sbg", "rtsp",
|
||||||
|
"rpl", "rl2", "r3d", "mlp", "mjpeg", "hevc", "h265", "265",
|
||||||
|
"h264", "h263", "h261", "drc", "avs", "pva", "pmp", "ogg",
|
||||||
|
"nut", "nuv", "nsv", "mxf", "mtv", "mvi", "mxg", "lxf",
|
||||||
|
"lvf", "ivf", "mve", "cin", "hnm", "gxf", "fli", "flc",
|
||||||
|
"flx", "ffm", "wve", "uv2", "dxa", "dv", "cdxl", "cdg",
|
||||||
|
"bfi", "jv", "bik", "vid", "vb", "son", "avs", "paf", "mm",
|
||||||
|
"flm", "tmv", "4xm"); //NON-NLS
|
||||||
|
private static final TreeSet<String> SUPPORTED_IMAGE_MIME_TYPES;
|
||||||
|
private static final List<String> SUPPORTED_VIDEO_MIME_TYPES
|
||||||
|
= Arrays.asList("application/x-shockwave-flash", "video/x-m4v", "video/quicktime", "video/avi", "video/msvideo", "video/x-msvideo",
|
||||||
|
"video/mp4", "video/x-ms-wmv", "video/mpeg", "video/asf"); //NON-NLS
|
||||||
|
private static final boolean openCVLoaded;
|
||||||
|
|
||||||
/** initialized lazily */
|
static {
|
||||||
|
ImageIO.scanForPlugins();
|
||||||
|
BufferedImage tempImage;
|
||||||
|
try {
|
||||||
|
tempImage = ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/file-icon.png"));//NON-NLS
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to load default icon.", ex);
|
||||||
|
tempImage = null;
|
||||||
|
}
|
||||||
|
DEFAULT_THUMBNAIL = tempImage;
|
||||||
|
|
||||||
|
//load opencv libraries
|
||||||
|
boolean openCVLoadedTemp;
|
||||||
|
try {
|
||||||
|
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
|
||||||
|
if (System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64")) {
|
||||||
|
System.loadLibrary("opencv_ffmpeg248_64");
|
||||||
|
} else {
|
||||||
|
System.loadLibrary("opencv_ffmpeg248");
|
||||||
|
}
|
||||||
|
|
||||||
|
openCVLoadedTemp = true;
|
||||||
|
} catch (UnsatisfiedLinkError e) {
|
||||||
|
openCVLoadedTemp = false;
|
||||||
|
LOGGER.log(Level.SEVERE, "OpenCV Native code library failed to load", e);
|
||||||
|
//TODO: show warning bubble
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
openCVLoaded = openCVLoadedTemp;
|
||||||
|
SUPPORTED_IMAGE_EXTENSIONS = Arrays.asList(ImageIO.getReaderFileSuffixes());
|
||||||
|
|
||||||
|
SUPPORTED_EXTENSIONS.addAll(SUPPORTED_IMAGE_EXTENSIONS);
|
||||||
|
SUPPORTED_EXTENSIONS.addAll(SUPPORTED_VIDEO_EXTENSIONS);
|
||||||
|
|
||||||
|
SUPPORTED_IMAGE_MIME_TYPES = new TreeSet<>(Arrays.asList(ImageIO.getReaderMIMETypes()));
|
||||||
|
/* special cases and variants that we support, but don't get registered
|
||||||
|
* with ImageIO automatically */
|
||||||
|
SUPPORTED_IMAGE_MIME_TYPES.addAll(Arrays.asList(
|
||||||
|
"image/x-rgb",
|
||||||
|
"image/x-ms-bmp",
|
||||||
|
"application/x-123"));
|
||||||
|
SUPPORTED_MIME_TYPES.addAll(SUPPORTED_IMAGE_MIME_TYPES);
|
||||||
|
SUPPORTED_MIME_TYPES.addAll(SUPPORTED_VIDEO_MIME_TYPES);
|
||||||
|
|
||||||
|
//this is rarely usefull
|
||||||
|
SUPPORTED_MIME_TYPES.removeIf("application/octet-stream"::equals);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default Icon, which is the icon for a file.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* /** initialized lazily */
|
||||||
private static FileTypeDetector fileTypeDetector;
|
private static FileTypeDetector fileTypeDetector;
|
||||||
|
|
||||||
private static final List<String> SUPPORTED_EXTENSIONS;
|
|
||||||
private static final TreeSet<String> SUPPORTED_MIME_TYPES;
|
|
||||||
|
|
||||||
/** thread that saves generated thumbnails to disk in the background */
|
/** thread that saves generated thumbnails to disk in the background */
|
||||||
private static final Executor imageSaver
|
private static final Executor imageSaver
|
||||||
= Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
|
= Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
|
||||||
.namingPattern("icon saver-%d").build());
|
.namingPattern("icon saver-%d").build());
|
||||||
|
|
||||||
static {
|
|
||||||
ImageIO.scanForPlugins();
|
|
||||||
BufferedImage defaultIcon = null;
|
|
||||||
try {
|
|
||||||
defaultIcon = ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/file-icon.png"));//NON-NLS
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LOGGER.log(Level.WARNING, "Failed to read default thumbnail.");
|
|
||||||
}
|
|
||||||
DEFAULT_THUMBNAIL = defaultIcon;
|
|
||||||
|
|
||||||
SUPPORTED_EXTENSIONS = Arrays.asList(ImageIO.getReaderFileSuffixes());
|
|
||||||
SUPPORTED_MIME_TYPES = new TreeSet<>(Arrays.asList(ImageIO.getReaderMIMETypes()));
|
|
||||||
|
|
||||||
/* special cases and variants that we support, but don't get registered
|
|
||||||
* with ImageIO automatically */
|
|
||||||
SUPPORTED_MIME_TYPES.addAll(Arrays.asList(
|
|
||||||
"image/x-rgb",
|
|
||||||
"image/x-ms-bmp",
|
|
||||||
"application/x-123"));
|
|
||||||
|
|
||||||
//this is rarely usefull
|
|
||||||
SUPPORTED_MIME_TYPES.removeIf("application/octet-stream"::equals);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ImageUtils() {
|
private ImageUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<String> getSupportedImageExtensions() {
|
||||||
|
return Collections.unmodifiableList(SUPPORTED_IMAGE_EXTENSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getSupportedVideoExtensions() {
|
||||||
|
return SUPPORTED_VIDEO_EXTENSIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SortedSet<String> getSupportedImageMimeTypes() {
|
||||||
|
return Collections.unmodifiableSortedSet(SUPPORTED_IMAGE_MIME_TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getSupportedVideoMimeTypes() {
|
||||||
|
return SUPPORTED_VIDEO_MIME_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
public static List<String> getSupportedExtensions() {
|
public static List<String> getSupportedExtensions() {
|
||||||
return Collections.unmodifiableList(SUPPORTED_EXTENSIONS);
|
return Collections.unmodifiableList(SUPPORTED_EXTENSIONS);
|
||||||
}
|
}
|
||||||
@ -142,17 +212,22 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can a thumbnail be generated for the file?
|
* Can a thumbnail be generated for the content?
|
||||||
*
|
*
|
||||||
* @param file
|
* @param content
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static boolean thumbnailSupported(AbstractFile file) {
|
public static boolean thumbnailSupported(Content content) {
|
||||||
if (file.getSize() == 0) {
|
|
||||||
|
if (content.getSize() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!(content instanceof AbstractFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AbstractFile file = (AbstractFile) content;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String mimeType = getFileTypeDetector().getFileType(file);
|
String mimeType = getFileTypeDetector().getFileType(file);
|
||||||
@ -162,7 +237,7 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
} catch (FileTypeDetector.FileTypeDetectorInitException | TskCoreException ex) {
|
||||||
LOGGER.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
LOGGER.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
|
||||||
if (!SUPPORTED_MIME_TYPES.isEmpty()) {
|
|
||||||
AbstractFile.MimeMatchEnum mimeMatch = file.isMimeType(SUPPORTED_MIME_TYPES);
|
AbstractFile.MimeMatchEnum mimeMatch = file.isMimeType(SUPPORTED_MIME_TYPES);
|
||||||
if (mimeMatch == AbstractFile.MimeMatchEnum.TRUE) {
|
if (mimeMatch == AbstractFile.MimeMatchEnum.TRUE) {
|
||||||
return true;
|
return true;
|
||||||
@ -170,7 +245,6 @@ public class ImageUtils {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// if we have an extension, check it
|
// if we have an extension, check it
|
||||||
final String extension = file.getNameExtension();
|
final String extension = file.getNameExtension();
|
||||||
@ -182,22 +256,6 @@ public class ImageUtils {
|
|||||||
return isJpegFileHeader(file) || isPngFileHeader(file);
|
return isJpegFileHeader(file) || isPngFileHeader(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Can a thumbnail be generated for the content?
|
|
||||||
*
|
|
||||||
* @param content
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*
|
|
||||||
* @deprecated use {@link #thumbnailSupported(org.sleuthkit.datamodel.AbstractFile) instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static boolean thumbnailSupported(Content content) {
|
|
||||||
return (content instanceof AbstractFile)
|
|
||||||
? thumbnailSupported((AbstractFile) content)
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a lazily instatiated FileTypeDetector
|
* returns a lazily instatiated FileTypeDetector
|
||||||
*
|
*
|
||||||
@ -220,11 +278,13 @@ public class ImageUtils {
|
|||||||
* @param content
|
* @param content
|
||||||
* @param iconSize
|
* @param iconSize
|
||||||
*
|
*
|
||||||
|
*
|
||||||
* @return a thumbnail for the given image or a default one if there was a
|
* @return a thumbnail for the given image or a default one if there was a
|
||||||
* problem making a thumbnail.
|
* problem making a thumbnail.
|
||||||
*
|
*
|
||||||
* @deprecated use {@link #getThumbnail(org.sleuthkit.datamodel.Content, int)
|
* @deprecated use {@link #getThumbnail(org.sleuthkit.datamodel.Content, int)
|
||||||
* } instead.
|
* } instead.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -253,8 +313,8 @@ public class ImageUtils {
|
|||||||
} else {
|
} else {
|
||||||
return thumbnail;
|
return thumbnail;
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (Exception ex) {
|
||||||
LOGGER.log(Level.WARNING, "Error while reading image.", ex); //NON-NLS
|
LOGGER.log(Level.WARNING, "Error while reading image: " + content.getName(), ex); //NON-NLS
|
||||||
return generateAndSaveThumbnail(content, iconSize, cacheFile);
|
return generateAndSaveThumbnail(content, iconSize, cacheFile);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -274,6 +334,7 @@ public class ImageUtils {
|
|||||||
*
|
*
|
||||||
* @deprecated use {@link #getCachedThumbnailFile(org.sleuthkit.datamodel.Content, int)
|
* @deprecated use {@link #getCachedThumbnailFile(org.sleuthkit.datamodel.Content, int)
|
||||||
* } instead.
|
* } instead.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@ -283,6 +344,7 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* Get a thumbnail of a specified size. Generates the image if it is
|
* Get a thumbnail of a specified size. Generates the image if it is
|
||||||
* not already cached.
|
* not already cached.
|
||||||
*
|
*
|
||||||
@ -306,15 +368,30 @@ public class ImageUtils {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*
|
*
|
||||||
* @deprecated this should never have been public.
|
*
|
||||||
|
* @deprecated use {@link #getCachedThumbnailLocation(long) } instead
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
|
||||||
public static File getFile(long id) {
|
public static File getFile(long id) {
|
||||||
return getCachedThumbnailLocation(id);
|
return getCachedThumbnailLocation(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static File getCachedThumbnailLocation(long id) {
|
/**
|
||||||
return Paths.get(Case.getCurrentCase().getCacheDirectory(), "thumbnails", id + ".png").toFile();
|
* Get a file object for where the cached icon should exist. The returned
|
||||||
|
* file may not exist.
|
||||||
|
*
|
||||||
|
* @param fileID
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static File getCachedThumbnailLocation(long fileID) {
|
||||||
|
return Paths.get(Case.getCurrentCase().getCacheDirectory(), "thumbnails", fileID + ".png").toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasImageFileHeader(AbstractFile file) {
|
||||||
|
return isJpegFileHeader(file) || isPngFileHeader(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -379,36 +456,54 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a thumbnail and save it to specified location.
|
* Generate an icon and save it to specified location.
|
||||||
*
|
*
|
||||||
* @param content File to generate icon for
|
* @param content File to generate icon for
|
||||||
* @param size the size of thumbnail to generate in pixels
|
* @param iconSize
|
||||||
* @param cacheFile Location to save thumbnail to
|
* @param cacheFile Location to save thumbnail to
|
||||||
*
|
*
|
||||||
* @return Generated icon or a default icon if a thumbnail could not be
|
* @return Generated icon or null on error
|
||||||
* made.
|
|
||||||
*/
|
*/
|
||||||
private static Image generateAndSaveThumbnail(Content content, int size, File cacheFile) {
|
private static Image generateAndSaveThumbnail(Content content, int iconSize, File cacheFile) {
|
||||||
BufferedImage thumbnail = generateThumbnail(content, size);
|
AbstractFile f = (AbstractFile) content;
|
||||||
if (Objects.nonNull(thumbnail)) {
|
final String extension = f.getNameExtension();
|
||||||
|
BufferedImage thumbnail = null;
|
||||||
|
try {
|
||||||
|
if (SUPPORTED_VIDEO_EXTENSIONS.contains(extension)) {
|
||||||
|
if (openCVLoaded) {
|
||||||
|
thumbnail = VideoUtils.generateVideoThumbnail((AbstractFile) content, iconSize);
|
||||||
|
} else {
|
||||||
|
return DEFAULT_THUMBNAIL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
thumbnail = generateImageThumbnail(content, iconSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thumbnail == null) {
|
||||||
|
return DEFAULT_THUMBNAIL;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
BufferedImage toSave = thumbnail;
|
||||||
imageSaver.execute(() -> {
|
imageSaver.execute(() -> {
|
||||||
try {
|
try {
|
||||||
Files.createParentDirs(cacheFile);
|
Files.createParentDirs(cacheFile);
|
||||||
if (cacheFile.exists()) {
|
if (cacheFile.exists()) {
|
||||||
cacheFile.delete();
|
cacheFile.delete();
|
||||||
}
|
}
|
||||||
ImageIO.write(thumbnail, FORMAT, cacheFile);
|
ImageIO.write(toSave, FORMAT, cacheFile);
|
||||||
} catch (IllegalArgumentException | IOException ex1) {
|
} catch (IllegalArgumentException | IOException ex1) {
|
||||||
LOGGER.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex1); //NON-NLS
|
LOGGER.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex1); //NON-NLS
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return thumbnail;
|
|
||||||
} else {
|
|
||||||
return getDefaultThumbnail();
|
|
||||||
}
|
}
|
||||||
|
} catch (NullPointerException ex) {
|
||||||
|
logger.log(Level.WARNING, "Could not write cache thumbnail: " + content, ex); //NON-NLS
|
||||||
|
}
|
||||||
|
return thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* Generate and return a scaled image
|
* Generate and return a scaled image
|
||||||
*
|
*
|
||||||
* @param content
|
* @param content
|
||||||
@ -418,7 +513,7 @@ public class ImageUtils {
|
|||||||
* there was a problem.
|
* there was a problem.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static BufferedImage generateThumbnail(Content content, int iconSize) {
|
private static BufferedImage generateImageThumbnail(Content content, int iconSize) {
|
||||||
|
|
||||||
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(content));) {
|
try (InputStream inputStream = new BufferedInputStream(new ReadContentInputStream(content));) {
|
||||||
BufferedImage bi = ImageIO.read(inputStream);
|
BufferedImage bi = ImageIO.read(inputStream);
|
||||||
@ -436,10 +531,13 @@ public class ImageUtils {
|
|||||||
}
|
}
|
||||||
} catch (OutOfMemoryError e) {
|
} catch (OutOfMemoryError e) {
|
||||||
LOGGER.log(Level.WARNING, "Could not scale image (too large): " + content.getName(), e); //NON-NLS
|
LOGGER.log(Level.WARNING, "Could not scale image (too large): " + content.getName(), e); //NON-NLS
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.log(Level.WARNING, "Could not scale image: " + content.getName(), e); //NON-NLS
|
LOGGER.log(Level.WARNING, "Could not load image: " + content.getName(), e); //NON-NLS
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
127
Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java
Normal file
127
Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2015 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.sleuthkit.autopsy.coreutils;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.highgui.VideoCapture;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
|
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class VideoUtils {
|
||||||
|
|
||||||
|
private static final int THUMB_COLUMNS = 3;
|
||||||
|
private static final int THUMB_ROWS = 3;
|
||||||
|
private static final int CV_CAP_PROP_POS_MSEC = 0;
|
||||||
|
private static final int CV_CAP_PROP_FRAME_COUNT = 7;
|
||||||
|
private static final int CV_CAP_PROP_FPS = 5;
|
||||||
|
|
||||||
|
static final Logger LOGGER = Logger.getLogger(VideoUtils.class.getName());
|
||||||
|
|
||||||
|
private VideoUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getTempVideoFile(AbstractFile file) {
|
||||||
|
return Paths.get(Case.getCurrentCase().getTempDirectory(), "videos", file.getId() + "." + file.getNameExtension()).toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
static BufferedImage generateVideoThumbnail(AbstractFile file, int iconSize) {
|
||||||
|
java.io.File tempFile = getTempVideoFile(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
||||||
|
com.google.common.io.Files.createParentDirs(tempFile);
|
||||||
|
ProgressHandle progress = ProgressHandleFactory.createHandle("extracting temporary file " + file.getName());
|
||||||
|
progress.start(100);
|
||||||
|
try {
|
||||||
|
ContentUtils.writeToFile(file, tempFile, progress, null, true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
progress.finish();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCapture videoFile = new VideoCapture(); // will contain the video
|
||||||
|
|
||||||
|
if (!videoFile.open(tempFile.toString())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
double fps = videoFile.get(CV_CAP_PROP_FPS); // gets frame per second
|
||||||
|
double totalFrames = videoFile.get(CV_CAP_PROP_FRAME_COUNT); // gets total frames
|
||||||
|
if (fps <= 0 || totalFrames <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
double milliseconds = 1000 * (totalFrames / fps); //total milliseconds
|
||||||
|
|
||||||
|
double timestamp = Math.min(milliseconds, 500); //default time to check for is 500ms, unless the files is extremely small
|
||||||
|
|
||||||
|
int framkeskip = Double.valueOf(Math.floor((milliseconds - timestamp) / (THUMB_COLUMNS * THUMB_ROWS))).intValue();
|
||||||
|
|
||||||
|
Mat imageMatrix = new Mat();
|
||||||
|
BufferedImage bufferedImage = null;
|
||||||
|
|
||||||
|
for (int x = 0; x < THUMB_COLUMNS; x++) {
|
||||||
|
for (int y = 0; y < THUMB_ROWS; y++) {
|
||||||
|
if (!videoFile.set(CV_CAP_PROP_POS_MSEC, timestamp + x * framkeskip + y * framkeskip * THUMB_COLUMNS)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//read the frame into the image/matrix
|
||||||
|
if (!videoFile.read(imageMatrix)) {
|
||||||
|
break; //if the image for some reason is bad, return default icon
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferedImage == null) {
|
||||||
|
bufferedImage = new BufferedImage(imageMatrix.cols() * THUMB_COLUMNS, imageMatrix.rows() * THUMB_ROWS, BufferedImage.TYPE_3BYTE_BGR);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = new byte[imageMatrix.rows() * imageMatrix.cols() * (int) (imageMatrix.elemSize())];
|
||||||
|
imageMatrix.get(0, 0, data); //copy the image to data
|
||||||
|
|
||||||
|
//todo: this looks like we are swapping the first and third channels. so we can use BufferedImage.TYPE_3BYTE_BGR
|
||||||
|
if (imageMatrix.channels() == 3) {
|
||||||
|
for (int k = 0; k < data.length; k += 3) {
|
||||||
|
byte temp = data[k];
|
||||||
|
data[k] = data[k + 2];
|
||||||
|
data[k + 2] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferedImage.getRaster().setDataElements(imageMatrix.cols() * x, imageMatrix.rows() * y, imageMatrix.cols(), imageMatrix.rows(), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
videoFile.release(); // close the file
|
||||||
|
|
||||||
|
return ScalrWrapper.resizeFast(bufferedImage, iconSize);
|
||||||
|
}
|
||||||
|
}
|
@ -23,14 +23,15 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.prefs.PreferenceChangeEvent;
|
import java.util.prefs.PreferenceChangeEvent;
|
||||||
import java.util.prefs.PreferenceChangeListener;
|
import java.util.prefs.PreferenceChangeListener;
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
import org.sleuthkit.autopsy.core.UserPreferences;
|
import org.sleuthkit.autopsy.core.UserPreferences;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.ContentVisitor;
|
import org.sleuthkit.datamodel.ContentVisitor;
|
||||||
@ -176,8 +177,8 @@ public final class ContentUtils {
|
|||||||
* @return number of bytes extracted
|
* @return number of bytes extracted
|
||||||
* @throws IOException if file could not be written
|
* @throws IOException if file could not be written
|
||||||
*/
|
*/
|
||||||
public static <T,V> long writeToFile(Content content, java.io.File outputFile,
|
public static <T> long writeToFile(Content content, java.io.File outputFile,
|
||||||
ProgressHandle progress, SwingWorker<T,V> worker, boolean source) throws IOException {
|
ProgressHandle progress, Future<T> worker, boolean source) throws IOException {
|
||||||
|
|
||||||
InputStream in = new ReadContentInputStream(content);
|
InputStream in = new ReadContentInputStream(content);
|
||||||
|
|
||||||
|
BIN
Core/src/org/sleuthkit/autopsy/images/working_spinner.gif
Normal file
BIN
Core/src/org/sleuthkit/autopsy/images/working_spinner.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
@ -382,7 +382,7 @@ public class IngestJobSettings {
|
|||||||
if (isPythonModuleSettingsFile(moduleSettingsFilePath)) {
|
if (isPythonModuleSettingsFile(moduleSettingsFilePath)) {
|
||||||
// compiled python modules have variable instance number as a part of their file name.
|
// compiled python modules have variable instance number as a part of their file name.
|
||||||
// This block of code gets rid of that variable instance number and helps maitains constant module name over multiple runs.
|
// This block of code gets rid of that variable instance number and helps maitains constant module name over multiple runs.
|
||||||
moduleSettingsFilePath.replaceAll("[$][\\d]+.settings$", "\\$.settings");
|
moduleSettingsFilePath = moduleSettingsFilePath.replaceAll("[$][\\d]+.settings$", "\\$.settings");
|
||||||
}
|
}
|
||||||
try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(moduleSettingsFilePath))) {
|
try (NbObjectOutputStream out = new NbObjectOutputStream(new FileOutputStream(moduleSettingsFilePath))) {
|
||||||
out.writeObject(settings);
|
out.writeObject(settings);
|
||||||
|
@ -14,3 +14,7 @@ PhotoRecIngestModule.processTerminated=PhotoRec Carver ingest module was termina
|
|||||||
PhotoRecIngestModule.moduleError=PhotoRec Carver Module Error
|
PhotoRecIngestModule.moduleError=PhotoRec Carver Module Error
|
||||||
PhotoRecIngestModule.UnableToCarve=Unable to carve file: {0}
|
PhotoRecIngestModule.UnableToCarve=Unable to carve file: {0}
|
||||||
PhotoRecIngestModule.NotEnoughDiskSpace=Not enough disk space to save unallocated file. Carving will be skipped.
|
PhotoRecIngestModule.NotEnoughDiskSpace=Not enough disk space to save unallocated file. Carving will be skipped.
|
||||||
|
PhotoRecIngestModule.complete.numberOfCarved=Number of Files Carved\:
|
||||||
|
PhotoRecIngestModule.complete.totalWritetime=Total Time To Write To Disk
|
||||||
|
PhotoRecIngestModule.complete.totalParsetime=Total Parsing Time
|
||||||
|
PhotoRecIngestModule.complete.photoRecResults=PhotoRec Results
|
@ -32,7 +32,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.openide.modules.InstalledFileLocator;
|
import org.openide.modules.InstalledFileLocator;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
@ -46,6 +48,7 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
|||||||
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
import org.sleuthkit.autopsy.ingest.FileIngestModule;
|
||||||
import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator;
|
import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||||
|
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModule;
|
import org.sleuthkit.autopsy.ingest.IngestModule;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||||
@ -71,12 +74,30 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
private static final String LOG_FILE = "run_log.txt"; //NON-NLS
|
private static final String LOG_FILE = "run_log.txt"; //NON-NLS
|
||||||
private static final String TEMP_DIR_NAME = "temp"; // NON-NLS
|
private static final String TEMP_DIR_NAME = "temp"; // NON-NLS
|
||||||
private static final Logger logger = Logger.getLogger(PhotoRecCarverFileIngestModule.class.getName());
|
private static final Logger logger = Logger.getLogger(PhotoRecCarverFileIngestModule.class.getName());
|
||||||
|
private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
|
||||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||||
private static final Map<Long, WorkingPaths> pathsByJob = new ConcurrentHashMap<>();
|
private static final Map<Long, WorkingPaths> pathsByJob = new ConcurrentHashMap<>();
|
||||||
private IngestJobContext context;
|
private IngestJobContext context;
|
||||||
private Path rootOutputDirPath;
|
private Path rootOutputDirPath;
|
||||||
private File executableFile;
|
private File executableFile;
|
||||||
private IngestServices services;
|
private IngestServices services;
|
||||||
|
private long jobId;
|
||||||
|
|
||||||
|
private static class IngestJobTotals {
|
||||||
|
|
||||||
|
private AtomicLong totalItemsRecovered = new AtomicLong(0);
|
||||||
|
private AtomicLong totalWritetime = new AtomicLong(0);
|
||||||
|
private AtomicLong totalParsetime = new AtomicLong(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
|
||||||
|
IngestJobTotals totals = totalsForIngestJobs.get(ingestJobId);
|
||||||
|
if (totals == null) {
|
||||||
|
totals = new PhotoRecCarverFileIngestModule.IngestJobTotals();
|
||||||
|
totalsForIngestJobs.put(ingestJobId, totals);
|
||||||
|
}
|
||||||
|
return totals;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
@ -85,6 +106,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.services = IngestServices.getInstance();
|
this.services = IngestServices.getInstance();
|
||||||
|
this.jobId = this.context.getJobId();
|
||||||
|
|
||||||
// If the global unallocated space processing setting and the module
|
// If the global unallocated space processing setting and the module
|
||||||
// process unallocated space only setting are not in sych, throw an
|
// process unallocated space only setting are not in sych, throw an
|
||||||
@ -99,7 +121,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
Path execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_EXECUTABLE);
|
Path execName = Paths.get(PHOTOREC_DIRECTORY, PHOTOREC_EXECUTABLE);
|
||||||
executableFile = locateExecutable(execName.toString());
|
executableFile = locateExecutable(execName.toString());
|
||||||
|
|
||||||
if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(this.context.getJobId()) == 1) {
|
if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(this.jobId) == 1) {
|
||||||
try {
|
try {
|
||||||
// The first instance creates an output subdirectory with a date and time stamp
|
// The first instance creates an output subdirectory with a date and time stamp
|
||||||
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS"); // NON-NLS
|
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS"); // NON-NLS
|
||||||
@ -113,9 +135,8 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
Files.createDirectory(tempDirPath);
|
Files.createDirectory(tempDirPath);
|
||||||
|
|
||||||
// Save the directories for the current job.
|
// Save the directories for the current job.
|
||||||
PhotoRecCarverFileIngestModule.pathsByJob.put(this.context.getJobId(), new WorkingPaths(outputDirPath, tempDirPath));
|
PhotoRecCarverFileIngestModule.pathsByJob.put(this.jobId, new WorkingPaths(outputDirPath, tempDirPath));
|
||||||
}
|
} catch (SecurityException | IOException | UnsupportedOperationException ex) {
|
||||||
catch (SecurityException | IOException | UnsupportedOperationException ex) {
|
|
||||||
throw new IngestModule.IngestModuleException(NbBundle.getMessage(this.getClass(), "cannotCreateOutputDir.message", ex.getLocalizedMessage()));
|
throw new IngestModule.IngestModuleException(NbBundle.getMessage(this.getClass(), "cannotCreateOutputDir.message", ex.getLocalizedMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,6 +152,9 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
return IngestModule.ProcessResult.OK;
|
return IngestModule.ProcessResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Safely get a reference to the totalsForIngestJobs object
|
||||||
|
IngestJobTotals totals = getTotalsForIngestJobs(jobId);
|
||||||
|
|
||||||
Path tempFilePath = null;
|
Path tempFilePath = null;
|
||||||
try {
|
try {
|
||||||
long id = getRootId(file);
|
long id = getRootId(file);
|
||||||
@ -160,7 +184,8 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write the file to disk.
|
// Write the file to disk.
|
||||||
WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.get(this.context.getJobId());
|
long writestart = System.currentTimeMillis();
|
||||||
|
WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.get(this.jobId);
|
||||||
tempFilePath = Paths.get(paths.getTempDirPath().toString(), file.getName());
|
tempFilePath = Paths.get(paths.getTempDirPath().toString(), file.getName());
|
||||||
ContentUtils.writeToFile(file, tempFilePath.toFile());
|
ContentUtils.writeToFile(file, tempFilePath.toFile());
|
||||||
|
|
||||||
@ -211,21 +236,24 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
long writedelta = (System.currentTimeMillis() - writestart);
|
||||||
|
totals.totalWritetime.addAndGet(writedelta);
|
||||||
|
|
||||||
// Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database
|
// Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database
|
||||||
|
long calcstart = System.currentTimeMillis();
|
||||||
PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath);
|
PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath);
|
||||||
List<LayoutFile> theList = parser.parse(newAuditFile, id, file);
|
List<LayoutFile> carvedItems = parser.parse(newAuditFile, id, file);
|
||||||
if (theList != null) { // if there were any results from carving, add the unallocated carving event to the reports list.
|
long calcdelta = (System.currentTimeMillis() - calcstart);
|
||||||
context.addFilesToJob(new ArrayList<>(theList));
|
totals.totalParsetime.addAndGet(calcdelta);
|
||||||
services.fireModuleContentEvent(new ModuleContentEvent(theList.get(0))); // fire an event to update the tree
|
if (carvedItems != null) { // if there were any results from carving, add the unallocated carving event to the reports list.
|
||||||
|
totals.totalItemsRecovered.addAndGet(carvedItems.size());
|
||||||
|
context.addFilesToJob(new ArrayList<>(carvedItems));
|
||||||
|
services.fireModuleContentEvent(new ModuleContentEvent(carvedItems.get(0))); // fire an event to update the tree
|
||||||
}
|
}
|
||||||
}
|
} catch (IOException ex) {
|
||||||
catch (IOException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error processing " + file.getName() + " with PhotoRec carver", ex); // NON-NLS
|
logger.log(Level.SEVERE, "Error processing " + file.getName() + " with PhotoRec carver", ex); // NON-NLS
|
||||||
return IngestModule.ProcessResult.ERROR;
|
return IngestModule.ProcessResult.ERROR;
|
||||||
}
|
} finally {
|
||||||
|
|
||||||
finally {
|
|
||||||
if (null != tempFilePath && Files.exists(tempFilePath)) {
|
if (null != tempFilePath && Files.exists(tempFilePath)) {
|
||||||
// Get rid of the unallocated space file.
|
// Get rid of the unallocated space file.
|
||||||
tempFilePath.toFile().delete();
|
tempFilePath.toFile().delete();
|
||||||
@ -243,18 +271,47 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized void postSummary() {
|
||||||
|
IngestJobTotals jobTotals = totalsForIngestJobs.remove(jobId);
|
||||||
|
|
||||||
|
StringBuilder detailsSb = new StringBuilder();
|
||||||
|
//details
|
||||||
|
detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
|
||||||
|
|
||||||
|
detailsSb.append("<tr><td>") //NON-NLS
|
||||||
|
.append(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.complete.numberOfCarved"))
|
||||||
|
.append("</td>"); //NON-NLS
|
||||||
|
detailsSb.append("<td>").append(jobTotals.totalItemsRecovered.get()).append("</td></tr>"); //NON-NLS
|
||||||
|
|
||||||
|
detailsSb.append("<tr><td>") //NON-NLS
|
||||||
|
.append(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.complete.totalWritetime"))
|
||||||
|
.append("</td><td>").append(jobTotals.totalWritetime.get()).append("</td></tr>\n"); //NON-NLS
|
||||||
|
detailsSb.append("<tr><td>") //NON-NLS
|
||||||
|
.append(NbBundle.getMessage(this.getClass(), "PhotoRecIngestModule.complete.totalParsetime"))
|
||||||
|
.append("</td><td>").append(jobTotals.totalParsetime.get()).append("</td></tr>\n"); //NON-NLS
|
||||||
|
detailsSb.append("</table>"); //NON-NLS
|
||||||
|
|
||||||
|
services.postMessage(IngestMessage.createMessage(
|
||||||
|
IngestMessage.MessageType.INFO,
|
||||||
|
PhotoRecCarverIngestModuleFactory.getModuleName(),
|
||||||
|
NbBundle.getMessage(this.getClass(),
|
||||||
|
"PhotoRecIngestModule.complete.photoRecResults"),
|
||||||
|
detailsSb.toString()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void shutDown() {
|
public void shutDown() {
|
||||||
if (this.context != null && refCounter.decrementAndGet(this.context.getJobId()) == 0) {
|
if (this.context != null && refCounter.decrementAndGet(this.jobId) == 0) {
|
||||||
try {
|
try {
|
||||||
// The last instance of this module for an ingest job cleans out
|
// The last instance of this module for an ingest job cleans out
|
||||||
// the working paths map entry for the job and deletes the temp dir.
|
// the working paths map entry for the job and deletes the temp dir.
|
||||||
WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.context.getJobId());
|
WorkingPaths paths = PhotoRecCarverFileIngestModule.pathsByJob.remove(this.jobId);
|
||||||
FileUtil.deleteDir(new File(paths.getTempDirPath().toString()));
|
FileUtil.deleteDir(new File(paths.getTempDirPath().toString()));
|
||||||
|
postSummary();
|
||||||
}
|
}
|
||||||
catch (SecurityException ex) {
|
catch (SecurityException ex) {
|
||||||
logger.log(Level.SEVERE, "Error shutting down PhotoRec carver module", ex); // NON-NLS
|
logger.log(Level.SEVERE, "Error shutting down PhotoRec carver module", ex); // NON-NLS
|
||||||
|
@ -251,7 +251,7 @@ public interface TimeLineChart<X> extends TimeLineView {
|
|||||||
final X end = getSpanEnd();
|
final X end = getSpanEnd();
|
||||||
Tooltip.uninstall(this, tooltip);
|
Tooltip.uninstall(this, tooltip);
|
||||||
tooltip = new Tooltip(
|
tooltip = new Tooltip(
|
||||||
NbBundle.getMessage(this.getClass(), "Timeline.ui.TimeLineChart.tooltip.text", formatSpan(start),
|
NbBundle.getMessage(TimeLineChart.class, "Timeline.ui.TimeLineChart.tooltip.text", formatSpan(start),
|
||||||
formatSpan(end)));
|
formatSpan(end)));
|
||||||
Tooltip.install(this, tooltip);
|
Tooltip.install(this, tooltip);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
|
<data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
|
||||||
<code-name-base>org.sleuthkit.autopsy.imagegallery</code-name-base>
|
<code-name-base>org.sleuthkit.autopsy.imagegallery</code-name-base>
|
||||||
<standalone/>
|
<suite-component/>
|
||||||
<module-dependencies>
|
<module-dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<code-name-base>org.netbeans.api.progress</code-name-base>
|
<code-name-base>org.netbeans.api.progress</code-name-base>
|
||||||
@ -103,7 +103,7 @@
|
|||||||
<compile-dependency/>
|
<compile-dependency/>
|
||||||
<run-dependency>
|
<run-dependency>
|
||||||
<release-version>10</release-version>
|
<release-version>10</release-version>
|
||||||
<specification-version>10.0.11</specification-version>
|
<specification-version>10.3</specification-version>
|
||||||
</run-dependency>
|
</run-dependency>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -26,6 +26,7 @@ import static java.util.Objects.isNull;
|
|||||||
import static java.util.Objects.nonNull;
|
import static java.util.Objects.nonNull;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.TreeSet;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -80,6 +81,10 @@ public enum FileTypeUtils {
|
|||||||
*/
|
*/
|
||||||
private static FileTypeDetector FILE_TYPE_DETECTOR;
|
private static FileTypeDetector FILE_TYPE_DETECTOR;
|
||||||
|
|
||||||
|
private static final String IMAGE_GIF_MIME = "image/gif";
|
||||||
|
|
||||||
|
private static final TreeSet<String> GIF_MIME_SET = new TreeSet<>(Arrays.asList(IMAGE_GIF_MIME));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* static initalizer block to initialize sets of extensions and mimetypes
|
* static initalizer block to initialize sets of extensions and mimetypes
|
||||||
* to be supported
|
* to be supported
|
||||||
@ -164,7 +169,7 @@ public enum FileTypeUtils {
|
|||||||
*
|
*
|
||||||
* @return true if this file is supported or false if not
|
* @return true if this file is supported or false if not
|
||||||
*/
|
*/
|
||||||
public static Boolean isDrawable(AbstractFile file) {
|
public static boolean isDrawable(AbstractFile file) {
|
||||||
return hasDrawableMimeType(file).orElseGet(() -> {
|
return hasDrawableMimeType(file).orElseGet(() -> {
|
||||||
final boolean contains = FileTypeUtils.supportedExtensions.contains(file.getNameExtension());
|
final boolean contains = FileTypeUtils.supportedExtensions.contains(file.getNameExtension());
|
||||||
final boolean jpegFileHeader = ImageUtils.isJpegFileHeader(file);
|
final boolean jpegFileHeader = ImageUtils.isJpegFileHeader(file);
|
||||||
@ -175,6 +180,30 @@ public enum FileTypeUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isGIF(AbstractFile file) {
|
||||||
|
try {
|
||||||
|
final FileTypeDetector fileTypeDetector = getFileTypeDetector();
|
||||||
|
if (nonNull(fileTypeDetector)) {
|
||||||
|
String fileType = fileTypeDetector.getFileType(file);
|
||||||
|
return IMAGE_GIF_MIME.equalsIgnoreCase(fileType);
|
||||||
|
}
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Failed to get mime type with FileTypeDetector.", ex);
|
||||||
|
}
|
||||||
|
LOGGER.log(Level.WARNING, "Falling back on direct mime type check.");
|
||||||
|
switch (file.isMimeType(GIF_MIME_SET)) {
|
||||||
|
|
||||||
|
case TRUE:
|
||||||
|
return true;
|
||||||
|
case UNDEFINED:
|
||||||
|
LOGGER.log(Level.WARNING, "Falling back on extension check.");
|
||||||
|
return "gif".equals(file.getNameExtension());
|
||||||
|
case FALSE:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* does the given file have drawable/supported mime type
|
* does the given file have drawable/supported mime type
|
||||||
*
|
*
|
||||||
|
@ -751,9 +751,9 @@ public final class ImageGalleryController {
|
|||||||
"' or name LIKE '%.")
|
"' or name LIKE '%.")
|
||||||
+ "')";
|
+ "')";
|
||||||
static private final String MIMETYPE_CLAUSE
|
static private final String MIMETYPE_CLAUSE
|
||||||
= "blackboard_attributes.value_text LIKE "
|
= "blackboard_attributes.value_text LIKE '"
|
||||||
+ StringUtils.join(FileTypeUtils.getAllSupportedMimeTypes(),
|
+ StringUtils.join(FileTypeUtils.getAllSupportedMimeTypes(),
|
||||||
" OR blackboard_attributes.value_text LIKE ");
|
"' OR blackboard_attributes.value_text LIKE '") + "' ";
|
||||||
|
|
||||||
static private final String DRAWABLE_QUERY = FILE_EXTESNION_CLAUSE + " OR tsk_files.obj_id IN ("
|
static private final String DRAWABLE_QUERY = FILE_EXTESNION_CLAUSE + " OR tsk_files.obj_id IN ("
|
||||||
+ "SELECT tsk_files.obj_id from tsk_files , blackboard_artifacts, blackboard_attributes"
|
+ "SELECT tsk_files.obj_id from tsk_files , blackboard_artifacts, blackboard_attributes"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-15 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -23,6 +23,7 @@ import com.google.common.cache.CacheBuilder;
|
|||||||
import com.google.common.cache.CacheLoader;
|
import com.google.common.cache.CacheLoader;
|
||||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
@ -42,6 +43,7 @@ import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
||||||
|
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
|
|
||||||
/** Singleton to manage creation and access of icons. Keeps a cache in memory of
|
/** Singleton to manage creation and access of icons. Keeps a cache in memory of
|
||||||
@ -54,7 +56,7 @@ public enum ThumbnailCache {
|
|||||||
|
|
||||||
instance;
|
instance;
|
||||||
|
|
||||||
private static final int MAX_ICON_SIZE = 300;
|
private static final int MAX_THUMBNAIL_SIZE = 300;
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ThumbnailCache.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(ThumbnailCache.class.getName());
|
||||||
|
|
||||||
@ -118,17 +120,23 @@ public enum ThumbnailCache {
|
|||||||
*/
|
*/
|
||||||
private Optional<Image> load(DrawableFile<?> file) {
|
private Optional<Image> load(DrawableFile<?> file) {
|
||||||
|
|
||||||
BufferedImage thumbnail;
|
if (FileTypeUtils.isGIF(file)) {
|
||||||
try {
|
//directly read gif to preserve potential animation,
|
||||||
thumbnail = getCacheFile(file).map(new Function<File, BufferedImage>() {
|
//NOTE: not saved to disk!
|
||||||
|
return Optional.of(new Image(new BufferedInputStream(new ReadContentInputStream(file.getAbstractFile())), MAX_THUMBNAIL_SIZE, MAX_THUMBNAIL_SIZE, true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage thumbnail = getCacheFile(file).map(new Function<File, BufferedImage>() {
|
||||||
@Override
|
@Override
|
||||||
public BufferedImage apply(File cachFile) {
|
public BufferedImage apply(File cachFile) {
|
||||||
if (cachFile.exists()) {
|
if (cachFile.exists()) {
|
||||||
// If a thumbnail file is already saved locally, load it
|
// If a thumbnail file is already saved locally, load it
|
||||||
try {
|
try {
|
||||||
BufferedImage read = ImageIO.read(cachFile);
|
BufferedImage read = ImageIO.read(cachFile);
|
||||||
if (read.getWidth() == MAX_ICON_SIZE) {
|
|
||||||
|
if (read.getWidth() < MAX_THUMBNAIL_SIZE) {
|
||||||
return read;
|
return read;
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (MalformedURLException ex) {
|
} catch (MalformedURLException ex) {
|
||||||
LOGGER.log(Level.WARNING, "Unable to parse cache file path..");
|
LOGGER.log(Level.WARNING, "Unable to parse cache file path..");
|
||||||
@ -139,17 +147,17 @@ public enum ThumbnailCache {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).orElseGet(() -> {
|
}).orElseGet(() -> {
|
||||||
return (BufferedImage) ImageUtils.getThumbnail(file.getAbstractFile(), MAX_ICON_SIZE);
|
return (BufferedImage) ImageUtils.getThumbnail(file.getAbstractFile(), MAX_THUMBNAIL_SIZE);
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (IllegalStateException e) {
|
// } catch (IllegalStateException e) {
|
||||||
LOGGER.log(Level.WARNING, "can't load icon when no case is open");
|
// LOGGER.log(Level.WARNING, "can't load icon when no case is open");
|
||||||
return Optional.empty();
|
// return Optional.empty();
|
||||||
}
|
// }
|
||||||
WritableImage jfxthumbnail;
|
WritableImage jfxthumbnail;
|
||||||
if (thumbnail == ImageUtils.getDefaultThumbnail()) {
|
if (thumbnail == ImageUtils.getDefaultThumbnail()) {
|
||||||
jfxthumbnail = null // if we go the default icon, ignore it
|
// if we go the default icon, ignore it
|
||||||
;
|
jfxthumbnail = null;
|
||||||
} else {
|
} else {
|
||||||
jfxthumbnail = SwingFXUtils.toFXImage(thumbnail, null);
|
jfxthumbnail = SwingFXUtils.toFXImage(thumbnail, null);
|
||||||
}
|
}
|
||||||
@ -167,7 +175,8 @@ public enum ThumbnailCache {
|
|||||||
*/
|
*/
|
||||||
private static Optional<File> getCacheFile(DrawableFile<?> file) {
|
private static Optional<File> getCacheFile(DrawableFile<?> file) {
|
||||||
try {
|
try {
|
||||||
return Optional.of(ImageUtils.getCachedThumbnailFile(file.getAbstractFile(), MAX_ICON_SIZE));
|
return Optional.of(ImageUtils.getCachedThumbnailFile(file.getAbstractFile(), MAX_THUMBNAIL_SIZE));
|
||||||
|
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
LOGGER.log(Level.WARNING, "Failed to create cache file.{0}", e.getLocalizedMessage());
|
LOGGER.log(Level.WARNING, "Failed to create cache file.{0}", e.getLocalizedMessage());
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Autopsy Forensic Browser
|
* Autopsy Forensic Browser
|
||||||
*
|
*
|
||||||
* Copyright 2013 Basis Technology Corp.
|
* Copyright 2013-15 Basis Technology Corp.
|
||||||
* Contact: carrier <at> sleuthkit <dot> org
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -26,7 +26,6 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.ButtonType;
|
|
||||||
import javafx.scene.control.Menu;
|
import javafx.scene.control.Menu;
|
||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import org.openide.util.Utilities;
|
import org.openide.util.Utilities;
|
||||||
@ -91,19 +90,16 @@ public class AddDrawableTagAction extends AddTagAction {
|
|||||||
.findAny();
|
.findAny();
|
||||||
|
|
||||||
if (duplicateTagName.isPresent()) {
|
if (duplicateTagName.isPresent()) {
|
||||||
Platform.runLater(() -> {
|
LOGGER.log(Level.INFO, "{0} already tagged as {1}. Skipping.", new Object[]{file.getName(), tagName.getDisplayName()});
|
||||||
Alert alert = new Alert(Alert.AlertType.WARNING, "Unable to tag " + file.getName() + ". It has already been tagged as \"" + tagName.getDisplayName() + ". Cannot reapply the same tag.", ButtonType.OK);
|
|
||||||
alert.setHeaderText("Tag Error");
|
|
||||||
alert.show();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
|
LOGGER.log(Level.INFO, "Tagging {0} as {1}", new Object[]{file.getName(), tagName.getDisplayName()});
|
||||||
controller.getTagsManager().addContentTag(file, tagName, comment);
|
controller.getTagsManager().addContentTag(file, tagName, comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (TskCoreException tskCoreException) {
|
} catch (TskCoreException tskCoreException) {
|
||||||
LOGGER.log(Level.SEVERE, "Error tagging result", tskCoreException);
|
LOGGER.log(Level.SEVERE, "Error tagging file", tskCoreException);
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
new Alert(Alert.AlertType.ERROR, "Unable to file " + fileID + ".").show();
|
new Alert(Alert.AlertType.ERROR, "Unable to tag file " + fileID + ".").show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.datamodel;
|
package org.sleuthkit.autopsy.imagegallery.datamodel;
|
||||||
|
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -37,14 +39,10 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
|||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE;
|
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE;
|
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER;
|
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.LONG;
|
|
||||||
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING;
|
|
||||||
import org.sleuthkit.datamodel.Content;
|
import org.sleuthkit.datamodel.Content;
|
||||||
import org.sleuthkit.datamodel.ContentVisitor;
|
import org.sleuthkit.datamodel.ContentVisitor;
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||||
@ -90,6 +88,8 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SoftReference<Image> imageRef;
|
||||||
|
|
||||||
private String drawablePath;
|
private String drawablePath;
|
||||||
|
|
||||||
protected T file;
|
protected T file;
|
||||||
@ -286,7 +286,12 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Image getThumbnail();
|
|
||||||
|
public Image getThumbnail() {
|
||||||
|
return ThumbnailCache.getDefault().get(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Image getFullSizeImage();
|
||||||
|
|
||||||
public void setAnalyzed(Boolean analyzed) {
|
public void setAnalyzed(Boolean analyzed) {
|
||||||
this.analyzed.set(analyzed);
|
this.analyzed.set(analyzed);
|
||||||
@ -318,5 +323,8 @@ public abstract class DrawableFile<T extends AbstractFile> extends AbstractFile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean isDisplayable();
|
public boolean isDisplayableAsImage() {
|
||||||
|
Image thumbnail = getThumbnail();
|
||||||
|
return Objects.nonNull(thumbnail) && thumbnail.errorProperty().get() == false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,12 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.embed.swing.SwingFXUtils;
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
import org.sleuthkit.autopsy.imagegallery.FileTypeUtils;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||||
|
|
||||||
@ -45,22 +44,20 @@ public class ImageFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
ImageIO.scanForPlugins();
|
ImageIO.scanForPlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SoftReference<Image> imageRef;
|
|
||||||
|
|
||||||
ImageFile(T f, Boolean analyzed) {
|
ImageFile(T f, Boolean analyzed) {
|
||||||
super(f, analyzed);
|
super(f, analyzed);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Image getThumbnail() {
|
|
||||||
return ThumbnailCache.getDefault().get(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
public Image getFullSizeImage() {
|
public Image getFullSizeImage() {
|
||||||
Image image = null;
|
Image image = (imageRef != null) ? imageRef.get() : null;
|
||||||
if (imageRef != null) {
|
if (image == null || image.isError()) {
|
||||||
image = imageRef.get();
|
if (FileTypeUtils.isGIF(file)) {
|
||||||
|
//directly read gif to preserve potential animation,
|
||||||
|
image = new Image(new BufferedInputStream(new ReadContentInputStream(file)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (image == null || image.isError()) {
|
if (image == null || image.isError()) {
|
||||||
try (BufferedInputStream readContentInputStream = new BufferedInputStream(new ReadContentInputStream(this.getAbstractFile()))) {
|
try (BufferedInputStream readContentInputStream = new BufferedInputStream(new ReadContentInputStream(this.getAbstractFile()))) {
|
||||||
@ -75,12 +72,6 @@ public class ImageFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDisplayable() {
|
|
||||||
Image thumbnail = getThumbnail();
|
|
||||||
return Objects.nonNull(thumbnail) && thumbnail.errorProperty().get() == false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Double getWidth() {
|
Double getWidth() {
|
||||||
final Image fullSizeImage = getFullSizeImage();
|
final Image fullSizeImage = getFullSizeImage();
|
||||||
|
@ -19,17 +19,22 @@
|
|||||||
package org.sleuthkit.autopsy.imagegallery.datamodel;
|
package org.sleuthkit.autopsy.imagegallery.datamodel;
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.SoftReference;
|
import java.lang.ref.SoftReference;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.media.Media;
|
import javafx.scene.media.Media;
|
||||||
import javafx.scene.media.MediaException;
|
import javafx.scene.media.MediaException;
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.ImageUtils;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
|
import org.sleuthkit.autopsy.coreutils.VideoUtils;
|
||||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
|
|
||||||
@ -46,25 +51,36 @@ public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Image getThumbnail() {
|
public Image getFullSizeImage() {
|
||||||
//TODO: implement video thumbnailing here?
|
Image image = (null == imageRef) ? null : imageRef.get();
|
||||||
return getGenericVideoThumbnail();
|
|
||||||
|
if (image == null) {
|
||||||
|
final BufferedImage bufferedImage = (BufferedImage) ImageUtils.getThumbnail(getAbstractFile(), 1024);
|
||||||
|
image = (bufferedImage == ImageUtils.getDefaultThumbnail()) ? null : SwingFXUtils.toFXImage(bufferedImage, null);
|
||||||
|
imageRef = new SoftReference<>(image);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoftReference<Media> mediaRef;
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SoftReference<Media> mediaRef;
|
||||||
|
|
||||||
public Media getMedia() throws IOException, MediaException {
|
public Media getMedia() throws IOException, MediaException {
|
||||||
Media media = null;
|
Media media = (mediaRef != null) ? mediaRef.get() : null;
|
||||||
if (mediaRef != null) {
|
|
||||||
media = mediaRef.get();
|
|
||||||
}
|
|
||||||
if (media != null) {
|
if (media != null) {
|
||||||
return media;
|
return media;
|
||||||
}
|
}
|
||||||
final File cacheFile = getCacheFile(this.getId());
|
final File cacheFile = VideoUtils.getTempVideoFile(this.getAbstractFile());
|
||||||
if (cacheFile.exists() == false) {
|
|
||||||
|
if (cacheFile.exists() == false || cacheFile.length() < getAbstractFile().getSize()) {
|
||||||
|
|
||||||
Files.createParentDirs(cacheFile);
|
Files.createParentDirs(cacheFile);
|
||||||
ContentUtils.writeToFile(this.getAbstractFile(), cacheFile);
|
ProgressHandle progressHandle = ProgressHandleFactory.createHandle("writing temporary file to disk");
|
||||||
|
progressHandle.start(100);
|
||||||
|
ContentUtils.writeToFile(this.getAbstractFile(), cacheFile, progressHandle, null, true);
|
||||||
|
progressHandle.finish();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
media = new Media(Paths.get(cacheFile.getAbsolutePath()).toUri().toString());
|
media = new Media(Paths.get(cacheFile.getAbsolutePath()).toUri().toString());
|
||||||
@ -73,12 +89,7 @@ public class VideoFile<T extends AbstractFile> extends DrawableFile<T> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getCacheFile(long id) {
|
public boolean isDisplayableAsMedia() {
|
||||||
return Paths.get(Case.getCurrentCase().getCacheDirectory(), "videos", "" + id).toFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDisplayable() {
|
|
||||||
try {
|
try {
|
||||||
Media media = getMedia();
|
Media media = getMedia();
|
||||||
return Objects.nonNull(media) && Objects.isNull(media.getError());
|
return Objects.nonNull(media) && Objects.isNull(media.getError());
|
||||||
|
@ -44,7 +44,6 @@ import org.openide.util.Exceptions;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
import org.sleuthkit.autopsy.imagegallery.FileIDSelectionModel;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ThumbnailCache;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
@ -208,7 +207,6 @@ public class Toolbar extends ToolBar {
|
|||||||
|
|
||||||
orderGroup.selectedToggleProperty().addListener(queryInvalidationListener);
|
orderGroup.selectedToggleProperty().addListener(queryInvalidationListener);
|
||||||
|
|
||||||
ThumbnailCache.getDefault().iconSize.bind(sizeSlider.valueProperty());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.gui;
|
package org.sleuthkit.autopsy.imagegallery.gui;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
@ -27,7 +26,6 @@ import javafx.beans.InvalidationListener;
|
|||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Slider;
|
import javafx.scene.control.Slider;
|
||||||
@ -46,7 +44,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
||||||
|
|
||||||
public class MediaControl extends BorderPane {
|
public class VideoPlayer extends BorderPane {
|
||||||
|
|
||||||
private static final Image VOLUME_HIGH = new Image("/org/sleuthkit/autopsy/imagegallery/images/speaker-volume.png");
|
private static final Image VOLUME_HIGH = new Image("/org/sleuthkit/autopsy/imagegallery/images/speaker-volume.png");
|
||||||
private static final Image VOLUME_LOW = new Image("/org/sleuthkit/autopsy/imagegallery/images/speaker-volume-low.png");
|
private static final Image VOLUME_LOW = new Image("/org/sleuthkit/autopsy/imagegallery/images/speaker-volume-low.png");
|
||||||
@ -107,21 +105,6 @@ public class MediaControl extends BorderPane {
|
|||||||
};
|
};
|
||||||
private final VideoFile<?> file;
|
private final VideoFile<?> file;
|
||||||
|
|
||||||
public static Node create(VideoFile<?> file) {
|
|
||||||
try {
|
|
||||||
return new MediaControl(new MediaPlayer(file.getMedia()), file);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
Logger.getLogger(VideoFile.class.getName()).log(Level.WARNING, "failed to initialize MediaControl for file " + file.getName(), ex);
|
|
||||||
return new Text(ex.getLocalizedMessage() + "\nSee the logs for details.\n\nTry the \"Open In External Viewer\" action.");
|
|
||||||
} catch (MediaException ex) {
|
|
||||||
Logger.getLogger(VideoFile.class.getName()).log(Level.WARNING, ex.getType() + " Failed to initialize MediaControl for file " + file.getName(), ex);
|
|
||||||
return new Text(ex.getType() + "\nSee the logs for details.\n\nTry the \"Open In External Viewer\" action.");
|
|
||||||
} catch (OutOfMemoryError ex) {
|
|
||||||
Logger.getLogger(VideoFile.class.getName()).log(Level.WARNING, "failed to initialize MediaControl for file " + file.getName(), ex);
|
|
||||||
return new Text("There was a problem playing video file.\nSee the logs for details.\n\nTry the \"Open In External Viewer\" action.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void initialize() {
|
void initialize() {
|
||||||
assert controlButton != null : "fx:id=\"controlButton\" was not injected: check your FXML file 'MediaControl.fxml'.";
|
assert controlButton != null : "fx:id=\"controlButton\" was not injected: check your FXML file 'MediaControl.fxml'.";
|
||||||
@ -250,7 +233,7 @@ public class MediaControl extends BorderPane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MediaControl(MediaPlayer mp, VideoFile<?> file) {
|
public VideoPlayer(MediaPlayer mp, VideoFile<?> file) {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.mp = mp;
|
this.mp = mp;
|
||||||
FXMLConstructor.construct(this, "MediaControl.fxml");
|
FXMLConstructor.construct(this, "MediaControl.fxml");
|
@ -26,12 +26,10 @@ import javafx.scene.CacheHint;
|
|||||||
import javafx.scene.control.Control;
|
import javafx.scene.control.Control;
|
||||||
import javafx.scene.effect.DropShadow;
|
import javafx.scene.effect.DropShadow;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
import org.sleuthkit.autopsy.imagegallery.FXMLConstructor;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
import org.sleuthkit.autopsy.imagegallery.gui.Toolbar;
|
||||||
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableTileBase.globalSelectionModel;
|
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableTileBase.globalSelectionModel;
|
||||||
import org.sleuthkit.datamodel.AbstractContent;
|
import org.sleuthkit.datamodel.AbstractContent;
|
||||||
@ -50,17 +48,6 @@ public class DrawableTile extends DrawableTileBase {
|
|||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(DrawableTile.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(DrawableTile.class.getName());
|
||||||
|
|
||||||
/**
|
|
||||||
* the central ImageView that shows a thumbnail of the represented file
|
|
||||||
*/
|
|
||||||
@FXML
|
|
||||||
private ImageView imageView;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void disposeContent() {
|
|
||||||
//no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@Override
|
@Override
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
@ -90,12 +77,6 @@ public class DrawableTile extends DrawableTileBase {
|
|||||||
FXMLConstructor.construct(this, "DrawableTile.fxml");
|
FXMLConstructor.construct(this, "DrawableTile.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
|
||||||
protected void clearContent() {
|
|
||||||
imageView.setImage(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc }
|
* {@inheritDoc }
|
||||||
*/
|
*/
|
||||||
@ -109,21 +90,13 @@ public class DrawableTile extends DrawableTileBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Runnable getContentUpdateRunnable() {
|
CachedLoaderTask<Image, DrawableFile<?>> getNewImageLoadTask(DrawableFile<?> file) {
|
||||||
if (getFile().isPresent()) {
|
return new ThumbnailLoaderTask(file);
|
||||||
Image image = getFile().get().getThumbnail();
|
|
||||||
|
|
||||||
return () -> {
|
|
||||||
imageView.setImage(image);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return () -> { //no-op
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getTextForLabel() {
|
protected String getTextForLabel() {
|
||||||
return getFile().map(AbstractContent::getName).orElse("");
|
return getFile().map(AbstractContent::getName).orElse("");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,6 @@ import org.openide.windows.WindowManager;
|
|||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined.ThreadType;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
import org.sleuthkit.autopsy.datamodel.FileNode;
|
||||||
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||||
@ -71,6 +69,7 @@ import org.sleuthkit.autopsy.imagegallery.actions.DeleteFollowUpTagAction;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
import org.sleuthkit.autopsy.imagegallery.actions.SwingMenuItemAdapter;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.grouping.GroupViewMode;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
@ -106,7 +105,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
protected ImageView undisplayableImageView;
|
protected ImageView undisplayableImageView;
|
||||||
** displays the icon representing follow up tag */
|
/** displays the icon representing follow up tag */
|
||||||
@FXML
|
@FXML
|
||||||
private ImageView followUpImageView;
|
private ImageView followUpImageView;
|
||||||
|
|
||||||
@ -120,8 +119,13 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
@FXML
|
@FXML
|
||||||
Label nameLabel;
|
Label nameLabel;
|
||||||
|
|
||||||
/** the groupPane this {@link DrawableTileBase} is embedded in */
|
@FXML
|
||||||
|
protected ImageView imageView;
|
||||||
|
/**
|
||||||
|
* the groupPane this {@link DrawableTileBase} is embedded in
|
||||||
|
*/
|
||||||
final private GroupPane groupPane;
|
final private GroupPane groupPane;
|
||||||
|
|
||||||
volatile private boolean registered = false;
|
volatile private boolean registered = false;
|
||||||
|
|
||||||
protected DrawableTileBase(GroupPane groupPane) {
|
protected DrawableTileBase(GroupPane groupPane) {
|
||||||
@ -235,13 +239,6 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
return groupPane;
|
return groupPane;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ThreadConfined(type = ThreadType.UI)
|
|
||||||
protected abstract void clearContent();
|
|
||||||
|
|
||||||
protected abstract void disposeContent();
|
|
||||||
|
|
||||||
protected abstract Runnable getContentUpdateRunnable();
|
|
||||||
|
|
||||||
protected abstract String getTextForLabel();
|
protected abstract String getTextForLabel();
|
||||||
|
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
@ -279,6 +276,8 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
@Override
|
@Override
|
||||||
synchronized protected void setFileHelper(final Long newFileID) {
|
synchronized protected void setFileHelper(final Long newFileID) {
|
||||||
setFileIDOpt(Optional.ofNullable(newFileID));
|
setFileIDOpt(Optional.ofNullable(newFileID));
|
||||||
|
setFileOpt(Optional.empty());
|
||||||
|
|
||||||
disposeContent();
|
disposeContent();
|
||||||
|
|
||||||
if (getFileID().isPresent() == false || Case.isCaseOpen() == false) {
|
if (getFileID().isPresent() == false || Case.isCaseOpen() == false) {
|
||||||
@ -287,23 +286,18 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
getController().getTagsManager().unregisterListener(this);
|
getController().getTagsManager().unregisterListener(this);
|
||||||
registered = false;
|
registered = false;
|
||||||
}
|
}
|
||||||
setFileOpt(Optional.empty());
|
updateContent();
|
||||||
Platform.runLater(() -> {
|
|
||||||
clearContent();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
if (registered == false) {
|
if (registered == false) {
|
||||||
getController().getCategoryManager().registerListener(this);
|
getController().getCategoryManager().registerListener(this);
|
||||||
getController().getTagsManager().registerListener(this);
|
getController().getTagsManager().registerListener(this);
|
||||||
registered = true;
|
registered = true;
|
||||||
}
|
}
|
||||||
setFileOpt(Optional.empty());
|
|
||||||
|
|
||||||
updateSelectionState();
|
updateSelectionState();
|
||||||
updateCategory();
|
updateCategory();
|
||||||
updateFollowUpIcon();
|
updateFollowUpIcon();
|
||||||
updateUI();
|
updateUI();
|
||||||
Platform.runLater(getContentUpdateRunnable());
|
updateContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,7 +305,7 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
getFile().ifPresent(file -> {
|
getFile().ifPresent(file -> {
|
||||||
final boolean isVideo = file.isVideo();
|
final boolean isVideo = file.isVideo();
|
||||||
final boolean hasHashSetHits = hasHashHit();
|
final boolean hasHashSetHits = hasHashHit();
|
||||||
final boolean isUndisplayable = file.isDisplayable() == false;
|
final boolean isUndisplayable = (isVideo ? ((VideoFile<?>) file).isDisplayableAsMedia() : file.isDisplayableAsImage()) == false;
|
||||||
final String text = getTextForLabel();
|
final String text = getTextForLabel();
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@ -387,4 +381,40 @@ public abstract class DrawableTileBase extends DrawableUIBase {
|
|||||||
followUpToggle.setSelected(hasFollowUp);
|
followUpToggle.setSelected(hasFollowUp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// Node getContentNode() {
|
||||||
|
// if (getFile().isPresent() == false) {
|
||||||
|
// imageCache = null;
|
||||||
|
// Platform.runLater(() -> {
|
||||||
|
// imageView.setImage(null);
|
||||||
|
// });
|
||||||
|
// return null;
|
||||||
|
// } else {
|
||||||
|
// Image thumbnail = isNull(imageCache) ? null : imageCache.get();
|
||||||
|
//
|
||||||
|
// if (nonNull(thumbnail)) {
|
||||||
|
// Platform.runLater(() -> {
|
||||||
|
// imageView.setImage(thumbnail);
|
||||||
|
// });
|
||||||
|
// return imageView;
|
||||||
|
// } else {
|
||||||
|
// DrawableFile<?> file = getFile().get();
|
||||||
|
//
|
||||||
|
// if (isNull(imageTask) || imageTask.isDone()) {
|
||||||
|
// imageTask = new ImageLoadTask(file) {
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// void saveToCache(Image result) {
|
||||||
|
// synchronized (DrawableTileBase.this) {
|
||||||
|
// imageCache = new SoftReference<>(result);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// new Thread(imageTask).start();
|
||||||
|
// }
|
||||||
|
// return getLoadingProgressIndicator();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,22 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import static java.util.Objects.isNull;
|
||||||
import static java.util.Objects.nonNull;
|
import static java.util.Objects.nonNull;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.ProgressIndicator;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
@ -33,11 +44,21 @@ import org.sleuthkit.datamodel.TskCoreException;
|
|||||||
*/
|
*/
|
||||||
abstract public class DrawableUIBase extends AnchorPane implements DrawableView {
|
abstract public class DrawableUIBase extends AnchorPane implements DrawableView {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(DrawableUIBase.class.getName());
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
protected BorderPane imageBorder;
|
||||||
|
@FXML
|
||||||
|
protected ImageView imageView;
|
||||||
|
|
||||||
private final ImageGalleryController controller;
|
private final ImageGalleryController controller;
|
||||||
|
|
||||||
private Optional<DrawableFile<?>> fileOpt = Optional.empty();
|
private Optional<DrawableFile<?>> fileOpt = Optional.empty();
|
||||||
|
|
||||||
private Optional<Long> fileIDOpt = Optional.empty();
|
private Optional<Long> fileIDOpt = Optional.empty();
|
||||||
|
private Task<Image> imageTask;
|
||||||
|
private SoftReference<Image> imageCache;
|
||||||
|
private ProgressIndicator progressIndicator;
|
||||||
|
|
||||||
public DrawableUIBase(ImageGalleryController controller) {
|
public DrawableUIBase(ImageGalleryController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
@ -86,10 +107,133 @@ abstract public class DrawableUIBase extends AnchorPane implements DrawableView
|
|||||||
synchronized public void setFile(Long newFileID) {
|
synchronized public void setFile(Long newFileID) {
|
||||||
if (getFileID().isPresent()) {
|
if (getFileID().isPresent()) {
|
||||||
if (Objects.equals(newFileID, getFileID().get()) == false) {
|
if (Objects.equals(newFileID, getFileID().get()) == false) {
|
||||||
|
if (Objects.nonNull(newFileID)) {
|
||||||
setFileHelper(newFileID);
|
setFileHelper(newFileID);
|
||||||
}
|
}
|
||||||
} else if (nonNull(newFileID)) {
|
}
|
||||||
|
} else if (Objects.nonNull(newFileID)) {
|
||||||
setFileHelper(newFileID);
|
setFileHelper(newFileID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized protected void updateContent() {
|
||||||
|
Node content = getContentNode();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
imageBorder.setCenter(content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized protected void disposeContent() {
|
||||||
|
if (imageTask != null) {
|
||||||
|
imageTask.cancel(true);
|
||||||
|
}
|
||||||
|
imageTask = null;
|
||||||
|
imageCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgressIndicator getLoadingProgressIndicator() {
|
||||||
|
if (progressIndicator == null) {
|
||||||
|
progressIndicator = new ProgressIndicator();
|
||||||
|
}
|
||||||
|
return progressIndicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node getContentNode() {
|
||||||
|
if (getFile().isPresent() == false) {
|
||||||
|
imageCache = null;
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (imageView != null) {
|
||||||
|
imageView.setImage(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
Image thumbnail = isNull(imageCache) ? null : imageCache.get();
|
||||||
|
|
||||||
|
if (nonNull(thumbnail)) {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
if (imageView != null) {
|
||||||
|
imageView.setImage(thumbnail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return imageView;
|
||||||
|
} else {
|
||||||
|
DrawableFile<?> file = getFile().get();
|
||||||
|
|
||||||
|
if (isNull(imageTask)) {
|
||||||
|
imageTask = getNewImageLoadTask(file);
|
||||||
|
new Thread(imageTask).start();
|
||||||
|
} else if (imageTask.isDone()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getLoadingProgressIndicator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract CachedLoaderTask<Image, DrawableFile<?>> getNewImageLoadTask(DrawableFile<?> file);
|
||||||
|
|
||||||
|
abstract class CachedLoaderTask<X, Y extends DrawableFile<?>> extends Task<X> {
|
||||||
|
|
||||||
|
protected final Y file;
|
||||||
|
|
||||||
|
public CachedLoaderTask(Y file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected X call() throws Exception {
|
||||||
|
return (isCancelled() == false) ? load() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract X load();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
super.succeeded();
|
||||||
|
if (isCancelled() == false) {
|
||||||
|
try {
|
||||||
|
saveToCache(get());
|
||||||
|
updateContent();
|
||||||
|
} catch (InterruptedException | ExecutionException ex) {
|
||||||
|
LOGGER.log(Level.WARNING, "Failed to cache content for" + file.getName(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
super.failed();
|
||||||
|
LOGGER.log(Level.SEVERE, "Failed to cache content for" + file.getName(), getException());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract void saveToCache(X result);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ImageLoaderTask extends CachedLoaderTask<Image, DrawableFile<?>> {
|
||||||
|
|
||||||
|
public ImageLoaderTask(DrawableFile<?> file) {
|
||||||
|
super(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void saveToCache(Image result) {
|
||||||
|
synchronized (DrawableUIBase.this) {
|
||||||
|
imageCache = new SoftReference<>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThumbnailLoaderTask extends ImageLoaderTask {
|
||||||
|
|
||||||
|
public ThumbnailLoaderTask(DrawableFile<?> file) {
|
||||||
|
super(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Image load() {
|
||||||
|
return isCancelled() ? null : file.getThumbnail();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ public class GroupPane extends BorderPane {
|
|||||||
|
|
||||||
private final InvalidationListener filesSyncListener = (observable) -> {
|
private final InvalidationListener filesSyncListener = (observable) -> {
|
||||||
final String header = getHeaderString();
|
final String header = getHeaderString();
|
||||||
final List<Long> fileIds = getGrouping().fileIds();
|
final List<Long> fileIds = getGroup().fileIds();
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
slideShowToggle.setDisable(fileIds.isEmpty());
|
slideShowToggle.setDisable(fileIds.isEmpty());
|
||||||
gridView.getItems().setAll(fileIds);
|
gridView.getItems().setAll(fileIds);
|
||||||
@ -247,8 +247,8 @@ public class GroupPane extends BorderPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//assign last selected file or if none first file in group
|
//assign last selected file or if none first file in group
|
||||||
if (slideShowFileID == null || getGrouping().fileIds().contains(slideShowFileID) == false) {
|
if (slideShowFileID == null || getGroup().fileIds().contains(slideShowFileID) == false) {
|
||||||
slideShowPane.setFile(getGrouping().fileIds().get(0));
|
slideShowPane.setFile(getGroup().fileIds().get(0));
|
||||||
} else {
|
} else {
|
||||||
slideShowPane.setFile(slideShowFileID);
|
slideShowPane.setFile(slideShowFileID);
|
||||||
}
|
}
|
||||||
@ -269,7 +269,7 @@ public class GroupPane extends BorderPane {
|
|||||||
this.scrollToFileID(globalSelectionModel.lastSelectedProperty().get());
|
this.scrollToFileID(globalSelectionModel.lastSelectedProperty().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawableGroup getGrouping() {
|
public DrawableGroup getGroup() {
|
||||||
return grouping.get();
|
return grouping.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ public class GroupPane extends BorderPane {
|
|||||||
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent t) {
|
public void handle(ActionEvent t) {
|
||||||
Set<Long> fileIdSet = new HashSet<>(getGrouping().fileIds());
|
Set<Long> fileIdSet = new HashSet<>(getGroup().fileIds());
|
||||||
new CategorizeAction(controller).addTagsToFiles(controller.getTagsManager().getTagName(cat), "", fileIdSet);
|
new CategorizeAction(controller).addTagsToFiles(controller.getTagsManager().getTagName(cat), "", fileIdSet);
|
||||||
|
|
||||||
grpCatSplitMenu.setText(cat.getDisplayName());
|
grpCatSplitMenu.setText(cat.getDisplayName());
|
||||||
@ -293,7 +293,7 @@ public class GroupPane extends BorderPane {
|
|||||||
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
menuItem.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent t) {
|
public void handle(ActionEvent t) {
|
||||||
Set<Long> fileIdSet = new HashSet<>(getGrouping().fileIds());
|
Set<Long> fileIdSet = new HashSet<>(getGroup().fileIds());
|
||||||
new AddDrawableTagAction(controller).addTagsToFiles(tn, "", fileIdSet);
|
new AddDrawableTagAction(controller).addTagsToFiles(tn, "", fileIdSet);
|
||||||
|
|
||||||
grpTagSplitMenu.setText(tn.getDisplayName());
|
grpTagSplitMenu.setText(tn.getDisplayName());
|
||||||
@ -304,14 +304,14 @@ public class GroupPane extends BorderPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void selectAllFiles() {
|
private void selectAllFiles() {
|
||||||
globalSelectionModel.clearAndSelectAll(getGrouping().fileIds());
|
globalSelectionModel.clearAndSelectAll(getGroup().fileIds());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** create the string to display in the group header */
|
/** create the string to display in the group header */
|
||||||
protected String getHeaderString() {
|
protected String getHeaderString() {
|
||||||
return isNull(getGrouping()) ? ""
|
return isNull(getGroup()) ? ""
|
||||||
: StringUtils.defaultIfBlank(getGrouping().getGroupByValueDislpayName(), DrawableGroup.getBlankGroupName()) + " -- "
|
: StringUtils.defaultIfBlank(getGroup().getGroupByValueDislpayName(), DrawableGroup.getBlankGroupName()) + " -- "
|
||||||
+ getGrouping().getHashSetHitsCount() + " hash set hits / " + getGrouping().getSize() + " files";
|
+ getGroup().getHashSetHitsCount() + " hash set hits / " + getGroup().getSize() + " files";
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextMenu getContextMenu() {
|
ContextMenu getContextMenu() {
|
||||||
@ -580,11 +580,11 @@ public class GroupPane extends BorderPane {
|
|||||||
* @param grouping the new grouping assigned to this group
|
* @param grouping the new grouping assigned to this group
|
||||||
*/
|
*/
|
||||||
void setViewState(GroupViewState viewState) {
|
void setViewState(GroupViewState viewState) {
|
||||||
if (nonNull(getGrouping())) {
|
|
||||||
getGrouping().fileIds().removeListener(filesSyncListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNull(viewState) || isNull(viewState.getGroup())) {
|
if (isNull(viewState) || isNull(viewState.getGroup())) {
|
||||||
|
if (nonNull(getGroup())) {
|
||||||
|
getGroup().fileIds().removeListener(filesSyncListener);
|
||||||
|
}
|
||||||
this.grouping.set(null);
|
this.grouping.set(null);
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
@ -600,15 +600,18 @@ public class GroupPane extends BorderPane {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (this.grouping.get() != viewState.getGroup()) {
|
if (getGroup() != viewState.getGroup()) {
|
||||||
|
if (nonNull(getGroup())) {
|
||||||
|
getGroup().fileIds().removeListener(filesSyncListener);
|
||||||
|
}
|
||||||
this.grouping.set(viewState.getGroup());
|
this.grouping.set(viewState.getGroup());
|
||||||
|
|
||||||
this.getGrouping().fileIds().addListener(filesSyncListener);
|
getGroup().fileIds().addListener(filesSyncListener);
|
||||||
|
|
||||||
final String header = getHeaderString();
|
final String header = getHeaderString();
|
||||||
|
|
||||||
gridView.getItems().setAll(getGrouping().fileIds());
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
|
gridView.getItems().setAll(getGroup().fileIds());
|
||||||
slideShowToggle.setDisable(gridView.getItems().isEmpty());
|
slideShowToggle.setDisable(gridView.getItems().isEmpty());
|
||||||
groupLabel.setText(header);
|
groupLabel.setText(header);
|
||||||
resetScrollBar();
|
resetScrollBar();
|
||||||
@ -687,6 +690,7 @@ public class GroupPane extends BorderPane {
|
|||||||
@Override
|
@Override
|
||||||
protected void updateItem(Long item, boolean empty) {
|
protected void updateItem(Long item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
super.updateItem(item, empty);
|
||||||
|
|
||||||
tile.setFile(item);
|
tile.setFile(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<children>
|
<children>
|
||||||
<VBox alignment="TOP_CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<VBox alignment="TOP_CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<children>
|
||||||
<BorderPane id="imageAnchor" fx:id="imageBorder" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="NEVER">
|
<BorderPane id="imageAnchor" fx:id="imageBorder" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="220.0" minWidth="220.0" prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="NEVER">
|
||||||
<center>
|
<center>
|
||||||
<ImageView fx:id="imageView" fitHeight="200.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
|
<ImageView fx:id="imageView" fitHeight="200.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
|
||||||
</center>
|
</center>
|
||||||
|
@ -38,7 +38,6 @@ import javafx.scene.control.TableColumn;
|
|||||||
import javafx.scene.control.TableView;
|
import javafx.scene.control.TableView;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.util.Pair;
|
import javafx.util.Pair;
|
||||||
@ -50,6 +49,7 @@ import org.sleuthkit.autopsy.imagegallery.ImageGalleryController;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.CategoryManager;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.datamodel.ContentTag;
|
import org.sleuthkit.datamodel.ContentTag;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
|
|
||||||
@ -60,9 +60,6 @@ public class MetaDataPane extends DrawableUIBase {
|
|||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MetaDataPane.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(MetaDataPane.class.getName());
|
||||||
|
|
||||||
@FXML
|
|
||||||
private ImageView imageView;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Pair<DrawableAttribute<?>, ? extends Object>, DrawableAttribute<?>> attributeColumn;
|
private TableColumn<Pair<DrawableAttribute<?>, ? extends Object>, DrawableAttribute<?>> attributeColumn;
|
||||||
|
|
||||||
@ -72,9 +69,6 @@ public class MetaDataPane extends DrawableUIBase {
|
|||||||
@FXML
|
@FXML
|
||||||
private TableColumn<Pair<DrawableAttribute<?>, ? extends Object>, String> valueColumn;
|
private TableColumn<Pair<DrawableAttribute<?>, ? extends Object>, String> valueColumn;
|
||||||
|
|
||||||
@FXML
|
|
||||||
private BorderPane imageBorder;
|
|
||||||
|
|
||||||
public MetaDataPane(ImageGalleryController controller) {
|
public MetaDataPane(ImageGalleryController controller) {
|
||||||
super(controller);
|
super(controller);
|
||||||
|
|
||||||
@ -155,22 +149,27 @@ public class MetaDataPane extends DrawableUIBase {
|
|||||||
if (newFileID == null) {
|
if (newFileID == null) {
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
imageView.setImage(null);
|
imageView.setImage(null);
|
||||||
|
imageBorder.setCenter(null);
|
||||||
tableView.getItems().clear();
|
tableView.getItems().clear();
|
||||||
getCategoryBorderRegion().setBorder(null);
|
getCategoryBorderRegion().setBorder(null);
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
disposeContent();
|
||||||
updateUI();
|
updateUI();
|
||||||
|
updateContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
CachedLoaderTask<Image, DrawableFile<?>> getNewImageLoadTask(DrawableFile<?> file) {
|
||||||
|
return new ThumbnailLoaderTask(file);
|
||||||
|
}
|
||||||
|
|
||||||
public void updateUI() {
|
public void updateUI() {
|
||||||
getFile().ifPresent(file -> {
|
getFile().ifPresent(file -> {
|
||||||
final Image icon = file.getThumbnail();
|
|
||||||
final ObservableList<Pair<DrawableAttribute<?>, ? extends Object>> attributesList = file.getAttributesList();
|
final ObservableList<Pair<DrawableAttribute<?>, ? extends Object>> attributesList = file.getAttributesList();
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
imageView.setImage(icon);
|
|
||||||
tableView.getItems().clear();
|
tableView.getItems().clear();
|
||||||
tableView.getItems().setAll(attributesList);
|
tableView.getItems().setAll(attributesList);
|
||||||
});
|
});
|
||||||
|
@ -127,7 +127,10 @@
|
|||||||
</BorderPane>
|
</BorderPane>
|
||||||
</bottom>
|
</bottom>
|
||||||
<center>
|
<center>
|
||||||
<BorderPane fx:id="imageBorder" center="$imageView" maxHeight="-1.0" prefHeight="-1.0" prefWidth="-1.0" style="-fx-border-color: lightgray; -fx-border-width:10; -fx-border-radius:2;" BorderPane.alignment="CENTER" />
|
<BorderPane fx:id="imageBorder" center="$imageView" maxHeight="-1.0" prefHeight="-1.0" prefWidth="-1.0" style="-fx-border-color: lightgray; -fx-border-width:10; -fx-border-radius:2;" BorderPane.alignment="CENTER">
|
||||||
|
<center>
|
||||||
|
<ImageView fx:id="imageView" fitHeight="200.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" BorderPane.alignment="CENTER" />
|
||||||
|
</center></BorderPane>
|
||||||
</center>
|
</center>
|
||||||
</BorderPane>
|
</BorderPane>
|
||||||
</children>
|
</children>
|
@ -18,21 +18,27 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
package org.sleuthkit.autopsy.imagegallery.gui.drawableviews;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.SoftReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.function.Function;
|
import static java.util.Objects.isNull;
|
||||||
|
import static java.util.Objects.nonNull;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.MenuItem;
|
import javafx.scene.control.MenuItem;
|
||||||
import javafx.scene.control.SplitMenuButton;
|
import javafx.scene.control.SplitMenuButton;
|
||||||
import javafx.scene.control.ToggleButton;
|
import javafx.scene.control.ToggleButton;
|
||||||
import javafx.scene.control.ToolBar;
|
import javafx.scene.control.ToolBar;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import static javafx.scene.input.KeyCode.LEFT;
|
import static javafx.scene.input.KeyCode.LEFT;
|
||||||
import static javafx.scene.input.KeyCode.RIGHT;
|
import static javafx.scene.input.KeyCode.RIGHT;
|
||||||
@ -46,6 +52,10 @@ import javafx.scene.layout.CornerRadii;
|
|||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.media.Media;
|
||||||
|
import javafx.scene.media.MediaException;
|
||||||
|
import javafx.scene.media.MediaPlayer;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
import org.openide.util.Exceptions;
|
import org.openide.util.Exceptions;
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||||
@ -56,10 +66,9 @@ import org.sleuthkit.autopsy.imagegallery.actions.CategorizeAction;
|
|||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.Category;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableAttribute;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.DrawableFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.ImageFile;
|
|
||||||
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
import org.sleuthkit.autopsy.imagegallery.datamodel.VideoFile;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
|
import org.sleuthkit.autopsy.imagegallery.gui.GuiUtils;
|
||||||
import org.sleuthkit.autopsy.imagegallery.gui.MediaControl;
|
import org.sleuthkit.autopsy.imagegallery.gui.VideoPlayer;
|
||||||
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableView.CAT_BORDER_WIDTH;
|
import static org.sleuthkit.autopsy.imagegallery.gui.drawableviews.DrawableView.CAT_BORDER_WIDTH;
|
||||||
import org.sleuthkit.datamodel.TagName;
|
import org.sleuthkit.datamodel.TagName;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
@ -100,31 +109,35 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
private ToolBar toolBar;
|
private ToolBar toolBar;
|
||||||
@FXML
|
@FXML
|
||||||
private BorderPane footer;
|
private BorderPane footer;
|
||||||
|
private Task<Node> mediaTask;
|
||||||
|
|
||||||
SlideShowView(GroupPane gp) {
|
SlideShowView(GroupPane gp) {
|
||||||
super(gp);
|
super(gp);
|
||||||
FXMLConstructor.construct(this, "SlideShow.fxml");
|
FXMLConstructor.construct(this, "SlideShowView.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@Override
|
@Override
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
super.initialize();
|
super.initialize();
|
||||||
assert cat0Toggle != null : "fx:id=\"cat0Toggle\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert cat0Toggle != null : "fx:id=\"cat0Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert cat1Toggle != null : "fx:id=\"cat1Toggle\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert cat1Toggle != null : "fx:id=\"cat1Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert cat2Toggle != null : "fx:id=\"cat2Toggle\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert cat2Toggle != null : "fx:id=\"cat2Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert cat3Toggle != null : "fx:id=\"cat3Toggle\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert cat3Toggle != null : "fx:id=\"cat3Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert cat4Toggle != null : "fx:id=\"cat4Toggle\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert cat4Toggle != null : "fx:id=\"cat4Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert cat5Toggle != null : "fx:id=\"cat5Toggle\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert cat5Toggle != null : "fx:id=\"cat5Toggle\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert leftButton != null : "fx:id=\"leftButton\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert leftButton != null : "fx:id=\"leftButton\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert rightButton != null : "fx:id=\"rightButton\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert rightButton != null : "fx:id=\"rightButton\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
assert tagSplitButton != null : "fx:id=\"tagSplitButton\" was not injected: check your FXML file 'SlideShow.fxml'.";
|
assert tagSplitButton != null : "fx:id=\"tagSplitButton\" was not injected: check your FXML file 'SlideShowView.fxml'.";
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
HBox.setHgrow(spring, Priority.ALWAYS);
|
HBox.setHgrow(spring, Priority.ALWAYS);
|
||||||
spring.setMinWidth(Region.USE_PREF_SIZE);
|
spring.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
imageView.fitWidthProperty().bind(imageBorder.widthProperty().subtract(CAT_BORDER_WIDTH * 2));
|
||||||
|
imageView.fitHeightProperty().bind(heightProperty().subtract(CAT_BORDER_WIDTH * 4).subtract(footer.heightProperty()).subtract(toolBar.heightProperty()));
|
||||||
|
|
||||||
tagSplitButton.setOnAction((ActionEvent t) -> {
|
tagSplitButton.setOnAction((ActionEvent t) -> {
|
||||||
try {
|
try {
|
||||||
GuiUtils.createSelTagMenuItem(getController().getTagsManager().getFollowUpTagName(), tagSplitButton, getController()).getOnAction().handle(t);
|
GuiUtils.createSelTagMenuItem(getController().getTagsManager().getFollowUpTagName(), tagSplitButton, getController()).getOnAction().handle(t);
|
||||||
@ -195,8 +208,8 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
|
|
||||||
getGroupPane().grouping().addListener((Observable observable) -> {
|
getGroupPane().grouping().addListener((Observable observable) -> {
|
||||||
syncButtonVisibility();
|
syncButtonVisibility();
|
||||||
if (getGroupPane().getGrouping() != null) {
|
if (getGroupPane().getGroup() != null) {
|
||||||
getGroupPane().getGrouping().fileIds().addListener((Observable observable1) -> {
|
getGroupPane().getGroup().fileIds().addListener((Observable observable1) -> {
|
||||||
syncButtonVisibility();
|
syncButtonVisibility();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -206,7 +219,7 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
@ThreadConfined(type = ThreadType.ANY)
|
@ThreadConfined(type = ThreadType.ANY)
|
||||||
private void syncButtonVisibility() {
|
private void syncButtonVisibility() {
|
||||||
try {
|
try {
|
||||||
final boolean hasMultipleFiles = getGroupPane().getGrouping().fileIds().size() > 1;
|
final boolean hasMultipleFiles = getGroupPane().getGroup().fileIds().size() > 1;
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
rightButton.setVisible(hasMultipleFiles);
|
rightButton.setVisible(hasMultipleFiles);
|
||||||
leftButton.setVisible(hasMultipleFiles);
|
leftButton.setVisible(hasMultipleFiles);
|
||||||
@ -221,8 +234,8 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
|
|
||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
public void stopVideo() {
|
public void stopVideo() {
|
||||||
if (imageBorder.getCenter() instanceof MediaControl) {
|
if (imageBorder.getCenter() instanceof VideoPlayer) {
|
||||||
((MediaControl) imageBorder.getCenter()).stopVideo();
|
((VideoPlayer) imageBorder.getCenter()).stopVideo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,40 +252,40 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
@Override
|
@Override
|
||||||
protected void disposeContent() {
|
protected void disposeContent() {
|
||||||
stopVideo();
|
stopVideo();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
super.disposeContent();
|
||||||
@ThreadConfined(type = ThreadType.UI)
|
if (mediaTask != null) {
|
||||||
protected void clearContent() {
|
mediaTask.cancel(true);
|
||||||
stopVideo();
|
|
||||||
imageBorder.setCenter(null);
|
|
||||||
}
|
}
|
||||||
|
mediaTask = null;
|
||||||
|
mediaCache = null;
|
||||||
|
}
|
||||||
|
private SoftReference<Node> mediaCache;
|
||||||
|
|
||||||
/** {@inheritDoc } */
|
/** {@inheritDoc } */
|
||||||
@Override
|
@Override
|
||||||
protected Runnable getContentUpdateRunnable() {
|
Node getContentNode() {
|
||||||
|
if (getFile().isPresent() == false) {
|
||||||
return getFile().map(new Function<DrawableFile<?>, Runnable>() {
|
mediaCache = null;
|
||||||
|
return super.getContentNode();
|
||||||
@Override
|
|
||||||
public Runnable apply(DrawableFile<?> file) {
|
|
||||||
|
|
||||||
if (file.isVideo()) {
|
|
||||||
return () -> {
|
|
||||||
imageBorder.setCenter(MediaControl.create((VideoFile<?>) file));
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
ImageView imageView = new ImageView(((ImageFile<?>) file).getFullSizeImage());
|
DrawableFile<?> file = getFile().get();
|
||||||
imageView.setPreserveRatio(true);
|
if (file.isVideo()) {
|
||||||
imageView.fitWidthProperty().bind(imageBorder.widthProperty().subtract(CAT_BORDER_WIDTH * 2));
|
Node mediaNode = (isNull(mediaCache)) ? null : mediaCache.get();
|
||||||
imageView.fitHeightProperty().bind(heightProperty().subtract(CAT_BORDER_WIDTH * 4).subtract(footer.heightProperty()).subtract(toolBar.heightProperty()));
|
if (nonNull(mediaNode)) {
|
||||||
return () -> {
|
return mediaNode;
|
||||||
imageBorder.setCenter(imageView);
|
} else {
|
||||||
};
|
if (isNull(mediaTask)) {
|
||||||
|
mediaTask = new MediaLoadTask(((VideoFile<?>) file));
|
||||||
|
new Thread(mediaTask).start();
|
||||||
|
} else if (mediaTask.isDone()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getLoadingProgressIndicator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).orElse(() -> {
|
return super.getContentNode();
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc } */
|
/** {@inheritDoc } */
|
||||||
@ -292,12 +305,12 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
@ThreadConfined(type = ThreadType.JFX)
|
@ThreadConfined(type = ThreadType.JFX)
|
||||||
synchronized private void cycleSlideShowImage(int direction) {
|
synchronized private void cycleSlideShowImage(int direction) {
|
||||||
stopVideo();
|
stopVideo();
|
||||||
final int groupSize = getGroupPane().getGrouping().fileIds().size();
|
final int groupSize = getGroupPane().getGroup().fileIds().size();
|
||||||
final Integer nextIndex = getFileID().map(fileID -> {
|
final Integer nextIndex = getFileID().map(fileID -> {
|
||||||
final int currentIndex = getGroupPane().getGrouping().fileIds().indexOf(fileID);
|
final int currentIndex = getGroupPane().getGroup().fileIds().indexOf(fileID);
|
||||||
return (currentIndex + direction + groupSize) % groupSize;
|
return (currentIndex + direction + groupSize) % groupSize;
|
||||||
}).orElse(0);
|
}).orElse(0);
|
||||||
setFile(getGroupPane().getGrouping().fileIds().get(nextIndex)
|
setFile(getGroupPane().getGroup().fileIds().get(nextIndex)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +319,7 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
* of y"
|
* of y"
|
||||||
*/
|
*/
|
||||||
private String getSupplementalText() {
|
private String getSupplementalText() {
|
||||||
final ObservableList<Long> fileIds = getGroupPane().getGrouping().fileIds();
|
final ObservableList<Long> fileIds = getGroupPane().getGroup().fileIds();
|
||||||
return getFileID().map(fileID -> " ( " + (fileIds.indexOf(fileID) + 1) + " of " + fileIds.size() + " in group )")
|
return getFileID().map(fileID -> " ( " + (fileIds.indexOf(fileID) + 1) + " of " + fileIds.size() + " in group )")
|
||||||
.orElse("");
|
.orElse("");
|
||||||
|
|
||||||
@ -365,4 +378,49 @@ public class SlideShowView extends DrawableTileBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
CachedLoaderTask<Image, DrawableFile<?>> getNewImageLoadTask(DrawableFile<?> file) {
|
||||||
|
|
||||||
|
return new ImageLoaderTask(file) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Image load() {
|
||||||
|
return isCancelled() ? null : file.getFullSizeImage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MediaLoadTask extends CachedLoaderTask<Node, VideoFile<?>> {
|
||||||
|
|
||||||
|
public MediaLoadTask(VideoFile<?> file) {
|
||||||
|
super(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void saveToCache(Node result) {
|
||||||
|
synchronized (SlideShowView.this) {
|
||||||
|
mediaCache = new SoftReference<>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Node load() {
|
||||||
|
try {
|
||||||
|
final Media media = file.getMedia();
|
||||||
|
return new VideoPlayer(new MediaPlayer(media), file);
|
||||||
|
} catch (MediaException | IOException | OutOfMemoryError ex) {
|
||||||
|
Logger.getLogger(VideoFile.class.getName()).log(Level.WARNING, "failed to initialize MediaControl for file " + file.getName(), ex);
|
||||||
|
|
||||||
|
if (file.isDisplayableAsImage()) {
|
||||||
|
Image fullSizeImage = file.getFullSizeImage();
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
imageView.setImage(fullSizeImage);
|
||||||
|
});
|
||||||
|
return imageView;
|
||||||
|
}
|
||||||
|
return new Text(ex.getLocalizedMessage() + "\nSee the logs for details.\n\nTry the \"Open In External Viewer\" action.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,8 @@ KeywordSearch.cancelImportMsg=Cancel import
|
|||||||
KeywordSearch.overwriteListPrompt=Keyword list <{0}> already exists locally, overwrite?
|
KeywordSearch.overwriteListPrompt=Keyword list <{0}> already exists locally, overwrite?
|
||||||
KeywordSearch.importOwConflict=Import list conflict
|
KeywordSearch.importOwConflict=Import list conflict
|
||||||
KeywordSearch.kwListFailImportMsg=Keyword list not imported
|
KeywordSearch.kwListFailImportMsg=Keyword list not imported
|
||||||
KeywordSearchListsManagementPanel.fileExtensionFilterLbl=Keyword List File
|
KeywordSearchListsManagementPanel.fileExtensionFilterLbl=Autopsy Keyword List File (xml)
|
||||||
|
KeywordSearchListsManagementPanel.fileExtensionFilterLb2=Encase Keyword List File (txt)
|
||||||
KeywordSearch.listImportFeatureTitle=Keyword List Import
|
KeywordSearch.listImportFeatureTitle=Keyword List Import
|
||||||
KeywordSearchIngestModule.moduleName=Keyword Search
|
KeywordSearchIngestModule.moduleName=Keyword Search
|
||||||
KeywordSearchIngestModule.moduleDescription=Performs file indexing and periodic search using keywords and regular expressions in lists.
|
KeywordSearchIngestModule.moduleDescription=Performs file indexing and periodic search using keywords and regular expressions in lists.
|
||||||
|
@ -210,10 +210,15 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
|
|||||||
private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed
|
private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed
|
||||||
|
|
||||||
JFileChooser chooser = new JFileChooser();
|
JFileChooser chooser = new JFileChooser();
|
||||||
final String[] EXTENSION = new String[]{"xml", "txt"}; //NON-NLS
|
final String[] AUTOPSY_EXTENSIONS = new String[]{"xml"}; //NON-NLS
|
||||||
FileNameExtensionFilter filter = new FileNameExtensionFilter(
|
final String[] ENCASE_EXTENSIONS = new String[]{"txt"}; //NON-NLS
|
||||||
NbBundle.getMessage(this.getClass(), "KeywordSearchListsManagementPanel.fileExtensionFilterLbl"), EXTENSION);
|
FileNameExtensionFilter autopsyFilter = new FileNameExtensionFilter(
|
||||||
chooser.setFileFilter(filter);
|
NbBundle.getMessage(this.getClass(), "KeywordSearchListsManagementPanel.fileExtensionFilterLbl"), AUTOPSY_EXTENSIONS);
|
||||||
|
FileNameExtensionFilter encaseFilter = new FileNameExtensionFilter(
|
||||||
|
NbBundle.getMessage(this.getClass(), "KeywordSearchListsManagementPanel.fileExtensionFilterLb2"), ENCASE_EXTENSIONS);
|
||||||
|
chooser.addChoosableFileFilter(autopsyFilter);
|
||||||
|
chooser.addChoosableFileFilter(encaseFilter);
|
||||||
|
chooser.setAcceptAllFileFilterUsed(false);
|
||||||
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
|
||||||
|
|
||||||
String listName = null;
|
String listName = null;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#Updated by build script
|
#Updated by build script
|
||||||
#Wed, 15 Apr 2015 18:11:08 -0400
|
#Tue, 28 Jul 2015 13:44:22 -0400
|
||||||
LBL_splash_window_title=Starting Autopsy
|
LBL_splash_window_title=Starting Autopsy
|
||||||
SPLASH_HEIGHT=314
|
SPLASH_HEIGHT=314
|
||||||
SPLASH_WIDTH=538
|
SPLASH_WIDTH=538
|
||||||
@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18
|
|||||||
SplashRunningTextColor=0x0
|
SplashRunningTextColor=0x0
|
||||||
SplashRunningTextFontSize=19
|
SplashRunningTextFontSize=19
|
||||||
|
|
||||||
currentVersion=Autopsy 3.1.2
|
currentVersion=Autopsy 3.1.3
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#Updated by build script
|
#Updated by build script
|
||||||
#Wed, 15 Apr 2015 18:11:08 -0400
|
#Tue, 28 Jul 2015 13:44:22 -0400
|
||||||
|
|
||||||
CTL_MainWindow_Title=Autopsy 3.1.2
|
CTL_MainWindow_Title=Autopsy 3.1.3
|
||||||
CTL_MainWindow_Title_No_Project=Autopsy 3.1.2
|
CTL_MainWindow_Title_No_Project=Autopsy 3.1.3
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
branding.token=autopsy
|
branding.token=autopsy
|
||||||
|
nbjdk.active=JDK_1.8u40x64
|
||||||
# Version of platform that is automatically downloaded
|
# Version of platform that is automatically downloaded
|
||||||
# Note build.xml has similar definitions that should be kept in sync (manually)
|
# Note build.xml has similar definitions that should be kept in sync (manually)
|
||||||
netbeans-plat-version=7.3.1
|
netbeans-plat-version=7.3.1
|
||||||
@ -13,111 +14,7 @@ cluster.path=\
|
|||||||
${nbplatform.active.dir}/java:\
|
${nbplatform.active.dir}/java:\
|
||||||
${nbplatform.active.dir}/platform
|
${nbplatform.active.dir}/platform
|
||||||
disabled.modules=\
|
disabled.modules=\
|
||||||
org.apache.tools.ant.module,\
|
org.netbeans.modules.jellytools.java,\
|
||||||
org.netbeans.api.debugger.jpda,\
|
|
||||||
org.netbeans.api.java,\
|
|
||||||
org.netbeans.lib.nbjavac,\
|
|
||||||
org.netbeans.libs.cglib,\
|
|
||||||
org.netbeans.libs.javacapi,\
|
|
||||||
org.netbeans.libs.javacimpl,\
|
|
||||||
org.netbeans.libs.springframework,\
|
|
||||||
org.netbeans.modules.ant.browsetask,\
|
|
||||||
org.netbeans.modules.ant.debugger,\
|
|
||||||
org.netbeans.modules.ant.freeform,\
|
|
||||||
org.netbeans.modules.ant.grammar,\
|
|
||||||
org.netbeans.modules.ant.kit,\
|
|
||||||
org.netbeans.modules.beans,\
|
|
||||||
org.netbeans.modules.classfile,\
|
|
||||||
org.netbeans.modules.dbschema,\
|
|
||||||
org.netbeans.modules.debugger.jpda,\
|
|
||||||
org.netbeans.modules.debugger.jpda.ant,\
|
|
||||||
org.netbeans.modules.debugger.jpda.kit,\
|
|
||||||
org.netbeans.modules.debugger.jpda.projects,\
|
|
||||||
org.netbeans.modules.debugger.jpda.ui,\
|
|
||||||
org.netbeans.modules.debugger.jpda.visual,\
|
|
||||||
org.netbeans.modules.findbugs.installer,\
|
|
||||||
org.netbeans.modules.form,\
|
|
||||||
org.netbeans.modules.form.binding,\
|
|
||||||
org.netbeans.modules.form.j2ee,\
|
|
||||||
org.netbeans.modules.form.kit,\
|
|
||||||
org.netbeans.modules.form.nb,\
|
|
||||||
org.netbeans.modules.form.refactoring,\
|
|
||||||
org.netbeans.modules.hibernate,\
|
|
||||||
org.netbeans.modules.hibernatelib,\
|
|
||||||
org.netbeans.modules.hudson.ant,\
|
|
||||||
org.netbeans.modules.hudson.maven,\
|
|
||||||
org.netbeans.modules.i18n,\
|
|
||||||
org.netbeans.modules.i18n.form,\
|
|
||||||
org.netbeans.modules.j2ee.core.utilities,\
|
|
||||||
org.netbeans.modules.j2ee.eclipselink,\
|
|
||||||
org.netbeans.modules.j2ee.eclipselinkmodelgen,\
|
|
||||||
org.netbeans.modules.j2ee.jpa.refactoring,\
|
|
||||||
org.netbeans.modules.j2ee.jpa.verification,\
|
|
||||||
org.netbeans.modules.j2ee.metadata,\
|
|
||||||
org.netbeans.modules.j2ee.metadata.model.support,\
|
|
||||||
org.netbeans.modules.j2ee.persistence,\
|
|
||||||
org.netbeans.modules.j2ee.persistence.kit,\
|
|
||||||
org.netbeans.modules.j2ee.persistenceapi,\
|
|
||||||
org.netbeans.modules.java.api.common,\
|
|
||||||
org.netbeans.modules.java.debug,\
|
|
||||||
org.netbeans.modules.java.editor,\
|
|
||||||
org.netbeans.modules.java.editor.lib,\
|
|
||||||
org.netbeans.modules.java.examples,\
|
|
||||||
org.netbeans.modules.java.freeform,\
|
|
||||||
org.netbeans.modules.java.guards,\
|
|
||||||
org.netbeans.modules.java.helpset,\
|
|
||||||
org.netbeans.modules.java.hints,\
|
|
||||||
org.netbeans.modules.java.hints.declarative,\
|
|
||||||
org.netbeans.modules.java.hints.declarative.test,\
|
|
||||||
org.netbeans.modules.java.hints.legacy.spi,\
|
|
||||||
org.netbeans.modules.java.hints.test,\
|
|
||||||
org.netbeans.modules.java.hints.ui,\
|
|
||||||
org.netbeans.modules.java.j2seplatform,\
|
|
||||||
org.netbeans.modules.java.j2seproject,\
|
|
||||||
org.netbeans.modules.java.kit,\
|
|
||||||
org.netbeans.modules.java.lexer,\
|
|
||||||
org.netbeans.modules.java.navigation,\
|
|
||||||
org.netbeans.modules.java.platform,\
|
|
||||||
org.netbeans.modules.java.preprocessorbridge,\
|
|
||||||
org.netbeans.modules.java.project,\
|
|
||||||
org.netbeans.modules.java.source,\
|
|
||||||
org.netbeans.modules.java.source.ant,\
|
|
||||||
org.netbeans.modules.java.source.queries,\
|
|
||||||
org.netbeans.modules.java.source.queriesimpl,\
|
|
||||||
org.netbeans.modules.java.sourceui,\
|
|
||||||
org.netbeans.modules.java.testrunner,\
|
|
||||||
org.netbeans.modules.javadoc,\
|
|
||||||
org.netbeans.modules.javawebstart,\
|
|
||||||
org.netbeans.modules.junit,\
|
org.netbeans.modules.junit,\
|
||||||
org.netbeans.modules.maven,\
|
org.netbeans.modules.whitelist
|
||||||
org.netbeans.modules.maven.checkstyle,\
|
|
||||||
org.netbeans.modules.maven.coverage,\
|
|
||||||
org.netbeans.modules.maven.embedder,\
|
|
||||||
org.netbeans.modules.maven.grammar,\
|
|
||||||
org.netbeans.modules.maven.graph,\
|
|
||||||
org.netbeans.modules.maven.hints,\
|
|
||||||
org.netbeans.modules.maven.indexer,\
|
|
||||||
org.netbeans.modules.maven.junit,\
|
|
||||||
org.netbeans.modules.maven.kit,\
|
|
||||||
org.netbeans.modules.maven.model,\
|
|
||||||
org.netbeans.modules.maven.osgi,\
|
|
||||||
org.netbeans.modules.maven.persistence,\
|
|
||||||
org.netbeans.modules.maven.refactoring,\
|
|
||||||
org.netbeans.modules.maven.repository,\
|
|
||||||
org.netbeans.modules.maven.search,\
|
|
||||||
org.netbeans.modules.maven.spring,\
|
|
||||||
org.netbeans.modules.projectimport.eclipse.core,\
|
|
||||||
org.netbeans.modules.projectimport.eclipse.j2se,\
|
|
||||||
org.netbeans.modules.refactoring.java,\
|
|
||||||
org.netbeans.modules.spellchecker.bindings.java,\
|
|
||||||
org.netbeans.modules.spring.beans,\
|
|
||||||
org.netbeans.modules.testng,\
|
|
||||||
org.netbeans.modules.testng.ant,\
|
|
||||||
org.netbeans.modules.testng.maven,\
|
|
||||||
org.netbeans.modules.websvc.jaxws21,\
|
|
||||||
org.netbeans.modules.websvc.jaxws21api,\
|
|
||||||
org.netbeans.modules.websvc.saas.codegen.java,\
|
|
||||||
org.netbeans.modules.xml.jaxb,\
|
|
||||||
org.netbeans.modules.xml.tools.java,\
|
|
||||||
org.netbeans.spi.java.hints
|
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ app.version=3.1.3
|
|||||||
#build.type=RELEASE
|
#build.type=RELEASE
|
||||||
build.type=DEVELOPMENT
|
build.type=DEVELOPMENT
|
||||||
|
|
||||||
|
project.org.sleuthkit.autopsy.imagegallery=ImageGallery
|
||||||
update_versions=false
|
update_versions=false
|
||||||
#custom JVM options
|
#custom JVM options
|
||||||
#Note: can be higher on 64 bit systems, should be in sync with build.xml
|
#Note: can be higher on 64 bit systems, should be in sync with build.xml
|
||||||
@ -27,7 +28,8 @@ modules=\
|
|||||||
${project.org.sleuthkit.autopsy.testing}:\
|
${project.org.sleuthkit.autopsy.testing}:\
|
||||||
${project.org.sleuthkit.autopsy.thunderbirdparser}:\
|
${project.org.sleuthkit.autopsy.thunderbirdparser}:\
|
||||||
${project.org.sleuthkit.autopsy.core}:\
|
${project.org.sleuthkit.autopsy.core}:\
|
||||||
${project.org.sleuthkit.autopsy.corelibs}
|
${project.org.sleuthkit.autopsy.corelibs}:\
|
||||||
|
${project.org.sleuthkit.autopsy.imagegallery}
|
||||||
project.org.sleuthkit.autopsy.core=Core
|
project.org.sleuthkit.autopsy.core=Core
|
||||||
project.org.sleuthkit.autopsy.corelibs=CoreLibs
|
project.org.sleuthkit.autopsy.corelibs=CoreLibs
|
||||||
project.org.sleuthkit.autopsy.keywordsearch=KeywordSearch
|
project.org.sleuthkit.autopsy.keywordsearch=KeywordSearch
|
||||||
|
@ -870,7 +870,9 @@ class TestResultsDiffer(object):
|
|||||||
diff_path = os.path.splitext(os.path.basename(output_file))[0]
|
diff_path = os.path.splitext(os.path.basename(output_file))[0]
|
||||||
diff_path += "-Diff.txt"
|
diff_path += "-Diff.txt"
|
||||||
diff_file = codecs.open(diff_path, "wb", "utf_8")
|
diff_file = codecs.open(diff_path, "wb", "utf_8")
|
||||||
dffcmdlst = ["diff", output_file, gold_file]
|
|
||||||
|
# Gold needs to be passed in before output.
|
||||||
|
dffcmdlst = ["diff", gold_file, output_file]
|
||||||
subprocess.call(dffcmdlst, stdout = diff_file)
|
subprocess.call(dffcmdlst, stdout = diff_file)
|
||||||
Errors.add_errors_out(diff_path)
|
Errors.add_errors_out(diff_path)
|
||||||
|
|
||||||
@ -902,7 +904,8 @@ class TestResultsDiffer(object):
|
|||||||
gold_report_path = test_data.get_html_report_path(DBType.GOLD)
|
gold_report_path = test_data.get_html_report_path(DBType.GOLD)
|
||||||
output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
|
output_report_path = test_data.get_html_report_path(DBType.OUTPUT)
|
||||||
try:
|
try:
|
||||||
(subprocess.check_output(['diff', '-r', '-N', '-x', '*.png', '-x', '*.ico', '--ignore-matching-lines',
|
# Ensure gold is passed before output
|
||||||
|
(subprocess.check_output(["diff", '-r', '-N', '-x', '*.png', '-x', '*.ico', '--ignore-matching-lines',
|
||||||
'HTML Report Generated on \|Autopsy Report for case \|Case:\|Case Number:'
|
'HTML Report Generated on \|Autopsy Report for case \|Case:\|Case Number:'
|
||||||
'\|Examiner:', gold_report_path, output_report_path]))
|
'\|Examiner:', gold_report_path, output_report_path]))
|
||||||
print_report("", "REPORT COMPARISON", "The test reports matched the gold reports")
|
print_report("", "REPORT COMPARISON", "The test reports matched the gold reports")
|
||||||
|
@ -139,6 +139,7 @@ class TskDbDiff(object):
|
|||||||
|
|
||||||
# If they are different, invoke 'diff'
|
# If they are different, invoke 'diff'
|
||||||
diff_file = codecs.open(diff_path, "wb", "utf_8")
|
diff_file = codecs.open(diff_path, "wb", "utf_8")
|
||||||
|
# Gold needs to be passed in as 1st arg and output as 2nd
|
||||||
dffcmdlst = ["diff", gold_file, output_file]
|
dffcmdlst = ["diff", gold_file, output_file]
|
||||||
subprocess.call(dffcmdlst, stdout = diff_file)
|
subprocess.call(dffcmdlst, stdout = diff_file)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user