Compare commits

..

20 Commits

Author SHA1 Message Date
919f45c79c Document info modal #19 2020-01-16 14:37:19 -05:00
d42129cfcb CI fix attempt 2020-01-15 20:11:45 -05:00
754983e34a Minor cleanup 2020-01-15 18:16:06 -05:00
7c8a3e2f9d Support for external json indices 2020-01-14 15:44:31 -05:00
3bb24b4453 Use bundled libtiff 2020-01-14 12:21:26 -05:00
9a56b959d3 Fix build problems... 2020-01-14 10:55:02 -05:00
5e3a2dbcc2 Update README 2020-01-14 10:47:00 -05:00
573f94f24e OCR support, remove static build 2020-01-14 10:26:40 -05:00
f5db78a69f Ignore special ascii chars, strip binary in docker build 2020-01-12 10:59:17 -05:00
5a2820d339 UI tweak auto-select based on query args 2020-01-11 17:48:51 -05:00
b7f13f425c Fix memory leaks (whoops) 2020-01-11 17:34:34 -05:00
d1a2f9b1d5 Strip binary (CI) 2020-01-07 14:32:39 -05:00
71f17986db build settings 2020-01-06 21:34:41 -05:00
acdd2fb3c1 Use bundled ffmpeg libraries 2020-01-06 16:25:34 -05:00
0cda6c00e1 CI attempt 2020-01-03 20:21:07 -05:00
14d0e5a1e1 possible fix for #18 2019-12-28 14:32:42 -05:00
0d06d39281 Path in list view #16 2019-12-28 14:32:05 -05:00
80708ca636 Merge pull request #17 from dpieski/patch-1
maybe a typo in cli.c
2019-12-23 18:33:28 -05:00
Andrew
43b7b40dc4 maybe a typo in cli.c
possibly corrected a typo
2019-12-23 13:18:18 -06:00
d051f541e2 Show client error on ES connection failure, fixes #13 2019-12-21 20:52:53 -05:00
50 changed files with 812 additions and 493 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ bundle*.css
bundle.js
*.a
vgcore.*
build/

21
.gitmodules vendored
View File

@@ -31,15 +31,12 @@
[submodule "lib/mupdf"]
path = lib/mupdf
url = git://git.ghostscript.com/mupdf.git
[submodule "lib/zstd"]
path = lib/zstd
url = https://github.com/facebook/zstd
[submodule "lib/lz4"]
path = lib/lz4
url = https://github.com/lz4/lz4
[submodule "lib/libarchive"]
path = lib/libarchive
url = https://github.com/libarchive/libarchive
[submodule "lib/libxml2"]
path = lib/libxml2
url = https://github.com/GNOME/libxml2
[submodule "lib/tesseract"]
path = lib/tesseract
url = https://github.com/tesseract-ocr/tesseract
[submodule "lib/leptonica"]
path = lib/leptonica
url = https://github.com/danbloomberg/leptonica
[submodule "lib/libtiff"]
path = lib/libtiff
url = https://gitlab.com/libtiff/libtiff

69
.teamcity/settings.kts vendored Normal file
View File

@@ -0,0 +1,69 @@
import jetbrains.buildServer.configs.kotlin.v2019_2.*
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.ExecBuildStep
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.exec
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
import jetbrains.buildServer.configs.kotlin.v2019_2.vcs.GitVcsRoot
/*
The settings script is an entry point for defining a TeamCity
project hierarchy. The script should contain a single call to the
project() function with a Project instance or an init function as
an argument.
VcsRoots, BuildTypes, Templates, and subprojects can be
registered inside the project using the vcsRoot(), buildType(),
template(), and subProject() methods respectively.
To debug settings scripts in command-line, run the
mvnDebug org.jetbrains.teamcity:teamcity-configs-maven-plugin:generate
command and attach your debugger to the port 8000.
To debug in IntelliJ Idea, open the 'Maven Projects' tool window (View
-> Tool Windows -> Maven Projects), find the generate task node
(Plugins -> teamcity-configs -> teamcity-configs:generate), the
'Debug' option is available in the context menu for the task.
*/
version = "2019.2"
project {
vcsRoot(HttpsGithubComSimon987sist2refsHeadsMaster)
buildType(Build)
}
object Build : BuildType({
name = "Build"
artifactRules = """
sist2
sist2_scan
""".trimIndent()
vcs {
root(HttpsGithubComSimon987sist2refsHeadsMaster)
}
steps {
exec {
name = "Build"
path = "./ci/build.sh"
dockerImage = "simon987/general_ci"
dockerImagePlatform = ExecBuildStep.ImagePlatform.Linux
dockerPull = true
}
}
triggers {
vcs {
}
}
})
object HttpsGithubComSimon987sist2refsHeadsMaster : GitVcsRoot({
name = "https://github.com/simon987/sist2#refs/heads/master"
url = "https://github.com/simon987/sist2"
})

View File

