From 1c89d12e28c2c0da02f094a66e1ee471f6990965 Mon Sep 17 00:00:00 2001 From: "Oudsen, Erik" Date: Fri, 13 Oct 2023 10:08:11 +0200 Subject: [PATCH 1/4] Change Comma Separated Values to Tab Separated Values, due to change in how Hansken parses SQLite databases --- .../java/org/hansken/plugin/extraction/QuickLookPlugin.java | 6 +++--- python/quicklook/plugin/quicklook_plugin.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java b/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java index a47d921..4870a21 100644 --- a/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java +++ b/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java @@ -82,10 +82,10 @@ public void process(final Trace trace, final DataContext dataContext, final Trac // Search for SQLite tables "files" and "thumbnails" where the path matches the current trace final SearchTrace filesTrace = - searchForTrace(searcher, format("data.raw.fileType='Comma Separated Values' AND path='%s'", + searchForTrace(searcher, format("data.raw.fileType='Tab Separated Values' AND path='%s'", getExpectedTracePath(trace, "files"))); final SearchTrace thumbnailsTrace = - searchForTrace(searcher, format("data.raw.fileType='Comma Separated Values' AND path='%s'", + searchForTrace(searcher, format("data.raw.fileType='Tab Separated Values' AND path='%s'", getExpectedTracePath(trace, "thumbnails"))); // Parse the found traces of files and thumbnails and add as child traces @@ -248,4 +248,4 @@ static TableRow getFileInfo(final Map files, final TableRow t return files.get(fileId); } -} \ No newline at end of file +} diff --git a/python/quicklook/plugin/quicklook_plugin.py b/python/quicklook/plugin/quicklook_plugin.py index 29dfd50..863b498 100644 --- a/python/quicklook/plugin/quicklook_plugin.py +++ b/python/quicklook/plugin/quicklook_plugin.py @@ -167,12 +167,12 @@ def process(self, trace: ExtractionTrace, data_context, searcher): # Search for SQLite tables "files" and "thumbnails" where the path matches the current trace files_path = get_expected_trace_path(trace, "files") - files_trace = search_for_trace(searcher, f"data.raw.fileType='Comma Separated Values' AND path='{files_path}'") + files_trace = search_for_trace(searcher, f"data.raw.fileType='Tab Separated Values' AND path='{files_path}'") files = parse_database_table(files_trace) # Parse the contents of the "files"-table thumbnails_path = get_expected_trace_path(trace, "thumbnails") thumbnails_trace = search_for_trace(searcher, - f"data.raw.fileType='Comma Separated Values' AND path='{thumbnails_path}'") + f"data.raw.fileType='Tab Separated Values' AND path='{thumbnails_path}'") thumbnails = parse_database_table(thumbnails_trace) # Parse the contents of the "thumbnails"-table # Keep track of unused file indexes, there may be files which do not have a thumbnail anymore From ffb76a175ea3f3b93e2890adf76cc4734ade14a9 Mon Sep 17 00:00:00 2001 From: "Oudsen, Erik" Date: Fri, 13 Oct 2023 11:26:20 +0200 Subject: [PATCH 2/4] Make fileType either Tab or Comma separated --- .../java/org/hansken/plugin/extraction/QuickLookPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java b/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java index 4870a21..6393e0b 100644 --- a/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java +++ b/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java @@ -82,10 +82,10 @@ public void process(final Trace trace, final DataContext dataContext, final Trac // Search for SQLite tables "files" and "thumbnails" where the path matches the current trace final SearchTrace filesTrace = - searchForTrace(searcher, format("data.raw.fileType='Tab Separated Values' AND path='%s'", + searchForTrace(searcher, format("(data.raw.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='%s'", getExpectedTracePath(trace, "files"))); final SearchTrace thumbnailsTrace = - searchForTrace(searcher, format("data.raw.fileType='Tab Separated Values' AND path='%s'", + searchForTrace(searcher, format("(data.raw.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='%s'", getExpectedTracePath(trace, "thumbnails"))); // Parse the found traces of files and thumbnails and add as child traces From 4fea56a3c5a86b02e8441d0d91cc7da56ead110c Mon Sep 17 00:00:00 2001 From: "Oudsen, Erik" Date: Fri, 13 Oct 2023 11:28:47 +0200 Subject: [PATCH 3/4] Make filetype either tab or comma separated in python quicklook plugin --- python/quicklook/plugin/quicklook_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/quicklook/plugin/quicklook_plugin.py b/python/quicklook/plugin/quicklook_plugin.py index 863b498..e458f8c 100644 --- a/python/quicklook/plugin/quicklook_plugin.py +++ b/python/quicklook/plugin/quicklook_plugin.py @@ -167,12 +167,12 @@ def process(self, trace: ExtractionTrace, data_context, searcher): # Search for SQLite tables "files" and "thumbnails" where the path matches the current trace files_path = get_expected_trace_path(trace, "files") - files_trace = search_for_trace(searcher, f"data.raw.fileType='Tab Separated Values' AND path='{files_path}'") + files_trace = search_for_trace(searcher, f"(data.raw.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='{files_path}'") files = parse_database_table(files_trace) # Parse the contents of the "files"-table thumbnails_path = get_expected_trace_path(trace, "thumbnails") thumbnails_trace = search_for_trace(searcher, - f"data.raw.fileType='Tab Separated Values' AND path='{thumbnails_path}'") + f"(data.raw.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='{thumbnails_path}'") thumbnails = parse_database_table(thumbnails_trace) # Parse the contents of the "thumbnails"-table # Keep track of unused file indexes, there may be files which do not have a thumbnail anymore From 8736ca9237c37ead0ebe5cb5420a413f36e1d6c9 Mon Sep 17 00:00:00 2001 From: "Oudsen, Erik" Date: Mon, 16 Oct 2023 14:15:53 +0200 Subject: [PATCH 4/4] Update version number and add parsing of TSV. --- java/quicklook/pom.xml | 2 +- .../plugin/extraction/QuickLookPlugin.java | 49 +++++++++++++------ 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/java/quicklook/pom.xml b/java/quicklook/pom.xml index 7092f21..8681c4a 100644 --- a/java/quicklook/pom.xml +++ b/java/quicklook/pom.xml @@ -11,7 +11,7 @@ quicklookplugin - 1.0.0 + 1.0.1 8.27 diff --git a/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java b/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java index 6393e0b..73aef26 100644 --- a/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java +++ b/java/quicklook/src/main/java/org/hansken/plugin/extraction/QuickLookPlugin.java @@ -64,7 +64,6 @@ public PluginInfo pluginInfo() { // the plugin is deferred at least for one extraction cycle *after* all the other extractions have finished. return PluginInfo.builderFor(this) .id(new PluginId("nfi.nl", "picture", "QuickLookPluginJava")) - .pluginVersion("1.0.0") .description("Example Extraction Plugin: This plugin extracts thumbnails from thumbnail.data and " + "index.sqlite found in com.apple.QuickLook.thumbnailcache.") .author(author) @@ -82,10 +81,10 @@ public void process(final Trace trace, final DataContext dataContext, final Trac // Search for SQLite tables "files" and "thumbnails" where the path matches the current trace final SearchTrace filesTrace = - searchForTrace(searcher, format("(data.raw.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='%s'", + searchForTrace(searcher, format("(data.plain.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='%s'", getExpectedTracePath(trace, "files"))); final SearchTrace thumbnailsTrace = - searchForTrace(searcher, format("(data.raw.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='%s'", + searchForTrace(searcher, format("(data.plain.fileType='Tab Separated Values' OR data.raw.fileType='Comma Separated Values') AND path='%s'", getExpectedTracePath(trace, "thumbnails"))); // Parse the found traces of files and thumbnails and add as child traces @@ -114,15 +113,30 @@ private Path getExpectedTracePath(final Trace trace, final String tableName) { private Map parseDatabaseTable(final SearchTrace searchTrace) throws IOException { final Map tableRows = new HashMap<>(); - final RandomAccessData data = searchTrace.getData("raw"); + final RandomAccessData data; + if (searchTrace.getDataTypes().contains("plain")) { + data = searchTrace.getData("plain"); + } + else { + data = searchTrace.getData("raw"); + } final Scanner scanner = new Scanner(new String(data.readNBytes((int) data.remaining()))); - final String[] keys = scanner.nextLine().split(","); + final String[] keys = scanner.nextLine().strip().split("[\t,]"); int idCounter = 1; // QuickLook Files table internal row id starts with index 1! while (scanner.hasNext()) { - final String nextLine = scanner.nextLine(); + final String nextLine = scanner.nextLine().strip(); // Regex matches all commas which are not between quotes - final String[] values = nextLine.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1); + final String[] values; + if (nextLine.contains("\t")) { + values = nextLine.split("\t"); + if (values.length > 15) { + values[16] = "\"" + values[16] + "\""; + } + } + else { + values = nextLine.split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1); + } final Map properties = IntStream.range(0, keys.length) .boxed() .collect(Collectors.toMap(i -> keys[i], i -> values[i])); @@ -130,7 +144,6 @@ private Map parseDatabaseTable(final SearchTrace searchTrace) tableRows.put(idCounter, new TableRow(properties)); idCounter++; } - return tableRows; } @@ -144,7 +157,7 @@ private void addChildTraces(final Trace trace, final TraceSearcher searcher, fin final TableRow fileInfo = getFileInfo(files, thumbnailInfo, unusedFileIndexes); addChildTrace(trace, thumbnailInfo, fileInfo, - parseFileInfoPlist(searcher, fileInfo), + parseFileInfoPlist(searcher, fileInfo, getIntProperty(thumbnailInfo, "file_id") - 1), getBufferedImage(thumbnailsData, thumbnailInfo), childIndex); childIndex++; @@ -154,7 +167,7 @@ private void addChildTraces(final Trace trace, final TraceSearcher searcher, fin // es which have no thumbnail data available anymore for (final Integer index : unusedFileIndexes) { final TableRow fileInfo = files.get(index); - addChildTrace(trace, null, fileInfo, parseFileInfoPlist(searcher, fileInfo), null, childIndex); + addChildTrace(trace, null, fileInfo, parseFileInfoPlist(searcher, fileInfo, index - 1), null, childIndex); childIndex++; } } @@ -214,14 +227,20 @@ private byte[] getThumbnailData(final RandomAccessData thumbnailsData, final Tab return convertRGBAToABGR(bufferRGBA); } - private Map parseFileInfoPlist(final TraceSearcher searcher, final TableRow fileInfo) + private Map parseFileInfoPlist(final TraceSearcher searcher, final TableRow fileInfo, final int rowIndex) throws ExecutionException, InterruptedException, IOException { final Map result = new HashMap<>(); - // Get the plist trace by its name which is known by the fileInfo property "version" - final String plistName = fileInfo.getProperty("version") - .replace("", ""); + final String version = fileInfo.getProperty("version"); + final String plistName; + if (version.contains("[")) { + plistName = "RowIndex: " + rowIndex + ", ColumnName: version"; + } + else { + // Get the plist trace by its name which is known by the fileInfo property "version" + plistName = version.replace("", ""); + } final SearchTrace plistTrace = searchForTrace(searcher, format("data.raw.fileType='Binary Plist' AND name='%s'", plistName)); final RandomAccessData plistData = plistTrace.getData("plain");