mirror of
https://github.com/simon987/sist2.git
synced 2025-12-13 07:19:06 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8f1a27180 | |||
| 784c3c9435 | |||
| f8b081a3f4 | |||
| 5661573b06 |
@@ -116,10 +116,10 @@ if (WITH_SIST2)
|
|||||||
|
|
||||||
target_compile_options(sist2
|
target_compile_options(sist2
|
||||||
PRIVATE
|
PRIVATE
|
||||||
-O3
|
# -O3
|
||||||
# -march=native
|
# -march=native
|
||||||
-fno-stack-protector
|
# -fno-stack-protector
|
||||||
-fomit-frame-pointer
|
# -fomit-frame-pointer
|
||||||
)
|
)
|
||||||
|
|
||||||
TARGET_LINK_LIBRARIES(
|
TARGET_LINK_LIBRARIES(
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ sist2 web --bind 0.0.0.0 --port 4321 ./my_idx1 ./my_idx2 ./my_idx3
|
|||||||
|
|
||||||
File type | Library | Content | Thumbnail | Metadata
|
File type | Library | Content | Thumbnail | Metadata
|
||||||
:---|:---|:---|:---|:---
|
:---|:---|:---|:---|:---
|
||||||
pdf,xps,cbz,cbr,fb2,epub | MuPDF | yes | yes, `png` | *planned* |
|
pdf,xps,cbz,cbr,fb2,epub | MuPDF | yes | yes, `png` | title |
|
||||||
`audio/*` | libav | - | yes, `jpeg` | ID3 tags |
|
`audio/*` | libav | - | yes, `jpeg` | ID3 tags |
|
||||||
`video/*` | libav | - | yes, `jpeg` | *planned* |
|
`video/*` | libav | - | yes, `jpeg` | *planned* |
|
||||||
`image/*` | libav | - | yes, `jpeg` | *planned* |
|
`image/*` | libav | - | yes, `jpeg` | *planned* |
|
||||||
@@ -85,7 +85,7 @@ binaries.
|
|||||||
```bash
|
```bash
|
||||||
pkg install cmake gcc yasm gmake bash ffmpeg e2fsprogs-uuid
|
pkg install cmake gcc yasm gmake bash ffmpeg e2fsprogs-uuid
|
||||||
```
|
```
|
||||||
__
|
|
||||||
2. Build
|
2. Build
|
||||||
```bash
|
```bash
|
||||||
git clone --recurse-submodules https://github.com/simon987/sist2
|
git clone --recurse-submodules https://github.com/simon987/sist2
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#define EPILOG "Made by simon987 <me@simon987.net>. Released under GPL-3.0"
|
#define EPILOG "Made by simon987 <me@simon987.net>. Released under GPL-3.0"
|
||||||
|
|
||||||
|
|
||||||
static const char *const Version = "1.0.11";
|
static const char *const Version = "1.0.14";
|
||||||
static const char *const usage[] = {
|
static const char *const usage[] = {
|
||||||
"sist2 scan [OPTION]... PATH",
|
"sist2 scan [OPTION]... PATH",
|
||||||
"sist2 index [OPTION]... INDEX",
|
"sist2 index [OPTION]... INDEX",
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ typedef struct text_dimensions {
|
|||||||
} text_dimensions_t;
|
} text_dimensions_t;
|
||||||
|
|
||||||
typedef struct glyph {
|
typedef struct glyph {
|
||||||
unsigned int top;
|
int top;
|
||||||
unsigned int height;
|
int height;
|
||||||
unsigned int width;
|
int width;
|
||||||
unsigned int descent;
|
int descent;
|
||||||
unsigned int ascent;
|
int ascent;
|
||||||
unsigned int advance_width;
|
int advance_width;
|
||||||
unsigned char *pixmap;
|
unsigned char *pixmap;
|
||||||
} glyph_t;
|
} glyph_t;
|
||||||
|
|
||||||
@@ -39,10 +39,10 @@ glyph_t ft_glyph_to_glyph(FT_GlyphSlot slot) {
|
|||||||
|
|
||||||
glyph.pixmap = slot->bitmap.buffer;
|
glyph.pixmap = slot->bitmap.buffer;
|
||||||
|
|
||||||
glyph.width = slot->bitmap.width;
|
glyph.width = (int) slot->bitmap.width;
|
||||||
glyph.height = slot->bitmap.rows;
|
glyph.height = (int) slot->bitmap.rows;
|
||||||
glyph.top = slot->bitmap_top;
|
glyph.top = slot->bitmap_top;
|
||||||
glyph.advance_width = slot->advance.x / 64;
|
glyph.advance_width = (int) slot->advance.x / 64;
|
||||||
|
|
||||||
glyph.descent = MAX(0, glyph.height - glyph.top);
|
glyph.descent = MAX(0, glyph.height - glyph.top);
|
||||||
glyph.ascent = MAX(0, MAX(glyph.top, glyph.height) - glyph.descent);
|
glyph.ascent = MAX(0, MAX(glyph.top, glyph.height) - glyph.descent);
|
||||||
@@ -50,10 +50,6 @@ glyph_t ft_glyph_to_glyph(FT_GlyphSlot slot) {
|
|||||||
return glyph;
|
return glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
__always_inline
|
|
||||||
glyph_t get_glyph(char character, FT_Face face) {
|
|
||||||
}
|
|
||||||
|
|
||||||
text_dimensions_t text_dimension(char *text, FT_Face face) {
|
text_dimensions_t text_dimension(char *text, FT_Face face) {
|
||||||
text_dimensions_t dimensions;
|
text_dimensions_t dimensions;
|
||||||
|
|
||||||
@@ -62,7 +58,7 @@ text_dimensions_t text_dimension(char *text, FT_Face face) {
|
|||||||
int num_chars = (int) strlen(text);
|
int num_chars = (int) strlen(text);
|
||||||
|
|
||||||
unsigned int max_ascent = 0;
|
unsigned int max_ascent = 0;
|
||||||
unsigned int max_descent = 0;
|
int max_descent = 0;
|
||||||
|
|
||||||
char pc = 0;
|
char pc = 0;
|
||||||
for (int i = 0; i < num_chars; i++) {
|
for (int i = 0; i < num_chars; i++) {
|
||||||
@@ -72,7 +68,7 @@ text_dimensions_t text_dimension(char *text, FT_Face face) {
|
|||||||
glyph_t glyph = ft_glyph_to_glyph(face->glyph);
|
glyph_t glyph = ft_glyph_to_glyph(face->glyph);
|
||||||
|
|
||||||
max_descent = MAX(max_descent, glyph.descent);
|
max_descent = MAX(max_descent, glyph.descent);
|
||||||
max_ascent = MAX(max_ascent, glyph.ascent);
|
max_ascent = MAX(max_ascent, MAX(glyph.height, glyph.ascent));
|
||||||
|
|
||||||
int kerning_x = kerning_offset(c, pc, face);
|
int kerning_x = kerning_offset(c, pc, face);
|
||||||
dimensions.width += MAX(glyph.advance_width, glyph.width) + kerning_x;
|
dimensions.width += MAX(glyph.advance_width, glyph.width) + kerning_x;
|
||||||
@@ -195,6 +191,9 @@ void parse_font(const char *buf, size_t buf_len, document_t *doc) {
|
|||||||
glyph_t glyph = ft_glyph_to_glyph(face->glyph);
|
glyph_t glyph = ft_glyph_to_glyph(face->glyph);
|
||||||
|
|
||||||
pen.x += kerning_offset(c, pc, face);
|
pen.x += kerning_offset(c, pc, face);
|
||||||
|
if (pen.x <= 0) {
|
||||||
|
pen.x = ABS(glyph.advance_width - glyph.width);
|
||||||
|
}
|
||||||
pen.y = dimensions.height - glyph.ascent - dimensions.baseline;
|
pen.y = dimensions.height - glyph.ascent - dimensions.baseline;
|
||||||
|
|
||||||
draw_glyph(&glyph, pen.x, pen.y, dimensions, bitmap);
|
draw_glyph(&glyph, pen.x, pen.y, dimensions, bitmap);
|
||||||
|
|||||||
@@ -107,6 +107,15 @@ AVFrame *read_frame(AVFormatContext *pFormatCtx, AVCodecContext *decoder, int st
|
|||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define APPEND_TAG_META(doc, tag, keyname) \
|
||||||
|
text_buffer_t tex = text_buffer_create(4096); \
|
||||||
|
text_buffer_append_string(&tex, tag->value); \
|
||||||
|
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); \
|
||||||
|
APPEND_META(doc, meta_tag) \
|
||||||
|
text_buffer_destroy(&tex);
|
||||||
|
|
||||||
void append_audio_meta(AVFormatContext *pFormatCtx, document_t *doc) {
|
void append_audio_meta(AVFormatContext *pFormatCtx, document_t *doc) {
|
||||||
|
|
||||||
AVDictionaryEntry *tag = NULL;
|
AVDictionaryEntry *tag = NULL;
|
||||||
@@ -115,35 +124,40 @@ void append_audio_meta(AVFormatContext *pFormatCtx, document_t *doc) {
|
|||||||
for (; *key; ++key) *key = (char) tolower(*key);
|
for (; *key; ++key) *key = (char) tolower(*key);
|
||||||
|
|
||||||
if (strcmp(tag->key, "artist") == 0) {
|
if (strcmp(tag->key, "artist") == 0) {
|
||||||
size_t len = strlen(tag->value);
|
APPEND_TAG_META(doc, tag, MetaArtist)
|
||||||
meta_line_t *meta_tag = malloc(sizeof(meta_line_t) + len);
|
|
||||||
meta_tag->key = MetaArtist;
|
|
||||||
memcpy(meta_tag->strval, tag->value, len);
|
|
||||||
APPEND_META(doc, meta_tag)
|
|
||||||
} else if (strcmp(tag->key, "genre") == 0) {
|
} else if (strcmp(tag->key, "genre") == 0) {
|
||||||
size_t len = strlen(tag->value);
|
APPEND_TAG_META(doc, tag, MetaGenre)
|
||||||
meta_line_t *meta_tag = malloc(sizeof(meta_line_t) + len);
|
|
||||||
meta_tag->key = MetaGenre;
|
|
||||||
memcpy(meta_tag->strval, tag->value, len);
|
|
||||||
APPEND_META(doc, meta_tag)
|
|
||||||
} else if (strcmp(tag->key, "title") == 0) {
|
} else if (strcmp(tag->key, "title") == 0) {
|
||||||
size_t len = strlen(tag->value);
|
APPEND_TAG_META(doc, tag, MetaTitle)
|
||||||
meta_line_t *meta_tag = malloc(sizeof(meta_line_t) + len);
|
|
||||||
meta_tag->key = MetaTitle;
|
|
||||||
memcpy(meta_tag->strval, tag->value, len);
|
|
||||||
APPEND_META(doc, meta_tag)
|
|
||||||
} else if (strcmp(tag->key, "album_artist") == 0) {
|
} else if (strcmp(tag->key, "album_artist") == 0) {
|
||||||
size_t len = strlen(tag->value);
|
APPEND_TAG_META(doc, tag, MetaAlbumArtist)
|
||||||
meta_line_t *meta_tag = malloc(sizeof(meta_line_t) + len);
|
|
||||||
meta_tag->key = MetaAlbumArtist;
|
|
||||||
memcpy(meta_tag->strval, tag->value, len);
|
|
||||||
APPEND_META(doc, meta_tag)
|
|
||||||
} else if (strcmp(tag->key, "album") == 0) {
|
} else if (strcmp(tag->key, "album") == 0) {
|
||||||
size_t len = strlen(tag->value);
|
APPEND_TAG_META(doc, tag, MetaAlbum)
|
||||||
meta_line_t *meta_tag = malloc(sizeof(meta_line_t) + len);
|
}
|
||||||
meta_tag->key = MetaAlbum;
|
}
|
||||||
memcpy(meta_tag->strval, tag->value, len);
|
}
|
||||||
APPEND_META(doc, meta_tag)
|
|
||||||
|
void append_video_meta(AVFormatContext *pFormatCtx, document_t *doc, int include_audio_tags) {
|
||||||
|
|
||||||
|
meta_line_t *meta_duration = malloc(sizeof(meta_line_t));
|
||||||
|
meta_duration->key = MetaMediaDuration;
|
||||||
|
meta_duration->longval = pFormatCtx->duration / AV_TIME_BASE;
|
||||||
|
APPEND_META(doc, meta_duration)
|
||||||
|
|
||||||
|
meta_line_t *meta_bitrate = malloc(sizeof(meta_line_t));
|
||||||
|
meta_bitrate->key = MetaMediaBitrate;
|
||||||
|
meta_bitrate->intval = pFormatCtx->bit_rate;
|
||||||
|
APPEND_META(doc, meta_bitrate)
|
||||||
|
|
||||||
|
AVDictionaryEntry *tag = NULL;
|
||||||
|
while ((tag = av_dict_get(pFormatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||||
|
char *key = tag->key;
|
||||||
|
for (; *key; ++key) *key = (char) tolower(*key);
|
||||||
|
|
||||||
|
if (strcmp(tag->key, "title") == 0 && include_audio_tags) {
|
||||||
|
APPEND_TAG_META(doc, tag, MetaTitle)
|
||||||
|
} else if (strcmp(tag->key, "comment") == 0) {
|
||||||
|
APPEND_TAG_META(doc, tag, MetaContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,15 +221,7 @@ void parse_media(const char *filepath, document_t *doc) {
|
|||||||
|
|
||||||
if (stream->nb_frames > 1) {
|
if (stream->nb_frames > 1) {
|
||||||
//This is a video (not a still image)
|
//This is a video (not a still image)
|
||||||
meta_line_t *meta_duration = malloc(sizeof(meta_line_t));
|
append_video_meta(pFormatCtx, doc, audio_stream == -1);
|
||||||
meta_duration->key = MetaMediaDuration;
|
|
||||||
meta_duration->longval = pFormatCtx->duration / AV_TIME_BASE;
|
|
||||||
APPEND_META(doc, meta_duration)
|
|
||||||
|
|
||||||
meta_line_t *meta_bitrate = malloc(sizeof(meta_line_t));
|
|
||||||
meta_bitrate->key = MetaMediaBitrate;
|
|
||||||
meta_bitrate->intval = pFormatCtx->bit_rate;
|
|
||||||
APPEND_META(doc, meta_bitrate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream->codecpar->width <= 20 || stream->codecpar->height <= 20) {
|
if (stream->codecpar->width <= 20 || stream->codecpar->height <= 20) {
|
||||||
|
|||||||
@@ -89,6 +89,14 @@ void text_buffer_terminate_string(text_buffer_t *buf) {
|
|||||||
dyn_buffer_write_char(&buf->dyn_buffer, '\0');
|
dyn_buffer_write_char(&buf->dyn_buffer, '\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int text_buffer_append_string(text_buffer_t *buf, char * str) {
|
||||||
|
char * ptr = str;
|
||||||
|
while (*ptr) {
|
||||||
|
text_buffer_append_char(buf, *ptr++);
|
||||||
|
}
|
||||||
|
text_buffer_terminate_string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
int text_buffer_append_char(text_buffer_t *buf, int c) {
|
int text_buffer_append_char(text_buffer_t *buf, int c) {
|
||||||
|
|
||||||
if (SHOULD_IGNORE_CHAR(c)) {
|
if (SHOULD_IGNORE_CHAR(c)) {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ text_buffer_t text_buffer_create(int max_size);
|
|||||||
|
|
||||||
void text_buffer_terminate_string(text_buffer_t *buf);
|
void text_buffer_terminate_string(text_buffer_t *buf);
|
||||||
|
|
||||||
|
int text_buffer_append_string(text_buffer_t *buf, char * str);
|
||||||
int text_buffer_append_char(text_buffer_t *buf, int c);
|
int text_buffer_append_char(text_buffer_t *buf, int c);
|
||||||
|
|
||||||
void incremental_put(GHashTable *table, unsigned long inode_no, int mtime);
|
void incremental_put(GHashTable *table, unsigned long inode_no, int mtime);
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,6 +15,8 @@ body {
|
|||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
background: #212121;
|
background: #212121;
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
|
border-radius: 1px;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ body {overflow-y:scroll;}
|
|||||||
|
|
||||||
.card {
|
.card {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
box-shadow: 0 .125rem .25rem rgba(0,0,0,.075) !important;
|
||||||
}
|
}
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
@@ -160,7 +161,7 @@ mark {
|
|||||||
.page-indicator {
|
.page-indicator {
|
||||||
line-height: 1rem;
|
line-height: 1rem;
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
background: #212121;
|
background: #f8f9fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-xs {
|
.btn-xs {
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ function shouldPlayVideo(hit) {
|
|||||||
*/
|
*/
|
||||||
function createDocCard(hit) {
|
function createDocCard(hit) {
|
||||||
let docCard = document.createElement("div");
|
let docCard = document.createElement("div");
|
||||||
docCard.setAttribute("class", "card shadow-sm");
|
docCard.setAttribute("class", "card");
|
||||||
|
|
||||||
let docCardBody = document.createElement("div");
|
let docCardBody = document.createElement("div");
|
||||||
docCardBody.setAttribute("class", "card-body document");
|
docCardBody.setAttribute("class", "card-body document");
|
||||||
@@ -257,7 +257,7 @@ function makePreloader() {
|
|||||||
|
|
||||||
function makePageIndicator(searchResult) {
|
function makePageIndicator(searchResult) {
|
||||||
let pageIndicator = document.createElement("div");
|
let pageIndicator = document.createElement("div");
|
||||||
pageIndicator.setAttribute("class", "page-indicator shadow-sm font-weight-light");
|
pageIndicator.setAttribute("class", "page-indicator font-weight-light");
|
||||||
const totalHits = searchResult["hits"]["total"].hasOwnProperty("value")
|
const totalHits = searchResult["hits"]["total"].hasOwnProperty("value")
|
||||||
? searchResult["hits"]["total"]["value"] : searchResult["hits"]["total"];
|
? searchResult["hits"]["total"]["value"] : searchResult["hits"]["total"];
|
||||||
pageIndicator.appendChild(document.createTextNode(docCount + " / " + totalHits));
|
pageIndicator.appendChild(document.createTextNode(docCount + " / " + totalHits));
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ jQuery["jsonPost"] = function (url, data) {
|
|||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
$("#theme").on("click", () => {
|
$("#theme").on("click", () => {
|
||||||
if (document.cookie.length === 0) {
|
if (!document.cookie.includes("sist")) {
|
||||||
document.cookie = "sist=dark";
|
document.cookie = "sist=dark";
|
||||||
} else {
|
} else {
|
||||||
document.cookie = "sist=; Max-Age=-99999999;";
|
document.cookie = "sist=; Max-Age=-99999999;";
|
||||||
@@ -116,7 +116,7 @@ $.jsonPost("es", {
|
|||||||
new autoComplete({
|
new autoComplete({
|
||||||
selector: '#pathBar',
|
selector: '#pathBar',
|
||||||
minChars: 1,
|
minChars: 1,
|
||||||
delay: 75,
|
delay: 400,
|
||||||
renderItem: function (item) {
|
renderItem: function (item) {
|
||||||
return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item + '</div>';
|
return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item + '</div>';
|
||||||
},
|
},
|
||||||
@@ -279,14 +279,6 @@ function search() {
|
|||||||
//Search stats
|
//Search stats
|
||||||
searchResults.appendChild(makeStatsCard(searchResult));
|
searchResults.appendChild(makeStatsCard(searchResult));
|
||||||
|
|
||||||
//Autocomplete
|
|
||||||
if (searchResult.hasOwnProperty("suggest") && searchResult["suggest"].hasOwnProperty("path")) {
|
|
||||||
pathAutoComplete = [];
|
|
||||||
for (let i = 0; i < searchResult["suggest"]["path"][0]["options"].length; i++) {
|
|
||||||
pathAutoComplete.push(searchResult["suggest"]["path"][0]["options"][i].text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Setup page
|
//Setup page
|
||||||
let resultContainer = makeResultContainer();
|
let resultContainer = makeResultContainer();
|
||||||
searchResults.appendChild(resultContainer);
|
searchResults.appendChild(resultContainer);
|
||||||
@@ -298,7 +290,6 @@ function search() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let pathAutoComplete = [];
|
|
||||||
let size_min = 0;
|
let size_min = 0;
|
||||||
let size_max = 10000000000000;
|
let size_max = 10000000000000;
|
||||||
|
|
||||||
@@ -306,8 +297,8 @@ let searchDebounced = _.debounce(function () {
|
|||||||
coolingDown = false;
|
coolingDown = false;
|
||||||
search()
|
search()
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
searchBar.addEventListener("keyup", searchDebounced);
|
searchBar.addEventListener("keyup", searchDebounced);
|
||||||
document.getElementById("pathBar").addEventListener("keyup", searchDebounced);
|
|
||||||
|
|
||||||
//Size slider
|
//Size slider
|
||||||
$("#sizeSlider").ionRangeSlider({
|
$("#sizeSlider").ionRangeSlider({
|
||||||
@@ -358,15 +349,18 @@ updateIndices();
|
|||||||
//Suggest
|
//Suggest
|
||||||
function getPathChoices() {
|
function getPathChoices() {
|
||||||
return new Promise(getPaths => {
|
return new Promise(getPaths => {
|
||||||
|
$.jsonPost("es", {
|
||||||
let xhttp = new XMLHttpRequest();
|
suggest: {
|
||||||
xhttp.onreadystatechange = function () {
|
path: {
|
||||||
if (this.readyState === 4 && this.status === 200) {
|
prefix: pathBar.value,
|
||||||
getPaths(JSON.parse(xhttp.responseText))
|
completion: {
|
||||||
|
field: "suggest-path",
|
||||||
|
skip_duplicates: true,
|
||||||
|
size: 10000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}).then(resp => getPaths(resp["suggest"]["path"][0]["options"].map(opt => opt["_source"]["path"])));
|
||||||
xhttp.open("GET", "suggest?prefix=" + pathBar.value, true);
|
})
|
||||||
xhttp.send();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user