mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-31 01:58:11 +08:00
improve (static): attempt at statically compile everything - revamp
This commit is contained in:
@ -234,7 +234,9 @@ func FileCat(ctx App, res http.ResponseWriter, req *http.Request) {
|
|||||||
if contentLength != -1 {
|
if contentLength != -1 {
|
||||||
header.Set("Content-Length", fmt.Sprintf("%d", contentLength))
|
header.Set("Content-Length", fmt.Sprintf("%d", contentLength))
|
||||||
}
|
}
|
||||||
|
if header.Get("Content-Type") == "" {
|
||||||
header.Set("Content-Type", GetMimeType(req.URL.Query().Get("path")))
|
header.Set("Content-Type", GetMimeType(req.URL.Query().Get("path")))
|
||||||
|
}
|
||||||
if header.Get("Content-Security-Policy") == "" {
|
if header.Get("Content-Security-Policy") == "" {
|
||||||
header.Set("Content-Security-Policy", "default-src 'none'; img-src 'self'; style-src 'unsafe-inline'")
|
header.Set("Content-Security-Policy", "default-src 'none'; img-src 'self'; style-src 'unsafe-inline'")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +1,15 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_image_light"
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_backend_backblaze"
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_backend_dav"
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_backend_mysql"
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_security_scanner"
|
||||||
|
_ "github.com/mickael-kerjean/filestash/server/plugin/plg_security_svg"
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
plg "plugin"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ex, _ := os.Executable()
|
Log.Debug("Plugin loader")
|
||||||
pPath := filepath.Join(filepath.Dir(ex), PLUGIN_PATH)
|
|
||||||
|
|
||||||
file, err := os.Open(pPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
files, err := file.Readdir(0)
|
|
||||||
for i:=0; i < len(files); i++ {
|
|
||||||
name := files[i].Name()
|
|
||||||
if strings.HasPrefix(name, ".") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
Log.Debug("Load plugin: '%s'", name)
|
|
||||||
p, err := plg.Open(pPath + "/" + name)
|
|
||||||
if err != nil {
|
|
||||||
Log.Warning("Can't load plugin: %s => %v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fn, err := p.Lookup("Init")
|
|
||||||
if err != nil {
|
|
||||||
Log.Warning("Can't register plugin: %s => %v", name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if obj, ok := fn.(func(config *Configuration)); ok {
|
|
||||||
obj(&Config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package main
|
package plg_backend_backblaze
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -38,7 +38,7 @@ type BackblazeError struct {
|
|||||||
Status int `json:"status"`
|
Status int `json:"status"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(config *Configuration) {
|
func init() {
|
||||||
Backend.Register("backblaze", Backblaze{})
|
Backend.Register("backblaze", Backblaze{})
|
||||||
BackblazeCache = NewAppCache()
|
BackblazeCache = NewAppCache()
|
||||||
cachePath := filepath.Join(GetCurrentDir(), BackblazeCachePath)
|
cachePath := filepath.Join(GetCurrentDir(), BackblazeCachePath)
|
||||||
|
|||||||
@ -1,10 +1,4 @@
|
|||||||
package main
|
package plg_backend_dav
|
||||||
|
|
||||||
/*
|
|
||||||
* Compilation
|
|
||||||
* ===========
|
|
||||||
* go build -buildmode=plugin -o dist/data/plugin/backend_dav.so server/plugin/plg_backend_dav/index.go
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
@ -28,7 +22,7 @@ const (
|
|||||||
CALDAV string = "caldav"
|
CALDAV string = "caldav"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(config *Configuration) {
|
func init() {
|
||||||
DavCache = NewAppCache(2, 1)
|
DavCache = NewAppCache(2, 1)
|
||||||
Backend.Register(CARDDAV, Dav{})
|
Backend.Register(CARDDAV, Dav{})
|
||||||
Backend.Register(CALDAV, Dav{})
|
Backend.Register(CALDAV, Dav{})
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package main
|
package plg_backend_mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
@ -20,7 +20,7 @@ type Mysql struct {
|
|||||||
db *sql.DB
|
db *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init(config *Configuration) {
|
func init() {
|
||||||
Backend.Register("mysql", Mysql{})
|
Backend.Register("mysql", Mysql{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
go build -buildmode=plugin -o ../../../dist/data/plugin/image.so index.go
|
|
||||||
18
server/plugin/plg_image_light/deps/README.md
Normal file
18
server/plugin/plg_image_light/deps/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
plg_image_light rely on a few libraries for:
|
||||||
|
- image transcoding: libtranscode.a: a library build on top of of libraw
|
||||||
|
- image resizing: libresize.a: a library built on top of libvips
|
||||||
|
|
||||||
|
To create the libraries to be used by Filestash:
|
||||||
|
./create_libtranscode.sh
|
||||||
|
./create_libresize.sh
|
||||||
|
|
||||||
|
To test the libraries are working fine:
|
||||||
|
```
|
||||||
|
# libtranscode:
|
||||||
|
gcc -Wall -c src/libtranscode_test.c
|
||||||
|
gcc -o main_transcode.bin libtranscode_test.o -lm -lgomp -llcms2 -lstdc++ -L. -l:libtranscode.a
|
||||||
|
|
||||||
|
# libresize:
|
||||||
|
gcc -Wall -c src/libresize_test.c `pkg-config --cflags glib-2.0`
|
||||||
|
gcc -o main_resize.bin libresize_test.o -lm -lgmodule-2.0 -lgobject-2.0 -lglib-2.0 -L. -l:libresize.a
|
||||||
|
```
|
||||||
88
server/plugin/plg_image_light/deps/create_libresize.sh
Executable file
88
server/plugin/plg_image_light/deps/create_libresize.sh
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# This script is run like this:
|
||||||
|
# docker run --name debian -ti -v /home/:/home/ debian bash
|
||||||
|
# cd /path/to/this/script
|
||||||
|
# ./create_libresize.sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Tooling
|
||||||
|
apt update
|
||||||
|
apt install -y curl make gcc g++ xz-utils pkg-config python3-pip autoconf libtool unzip python-setuptools cmake git
|
||||||
|
pip3 install --user meson ninja
|
||||||
|
export PATH=~/.local/bin:$PATH
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Stage 1: Get libvips and its dependencies + recompile for less headaches
|
||||||
|
INITIAL_PATH=`pwd`
|
||||||
|
mkdir -p /tmp/filestash/libresize/tmp
|
||||||
|
cd /tmp/filestash/libresize
|
||||||
|
apt install -y libvips-dev
|
||||||
|
cd tmp
|
||||||
|
curl -L -X GET https://github.com/libvips/libvips/releases/download/v8.7.0/vips-8.7.0.tar.gz > libvips.tar.gz
|
||||||
|
tar -zxf libvips.tar.gz
|
||||||
|
cd vips-8.7.0/
|
||||||
|
./configure --enable-static --without-magick --without-lcms --without-OpenEXR --without-nifti --without-pdfium --without-rsvg --without-matio --without-libwebp --without-cfitsio --without-zlib --without-poppler --without-pangoft2 --enable-introspection=no --without-openslide
|
||||||
|
make -j 8
|
||||||
|
make install
|
||||||
|
cd $INITIAL_PATH
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Stage 2: Create our own library as a static build
|
||||||
|
gcc -Wall -c src/libresize.c `pkg-config --cflags glib-2.0`
|
||||||
|
ar rcs libresize.a libresize.o
|
||||||
|
rm *.o
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Stage 3: Gather and assemble all the bits and pieces together
|
||||||
|
#ar x /tmp/libresize.a
|
||||||
|
ar x /usr/local/lib/libvips.a
|
||||||
|
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libz.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libbz2.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libjpeg.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libgif.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libdl.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libicui18n.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libthread.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libgsf-1.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libicuuc.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libicudata.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/liblzma.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libfreetype.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/liblqr-1.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libfftw3.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libfontconfig.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libXext.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libSM.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/liblXt.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libX11.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libgomp.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/liborc-0.4.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libltdl.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/librt.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libharfbuzz.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libgraphite2.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libexpat.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libgio-2.0.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libpng16.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libpixman-1.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libxcb.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libjbig.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libexif.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libxcb-shm
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libxcb-render
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libXrender
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libpcre.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libhdf5.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libresolv
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libXau
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libXdmcp
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libtiff.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libpangoft2-1.0.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libIlmThread.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libpoppler.a
|
||||||
|
|
||||||
|
ar rcs libresize.a *.o
|
||||||
|
rm *.o *.ao
|
||||||
29
server/plugin/plg_image_light/deps/create_libtranscode.sh
Executable file
29
server/plugin/plg_image_light/deps/create_libtranscode.sh
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# This script is run like this:
|
||||||
|
# docker run --name debian -ti -v /home/:/home/ debian bash
|
||||||
|
# cd /path/to/this/script
|
||||||
|
# ./create_libresize.sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Tooling
|
||||||
|
apt update
|
||||||
|
apt install -y curl make gcc g++ xz-utils pkg-config python3-pip autoconf libtool unzip python-setuptools cmake git
|
||||||
|
pip3 install --user meson ninja
|
||||||
|
export PATH=~/.local/bin:$PATH
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Stage 1: Get libraw and its dependencies
|
||||||
|
apt install libraw-dev
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Stage 2: Create our own library as a static build
|
||||||
|
gcc -Wall -c src/libtranscode.c
|
||||||
|
|
||||||
|
################################################
|
||||||
|
# Stage 3: Gather and assemble all the bits and pieces together
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libraw.a
|
||||||
|
ar x /usr/lib/x86_64-linux-gnu/libjpeg.a
|
||||||
|
|
||||||
|
ar rcs libtranscode.a *.o
|
||||||
|
rm *.o *.ao
|
||||||
@ -1,16 +1,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdio.h>
|
||||||
#include <vips/vips.h>
|
#include <vips/vips.h>
|
||||||
|
|
||||||
int resizer_init(const int ncpu){
|
int image_resize(const char *filename, void **buf, size_t *len, int size, int crop, int quality, int exif){
|
||||||
if(VIPS_INIT("filestash")){
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
vips_concurrency_set(1);
|
|
||||||
vips_cache_set_max(0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int resizer_process(const char *input, const char *output, int size, int crop, int quality, int exif){
|
|
||||||
VipsImage *img;
|
VipsImage *img;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -21,7 +12,7 @@ int resizer_process(const char *input, const char *output, int size, int crop, i
|
|||||||
|
|
||||||
if(crop == VIPS_INTERESTING_CENTRE){
|
if(crop == VIPS_INTERESTING_CENTRE){
|
||||||
// Generate a thumbnails: a square picture crop in the center
|
// Generate a thumbnails: a square picture crop in the center
|
||||||
err = vips_thumbnail(input, &img, size,
|
err = vips_thumbnail(filename, &img, size,
|
||||||
"size", VIPS_SIZE_BOTH,
|
"size", VIPS_SIZE_BOTH,
|
||||||
"auto_rotate", TRUE,
|
"auto_rotate", TRUE,
|
||||||
"crop", VIPS_INTERESTING_CENTRE,
|
"crop", VIPS_INTERESTING_CENTRE,
|
||||||
@ -29,7 +20,7 @@ int resizer_process(const char *input, const char *output, int size, int crop, i
|
|||||||
);
|
);
|
||||||
}else{
|
}else{
|
||||||
// normal resize of an image with libvips
|
// normal resize of an image with libvips
|
||||||
err = vips_thumbnail(input, &img, size,
|
err = vips_thumbnail(filename, &img, size,
|
||||||
"size", VIPS_SIZE_DOWN,
|
"size", VIPS_SIZE_DOWN,
|
||||||
"auto_rotate", TRUE,
|
"auto_rotate", TRUE,
|
||||||
"crop", VIPS_INTERESTING_NONE,
|
"crop", VIPS_INTERESTING_NONE,
|
||||||
@ -40,7 +31,15 @@ int resizer_process(const char *input, const char *output, int size, int crop, i
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = vips_jpegsave(img, output, NULL);
|
err = vips_jpegsave_buffer(img, buf, len, "Q", quality, "strip", exif, NULL);
|
||||||
g_object_unref(img);
|
g_object_unref(img);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __attribute__ ((constructor)) initLibrary(void) {
|
||||||
|
VIPS_INIT("imagevips");
|
||||||
|
vips_cache_set_max(0);
|
||||||
|
}
|
||||||
|
void __attribute__ ((destructor)) cleanUpLibrary(void) {
|
||||||
|
vips_shutdown();
|
||||||
|
}
|
||||||
4
server/plugin/plg_image_light/deps/src/libresize.h
Normal file
4
server/plugin/plg_image_light/deps/src/libresize.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <vips/vips.h>
|
||||||
|
|
||||||
|
int image_resize(const char *filename, void **buf, size_t *len, int size, int crop, int quality, int exif);
|
||||||
29
server/plugin/plg_image_light/deps/src/libresize_test.c
Normal file
29
server/plugin/plg_image_light/deps/src/libresize_test.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "libresize.h"
|
||||||
|
|
||||||
|
double benchmark_image_resize(int n, const char*input);
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if(argc != 2){
|
||||||
|
printf("missing argument: need a path to an image\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
printf("=> benchmark %s: %.2fms\n", argv[1], benchmark_image_resize(20, argv[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
double benchmark_image_resize(int n, const char* input) {
|
||||||
|
double total = 0;
|
||||||
|
void *buffer;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for(i=0; i<n; i++){
|
||||||
|
clock_t begin = clock();
|
||||||
|
image_resize(input, &buffer, &len, 200, 1, 90, 0);
|
||||||
|
clock_t end = clock();
|
||||||
|
total += (double)(end - begin) / CLOCKS_PER_SEC * 1000;
|
||||||
|
}
|
||||||
|
return total / n;
|
||||||
|
}
|
||||||
@ -1,14 +1,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <libraw/libraw.h>
|
#include <libraw/libraw.h>
|
||||||
|
|
||||||
int save_thumbnail(const char *filename, libraw_data_t *raw){
|
int save_thumbnail(const char *filename, libraw_data_t *raw);
|
||||||
int err;
|
|
||||||
err = libraw_dcraw_thumb_writer(raw, filename);
|
|
||||||
libraw_close(raw);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int raw_process(const char* filename, int min_width){
|
int image_transcode_compute(const char* filename, int min_width) {
|
||||||
int err;
|
int err;
|
||||||
libraw_data_t *raw;
|
libraw_data_t *raw;
|
||||||
int thumbnail_working = 0;
|
int thumbnail_working = 0;
|
||||||
@ -65,3 +60,10 @@ int raw_process(const char* filename, int min_width){
|
|||||||
libraw_close(raw);
|
libraw_close(raw);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int save_thumbnail(const char *filename, libraw_data_t *raw){
|
||||||
|
int err;
|
||||||
|
err = libraw_dcraw_thumb_writer(raw, filename);
|
||||||
|
libraw_close(raw);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
4
server/plugin/plg_image_light/deps/src/libtranscode.h
Normal file
4
server/plugin/plg_image_light/deps/src/libtranscode.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <libraw/libraw.h>
|
||||||
|
|
||||||
|
int image_transcode_compute(const char* filename, int min_width);
|
||||||
25
server/plugin/plg_image_light/deps/src/libtranscode_test.c
Normal file
25
server/plugin/plg_image_light/deps/src/libtranscode_test.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "libtranscode.h"
|
||||||
|
|
||||||
|
double benchmark_image_transcode(int n, const char*input);
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if(argc != 2){
|
||||||
|
printf("missing argument: need a path to an image\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
printf("=> benchmark %s: %.2fms\n", argv[1], benchmark_image_transcode(20, argv[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
double benchmark_image_transcode(int n, const char* input) {
|
||||||
|
double total = 0;
|
||||||
|
int i = 0;
|
||||||
|
for(i=0; i<n; i++){
|
||||||
|
clock_t begin = clock();
|
||||||
|
image_transcode_compute(input, 200);
|
||||||
|
clock_t end = clock();
|
||||||
|
total += (double)(end - begin) / CLOCKS_PER_SEC * 1000;
|
||||||
|
}
|
||||||
|
return total / n;
|
||||||
|
}
|
||||||
@ -1,9 +1,8 @@
|
|||||||
package main
|
package plg_image_light
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
"github.com/mickael-kerjean/filestash/server/plugin/plg_image_light/lib"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -12,17 +11,11 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const ImageCachePath = "data/cache/image/"
|
||||||
ImageCachePath = "data/cache/image/"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Config *Configuration
|
|
||||||
|
|
||||||
func Init(conf *Configuration) {
|
|
||||||
Config = conf
|
|
||||||
|
|
||||||
|
func init() {
|
||||||
plugin_enable := func() bool {
|
plugin_enable := func() bool {
|
||||||
return conf.Get("features.image.enable_image").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.image.enable_image").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
@ -36,7 +29,7 @@ func Init(conf *Configuration) {
|
|||||||
plugin_enable()
|
plugin_enable()
|
||||||
|
|
||||||
thumb_size := func() int {
|
thumb_size := func() int {
|
||||||
return conf.Get("features.image.thumbnail_size").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.image.thumbnail_size").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
@ -52,7 +45,7 @@ func Init(conf *Configuration) {
|
|||||||
thumb_size()
|
thumb_size()
|
||||||
|
|
||||||
thumb_quality := func() int {
|
thumb_quality := func() int {
|
||||||
return conf.Get("features.image.thumbnail_quality").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.image.thumbnail_quality").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
@ -68,7 +61,7 @@ func Init(conf *Configuration) {
|
|||||||
thumb_quality()
|
thumb_quality()
|
||||||
|
|
||||||
thumb_caching := func() int {
|
thumb_caching := func() int {
|
||||||
return conf.Get("features.image.thumbnail_caching").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.image.thumbnail_caching").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
@ -84,7 +77,7 @@ func Init(conf *Configuration) {
|
|||||||
thumb_caching()
|
thumb_caching()
|
||||||
|
|
||||||
image_quality := func() int {
|
image_quality := func() int {
|
||||||
return conf.Get("features.image.image_quality").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.image.image_quality").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
@ -100,7 +93,7 @@ func Init(conf *Configuration) {
|
|||||||
image_quality()
|
image_quality()
|
||||||
|
|
||||||
image_caching := func() int {
|
image_caching := func() int {
|
||||||
return conf.Get("features.image.image_caching").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.image.image_caching").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
@ -139,9 +132,8 @@ func Init(conf *Configuration) {
|
|||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// Specify transformation
|
// Specify transformation
|
||||||
transform := &lib.Transform{
|
transform := &Transform{
|
||||||
Input: GetAbsolutePath(ImageCachePath + "imagein_" + QuickString(10)),
|
Input: GetAbsolutePath(ImageCachePath + "imagein_" + QuickString(10)),
|
||||||
Output: GetAbsolutePath(ImageCachePath + "imageout_" + QuickString(10)),
|
|
||||||
Size: thumb_size(),
|
Size: thumb_size(),
|
||||||
Crop: true,
|
Crop: true,
|
||||||
Quality: thumb_quality(),
|
Quality: thumb_quality(),
|
||||||
@ -163,7 +155,7 @@ func Init(conf *Configuration) {
|
|||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Insert file in the fs
|
// Insert file in the fs
|
||||||
// => lower RAM usage while processing
|
// => impedance matching with something usable by CGO
|
||||||
file, err := os.OpenFile(transform.Input, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
file, err := os.OpenFile(transform.Input, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return reader, ErrFilesystemError
|
return reader, ErrFilesystemError
|
||||||
@ -173,13 +165,12 @@ func Init(conf *Configuration) {
|
|||||||
reader.Close()
|
reader.Close()
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Remove(transform.Input)
|
os.Remove(transform.Input)
|
||||||
os.Remove(transform.Output)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// Transcode RAW image
|
// Transcode RAW image
|
||||||
if lib.IsRaw(mType) {
|
if IsRaw(mType) {
|
||||||
if lib.ExtractPreview(transform) == nil {
|
if ExtractPreview(transform) == nil {
|
||||||
mType = "image/jpeg"
|
mType = "image/jpeg"
|
||||||
(*res).Header().Set("Content-Type", mType)
|
(*res).Header().Set("Content-Type", mType)
|
||||||
} else {
|
} else {
|
||||||
@ -188,11 +179,19 @@ func Init(conf *Configuration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////
|
/////////////////////////
|
||||||
// Final stage: resizing
|
// final stage: resizing
|
||||||
if mType != "image/jpeg" && mType != "image/png" && mType != "image/gif" && mType != "image/tiff" {
|
if mType != "image/jpeg" && mType != "image/png" && mType != "image/gif" && mType != "image/tiff" {
|
||||||
return reader, nil
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return lib.CreateThumbnail(transform)
|
return CreateThumbnail(transform)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Transform struct {
|
||||||
|
Input string
|
||||||
|
Size int
|
||||||
|
Crop bool
|
||||||
|
Quality int
|
||||||
|
Exif bool
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <libraw/libraw.h>
|
|
||||||
|
|
||||||
int raw_process(const char* filename, int min_width);
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
package lib
|
|
||||||
|
|
||||||
// #cgo pkg-config: vips
|
|
||||||
// #include <resizer.h>
|
|
||||||
// #include <stdlib.h>
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"unsafe"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
LIBVIPS_INSTALLED = false
|
|
||||||
VIPS_LOCK = &sync.Mutex{}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Transform struct {
|
|
||||||
Input string
|
|
||||||
Output string
|
|
||||||
Size int
|
|
||||||
Crop bool
|
|
||||||
Quality int
|
|
||||||
Exif bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
if C.resizer_init(C.int(runtime.NumCPU())) != 0 {
|
|
||||||
Log.Warning("Can't load libvips")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
LIBVIPS_INSTALLED = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateThumbnail(t *Transform) (io.ReadCloser, error) {
|
|
||||||
if LIBVIPS_INSTALLED == false {
|
|
||||||
return nil, NewError("Libvips not installed", 501)
|
|
||||||
}
|
|
||||||
filenameInput := C.CString(t.Input)
|
|
||||||
defer C.free(unsafe.Pointer(filenameInput))
|
|
||||||
|
|
||||||
filenameOutput := C.CString(t.Output)
|
|
||||||
defer C.free(unsafe.Pointer(filenameOutput))
|
|
||||||
|
|
||||||
VIPS_LOCK.Lock()
|
|
||||||
if C.resizer_process(filenameInput, filenameOutput, C.int(t.Size), boolToCInt(t.Crop), C.int(t.Quality), boolToCInt(t.Exif)) != 0 {
|
|
||||||
return nil, NewError("", 500)
|
|
||||||
}
|
|
||||||
VIPS_LOCK.Unlock()
|
|
||||||
return os.OpenFile(t.Output, os.O_RDONLY, os.ModePerm)
|
|
||||||
}
|
|
||||||
|
|
||||||
func boolToCInt(val bool) C.int {
|
|
||||||
if val == false {
|
|
||||||
return C.int(0)
|
|
||||||
}
|
|
||||||
return C.int(1)
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <vips/vips.h>
|
|
||||||
|
|
||||||
int resizer_init(const int ncpu);
|
|
||||||
|
|
||||||
int resizer_process(const char *input, const char *output, int size, int crop, int quality, int exif);
|
|
||||||
39
server/plugin/plg_image_light/lib_resize.go
Normal file
39
server/plugin/plg_image_light/lib_resize.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package plg_image_light
|
||||||
|
// #cgo pkg-config:glib-2.0
|
||||||
|
// #cgo CFLAGS: -I./deps/src
|
||||||
|
// #cgo LDFLAGS: -lm -lgmodule-2.0 -lgobject-2.0 -lglib-2.0 -ldl -L./deps -l:libresize.a
|
||||||
|
// #include "libresize.h"
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
|
"golang.org/x/sync/semaphore"
|
||||||
|
"io"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
var VIPS_LOCK = semaphore.NewWeighted(int64(10))
|
||||||
|
|
||||||
|
func CreateThumbnail(t *Transform) (io.ReadCloser, error) {
|
||||||
|
VIPS_LOCK.Acquire(context.Background(), 1)
|
||||||
|
defer VIPS_LOCK.Release(1)
|
||||||
|
|
||||||
|
filename := C.CString(t.Input)
|
||||||
|
defer C.free(unsafe.Pointer(filename))
|
||||||
|
var buffer unsafe.Pointer
|
||||||
|
len := C.size_t(0)
|
||||||
|
if C.image_resize(filename, &buffer, &len, C.int(t.Size), boolToCInt(t.Crop), C.int(t.Quality), boolToCInt(t.Exif)) != 0 {
|
||||||
|
return nil, NewError("", 500)
|
||||||
|
}
|
||||||
|
buf := C.GoBytes(buffer, C.int(len))
|
||||||
|
C.g_free(C.gpointer(buffer))
|
||||||
|
return NewReadCloserFromBytes(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToCInt(val bool) C.int {
|
||||||
|
if val == false {
|
||||||
|
return C.int(0)
|
||||||
|
}
|
||||||
|
return C.int(1)
|
||||||
|
}
|
||||||
@ -1,18 +1,17 @@
|
|||||||
package lib
|
package plg_image_light
|
||||||
|
// #cgo CFLAGS: -I./deps/src
|
||||||
// #cgo pkg-config: libraw
|
// #cgo LDFLAGS: -lm -lgomp -llcms2 -lstdc++ -L./deps -l:libtranscode.a
|
||||||
// #include <raw.h>
|
// #include "libtranscode.h"
|
||||||
// #include <stdlib.h>
|
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"golang.org/x/sync/semaphore"
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const LIBRAW_MEMORY_ERROR = -1
|
var LIBRAW_LOCK = semaphore.NewWeighted(int64(5))
|
||||||
|
|
||||||
func IsRaw(mType string) bool {
|
func IsRaw(mType string) bool {
|
||||||
switch mType {
|
switch mType {
|
||||||
@ -46,20 +45,14 @@ func IsRaw(mType string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExtractPreview(t *Transform) error {
|
func ExtractPreview(t *Transform) error {
|
||||||
filename := C.CString(t.Input)
|
LIBRAW_LOCK.Acquire(context.Background(), 1)
|
||||||
err := C.raw_process(filename, C.int(t.Size))
|
defer LIBRAW_LOCK.Release(1)
|
||||||
if err == LIBRAW_MEMORY_ERROR {
|
|
||||||
// libraw acts weird sometimes and I couldn't
|
|
||||||
// find a way to increase its available memory :(
|
|
||||||
r := rand.Intn(2000) + 500
|
|
||||||
time.Sleep(time.Duration(r) * time.Millisecond)
|
|
||||||
C.free(unsafe.Pointer(filename))
|
|
||||||
return ExtractPreview(t)
|
|
||||||
} else if err != 0 {
|
|
||||||
C.free(unsafe.Pointer(filename))
|
|
||||||
return NewError("", 500)
|
|
||||||
}
|
|
||||||
|
|
||||||
C.free(unsafe.Pointer(filename))
|
filename := C.CString(t.Input)
|
||||||
|
defer C.free(unsafe.Pointer(filename))
|
||||||
|
|
||||||
|
if err := C.image_transcode_compute(filename, C.int(t.Size)); err != 0 {
|
||||||
|
return ErrNotValid
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1,5 +1,4 @@
|
|||||||
// This plugins attempt to crash http scanners used by script kiddies
|
package plg_security_scanner
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -16,8 +15,8 @@ var (
|
|||||||
billionsOfLol *bytes.Buffer
|
billionsOfLol *bytes.Buffer
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(config *Configuration) {
|
func init() {
|
||||||
if plugin_enable := config.Get("features.protection.enable").Schema(func(f *FormElement) *FormElement{
|
if plugin_enable := Config.Get("features.protection.enable").Schema(func(f *FormElement) *FormElement{
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package main
|
package plg_security_svg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
. "github.com/mickael-kerjean/filestash/server/common"
|
||||||
@ -8,9 +8,9 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Init(conf *Configuration) {
|
func init() {
|
||||||
disable_svg := func() bool {
|
disable_svg := func() bool {
|
||||||
return conf.Get("features.protection.disable_svg").Schema(func(f *FormElement) *FormElement {
|
return Config.Get("features.protection.disable_svg").Schema(func(f *FormElement) *FormElement {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f = &FormElement{}
|
f = &FormElement{}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user