@@ -1,101 +1,57 @@
cmake_minimum_required(VERSION 3.7)
set(CMAKE_C_STANDARD 11)
option(WITH_SIST2 "Build main executable" ON)
option(WITH_SIST2_SCAN "Build scan executable" ON)
project(sist2 C)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMakeModules")
if (WITH_SIST2)
add_executable(
sist2
src/main.c
src/sist.h
src/io/walk.h src/io/walk.c
src/parsing/media.h src/parsing/media.c
src/parsing/pdf.h src/parsing/pdf.c
src/io/store.h src/io/store.c
src/tpool.h src/tpool.c
src/parsing/parse.h src/parsing/parse.c
src/io/serialize.h src/io/serialize.c
src/parsing/mime.h src/parsing/mime.c src/parsing/mime_generated.c
src/parsing/text.h src/parsing/text.c
src/index/web.c src/index/web.h
src/web/serve.c src/web/serve.h
src/web/auth_basic.h src/web/auth_basic.c
src/index/elastic.c src/index/elastic.h
src/util.c src/util.h
src/ctx.h src/types.h src/parsing/font.c src/parsing/font.h
src/parsing/arc.c src/parsing/arc.h
src/parsing/doc.c src/parsing/doc.h
src/log.c src/log.h
add_executable(
sist2
src/main.c
src/sist.h
src/io/walk.h src/io/walk.c
src/parsing/media.h src/parsing/media.c
src/parsing/pdf.h src/parsing/pdf.c
src/io/store.h src/io/store.c
src/tpool.h src/tpool.c
src/parsing/parse.h src/parsing/parse.c
src/io/serialize.h src/io/serialize.c
src/parsing/mime.h src/parsing/mime.c src/parsing/mime_generated.c
src/parsing/text.h src/parsing/text.c
src/index/web.c src/index/web.h
src/web/serve.c src/web/serve.h
src/web/auth_basic.h src/web/auth_basic.c
src/index/elastic.c src/index/elastic.h
src/util.c src/util.h
src/ctx.h src/types.h src/parsing/font.c src/parsing/font.h
src/parsing/arc.c src/parsing/arc.h
src/parsing/doc.c src/parsing/doc.h
src/log.c src/log.h
# argparse
argparse/argparse.h argparse/argparse.c
# argparse
argparse/argparse.h argparse/argparse.c
# cJSON
cJSON/cJSON.h cJSON/cJSON.c
# cJSON
cJSON/cJSON.h cJSON/cJSON.c
# LMDB
lmdb/libraries/liblmdb/lmdb.h lmdb/libraries/liblmdb/mdb.c
lmdb/libraries/liblmdb/midl.h lmdb/libraries/liblmdb/midl.c
src/cli.c src/cli.h
# LMDB
lmdb/libraries/liblmdb/lmdb.h lmdb/libraries/liblmdb/mdb.c
lmdb/libraries/liblmdb/midl.h lmdb/libraries/liblmdb/midl.c
src/cli.c src/cli.h
# utf8.h
utf8.h/utf8.h
)
endif ()
if (WITH_SIST2_SCAN)
add_executable(
sist2_scan
src/main.c
src/sist.h
src/io/walk.h src/io/walk.c
src/parsing/media.h src/parsing/media.c
src/parsing/pdf.h src/parsing/pdf.c
src/io/store.h src/io/store.c
src/tpool.h src/tpool.c
src/parsing/parse.h src/parsing/parse.c
src/io/serialize.h src/io/serialize.c
src/parsing/mime.h src/parsing/mime.c src/parsing/mime_generated.c
src/parsing/text.h src/parsing/text.c
src/util.c src/util.h
src/ctx.h src/types.h src/parsing/font.c src/parsing/font.h
src/parsing/arc.h src/parsing/arc.c
src/parsing/doc.h src/parsing/doc.c
src/log.h src/log.c
# argparse
argparse/argparse.h argparse/argparse.c
# cJSON
cJSON/cJSON.h cJSON/cJSON.c
# LMDB
lmdb/libraries/liblmdb/lmdb.h lmdb/libraries/liblmdb/mdb.c
lmdb/libraries/liblmdb/midl.h lmdb/libraries/liblmdb/midl.c
src/cli.c src/cli.h
# utf8.h
utf8.h/utf8.h
)
endif ()
# utf8.h
utf8.h/utf8.h
)
find_package(PkgConfig REQUIRED)
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig/")
find_package(LibMagic REQUIRED)
find_package(FFmpeg REQUIRED)
find_package(OpenSSL REQUIRED)
#find_package(OpenSSL REQUIRED)
find_package(Freetype REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0)
pkg_check_modules(GOBJECT REQUIRED gobject-2.0)
pkg_check_modules(UUID REQUIRED uuid)
add_definitions(${LIBMAGIC_CFLAGS_OTHER})
add_definitions(${UUID_CFLAGS_OTHER})
add_definitions(${GLIB_CFLAGS_OTHER})
add_definitions(${GOBJECT_CFLAGS_OTHER})
@@ -105,166 +61,80 @@ list(REMOVE_ITEM GLIB_LIBRARIES pcre)
list(REMOVE_ITEM GOBJECT_LIBRARIES pcre)
list(REMOVE_ITEM UUID_LIBRARIES pcre)
if (WITH_SIST2)
target_include_directories(
sist2 PUBLIC
${LIBMAGIC_INCLUDE_DIRS}
${GOBJECT_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
${FFMPEG_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
${UUID_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/
${PROJECT_SOURCE_DIR}/lmdb/libraries/liblmdb/
${PROJECT_SOURCE_DIR}/lib/onion/src/
${PROJECT_SOURCE_DIR}/lib/mupdf/include/
${PROJECT_SOURCE_DIR}/include/
/usr/include/libxml2/
)
target_link_directories(
sist2 PUBLIC
${UUID_LIBRARY_DIRS}
${FFMPEG_LIBRARY_DIRS}
)
target_include_directories(
sist2 PUBLIC
${GOBJECT_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/lib/ffmpeg/
${FREETYPE_INCLUDE_DIRS}
${UUID_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/
${PROJECT_SOURCE_DIR}/lmdb/libraries/liblmdb/
${PROJECT_SOURCE_DIR}/lib/onion/src/
${PROJECT_SOURCE_DIR}/lib/mupdf/include/
${PROJECT_SOURCE_DIR}/include/
/usr/include/libxml2/
${PROJECT_SOURCE_DIR}/lib/tesseract/include/
)
target_link_directories(
sist2 PUBLIC
${UUID_LIBRARY_DIRS}
)
target_compile_options(sist2
PRIVATE
-Ofast
# -march=native
-fno-stack-protector
-fomit-frame-pointer
)
target_compile_options(sist2
PRIVATE
-Ofast
# -march=native
-fno-stack-protector
-fomit-frame-pointer
)
TARGET_LINK_LIBRARIES(
sist2
TARGET_LINK_LIBRARIES(
sist2
${GLIB_LIBRARIES}
${GOBJECT_LIBRARIES}
${UUID_LIBRARIES}
${GLIB_LIBRARIES}
${GOBJECT_LIBRARIES}
${UUID_LIBRARIES}
# ffmpeg
${PROJECT_SOURCE_DIR}/lib/libavcodec.a
${PROJECT_SOURCE_DIR}/lib/libavformat.a
${PROJECT_SOURCE_DIR}/lib/libavutil.a
${PROJECT_SOURCE_DIR}/lib/libswscale.a
${PROJECT_SOURCE_DIR}/lib/libswresample.a
# ffmpeg
${PROJECT_SOURCE_DIR}/lib/libavcodec.a
${PROJECT_SOURCE_DIR}/lib/libavformat.a
${PROJECT_SOURCE_DIR}/lib/libavutil.a
${PROJECT_SOURCE_DIR}/lib/libswscale.a
${PROJECT_SOURCE_DIR}/lib/libswresample.a
# ${FFMPEG_LIBRARIES}
# swscale
# mupdf
${PROJECT_SOURCE_DIR}/lib/libmupdf.a
${PROJECT_SOURCE_DIR}/lib/libmupdf-third.a
# mupdf
${PROJECT_SOURCE_DIR}/lib/libmupdf.a
${PROJECT_SOURCE_DIR}/lib/libmupdf-third.a
# onion
${PROJECT_SOURCE_DIR}/lib/libonion_static.a
# onion
${PROJECT_SOURCE_DIR}/lib/libonion_static.a
pthread
curl
m
bz2
${PROJECT_SOURCE_DIR}/lib/libmagic.a
${PROJECT_SOURCE_DIR}/lib/libharfbuzz.a
${PROJECT_SOURCE_DIR}/lib/libopenjp2.a
freetype
archive
pthread
curl
m
bz2
magic
${PROJECT_SOURCE_DIR}/lib/libharfbuzz.a
${PROJECT_SOURCE_DIR}/lib/libopenjp2.a
freetype
archive
xml2
${PROJECT_SOURCE_DIR}/lib/libopc/libmce.a
${PROJECT_SOURCE_DIR}/lib/libopc/libopc.a
${PROJECT_SOURCE_DIR}/lib/libopc/libplib.a
# ${PROJECT_SOURCE_DIR}/lib/libxml2.a
xml2
${PROJECT_SOURCE_DIR}/lib/libopc/libmce.a
${PROJECT_SOURCE_DIR}/lib/libopc/libopc.a
${PROJECT_SOURCE_DIR}/lib/libopc/libplib.a
)
endif ()
if (WITH_SIST2_SCAN)
set_target_properties(
sist2_scan
PROPERTIES COMPILE_DEFINITIONS SIST_SCAN_ONLY
)
set_target_properties(
sist2_scan
PROPERTIES
COMPILE_DEFINITIONS SIST_SCAN_ONLY
LINK_FLAGS -static
)
target_include_directories(
sist2_scan PUBLIC
${LIBMAGIC_INCLUDE_DIRS}
${GOBJECT_INCLUDE_DIRS}
${OPENSSL_INCLUDE_DIR}
${FFMPEG_INCLUDE_DIRS}
${GLIB_INCLUDE_DIRS}
${UUID_INCLUDE_DIRS}
${FREETYPE_INCLUDE_DIRS}
${PROJECT_SOURCE_DIR}/
${PROJECT_SOURCE_DIR}/lmdb/libraries/liblmdb/
${PROJECT_SOURCE_DIR}/lib/onion/src/
${PROJECT_SOURCE_DIR}/lib/mupdf/include/
${PROJECT_SOURCE_DIR}/include/
/usr/include/libxml2/
)
target_link_directories(
sist2_scan PUBLIC
${UUID_LIBRARY_DIRS}
${FFMPEG_LIBRARY_DIRS}
)
target_compile_options(sist2_scan
PRIVATE
-Ofast
# -march=native
-fno-stack-protector
-fomit-frame-pointer
)
TARGET_LINK_LIBRARIES(
sist2_scan
${GLIB_LIBRARIES}
${GOBJECT_LIBRARIES}
${UUID_LIBRARIES}
# ffmpeg
${PROJECT_SOURCE_DIR}/lib/libavcodec.a
${PROJECT_SOURCE_DIR}/lib/libavformat.a
${PROJECT_SOURCE_DIR}/lib/libavutil.a
${PROJECT_SOURCE_DIR}/lib/libswscale.a
${PROJECT_SOURCE_DIR}/lib/libswresample.a
# mupdf
${PROJECT_SOURCE_DIR}/lib/libmupdf.a
${PROJECT_SOURCE_DIR}/lib/libmupdf-third.a
${PROJECT_SOURCE_DIR}/lib/libbz2.a
${PROJECT_SOURCE_DIR}/lib/libmagic.a
pthread
m
${PROJECT_SOURCE_DIR}/lib/libharfbuzz.a
${PROJECT_SOURCE_DIR}/lib/libopenjp2.a
freetype
${PROJECT_SOURCE_DIR}/lib/libarchive.a
${PROJECT_SOURCE_DIR}/lib/liblz4.a
${PROJECT_SOURCE_DIR}/lib/liblzma.a
${PROJECT_SOURCE_DIR}/lib/libzstd.a
${PROJECT_SOURCE_DIR}/lib/libxml2.a
${PROJECT_SOURCE_DIR}/lib/libopc/libmce.a
${PROJECT_SOURCE_DIR}/lib/libopc/libopc.a
${PROJECT_SOURCE_DIR}/lib/libopc/libplib.a
)
endif ()
${PROJECT_SOURCE_DIR}/lib/libtesseract.a
${PROJECT_SOURCE_DIR}/lib/liblept.a
${PROJECT_SOURCE_DIR}/lib/libtiff.a
png
stdc++
)
add_custom_target(
before_sist2
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/scripts/before_build.sh
)
IF (WITH_SIST2)
add_dependencies(sist2 before_sist2)
else ()
add_dependencies(sist2_scan before_sist2)
endif ()
add_dependencies(sist2 before_sist2)

View File

@@ -2,8 +2,18 @@ FROM ubuntu:19.10
MAINTAINER simon987 <me@simon987.net>
RUN apt update
RUN apt install -y libglib2.0-0 libcurl4 libmagic1 libharfbuzz-bin libopenjp2-7 libarchive13 liblzma5 libzstd1 liblz4-1
RUN apt install -y libglib2.0-0 libcurl4 libmagic1 libharfbuzz-bin libopenjp2-7 libarchive13 liblzma5 libzstd1 liblz4-1 \
curl libtiff5 libpng16-16
RUN mkdir -p /usr/share/tessdata && \
cd /usr/share/tessdata/ && \
curl -o /usr/share/tessdata/hin.traineddata https://raw.githubusercontent.com/tesseract-ocr/tessdata/master/hin.traineddata &&\
curl -o /usr/share/tessdata/jpn.traineddata https://raw.githubusercontent.com/tesseract-ocr/tessdata/master/jpn.traineddata &&\
curl -o /usr/share/tessdata/eng.traineddata https://raw.githubusercontent.com/tesseract-ocr/tessdata/master/eng.traineddata &&\
curl -o /usr/share/tessdata/fra.traineddata https://raw.githubusercontent.com/tesseract-ocr/tessdata/master/fra.traineddata &&\
curl -o /usr/share/tessdata/rus.traineddata https://raw.githubusercontent.com/tesseract-ocr/tessdata/master/rus.traineddata &&\
curl -o /usr/share/tessdata/spa.traineddata https://raw.githubusercontent.com/tesseract-ocr/tessdata/master/spa.traineddata && ls -lh
ADD sist2 /root/sist2
ENTRYPOINT ["/root/sist2"]
ENTRYPOINT ["/root/sist2"]

View File

@@ -1,5 +1,6 @@
rm ./sist2
cp ../sist2 .
strip sist2
version=$(./sist2 --version)

View File

@@ -1,5 +1,6 @@
![GitHub](https://img.shields.io/github/license/simon987/sist2.svg)
[![CodeFactor](https://www.codefactor.io/repository/github/simon987/sist2/badge?s=05daa325188aac4eae32c786f3d9cf4e0593f822)](https://www.codefactor.io/repository/github/simon987/sist2)
[![Development snapshots](https://ci.simon987.net/app/rest/builds/buildType(Sist2_Build)/statusIcon)](https://files.simon987.net/artifacts/Sist2/Build/)
# sist2
@@ -11,21 +12,24 @@ sist2 (Simple incremental search tool)
* Fast, low memory usage, multi-threaded
* Portable (all its features are packaged in a single executable)
* Extracts text from common file types\*
* Generates thumbnails\*
* Extracts text from common file types \*
* Generates thumbnails \*
* Incremental scanning
* Automatic tagging from file attributes via [user scripts](scripting/README.md)
* Recursive scan inside archive files \*\*
* OCR support with tesseract \*\*\*
\* See [format support](#format-support)
\** See [Archive files](#archive-files)
\*\* See [Archive files](#archive-files)
\*\*\* See [OCR](#ocr)
## Getting Started
1. Have an [Elasticsearch](https://www.elastic.co/downloads/elasticsearch) instance running
1.
1. Download the [latest sist2 release](https://github.com/simon987/sist2/releases) *
1. *(or)* Download an [development snapshot](https://files.simon987.net/artifacts/Sist2/Build/) *(Not recommended!)*
1. *(or)* `docker pull simon987/sist2:latest`
@@ -85,7 +89,7 @@ docker stop sist2
File type | Library | Content | Thumbnail | Metadata
:---|:---|:---|:---|:---
pdf,xps,cbz,fb2,epub | MuPDF | yes | yes, `png` | title |
pdf,xps,cbz,fb2,epub | MuPDF | text+ocr | yes, `png` | title |
`audio/*` | ffmpeg | - | yes, `jpeg` | ID3 tags |
`video/*` | ffmpeg | - | yes, `jpeg` | title, comment, artist |
`image/*` | ffmpeg | - | yes, `jpeg` | `EXIF:Artist`, `EXIF:ImageDescription` |
@@ -110,6 +114,21 @@ scan is also supported.
To check if a media file can be parsed without *seek*, execute `cat file.mp4 | ffprobe -`
### OCR
You can enable OCR support for pdf,xps,cbz,fb2,epub file types with the
`--ocr <lang>` option. Download the language data files with your
package manager (`apt install tesseract-ocr-eng`) or directly [from Github](https://github.com/tesseract-ocr/tesseract/wiki/Data-Files).
The `simon987/sist2` github image comes with common languages
(hin, jpn, eng, fra, rus, spa) pre-installed.
Examples
```bash
sist2 scan --ocr jpn ~/Books/Manga/
sist2 scan --ocr eng ~/Books/Textbooks/
```
## Build from source
@@ -121,15 +140,10 @@ binaries.
*(Debian)*
```bash
apt install git cmake pkg-config libglib2.0-dev\
libssl-dev uuid-dev libavformat-dev libswscale-dev \
python3 libmagic-dev libfreetype6-dev libcurl-dev \
libbz2-dev yasm libharfbuzz-dev ragel libarchive-dev
```
*(FreeBSD)*
```bash
pkg install cmake gcc yasm gmake bash ffmpeg e2fsprogs-uuid\
autotools ragel libarchive
apt install git cmake pkg-config libglib2.0-dev \
libssl-dev uuid-dev python3 libmagic-dev libfreetype6-dev \
libcurl-dev libbz2-dev yasm libharfbuzz-dev ragel \
libarchive-dev libtiff5 libpng16-16 libpango1.0-dev
```
2. Build

8
ci/build.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
./scripts/get_static_libs.sh
cmake .
make
strip sist2
strip sist2_scan

1
lib/leptonica Submodule

Submodule lib/leptonica added at cc03be70fd

Submodule lib/libarchive deleted from 6f4fceb714

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
lib/libtiff Submodule

Submodule lib/libtiff added at 3db0ff91bc

Submodule lib/libxml2 deleted from 0815302dee

Submodule lib/lz4 deleted from 0f749838bf

1
lib/tesseract Submodule

Submodule lib/tesseract added at f268e6615e

Submodule lib/zstd deleted from d73e2fb465

View File

@@ -252,7 +252,7 @@ text/html, acgi|htm|html|htmls|htx|shtml
text/javascript, js
text/mcf, mcf
text/pascal, pas
text/plain, com|cmd|conf|def|g|idc|list|lst|mar|sdml|text|txt|md|groovy|license|properties|desktop|ini|rst|cmake|ipynb|readme|less|lo|go|yml|d|cs|hpp|srt|nfo|sfv|m3u
text/plain, com|cmd|conf|def|g|idc|list|lst|mar|sdml|text|txt|md|groovy|license|properties|desktop|ini|rst|cmake|ipynb|readme|less|lo|go|yml|d|cs|hpp|srt|nfo|sfv|m3u|csv|eml
text/richtext, rt|rtf|rtx
text/rtf,
text/scriplet, wsc
1 application/arj arj
252 text/javascript js
253 text/mcf mcf
254 text/pascal pas
255 text/plain com|cmd|conf|def|g|idc|list|lst|mar|sdml|text|txt|md|groovy|license|properties|desktop|ini|rst|cmake|ipynb|readme|less|lo|go|yml|d|cs|hpp|srt|nfo|sfv|m3u com|cmd|conf|def|g|idc|list|lst|mar|sdml|text|txt|md|groovy|license|properties|desktop|ini|rst|cmake|ipynb|readme|less|lo|go|yml|d|cs|hpp|srt|nfo|sfv|m3u|csv|eml
256 text/richtext rt|rtf|rtx
257 text/rtf
258 text/scriplet wsc

View File

@@ -13,19 +13,24 @@
"type": "keyword"
},
"videoc": {
"type": "keyword"
"type": "keyword",
"index": false
},
"audioc": {
"type": "keyword"
"type": "keyword",
"index": false
},
"duration": {
"type": "float"
"type": "float",
"index": false
},
"width": {
"type": "integer"
"type": "integer",
"index": false
},
"height": {
"type": "integer"
"type": "integer",
"index": false
},
"mtime": {
"type": "integer"
@@ -70,6 +75,23 @@
"analyzer": "my_nGram",
"type": "text"
},
"_keyword.*": {
"type": "keyword"
},
"_text.*": {
"analyzer": "content_analyzer",
"type": "text",
"fields": {
"nGram": {
"type": "text",
"analyzer": "my_nGram"
}
}
},
"_url": {
"type": "keyword",
"index": false
},
"content": {
"analyzer": "content_analyzer",
"type": "text",

View File

@@ -1,8 +1,11 @@
#!/usr/bin/env bash
THREADS=$(nproc)
cd lib
cd mupdf
USE_SYSTEM_HARFBUZZ=yes USE_SYSTEM_OPENJPEG=yes HAVE_X11=no HAVE_GLUT=no make -j 4
CFLAGS=-fPIC make USE_SYSTEM_HARFBUZZ=yes USE_SYSTEM_OPENJPEG=yes HAVE_X11=no HAVE_GLUT=no -j $THREADS
cd ..
mv mupdf/build/release/libmupdf.a .
@@ -12,7 +15,7 @@ mv mupdf/build/release/libmupdf-third.a .
cd openjpeg
#cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O3 -march=native -DNDEBUG"
cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O3"
make -j 4
make -j $THREADS
cd ..
mv openjpeg/bin/libopenjp2.a .
@@ -20,7 +23,7 @@ mv openjpeg/bin/libopenjp2.a .
cd harfbuzz
./autogen.sh
./configure --disable-shared --enable-static
make -j 4
make -j $THREADS
cd ..
mv harfbuzz/src/.libs/libharfbuzz.a .
@@ -30,8 +33,9 @@ cd ffmpeg
--disable-ffprobe --disable-doc\
--disable-manpages --disable-postproc --disable-avfilter \
--disable-alsa --disable-lzma --disable-xlib --disable-debug\
--disable-vdpau --disable-vaapi --disable-sdl2 --disable-network
make -j 4
--disable-vdpau --disable-vaapi --disable-sdl2 --disable-network\
--extra-cflags=-fPIC
make -j $THREADS
cd ..
mv ffmpeg/libavcodec/libavcodec.a .
@@ -48,14 +52,14 @@ cmake -DONION_USE_SSL=false -DONION_USE_PAM=false -DONION_USE_PNG=false -DONION_
-DONION_USE_JPEG=false -DONION_USE_XML2=false -DONION_USE_SYSTEMD=false -DONION_USE_SQLITE3=false \
-DONION_USE_REDIS=false -DONION_USE_GC=false -DONION_USE_TESTS=false -DONION_EXAMPLES=false \
-DONION_USE_BINDINGS_CPP=false ..
make -j 4
make -j $THREADS
cd ../..
mv onion/build/src/onion/libonion_static.a .
#bzip2
cd bzip2-1.0.6
make -j 4
make -j $THREADS
cd ..
mv bzip2-1.0.6/libbz2.a .
@@ -63,47 +67,34 @@ mv bzip2-1.0.6/libbz2.a .
cd libmagic
./autogen.sh
./configure --enable-static --disable-shared
make -j 4
make -j $THREADS
cd ..
mv libmagic/src/.libs/libmagic.a .
# libarchive
cd libarchive/build
# tesseract
cd tesseract
mkdir build
cd build
cmake -DSTATIC=on -DBUILD_TRAINING_TOOLS=off ..
make -j $THREADS
cd ../..
mv tesseract/build/libtesseract.a .
# leptonica
cd leptonica
./autogen.sh
./configure --without-zlib --without-jpeg --without-giflib \
--without-giflib --without-libwebp --without-libwebpmux --without-libopenjpeg \
--enable-static --disable-shared
make -j $THREADS
cd ..
./configure --without-nettle --without-expat --without-xml2 --without-openssl
make -j 4
cd ..
mv libarchive/.libs/libarchive.a .
mv leptonica/src/.libs/liblept.a .
# lz4
cd lz4
make -j 4
cd ..
mv lz4/lib/liblz4.a .
# lzma
wget https://newcontinuum.dl.sourceforge.net/project/lzmautils/xz-5.2.3.tar.gz
tar -xzf xz-5.2.3.tar.gz
rm xz-5.2.3.tar.gz
cd xz-5.2.3
# tiff
cd libtiff
./autogen.sh
./configure
make -j 4
cd ..
mv xz-5.2.3/src/liblzma/.libs/liblzma.a .
# zstd
cd zstd
make -j 4
cd ..
mv zstd/lib/libzstd.a .
# xml2
cd libxml2
./autogen.sh --without-zlib --without-lzma
make
cd ..
mv libxml2/.libs/libxml2.a .
./configure --enable-static --disable-shared --disable-lzw --disable-jpeg --disable-webp \
--disable-lzma --disable-zstd --disable-jbig
make -j $THREADS
cd ..
mv libtiff/libtiff/.libs/libtiff.a .

View File

@@ -1,58 +0,0 @@
#!/usr/bin/env bash
cd lib
# mupdf
cd mupdf
HAVE_X11=no HAVE_GLUT=no gmake -j 4
cd ..
mv mupdf/build/release/libmupdf.a .
mv mupdf/build/release/libmupdf-third.a .
# openjp2
cd openjpeg
#cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O3 -march=native -DNDEBUG"
cmake . -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS="-O3"
gmake -j 4
cd ..
mv openjpeg/bin/libopenjp2.a .
# harfbuzz
cd harfbuzz
./autogen.sh
./configure --disable-shared --enable-static
gmake -j 4
cd ..
mv harfbuzz/src/.libs/libharfbuzz.a .
# ffmpeg
cd ffmpeg
./configure --disable-shared --enable-static --disable-ffmpeg --disable-ffplay \
--disable-ffprobe --disable-doc\
--disable-manpages --disable-postproc --disable-avfilter \
--disable-alsa --disable-lzma --disable-xlib --disable-debug\
--disable-vdpau --disable-vaapi --disable-sdl2 --disable-network
gmake -j 4
cd ..
mv ffmpeg/libavcodec/libavcodec.a .
mv ffmpeg/libavformat/libavformat.a .
mv ffmpeg/libavutil/libavutil.a .
mv ffmpeg/libswresample/libswresample.a .
mv ffmpeg/libswscale/libswscale.a .
#bzip2
cd bzip2-1.0.6
make -j 4
cd ..
mv bzip2-1.0.6/libbz2.a .
# magic
cd libmagic
./autogen.sh
./configure --enable-static --disable-shared
make -j 4
cd ..
mv libmagic/src/.libs/libmagic.a .
cd ..

View File

@@ -1,6 +1,8 @@
#include "cli.h"
#include "ctx.h"
#include <tesseract/capi.h>
#define DEFAULT_OUTPUT "index.sist2/"
#define DEFAULT_CONTENT_SIZE 32768
#define DEFAULT_QUALITY 5
@@ -35,8 +37,6 @@ void scan_args_destroy(scan_args_t *args) {
free(args);
}
#ifndef SIST_SCAN_ONLY
void index_args_destroy(index_args_t *args) {
//todo
free(args);
@@ -47,8 +47,6 @@ void web_args_destroy(web_args_t *args) {
free(args);
}
#endif
int scan_args_validate(scan_args_t *args, int argc, const char **argv) {
if (argc < 2) {
fprintf(stderr, "Required positional argument: PATH.\n");
@@ -136,25 +134,37 @@ int scan_args_validate(scan_args_t *args, int argc, const char **argv) {
return 1;
}
if (args->tesseract_lang != NULL) {
TessBaseAPI *api = TessBaseAPICreate();
ret = TessBaseAPIInit3(api, TESS_DATAPATH, args->tesseract_lang);
if (ret != 0) {
fprintf(stderr, "Could not initialize tesseract with lang '%s'\n", args->tesseract_lang);
return 1;
}
TessBaseAPIEnd(api);
TessBaseAPIDelete(api);
}
LOG_DEBUGF("cli.c", "arg quality=%f", args->quality)
LOG_DEBUGF("cli.c", "arg size=%d", args->size)
LOG_DEBUGF("cli.c", "arg content_size=%d", args->content_size)
LOG_DEBUGF("cli.c", "arg threads=%d", args->threads)
LOG_DEBUGF("cli.c", "arg incremental=%s", args->incremental)
LOG_DEBUGF("cli.c", "arg output=%s", args->incremental)
LOG_DEBUGF("cli.c", "arg output=%s", args->output)
LOG_DEBUGF("cli.c", "arg rewrite_url=%s", args->rewrite_url)
LOG_DEBUGF("cli.c", "arg name=%s", args->name)
LOG_DEBUGF("cli.c", "arg depth=%d", args->depth)
LOG_DEBUGF("cli.c", "arg path=%s", args->path)
LOG_DEBUGF("cli.c", "arg archive=%s", args->archive)
LOG_DEBUGF("cli.c", "arg ocr=%s", args->tesseract_lang)
return 0;
}
#ifndef SIST_SCAN_ONLY
int index_args_validate(index_args_t *args, int argc, const char **argv) {
LogCtx.verbose = 1;
if (argc < 2) {
fprintf(stderr, "Required positional argument: PATH.\n");
return 1;
@@ -216,6 +226,8 @@ int index_args_validate(index_args_t *args, int argc, const char **argv) {
int web_args_validate(web_args_t *args, int argc, const char **argv) {
LogCtx.verbose = 1;
if (argc < 2) {
fprintf(stderr, "Required positional argument: PATH.\n");
return 1;
@@ -273,5 +285,3 @@ web_args_t *web_args_create() {
return args;
}
#endif

View File

@@ -16,13 +16,13 @@ typedef struct scan_args {
char *path;
char *archive;
archive_mode_t archive_mode;
char *tesseract_lang;
} scan_args_t;
scan_args_t *scan_args_create();
void scan_args_destroy(scan_args_t *args);
int scan_args_validate(scan_args_t *args, int argc, const char **argv);
#ifndef SIST_SCAN_ONLY
typedef struct index_args {
char *es_url;
const char *index_path;
@@ -51,6 +51,5 @@ void web_args_destroy(web_args_t *args);
int index_args_validate(index_args_t *args, int argc, const char **argv);
int web_args_validate(web_args_t *args, int argc, const char **argv);
#endif
#endif

View File

@@ -27,6 +27,7 @@ struct {
GHashTable *copy_table;
pthread_mutex_t mupdf_mu;
char * tesseract_lang;
} ScanCtx;
struct {
@@ -35,7 +36,6 @@ struct {
int no_color;
} LogCtx;
#ifndef SIST_SCAN_ONLY
struct {
char *es_url;
int batch_size;
@@ -47,7 +47,6 @@ struct {
char *b64credentials;
struct index_t indices[16];
} WebCtx;
#endif
#endif

View File

@@ -27,13 +27,14 @@ void print_json(cJSON *document, const char uuid_str[UUID_STR_LEN]) {
cJSON_AddStringToObject(line, "_id", uuid_str);
cJSON_AddStringToObject(line, "_index", "sist2");
cJSON_AddStringToObject(line, "_type", "_doc");
cJSON_AddItemToObject(line, "_source", document);
cJSON_AddItemReferenceToObject(line, "_source", document);
char *json = cJSON_PrintUnformatted(line);
printf("%s\n", json);
cJSON_free(line);
cJSON_free(json);
cJSON_Delete(line);
}
void index_json(cJSON *document, const char uuid_str[UUID_STR_LEN]) {
@@ -68,7 +69,7 @@ void execute_update_script(const char *script, const char index_id[UUID_STR_LEN]
char bulk_url[4096];
snprintf(bulk_url, 4096, "%s/sist2/_update_by_query?pretty", Indexer->es_url);
response_t *r = web_post(bulk_url, str, "Content-Type: application/json");
printf("Executed user script <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Executed user script <%d>", r->status_code);
cJSON *resp = cJSON_Parse(r->body);
cJSON_free(str);
@@ -79,7 +80,7 @@ void execute_update_script(const char *script, const char index_id[UUID_STR_LEN]
if (error != NULL) {
char *error_str = cJSON_Print(error);
fprintf(stderr, "User script error: \n%s\n", error_str);
LOG_ERRORF("elastic.c", "User script error: \n%s", error_str);
cJSON_free(error_str);
}
@@ -132,11 +133,10 @@ void elastic_flush() {
response_t *r = web_post(bulk_url, buf, "Content-Type: application/x-ndjson");
if (r->status_code == 0) {
fprintf(stderr, "Could not connect to %s, make sure that elasticsearch is running!\n", IndexCtx.es_url);
exit(1);
LOG_FATALF("elastic.c", "Could not connect to %s, make sure that elasticsearch is running!\n", IndexCtx.es_url)
}
printf("Indexed %3d documents (%zukB) <%d>\n", count, buf_cur / 1024, r->status_code);
LOG_INFOF("elastic.c", "Indexed %d documents (%zukB) <%d>", count, buf_cur / 1024, r->status_code);
cJSON *ret_json = cJSON_Parse(r->body);
if (cJSON_GetObjectItem(ret_json, "errors")->valueint != 0) {
@@ -144,7 +144,7 @@ void elastic_flush() {
cJSON_ArrayForEach(err, cJSON_GetObjectItem(ret_json, "items")) {
if (cJSON_GetObjectItem(cJSON_GetObjectItem(err, "index"), "status")->valueint != 201) {
char* str = cJSON_Print(err);
fprintf(stderr, "%s\n", str);
LOG_ERRORF("elastic.c", "%s\n", str);
cJSON_free(str);
}
}
@@ -198,7 +198,7 @@ void destroy_indexer(char * script, char index_id[UUID_STR_LEN]) {
snprintf(url, sizeof(url), "%s/sist2/_refresh", IndexCtx.es_url);
response_t *r = web_post(url, "", NULL);
printf("Refresh index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Refresh index <%d>", r->status_code);
free_response(r);
if (script != NULL) {
@@ -207,12 +207,12 @@ void destroy_indexer(char * script, char index_id[UUID_STR_LEN]) {
snprintf(url, sizeof(url), "%s/sist2/_refresh", IndexCtx.es_url);
r = web_post(url, "", NULL);
printf("Refresh index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Refresh index <%d>", r->status_code);
free_response(r);
snprintf(url, sizeof(url), "%s/sist2/_forcemerge", IndexCtx.es_url);
r = web_post(url, "", NULL);
printf("Merge index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Merge index <%d>", r->status_code);
free_response(r);
if (Indexer != NULL) {
@@ -232,32 +232,32 @@ void elastic_init(int force_reset) {
if (!index_exists || force_reset) {
r = web_delete(url);
printf("Delete index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Delete index <%d>", r->status_code);
free_response(r);
snprintf(url, 4096, "%s/sist2", IndexCtx.es_url);
r = web_put(url, "", NULL);
printf("Create index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Create index <%d>", r->status_code);
free_response(r);
snprintf(url, 4096, "%s/sist2/_close", IndexCtx.es_url);
r = web_post(url, "", NULL);
printf("Close index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Close index <%d>", r->status_code);
free_response(r);
snprintf(url, 4096, "%s/sist2/_settings", IndexCtx.es_url);
r = web_put(url, settings_json, "Content-Type: application/json");
printf("Update settings <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Update settings <%d>", r->status_code);
free_response(r);
snprintf(url, 4096, "%s/sist2/_mappings/_doc?include_type_name=true", IndexCtx.es_url);
r = web_put(url, mappings_json, "Content-Type: application/json");
printf("Update mappings <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Update mappings <%d>", r->status_code);
free_response(r);
snprintf(url, 4096, "%s/sist2/_open", IndexCtx.es_url);
r = web_post(url, "", NULL);
printf("Open index <%d>\n", r->status_code);
LOG_INFOF("elastic.c", "Open index <%d>", r->status_code);
free_response(r);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -34,6 +34,7 @@ void write_index_descriptor(char *path, index_descriptor_t *desc) {
cJSON_AddStringToObject(json, "version", desc->version);
cJSON_AddStringToObject(json, "root", desc->root);
cJSON_AddStringToObject(json, "name", desc->name);
cJSON_AddStringToObject(json, "type", desc->type);
cJSON_AddStringToObject(json, "rewrite_url", desc->rewrite_url);
cJSON_AddNumberToObject(json, "timestamp", (double) desc->timestamp);
@@ -56,8 +57,7 @@ index_descriptor_t read_index_descriptor(char *path) {
int fd = open(path, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Invalid/corrupt index (Could not find descriptor)\n");
exit(1);
LOG_FATAL("serialize.c", "Invalid/corrupt index (Could not find descriptor)\n")
}
char *buf = malloc(info.st_size + 1);
@@ -75,6 +75,11 @@ index_descriptor_t read_index_descriptor(char *path) {
descriptor.root_len = (short) strlen(descriptor.root);
strcpy(descriptor.version, cJSON_GetObjectItem(json, "version")->valuestring);
strcpy(descriptor.uuid, cJSON_GetObjectItem(json, "uuid")->valuestring);
if (cJSON_GetObjectItem(json, "type") == NULL) {
strcpy(descriptor.type, INDEX_TYPE_BIN);
} else {
strcpy(descriptor.type, cJSON_GetObjectItem(json, "type")->valuestring);
}
cJSON_Delete(json);
free(buf);
@@ -172,8 +177,8 @@ void thread_cleanup() {
close(index_fd);
}
void read_index(const char *path, const char index_id[UUID_STR_LEN], index_func func) {
void read_index_bin(const char *path, const char *index_id, index_func func) {
line_t line;
dyn_buffer_t buf = dyn_buffer_create();
@@ -229,7 +234,7 @@ void read_index(const char *path, const char index_id[UUID_STR_LEN], index_func
case MetaMediaBitrate: {
long value;
fread(&value, sizeof(long), 1, file);
cJSON_AddNumberToObject(document, get_meta_key_text(key), value);
cJSON_AddNumberToObject(document, get_meta_key_text(key), (double) value);
break;
}
case MetaMediaAudioCodec:
@@ -262,7 +267,7 @@ void read_index(const char *path, const char index_id[UUID_STR_LEN], index_func
break;
}
default:
LOG_FATALF("serialize.c", "Invalid meta key (corrupt index): %x", key)
LOG_FATALF("serialize.c", "Invalid meta key (corrupt index): %x", key)
}
key = getc(file);
@@ -275,6 +280,89 @@ void read_index(const char *path, const char index_id[UUID_STR_LEN], index_func
fclose(file);
}
const char *json_type_copy_fields[] = {
"mime", "name", "path", "extension", "index", "size", "mtime", "parent",
// Meta
"title", "content", "width", "height", "duration", "audioc", "videoc",
"bitrate", "artist", "album", "album_artist", "genre", "title", "font_name",
// Special
"tag", "_url"
};
const char *json_type_array_fields[] = {
"_keyword", "_text"
};
void read_index_json(const char *path, UNUSED(const char *index_id), index_func func) {
FILE *file = fopen(path, "r");
while (1) {
char *line = NULL;
size_t len;
size_t read = getline(&line, &len, file);
if (read == -1) {
if (line) {
free(line);
}
break;
}
cJSON *input = cJSON_Parse(line);
if (input == NULL) {
LOG_FATALF("serialize.c", "Could not parse JSON line: \n%s", line)
}
if (line) {
free(line);
}
cJSON *document = cJSON_CreateObject();
const char *uuid_str = cJSON_GetObjectItem(input, "_id")->valuestring;
for (int i = 0; i < (sizeof(json_type_copy_fields) / sizeof(json_type_copy_fields[0])); i++) {
cJSON *value = cJSON_GetObjectItem(input, json_type_copy_fields[i]);
if (value != NULL) {
cJSON_AddItemReferenceToObject(document, json_type_copy_fields[i], value);
}
}
for (int i = 0; i < (sizeof(json_type_array_fields) / sizeof(json_type_array_fields[0])); i++) {
cJSON *arr = cJSON_GetObjectItem(input, json_type_array_fields[i]);
if (arr != NULL) {
cJSON *obj;
cJSON_ArrayForEach(obj, arr) {
char key[1024];
cJSON *k = cJSON_GetObjectItem(obj, "k");
cJSON *v = cJSON_GetObjectItem(obj, "v");
if (k == NULL || v == NULL || !cJSON_IsString(k) || !cJSON_IsString(v)) {
char *str = cJSON_Print(obj);
LOG_FATALF("serialize.c", "Invalid %s member: must contain .k and .v string fields: \n%s",
json_type_array_fields[i], str)
}
snprintf(key, sizeof(key), "%s.%s", json_type_array_fields[i], k->valuestring);
cJSON_AddStringToObject(document, key, v->valuestring);
}
}
}
func(document, uuid_str);
cJSON_Delete(document);
cJSON_Delete(input);
}
fclose(file);
}
void read_index(const char *path, const char index_id[UUID_STR_LEN], const char *type, index_func func) {
if (strcmp(type, INDEX_TYPE_BIN) == 0) {
read_index_bin(path, index_id, func);
} else if (strcmp(type, INDEX_TYPE_JSON) == 0) {
read_index_json(path, index_id, func);
}
}
void incremental_read(GHashTable *table, const char *filepath) {
FILE *file = fopen(filepath, "rb");
line_t line;

View File

@@ -11,7 +11,7 @@ void incremental_copy(store_t *store, store_t *dst_store, const char *filepath,
void write_document(document_t *doc);
void read_index(const char *path, const char[UUID_STR_LEN], index_func);
void read_index(const char *path, const char[UUID_STR_LEN], const char *type, index_func);
void incremental_read(GHashTable *table, const char *filepath);

View File

@@ -56,7 +56,7 @@ void sist_logf(char *filepath, int level, char *format, ...) {
log_len += 1;
}
write(STDOUT_FILENO, log_str, log_len);
write(STDERR_FILENO, log_str, log_len);
}
void sist_log(char *filepath, int level, char *str) {

View File

@@ -1,16 +1,12 @@
#include "sist.h"
#include "ctx.h"
#ifndef SIST_SCAN_ONLY
#define DESCRIPTION "Lightning-fast file system indexer and search tool."
#else
#define DESCRIPTION "Lightning-fast file system indexer and search tool. (SCAN ONLY)"
#endif
#define EPILOG "Made by simon987 <me@simon987.net>. Released under GPL-3.0"
static const char *const Version = "1.1.13";
static const char *const Version = "1.2.2";
static const char *const usage[] = {
"sist2 scan [OPTION]... PATH",
"sist2 index [OPTION]... INDEX",
@@ -19,9 +15,7 @@ static const char *const usage[] = {
};
void global_init() {
#ifndef SIST_SCAN_ONLY
curl_global_init(CURL_GLOBAL_NOTHING);
#endif
av_log_set_level(AV_LOG_QUIET);
opcInitLibrary();
}
@@ -35,6 +29,7 @@ void init_dir(const char *dirpath) {
uuid_unparse(uuid, ScanCtx.index.desc.uuid);
time(&ScanCtx.index.desc.timestamp);
strcpy(ScanCtx.index.desc.version, Version);
strcpy(ScanCtx.index.desc.type, INDEX_TYPE_BIN);
write_index_descriptor(path, &ScanCtx.index.desc);
}
@@ -55,6 +50,7 @@ void sist2_scan(scan_args_t *args) {
strncpy(ScanCtx.index.desc.name, args->name, sizeof(ScanCtx.index.desc.name));
strncpy(ScanCtx.index.desc.root, args->path, sizeof(ScanCtx.index.desc.root));
ScanCtx.index.desc.root_len = (short) strlen(ScanCtx.index.desc.root);
ScanCtx.tesseract_lang = args->tesseract_lang;
init_dir(ScanCtx.index.path);
@@ -122,8 +118,6 @@ void sist2_scan(scan_args_t *args) {
store_destroy(ScanCtx.index.store);
}
#ifndef SIST_SCAN_ONLY
void sist2_index(index_args_t *args) {
IndexCtx.es_url = args->es_url;
@@ -137,8 +131,12 @@ void sist2_index(index_args_t *args) {
snprintf(descriptor_path, PATH_MAX, "%s/descriptor.json", args->index_path);
index_descriptor_t desc = read_index_descriptor(descriptor_path);
if (strcmp(desc.version, Version) != 0) {
fprintf(stderr, "Version mismatch! Index is v%s but executable is v%s\n", desc.version, Version);
LOG_DEBUGF("main.c", "descriptor version %s (%s)", desc.version, desc.type)
if (strcmp(desc.version, Version) != 0 && strcmp(desc.version, INDEX_VERSION_EXTERNAL) != 0) {
fprintf(stderr, "Version mismatch! Index is %s but executable is %s/%s\n",
desc.version, Version, INDEX_VERSION_EXTERNAL);
return;
}
@@ -160,7 +158,7 @@ void sist2_index(index_args_t *args) {
if (strncmp(de->d_name, "_index_", sizeof("_index_") - 1) == 0) {
char file_path[PATH_MAX];
snprintf(file_path, PATH_MAX, "%s/%s", args->index_path, de->d_name);
read_index(file_path, desc.uuid, f);
read_index(file_path, desc.uuid, desc.type, f);
}
}
closedir(dir);
@@ -198,18 +196,14 @@ void sist2_web(web_args_t *args) {
serve(args->bind, args->port);
}
#endif
int main(int argc, const char *argv[]) {
global_init();
scan_args_t *scan_args = scan_args_create();
#ifndef SIST_SCAN_ONLY
index_args_t *index_args = index_args_create();
web_args_t *web_args = web_args_create();
#endif
int arg_version = 0;
@@ -240,8 +234,9 @@ int main(int argc, const char *argv[]) {
OPT_STRING(0, "archive", &scan_args->archive, "Archive file mode (skip|list|shallow|recurse). "
"skip: Don't parse, list: only get file names as text, "
"shallow: Don't parse archives inside archives. DEFAULT: recurse"),
OPT_STRING(0, "ocr", &scan_args->tesseract_lang, "Tesseract language (use tesseract --list-langs to see "
"which are installed on your machine)"),
#ifndef SIST_SCAN_ONLY
OPT_GROUP("Index options"),
OPT_STRING(0, "es-url", &common_es_url, "Elasticsearch url with port. DEFAULT=http://localhost:9200"),
OPT_BOOLEAN('p', "print", &index_args->print, "Just print JSON documents to stdout."),
@@ -255,7 +250,6 @@ int main(int argc, const char *argv[]) {
OPT_STRING(0, "bind", &web_args->bind, "Listen on this address. DEFAULT=localhost"),
OPT_STRING(0, "port", &web_args->port, "Listen on this port. DEFAULT=4090"),
OPT_STRING(0, "auth", &web_args->credentials, "Basic auth in user:password format"),
#endif
OPT_END(),
};
@@ -274,10 +268,8 @@ int main(int argc, const char *argv[]) {
LogCtx.verbose = 1;
}
#ifndef SIST_SCAN_ONLY
web_args->es_url = common_es_url;
index_args->es_url = common_es_url;
#endif
if (argc == 0) {
argparse_usage(&argparse);
@@ -292,7 +284,6 @@ int main(int argc, const char *argv[]) {
}
#ifndef SIST_SCAN_ONLY
else if (strcmp(argv[0], "index") == 0) {
int err = index_args_validate(index_args, argc, argv);
@@ -310,7 +301,6 @@ int main(int argc, const char *argv[]) {
sist2_web(web_args);
}
#endif
else {
fprintf(stderr, "Invalid command: '%s'\n", argv[0]);
argparse_usage(&argparse);
@@ -320,10 +310,8 @@ int main(int argc, const char *argv[]) {
scan_args_destroy(scan_args);
#ifndef SIST_SCAN_ONLY
index_args_destroy(index_args);
web_args_destroy(web_args);
#endif
return 0;
}

View File

@@ -1,7 +1,7 @@
#include "doc.h"
#include "src/ctx.h"
static void dump_text(mceTextReader_t *reader, dyn_buffer_t *buf) {
void dump_text(mceTextReader_t *reader, dyn_buffer_t *buf) {
mce_skip_attributes(reader);
@@ -66,7 +66,7 @@ void read_part(opcContainer *c, dyn_buffer_t *buf, opcPart part, document_t *doc
mce_start_element(&reader, NULL, NULL) {
dump_text(&reader, buf);
} mce_end_element(&reader);
}mce_end_document(&reader);
} mce_end_document(&reader);
mceTextReaderCleanup(&reader);
}

View File

@@ -127,6 +127,7 @@ AVFrame *read_frame(AVFormatContext *pFormatCtx, AVCodecContext *decoder, int st
#define APPEND_TAG_META(doc, tag_, keyname) \
text_buffer_t tex = text_buffer_create(-1); \
text_buffer_append_string0(&tex, tag_->value); \
text_buffer_terminate_string(&tex); \
meta_line_t *meta_tag = malloc(sizeof(meta_line_t) + tex.dyn_buffer.cur); \
meta_tag->key = keyname; \
strcpy(meta_tag->strval, tex.dyn_buffer.buf); \
@@ -322,6 +323,8 @@ void parse_media_filename(const char *filepath, document_t *doc) {
int res = avformat_open_input(&pFormatCtx, filepath, NULL, NULL);
if (res < 0) {
LOG_ERRORF(doc->filepath, "(media.c) avformat_open_input() returned [%d] %s", res, av_err2str(res))
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
return;
}
@@ -357,13 +360,22 @@ void parse_media_vfile(struct vfile *f, document_t *doc) {
int res = avformat_open_input(&pFormatCtx, "", NULL, NULL);
if (res == -5) {
// Tried to parse media that requires seek
av_free(io_ctx->buffer);
avio_context_free(&io_ctx);
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
return;
} else if (res < 0) {
LOG_ERRORF(doc->filepath, "(media.c) avformat_open_input() returned [%d] %s", res, av_err2str(res))
av_free(io_ctx->buffer);
avio_context_free(&io_ctx);
avformat_close_input(&pFormatCtx);
avformat_free_context(pFormatCtx);
return;
}
parse_media(pFormatCtx, doc);
av_free(io_ctx);
av_free(io_ctx->buffer);
avio_context_free(&io_ctx);
}

View File

@@ -1193,6 +1193,8 @@ g_hash_table_insert(ext_table, "srt", (gpointer)text_plain);
g_hash_table_insert(ext_table, "nfo", (gpointer)text_plain);
g_hash_table_insert(ext_table, "sfv", (gpointer)text_plain);
g_hash_table_insert(ext_table, "m3u", (gpointer)text_plain);
g_hash_table_insert(ext_table, "csv", (gpointer)text_plain);
g_hash_table_insert(ext_table, "eml", (gpointer)text_plain);
g_hash_table_insert(ext_table, "rt", (gpointer)text_richtext);
g_hash_table_insert(ext_table, "rtf", (gpointer)text_richtext);
g_hash_table_insert(ext_table, "rtx", (gpointer)text_richtext);

View File

@@ -1,4 +1,3 @@
#include <src/ctx.h>
#include "src/sist.h"
#include "src/ctx.h"

View File

@@ -1,6 +1,10 @@
#include "pdf.h"
#include "src/ctx.h"
#define MIN_OCR_SIZE 128
__thread text_buffer_t thread_buffer;
fz_page *render_cover(fz_context *ctx, document_t *doc, fz_document *fzdoc) {
int err = 0;
@@ -78,7 +82,8 @@ fz_page *render_cover(fz_context *ctx, document_t *doc, fz_document *fzdoc) {
fz_drop_pixmap(ctx, pixmap);
if (err != 0) {
LOG_WARNINGF(doc->filepath, "fz_new_buffer_from_pixmap_as_png() returned error code [%d] %s", err, ctx->error.message)
LOG_WARNINGF(doc->filepath, "fz_new_buffer_from_pixmap_as_png() returned error code [%d] %s", err,
ctx->error.message)
fz_drop_page(ctx, cover);
return NULL;
}
@@ -86,14 +91,13 @@ fz_page *render_cover(fz_context *ctx, document_t *doc, fz_document *fzdoc) {
return cover;
}
void fz_err_callback(void *user, __attribute__((unused)) const char *message) {
if (LogCtx.verbose) {
document_t *doc = (document_t*) user;
LOG_WARNINGF(doc->filepath, "FZ: %s", message)
}
void fz_err_callback(void *user, UNUSED(const char *message)) {
if (LogCtx.verbose) {
document_t *doc = (document_t *) user;
LOG_WARNINGF(doc->filepath, "FZ: %s", message)
}
}
__always_inline
void init_ctx(fz_context *ctx, document_t *doc) {
fz_disable_icc(ctx);
@@ -124,6 +128,40 @@ int read_stext_block(fz_stext_block *block, text_buffer_t *tex) {
return 0;
}
void fill_image(fz_context *ctx, UNUSED(fz_device *dev),
fz_image *img, UNUSED(fz_matrix ctm), UNUSED(float alpha),
UNUSED(fz_color_params color_params)) {
int l2factor = 0;
if (img->w > MIN_OCR_SIZE && img->h > MIN_OCR_SIZE) {
fz_pixmap *pix = img->get_pixmap(ctx, img, NULL, img->w, img->h, &l2factor);
if (pix->h > MIN_OCR_SIZE && img->h > MIN_OCR_SIZE && img->xres != 0) {
TessBaseAPI *api = TessBaseAPICreate();
TessBaseAPIInit3(api, TESS_DATAPATH, ScanCtx.tesseract_lang);
TessBaseAPISetImage(api, pix->samples, pix->w, pix->h, pix->n, pix->stride);
TessBaseAPISetSourceResolution(api, pix->xres);
char *text = TessBaseAPIGetUTF8Text(api);
size_t len = strlen(text);
text_buffer_append_string(&thread_buffer, text, len - 1);
LOG_DEBUGF(
"pdf.c",
"(OCR) %dx%d got %dB from tesseract (%s), buffer:%dB",
pix->w, pix->h, len, ScanCtx.tesseract_lang, thread_buffer.dyn_buffer.cur
)
TessBaseAPIEnd(api);
TessBaseAPIDelete(api);
fz_drop_pixmap(ctx, pix);
}
}
}
void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
if (buf == NULL) {
@@ -210,7 +248,7 @@ void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
if (ScanCtx.content_size > 0) {
fz_stext_options opts = {0};
text_buffer_t text_buf = text_buffer_create(ScanCtx.content_size);
thread_buffer = text_buffer_create(ScanCtx.content_size);
for (int current_page = 0; current_page < page_count; current_page++) {
fz_page *page = NULL;
@@ -224,7 +262,7 @@ void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
err = ctx->error.errcode;
if (err != 0) {
LOG_WARNINGF(doc->filepath, "fz_load_page() returned error code [%d] %s", err, ctx->error.message)
text_buffer_destroy(&text_buf);
text_buffer_destroy(&thread_buffer);
fz_drop_page(ctx, page);
fz_drop_stream(ctx, stream);
fz_drop_document(ctx, fzdoc);
@@ -235,6 +273,15 @@ void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
fz_stext_page *stext = fz_new_stext_page(ctx, fz_bound_page(ctx, page));
fz_device *dev = fz_new_stext_device(ctx, stext, &opts);
dev->stroke_path = NULL;
dev->stroke_text = NULL;
dev->clip_text = NULL;
dev->clip_stroke_path = NULL;
dev->clip_stroke_text = NULL;
if (ScanCtx.tesseract_lang != NULL) {
dev->fill_image = fill_image;
}
fz_var(err);
fz_try(ctx)
@@ -249,7 +296,7 @@ void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
if (err != 0) {
LOG_WARNINGF(doc->filepath, "fz_run_page() returned error code [%d] %s", err, ctx->error.message)
text_buffer_destroy(&text_buf);
text_buffer_destroy(&thread_buffer);
fz_drop_page(ctx, page);
fz_drop_stext_page(ctx, stext);
fz_drop_stream(ctx, stream);
@@ -260,7 +307,7 @@ void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
fz_stext_block *block = stext->first_block;
while (block != NULL) {
int ret = read_stext_block(block, &text_buf);
int ret = read_stext_block(block, &thread_buffer);
if (ret == TEXT_BUF_FULL) {
break;
}
@@ -269,18 +316,18 @@ void parse_pdf(void *buf, size_t buf_len, document_t *doc) {
fz_drop_stext_page(ctx, stext);
fz_drop_page(ctx, page);
if (text_buf.dyn_buffer.cur >= text_buf.dyn_buffer.size) {
if (thread_buffer.dyn_buffer.cur >= thread_buffer.dyn_buffer.size) {
break;
}
}
text_buffer_terminate_string(&text_buf);
text_buffer_terminate_string(&thread_buffer);
meta_line_t *meta_content = malloc(sizeof(meta_line_t) + text_buf.dyn_buffer.cur);
meta_line_t *meta_content = malloc(sizeof(meta_line_t) + thread_buffer.dyn_buffer.cur);
meta_content->key = MetaContent;
memcpy(meta_content->strval, text_buf.dyn_buffer.buf, text_buf.dyn_buffer.cur);
memcpy(meta_content->strval, thread_buffer.dyn_buffer.buf, thread_buffer.dyn_buffer.cur);
APPEND_META(doc, meta_content)
text_buffer_destroy(&text_buf);
text_buffer_destroy(&thread_buffer);
}
fz_drop_stream(ctx, stream);

View File

@@ -25,6 +25,7 @@ void parse_text(int bytes_read, struct vfile *f, char *buf, document_t *doc) {
}
text_buffer_t tex = text_buffer_create(ScanCtx.content_size);
text_buffer_append_string(&tex, intermediate_buf, intermediate_buf_len);
text_buffer_terminate_string(&tex);
meta_line_t *meta = malloc(sizeof(meta_line_t) + tex.dyn_buffer.cur);
meta->key = MetaContent;

View File

@@ -2,6 +2,8 @@
#define SIST_H
#define UUID_STR_LEN 37
#define UNUSED(x) __attribute__((__unused__)) x
#define TESS_DATAPATH "/usr/share/tessdata/"
#include <glib-2.0/glib.h>
#include <unistd.h>
@@ -12,11 +14,11 @@
#include <ftw.h>
#include <uuid.h>
#include <magic.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
#include <ctype.h>
#include <mupdf/fitz.h>
#include <mupdf/pdf.h>
@@ -32,15 +34,15 @@
#include <archive_entry.h>
#include <opc/opc.h>
#include <libxml/xmlstring.h>
#define BOOL int
#include <tesseract/capi.h>
#ifndef SIST_SCAN_ONLY
#include <onion/onion.h>
#include <onion/handler.h>
#include <onion/block.h>
#include <onion/shortcuts.h>
#include <onion/codecs.h>
#include <curl/curl.h>
#endif
#include "cJSON/cJSON.h"
@@ -62,12 +64,10 @@
#include "log.h"
#include "utf8.h/utf8.h"
#ifndef SIST_SCAN_ONLY
#include "src/index/elastic.h"
#include "index/web.h"
#include "web/serve.h"
#include "web/auth_basic.h"
#endif
;

View File

@@ -33,6 +33,10 @@ enum metakey {
MetaParent = 14 | META_STR_MASK,
};
#define INDEX_TYPE_BIN "binary"
#define INDEX_TYPE_JSON "json"
#define INDEX_VERSION_EXTERNAL "_external_v1"
typedef struct index_descriptor {
char uuid[UUID_STR_LEN];
char version[6];
@@ -41,6 +45,7 @@ typedef struct index_descriptor {
char rewrite_url[8196];
short root_len;
char name[1024];
char type[64];
} index_descriptor_t;
typedef struct index_t {
@@ -70,8 +75,6 @@ typedef struct document {
short ext;
meta_line_t *meta_head;
meta_line_t *meta_tail;
struct document *child_head;
struct document *child_tail;
char *filepath;
} document_t;
@@ -79,8 +82,6 @@ typedef struct vfile vfile_t;
typedef int (*read_func_t)(struct vfile *, void *buf, size_t size);
typedef int (*seek_func_t)(struct vfile *, size_t size, int whence);
typedef void (*close_func_t)(struct vfile *);
typedef struct vfile {

View File

@@ -149,7 +149,6 @@ int text_buffer_append_string(text_buffer_t *buf, char *str, size_t len) {
(0xe0 == (0xf0 & str[0]) && len < 3) ||
(0xc0 == (0xe0 & str[0]) && len == 1) ||
*(str) == 0) {
text_buffer_terminate_string(buf);
return 0;
}
@@ -158,7 +157,6 @@ int text_buffer_append_string(text_buffer_t *buf, char *str, size_t len) {
text_buffer_append_char(buf, c);
}
}
text_buffer_terminate_string(buf);
return 0;
}
@@ -169,7 +167,6 @@ int text_buffer_append_string0(text_buffer_t *buf, char *str) {
text_buffer_append_char(buf, c);
}
}
text_buffer_terminate_string(buf);
}
int text_buffer_append_char(text_buffer_t *buf, int c) {

View File

@@ -7,7 +7,7 @@
#define INITIAL_BUF_SIZE 1024 * 16
#define SHOULD_IGNORE_CHAR(c) !(SHOULD_KEEP_CHAR(c))
#define SHOULD_KEEP_CHAR(c) (c >= (int)'!')
#define SHOULD_KEEP_CHAR(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'z') || (c > 127))
typedef struct dyn_buffer {

View File

@@ -110,7 +110,7 @@ int thumbnail(void *p, onion_request *req, onion_response *res) {
int written = onion_response_write(res, data, data_len);
onion_response_flush(res);
if (written != data_len || data_len == 0) {
printf("Couldn't write thumb\n");
LOG_DEBUG("serve.c", "Couldn't write thumbnail");
}
free(data);
@@ -214,7 +214,7 @@ int chunked_response_file(const char *filename, const char *mime,
return OCS_PROCESSED;
}
int search(void *p, onion_request *req, onion_response *res) {
int search(UNUSED(void *p), onion_request *req, onion_response *res) {
int flags = onion_request_get_flags(req);
if ((flags & OR_METHODS) != OR_POST) {
@@ -254,7 +254,7 @@ int search(void *p, onion_request *req, onion_response *res) {
return OCS_PROCESSED;
}
int scroll(void *p, onion_request *req, onion_response *res) {
int scroll(UNUSED(void *p), onion_request *req, onion_response *res) {
int flags = onion_request_get_flags(req);
if ((flags & OR_METHODS) != OR_GET) {
@@ -327,7 +327,7 @@ int serve_file_from_disk(cJSON *json, index_t *idx, onion_request *req, onion_re
return chunked_response_file(full_path, mime, 1, req, res);
}
int index_info(void *p, onion_request *req, onion_response *res) {
int index_info(UNUSED(void *p), onion_request *req, onion_response *res) {
cJSON *json = cJSON_CreateObject();
cJSON *arr = cJSON_AddArrayToObject(json, "indices");
@@ -353,14 +353,47 @@ int index_info(void *p, onion_request *req, onion_response *res) {
return OCS_PROCESSED;
}
int file(void *p, onion_request *req, onion_response *res) {
int document_info(UNUSED(void *p), onion_request *req, onion_response *res) {
const char *arg_uuid = onion_request_get_query(req, "1");
if (arg_uuid == NULL) {
return OCS_PROCESSED;
}
char *next = arg_uuid;
cJSON *doc = elastic_get_document(arg_uuid);
cJSON *source = cJSON_GetObjectItem(doc, "_source");
cJSON *index_id = cJSON_GetObjectItem(source, "index");
if (index_id == NULL) {
cJSON_Delete(doc);
return OCS_NOT_PROCESSED;
}
index_t *idx = get_index_by_id(index_id->valuestring);
if (idx == NULL) {
cJSON_Delete(doc);
return OCS_NOT_PROCESSED;
}
onion_response_set_header(res, "Content-Type", "application/json");
char *json_str = cJSON_PrintUnformatted(source);
onion_response_write0(res, json_str);
free(json_str);
cJSON_Delete(doc);
return OCS_PROCESSED;
}
int file(UNUSED(void *p), onion_request *req, onion_response *res) {
const char *arg_uuid = onion_request_get_query(req, "1");
if (arg_uuid == NULL) {
return OCS_PROCESSED;
}
const char *next = arg_uuid;
cJSON *doc = NULL;
cJSON *index_id = NULL;
cJSON *source = NULL;
@@ -424,6 +457,7 @@ void serve(const char *hostname, const char *port) {
thumbnail
);
onion_url_add(urls, "^f/([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$", file);
onion_url_add(urls, "^d/([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$", document_info);
onion_url_add(urls, "i", index_info);

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,43 @@
outline: 0;
}
.info-icon {
width: 1rem;
margin-right: 0.2rem;
cursor: pointer;
color: #757575;
line-height: 1rem;
height: 1.1rem;
}
.info-icon:hover {
color: inherit;
}
.modal-title {
max-width: calc(100% - 2rem);
overflow: hidden;
text-overflow: ellipsis;
}
.path-row {
display: -ms-flexbox;
display: flex;
-ms-flex-align: start;
align-items: flex-start;
}
.tag-container {
margin-left: 0.3rem;
}
.path-line {
color: #BBB;
text-overflow: ellipsis;
overflow: hidden;
}
a {
color: #00BCD4;
}
@@ -15,7 +52,7 @@ body {
margin-top: 1em;
}
.card {
.card, .modal-content {
margin-top: 1em;
background: #212121;
color: #e0e0e0;
@@ -23,8 +60,33 @@ body {
border: none;
}
.table {
color: #e0e0e0;
}
.table td, .table th {
border: none;
}
.table thead th {
border-bottom: 1px solid #646464;
}
.modal-header .close {
color: #e0e0e0;
text-shadow: none;
}
.modal-header {
border-bottom: 1px solid #646464;
}
.sub-document {
background: #37474F;
background: #37474F !important;
}
.list-group-item.sub-document {
border-top: 1px solid #646464 !important;
}
.sub-document .text-muted {
@@ -112,6 +174,9 @@ body {
}
.file-title {
width: 100%;
line-height: 1rem;
height: 1.1rem;
font-size: 10pt;
white-space: nowrap;
text-overflow: ellipsis;

1
web/css/jquery.toast.min.css vendored Normal file
View File

@@ -0,0 +1 @@
.jq-toast-wrap,.jq-toast-wrap *{margin:0;padding:0}.jq-toast-wrap{display:block;position:fixed;width:250px;pointer-events:none!important;letter-spacing:normal;z-index:9000!important}.jq-toast-wrap.bottom-left{bottom:20px;left:20px}.jq-toast-wrap.bottom-right{bottom:20px;right:40px}.jq-toast-wrap.top-left{top:20px;left:20px}.jq-toast-wrap.top-right{top:20px;right:40px}.jq-toast-single{display:block;width:100%;padding:10px;margin:0 0 5px;border-radius:4px;font-size:12px;font-family:arial,sans-serif;line-height:17px;position:relative;pointer-events:all!important;background-color:#444;color:#fff}.jq-toast-single h2{font-family:arial,sans-serif;font-size:14px;margin:0 0 7px;background:0 0;color:inherit;line-height:inherit;letter-spacing:normal}.jq-toast-single a{color:#eee;text-decoration:none;font-weight:700;border-bottom:1px solid #fff;padding-bottom:3px;font-size:12px}.jq-toast-single ul{margin:0 0 0 15px;background:0 0;padding:0}.jq-toast-single ul li{list-style-type:disc!important;line-height:17px;background:0 0;margin:0;padding:0;letter-spacing:normal}.close-jq-toast-single{position:absolute;top:3px;right:7px;font-size:14px;cursor:pointer}.jq-toast-loader{display:block;position:absolute;top:-2px;height:5px;width:0;left:0;border-radius:5px;background:red}.jq-toast-loaded{width:100%}.jq-has-icon{padding:10px 10px 10px 50px;background-repeat:no-repeat;background-position:10px}.jq-icon-info{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=);background-color:#31708f;color:#d9edf7;border-color:#bce8f1}.jq-icon-warning{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=);background-color:#8a6d3b;color:#fcf8e3;border-color:#faebcc}.jq-icon-error{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=);background-color:#a94442;color:#f2dede;border-color:#ebccd1}.jq-icon-success{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==);color:#dff0d8;background-color:#3c763d;border-color:#d6e9c6}

View File

@@ -2,6 +2,42 @@
outline: 0;
}
.info-icon {
width: 1rem;
margin-right: 0.2rem;
cursor: pointer;
color: #757575;
line-height: 1rem;
height: 1rem;
}
.info-icon:hover {
color: inherit;
}
.modal-title {
max-width: calc(100% - 2rem);
overflow: hidden;
text-overflow: ellipsis;
}
.path-row {
display: -ms-flexbox;
display: flex;
-ms-flex-align: start;
align-items: flex-start;
}
.tag-container {
margin-left: 0.3rem;
}
.path-line {
color: #444;
text-overflow: ellipsis;
overflow: hidden;
}
body {
overflow-y: scroll;
}
@@ -16,7 +52,7 @@ body {
}
.sub-document {
background: #AB47BC1F;
background: #AB47BC1F !important;
}
.navbar-brand {
@@ -82,6 +118,9 @@ body {
}
.file-title {
width: 100%;
line-height: 1rem;
height: 1.1rem;
font-size: 10pt;
white-space: nowrap;
text-overflow: ellipsis;

1
web/js/7_jquery.toast.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -153,11 +153,44 @@ function getTags(hit, mimeCategory) {
return tags
}
/**
*
* @param hit
* @returns {Element}
*/
function infoButtonCb(hit) {
return () => {
getDocumentInfo(hit["_id"]).then(doc => {
$("#modal-title").text(doc["name"] + (doc["extension"] ? "." + doc["extension"] : ""));
const tbody = $("<tbody>");
$("#modal-body").empty()
.append($("<table class='table table-sm'>")
.append($("<thead>")
.append($("<tr>")
.append($("<th>").text("Field"))
.append($("<th>").text("Value"))
)
)
.append(tbody)
);
const displayFields = new Set([
"mime", "size", "mtime", "path", "title", "width", "height", "duration", "audioc", "videoc",
"bitrate", "artist", "album", "album_artist", "genre", "title", "font_name", "tag"
]);
Object.keys(doc)
.filter(key => key.startsWith("_keyword.") || key.startsWith("_text.") || displayFields.has(key))
.forEach(key => {
tbody.append($("<tr>")
.append($("<td>").text(key))
.append($("<td>").text(doc[key]))
);
});
if (doc.hasOwnProperty("content") && doc["content"]) {
$("#modal-body").append($("<div class='content-div'>").text(doc["content"]))
}
$("#modal").modal();
});
}
}
function createDocCard(hit) {
let docCard = document.createElement("div");
docCard.setAttribute("class", "card");
@@ -172,6 +205,7 @@ function createDocCard(hit) {
let link = document.createElement("a");
link.setAttribute("href", "f/" + hit["_id"]);
link.setAttribute("target", "_blank");
link.style.maxWidth = "calc(100% - 1.2rem)";
link.appendChild(title);
if (hit["_source"].hasOwnProperty("parent")) {
@@ -271,7 +305,15 @@ function createDocCard(hit) {
sizeTag.setAttribute("class", "text-muted");
tagContainer.appendChild(sizeTag);
docCardBody.appendChild(link);
const titleWrapper = document.createElement("div");
titleWrapper.style.display = "flex";
const infoButton = makeInfoButton(hit);
titleWrapper.appendChild(infoButton);
titleWrapper.appendChild(link);
docCardBody.appendChild(titleWrapper);
docCard.appendChild(docCardBody);
docCardBody.appendChild(tagContainer);
@@ -352,6 +394,14 @@ function makeThumbnail(mimeCategory, hit, imgWrapper, small) {
return thumbnail;
}
function makeInfoButton(hit) {
const infoButton = document.createElement("span");
infoButton.appendChild(document.createTextNode("🛈"));
infoButton.setAttribute("class", "info-icon");
infoButton.addEventListener("click", infoButtonCb(hit));
return infoButton;
}
function createDocLine(hit) {
const mime = hit["_source"]["mime"];
@@ -367,6 +417,12 @@ function createDocLine(hit) {
const line = document.createElement("div");
line.setAttribute("class", "list-group-item flex-column align-items-start");
if (hit["_source"].hasOwnProperty("parent")) {
line.classList.add("sub-document");
isSubDocument = true;
}
const infoButton = makeInfoButton(hit);
const title = makeTitle(hit);
@@ -376,8 +432,13 @@ function createDocLine(hit) {
link.appendChild(title);
const titleDiv = document.createElement("div");
titleDiv.setAttribute("class", "file-title");
titleDiv.appendChild(link);
const titleWrapper = document.createElement("div");
titleWrapper.style.display = "flex";
titleWrapper.appendChild(infoButton);
titleWrapper.appendChild(link);
titleDiv.appendChild(titleWrapper);
line.appendChild(media);
@@ -396,8 +457,16 @@ function createDocLine(hit) {
titleDiv.appendChild(contentDiv);
}
let pathLine = document.createElement("div");
pathLine.setAttribute("class", "path-row");
let path = document.createElement("div");
path.setAttribute("class", "path-line");
path.setAttribute("title", hit["_source"]["path"] + "/");
path.appendChild(document.createTextNode(hit["_source"]["path"] + "/"));
let tagContainer = document.createElement("div");
tagContainer.setAttribute("class", "");
tagContainer.setAttribute("class", "tag-container");
for (let i = 0; i < tags.length; i++) {
tagContainer.appendChild(tags[i]);
@@ -409,7 +478,9 @@ function createDocLine(hit) {
sizeTag.setAttribute("class", "text-muted");
tagContainer.appendChild(sizeTag);
titleDiv.appendChild(tagContainer);
titleDiv.appendChild(pathLine);
pathLine.appendChild(path);
pathLine.appendChild(tagContainer);
return line;
}

View File

@@ -19,6 +19,19 @@ if (localStorage.getItem("mode") === null) {
mode = localStorage.getItem("mode")
}
function showEsError() {
$.toast({
heading: "Elasticsearch connection error",
text: "sist2 web module encountered an error while connecting " +
"to Elasticsearch. See server logs for more information.",
stack: false,
bgColor: "#a94442",
textColor: "#f2dede",
position: 'bottom-right',
hideAfter: false
});
}
jQuery["jsonPost"] = function (url, data) {
return jQuery.ajax({
url: url,
@@ -26,6 +39,7 @@ jQuery["jsonPost"] = function (url, data) {
data: JSON.stringify(data),
contentType: "application/json"
}).fail(err => {
showEsError();
console.log(err);
});
};
@@ -46,19 +60,32 @@ function toggleFuzzy() {
}
$.jsonPost("i").then(resp => {
const urlIndices = (new URLSearchParams(location.search)).get("i");
resp["indices"].forEach(idx => {
const opt = $("<option>")
.attr("value", idx.id)
.append(idx.name);
if (!idx.name.includes("(nsfw)")) {
opt.attr("selected", !idx.name.includes("(nsfw)"));
if (urlIndices) {
if (urlIndices.split(",").indexOf(idx.name) !== -1) {
opt.attr("selected", true);
selectedIndices.push(idx.id);
}
} else if (!idx.name.includes("(nsfw)")) {
opt.attr("selected", true);
selectedIndices.push(idx.id);
}
$("#indices").append(opt);
});
});
function handleTreeClick (tree) {
function getDocumentInfo(id) {
return $.getJSON("d/" + id)
}
function handleTreeClick(tree) {
return (event, node, handler) => {
event.preventTreeDefault();
@@ -127,7 +154,7 @@ $.jsonPost("es", {
function leafTag(tag) {
const tokens = tag.split(".");
return tokens[tokens.length-1]
return tokens[tokens.length - 1]
}
// Tags tree
@@ -145,8 +172,8 @@ $.jsonPost("es", {
resp["aggregations"]["tags"]["buckets"]
.sort((a, b) => a["key"].localeCompare(b["key"]))
.forEach(bucket => {
addTag(tagMap, bucket["key"], bucket["key"], bucket["doc_count"])
});
addTag(tagMap, bucket["key"], bucket["key"], bucket["doc_count"])
});
tagMap.push({"text": "All", "id": "any"});
tagTree = new InspireTree({

View File

@@ -11,7 +11,7 @@
<nav class="navbar navbar-expand-lg">
<a class="navbar-brand" href="/">sist2</a>
<span class="badge badge-pill version">v1.1.13</span>
<span class="badge badge-pill version">v1.2.2</span>
<span class="tagline">Lightning-fast file system indexer and search tool </span>
<a style="margin-left: auto" id="theme" class="btn" title="Toggle theme" href="/">Theme</a>
</nav>
@@ -65,6 +65,20 @@
</div>
</div>
<div class="modal" id="modal" tabindex="-1" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modal-title"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" id="modal-body"></div>
</div>
</div>
</div>
<div id="searchResults"></div>
</div>