diff --git a/README.md b/README.md
index 33be4a4..39e4053 100644
--- a/README.md
+++ b/README.md
@@ -4,24 +4,49 @@
PEPIC
-Pepic is a small app that helps me to upload, store, convert and serve pictures (or videos) on my dedicated servers.
+Pepic is a small self-hosted media proxy that helps me to upload, store, serve and convert pictures and videos on my own servers locally.
-I use it as media proxy for my [pet-projects](https://github.com/vas3k/vas3k.club) and on [my blog](https://vas3k.com).
-Pepic can convert, resize and optimize media files in-flight to save you monies and bandwidth.
-Internally it uses [ffmpeg](https://ffmpeg.org/download.html) for videos and [vips](https://libvips.github.io/libvips/install.html) for images,
-which means it's quite fast and supports **JPG, PNG, GIF, WEBP, SVG, HEIF, TIFF** and wide range of video formats.
+Currently, I use it as a main storage for media files in my [pet-projects](https://github.com/vas3k/vas3k.club) and on [my blog](https://vas3k.blog).
-It's not meant to be used by anyone else except me. Use it only if you're brave. Scroll down this README for better alternatives.
+Pepic can upload and optimize media files in-flight to save you money and bandwidth. It's highly recommended to use it in combination with Cloudflare CDN for better caching.
-## 🤖 How to run it locally
+Internally, Pepic it uses [ffmpeg](https://ffmpeg.org/download.html) for videos and [vips](https://libvips.github.io/libvips/install.html) for images, which makes it quite fast and supports many media file formats.
-This command starts a local server on [localhost:8118](http://localhost:8118). Useful for development.
+Images: **JPG, PNG, GIF, WEBP, SVG, HEIF, TIFF, AVIF, etc**
+Video: **basically everything ffmpeg supports**
+Pepic is open source, however it's not meant to be used by anyone. Only if you're brave. Scroll down this README for better alternatives.
+
+
+## Main Features
+
+- **Local files upload**: Accept files in multipart/form-data or bytes and store them to a local directory.
+- **Automatic GIF to video conversion**: Convert GIFs to videos because GIFs suck, slow down web pages, and don't support hardware acceleration.
+- **Media format conversion and optimization**: Convert and optimize media files on-the-fly.
+- **Dynamic resizing**: Easily resize images in real-time by modifying the URL, which helps in reducing bandwidth and storage space on devices.
+- **High performance**: Pepic uses native libraries like `ffmpeg` and `vips` for video and image processing to ensure high performance and fast processing times.
+- **Local and containerized environments**: Designed to run smoothly in both local environments and within Docker containers, making it versatile for development and deployment.
+- **Custom configuration**: Flexible configuration options through `config.yml`, allowing adjustments to image size, quality, automatic conversion, templates, etc.
+
+![](static/images/screenshot1.png)
+
+## 🤖 How to Run
+
+1. Install `vips` and `ffmpeg` first, as they are external dependencies.
+
+```bash
+brew install vips ffmpeg
```
+
+2. Use the following command to start a local server on [localhost:8118](http://localhost:8118).
+
+```bash
go run main.go serve --config ./etc/pepic/config.yml
```
-> ⚠️ If you're getting `invalid flag in pkg-config` error, run `export CGO_CFLAGS_ALLOW="-Xpreprocessor"` in advance
+> ⚠️ If you're getting `invalid flag in pkg-config` error, run `brew install pkg-config` and `export CGO_CFLAGS_ALLOW="-Xpreprocessor"`. Then try `go run` again.
+
+3. Enjoy!
## 🐳 Running in Docker
@@ -29,38 +54,34 @@ go run main.go serve --config ./etc/pepic/config.yml
2. Clone the repo
-```
+```bash
git clone git@github.com:vas3k/pepic.git
cd pepic
```
3. Build and run the app
-```
+```bash
docker build .
docker run -p 8118:8118 -v ${PWD}/uploads:/app/uploads $(docker build -q .)
```
-4. Go to [http://localhost:8118](http://localhost:8118) and try uploading something.
-You should see uploaded images or videos in the data directory (`./uploads`) after that.
+4. Go to [http://localhost:8118](http://localhost:8118) and try uploading something. You should see uploaded images or videos in the data directory (`./uploads`) after that.
5. Try to resize an image by adding a number of pixels to its URL. For example: `https://localhost:8118/file.jpg -> https://localhost:8118/500/file.jpg`
-6. Check out the [etc/pepic/config.yml](etc/pepic/config.yml) file. Some stuff is turned off by default.
-You can tweak them for yourself and rebuild the docker again (step 3) to apply them.
+6. Check out the [etc/pepic/config.yml](etc/pepic/config.yml) file. Some stuff is turned off by default. You can tweak them for yourself and rebuild the Docker again (step 3) to apply them.
-![](static/images/screenshot1.png)
-## 🚢 Production Usage
+## 🚢 Production Deployment
-> ⚠️ If you plan to host anything bigger than a blog, always put it behind CDN.
-> CloudFlare offers a free one if you don't hate big corporations :D
+> ⚠️ If you plan to host anything bigger than a blog, always put Pepic behind a CDN. CloudFlare offers a free one if you don't hate big corporations :D
Let's say, you want to host it on `https://media.mydomain.org`
-#### 1. Modify `etc/pepic/config.yml` to your taste
+1. Modify `etc/pepic/config.yml` to your taste
-```
+```yaml
global:
host: 0.0.0.0
port: 8118 # internal host and port, leave it as it is
@@ -69,31 +90,44 @@ global:
max_upload_size: "500M"
```
-#### 2. Build and run production docker
+2. Build and run production Docker
-Don't forget to mount upload volume to store files on host (or you can lose those files when container will be killed).
+Don't forget to mount upload volume to store files on host (or you can lose those files when the container is killed).
-```
+```bash
docker run -p 8118:8118 -v /host/dir/uploads:/app/uploads --restart=always $(docker build -q .)
```
If you prefer docker-compose, you can use it too. Check out the included [docker-compose.example.yml](docker-compose.example.yml).
You can easily transform it into your favourite k8s config or whatever is fashionable this summer.
-> 👍 Don't forger to periodically backup the `/host/dir/uploads` directory just in case :)
+> 👍 Don't forget to periodically backup the `/host/dir/uploads` directory just in case :)
-#### 3. Use nginx or your other favourite proxy
+3. Use nginx or your other favourite proxy
-Just proxy all calls from the domain (media.mydomain.org) to pepic backend (0.0.0.0:8118). It can handle static files too.
+Just proxy all calls from the domain (media.mydomain.org) to Pepic backend (0.0.0.0:8118). Don't forget to set `max file size` and `proxy timeot` directives to avoid gateway errors on big files (especially videos).
-```
+```nginx
server {
listen 80;
server_name media.mydomain.org;
+ client_max_body_size 500M;
+ real_ip_header X-Real-IP;
+
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_read_timeout 300;
+ proxy_connect_timeout 300;
+ proxy_send_timeout 300;
+ send_timeout 300;
+
proxy_set_header Host $http_host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ proxy_redirect off;
+ proxy_buffering off;
+
proxy_pass http://0.0.0.0:8118;
}
}
@@ -103,7 +137,7 @@ server {
Contributions are welcome.
-Open an [Issue](https://github.com/vas3k/vas3k.club/issues) if you want to report a bug and propose an idea.
+Open an [Issue](https://github.com/vas3k/vas3k.club/issues) if you want to report a bug or propose an idea.
## ✅ TODO
@@ -111,7 +145,8 @@ Open an [Issue](https://github.com/vas3k/vas3k.club/issues) if you want to repor
- [ ] Upload by URL
- [ ] Crop, rotate and other useful transformations (face blur? pre-loader generator?)
- [ ] Live conversion by changing file's extension
-- [ ] Set format and quality during the upload (using GET/POST params?)
+- [ ] Set format and media quality during the upload (using GET/POST params?)
+
## 🤔 Alternatives
@@ -121,6 +156,7 @@ After reading all this, you probably realized how bad it is and looking for othe
- [imaginary](https://github.com/h2non/imaginary)
- [flyimg](https://github.com/flyimg/flyimg)
+
## 👩💼 License
It's [MIT](LICENSE).
diff --git a/etc/pepic/config.yml b/etc/pepic/config.yml
index b7abaf4..0d71d78 100644
--- a/etc/pepic/config.yml
+++ b/etc/pepic/config.yml
@@ -17,6 +17,7 @@ images:
live_resize: true # enables special URLs that return resized images (increases storage usage)
jpeg_quality: 95 # default quality of any saved jpeg
png_compression: 0 # 0 - default, -1 - no compression, -2 - best speed, -3 - best compression
+ gif_convert: "video/mp4" # video format for auto-converting gifs (turned off then store_originals=true)
videos:
store_originals: false # use "true" if you want to store original files (browser compatibility is on you)
@@ -37,7 +38,7 @@ videos:
pix_fmt: "yuv420p"
meta:
- blocks: # you can add your custom blocks to make it easier to copy-paste
+ image_templates: # you can add your custom blocks to make it easier to copy-paste
- title: "URL"
template: "{{ file.Url }}"
- title: "Simple Markdown"
@@ -50,11 +51,17 @@ meta:
template: "{% verbatim %}{{{{% endverbatim %}.block-side.block-side__right ![]({{ file.Url }}) {% verbatim %}}}}{% endverbatim %}"
- title: "Left"
template: "{% verbatim %}{{{{% endverbatim %}.block-side.block-side__left ![]({{ file.Url }}) {% verbatim %}}}}{% endverbatim %}"
- - title: "2 in a row"
- template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__2 ![]({{ file.Url }}) {% verbatim %}}}}{% endverbatim %}"
- - title: "3 in a row"
- template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__3-full ![]({{ file.Url }}) {% verbatim %}}}}{% endverbatim %}"
- title: "75% center"
- template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__body.width-75 ![]({{ file.Url }}) {% verbatim %}}}}{% endverbatim %}"
+ template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__body.width-75 {% for file in files %}![]({{ file.Url }}) {% endfor %} {% verbatim %}}}}{% endverbatim %}"
- title: "50% center"
- template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__body.width-50 ![]({{ file.Url }}) {% verbatim %}}}}{% endverbatim %}"
+ template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__body.width-50 {% for file in files %}![]({{ file.Url }}) {% endfor %} {% verbatim %}}}}{% endverbatim %}"
+ video_templates:
+ - title: "URL"
+ template: "{{ file.Url }}"
+ - title: "Simple Markdown"
+ template: "![]({{ file.Url }})"
+ multi_templates:
+ - title: "2 in a row"
+ template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__2 {% for file in files %}![]({{ file.Url }}) {% endfor %} {% verbatim %}}}}{% endverbatim %}"
+ - title: "3 in a row"
+ template: "{% verbatim %}{{{{% endverbatim %}.block-media.block-media__3-full {% for file in files %}![]({{ file.Url }}) {% endfor %} {% verbatim %}}}}{% endverbatim %}"
diff --git a/go.mod b/go.mod
index f93a088..56f7484 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,32 @@
module github.com/vas3k/pepic
-go 1.14
+go 1.22
require (
github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915
- github.com/h2non/bimg v1.1.5
+ github.com/h2non/bimg v1.1.9
github.com/ilyakaznacheev/cleanenv v1.2.3
- github.com/labstack/echo/v4 v4.1.16
+ github.com/labstack/echo/v4 v4.12.0
github.com/spf13/cobra v1.1.1
- github.com/xfrr/goffmpeg v0.0.0-20200403115021-c3a1545b29f4
+ github.com/xfrr/goffmpeg v1.0.0
+)
+
+require (
+ github.com/BurntSushi/toml v0.3.1 // indirect
+ github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
+ github.com/inconshreveable/mousetrap v1.0.0 // indirect
+ github.com/joho/godotenv v1.3.0 // indirect
+ github.com/labstack/gommon v0.4.2 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasttemplate v1.2.2 // indirect
+ golang.org/x/crypto v0.22.0 // indirect
+ golang.org/x/net v0.24.0 // indirect
+ golang.org/x/sys v0.19.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
+ gopkg.in/yaml.v2 v2.2.8 // indirect
+ olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24 // indirect
)
diff --git a/go.sum b/go.sum
index 13cbed3..4542e21 100644
--- a/go.sum
+++ b/go.sum
@@ -35,7 +35,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -52,6 +51,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -75,8 +76,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/h2non/bimg v1.1.5 h1:o3xsUBxM8s7+e7PmpiWIkEYdeYayJ94eh4cJLx67m1k=
-github.com/h2non/bimg v1.1.5/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
+github.com/h2non/bimg v1.1.9 h1:WH20Nxko9l/HFm4kZCA3Phbgu2cbHvYzxwxn9YROEGg=
+github.com/h2non/bimg v1.1.9/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -117,20 +118,18 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o=
-github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI=
-github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
-github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
+github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
+github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
+github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
+github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
-github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
@@ -185,19 +184,16 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
-github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4=
-github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
-github.com/xfrr/goffmpeg v0.0.0-20200403115021-c3a1545b29f4 h1:wlOx3qFG/zRKKJ40BND0dFrN03N+ofITOj5lPm30FXA=
-github.com/xfrr/goffmpeg v0.0.0-20200403115021-c3a1545b29f4/go.mod h1:fVs4qpwtgjOHD31cTmdHppcr/6vD8QHrAVAu2jTSVFI=
+github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
+github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
+github.com/xfrr/goffmpeg v1.0.0 h1:trxuLNb9ys50YlV7gTVNAII9J0r00WWqCGTE46Gc3XU=
+github.com/xfrr/goffmpeg v1.0.0/go.mod h1:zjLRiirHnip+/hVAT3lVE3QZ6SGynr0hcctUMNNISdQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -210,8 +206,8 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
-golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -244,8 +240,8 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -261,24 +257,25 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -319,7 +316,6 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -332,6 +328,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/html/meta.html b/html/meta.html
index c9dd608..7221b3a 100644
--- a/html/meta.html
+++ b/html/meta.html
@@ -23,16 +23,42 @@
{% endfor %}
+ {% if files|length > 1 %}
+
+ {% endif %}
+
diff --git a/main.go b/main.go
index e4b5a4a..a83f0d5 100644
--- a/main.go
+++ b/main.go
@@ -1,8 +1,9 @@
package main
import (
- "github.com/vas3k/pepic/pepic/cmd"
"log"
+
+ "github.com/vas3k/pepic/pepic/cmd"
)
func main() {
diff --git a/pepic/cmd/root.go b/pepic/cmd/root.go
index 48cada6..4ffad94 100644
--- a/pepic/cmd/root.go
+++ b/pepic/cmd/root.go
@@ -1,10 +1,11 @@
package cmd
import (
+ "log"
+
"github.com/ilyakaznacheev/cleanenv"
"github.com/spf13/cobra"
"github.com/vas3k/pepic/pepic/config"
- "log"
)
var (
diff --git a/pepic/cmd/serve.go b/pepic/cmd/serve.go
index 5022e5b..fbb7037 100644
--- a/pepic/cmd/serve.go
+++ b/pepic/cmd/serve.go
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
+
"github.com/labstack/echo/v4"
"github.com/vas3k/pepic/pepic/config"
"github.com/vas3k/pepic/pepic/handler"
diff --git a/pepic/config/app.go b/pepic/config/app.go
index 9475dd1..a2b171a 100644
--- a/pepic/config/app.go
+++ b/pepic/config/app.go
@@ -22,6 +22,7 @@ type Config struct {
AutoConvert string `yaml:"auto_convert" env:"IMAGE_AUTO_CONVERT" env-default:"false"`
JPEGQuality int `yaml:"jpeg_quality" env:"IMAGE_JPEG_QUALITY" env-default:"95"`
PNGCompression int `yaml:"png_compression" env:"IMAGE_PNG_COMPRESSION" env-default:"0"`
+ GIFConvert string `yaml:"gif_convert" env:"IMAGE_GIF_CONVERT" env-default:"video/mp4"`
} `yaml:"images"`
Videos struct {
@@ -45,10 +46,20 @@ type Config struct {
} `yaml:"videos"`
Meta struct {
- Blocks []struct {
+ ImageTemplates []struct {
Title string `yaml:"title"`
Template string `yaml:"template"`
- } `yaml:"blocks" env:"META_BLOCKS"`
+ } `yaml:"image_templates" env:"META_IMAGE_TEMPLATES"`
+
+ VideoTemplates []struct {
+ Title string `yaml:"title"`
+ Template string `yaml:"template"`
+ } `yaml:"video_templates" env:"META_VIDEO_TEMPLATES"`
+
+ MultiTemplates []struct {
+ Title string `yaml:"title"`
+ Template string `yaml:"template"`
+ } `yaml:"multi_templates" env:"META_MULTI_TEMPLATES"`
} `yaml:"meta"`
}
diff --git a/pepic/entity/file.go b/pepic/entity/file.go
index d82b534..910c1a0 100644
--- a/pepic/entity/file.go
+++ b/pepic/entity/file.go
@@ -1,8 +1,9 @@
package entity
import (
- "github.com/vas3k/pepic/pepic/config"
"strings"
+
+ "github.com/vas3k/pepic/pepic/config"
)
type ProcessingFile struct {
@@ -17,6 +18,10 @@ func (p *ProcessingFile) Url() string {
return config.App.Global.BaseUrl + p.Filename
}
+func (p *ProcessingFile) IsGIF() bool {
+ return p.Mime == "image/gif"
+}
+
func (p *ProcessingFile) IsImage() bool {
return strings.HasPrefix(p.Mime, "image/")
}
diff --git a/pepic/handler/errors.go b/pepic/handler/errors.go
index ae6fbaa..8d6d4f2 100644
--- a/pepic/handler/errors.go
+++ b/pepic/handler/errors.go
@@ -2,9 +2,10 @@ package handler
import (
"fmt"
- "github.com/labstack/echo/v4"
"log"
"net/http"
+
+ "github.com/labstack/echo/v4"
)
type JSONError struct {
diff --git a/pepic/handler/handler.go b/pepic/handler/handler.go
index 511cfb7..e8c5502 100644
--- a/pepic/handler/handler.go
+++ b/pepic/handler/handler.go
@@ -2,12 +2,13 @@ package handler
import (
"errors"
+ "net/http"
+ "time"
+
"github.com/labstack/echo/v4"
"github.com/vas3k/pepic/pepic/config"
"github.com/vas3k/pepic/pepic/processing"
"github.com/vas3k/pepic/pepic/storage"
- "net/http"
- "time"
)
type PepicHandler struct {
diff --git a/pepic/handler/index.go b/pepic/handler/index.go
index aa080cb..2a8c7ad 100644
--- a/pepic/handler/index.go
+++ b/pepic/handler/index.go
@@ -1,8 +1,9 @@
package handler
import (
- "github.com/labstack/echo/v4"
"net/http"
+
+ "github.com/labstack/echo/v4"
)
// GET /
diff --git a/pepic/handler/meta.go b/pepic/handler/meta.go
index 9488077..8c3c841 100644
--- a/pepic/handler/meta.go
+++ b/pepic/handler/meta.go
@@ -1,15 +1,16 @@
package handler
import (
+ "net/http"
+ "strings"
+
"github.com/labstack/echo/v4"
"github.com/vas3k/pepic/pepic/config"
"github.com/vas3k/pepic/pepic/entity"
- "net/http"
- "strings"
)
// GET /meta/:name
-// Returns HTML page with the image and its metadata
+// Render HTML page with uploaded images/videos and pre-defined templates for them
func (h *PepicHandler) GetMeta(c echo.Context) error {
names := strings.Split(c.Param("name"), ",")
var files []*entity.ProcessingFile
@@ -23,8 +24,8 @@ func (h *PepicHandler) GetMeta(c echo.Context) error {
}
return c.Render(http.StatusOK, "meta.html", map[string]interface{}{
- "files": files,
- "host": c.Request().URL,
- "blocks": config.App.Meta.Blocks,
+ "files": files,
+ "host": c.Request().URL,
+ "meta": config.App.Meta,
})
}
diff --git a/pepic/handler/proxy.go b/pepic/handler/proxy.go
index c2af339..5ed9dad 100644
--- a/pepic/handler/proxy.go
+++ b/pepic/handler/proxy.go
@@ -2,12 +2,13 @@ package handler
import (
"errors"
- "github.com/labstack/echo/v4"
- "github.com/vas3k/pepic/pepic/config"
- "github.com/vas3k/pepic/pepic/entity"
"net/http"
"path"
"strconv"
+
+ "github.com/labstack/echo/v4"
+ "github.com/vas3k/pepic/pepic/config"
+ "github.com/vas3k/pepic/pepic/entity"
)
const MinLength = 200
diff --git a/pepic/handler/upload.go b/pepic/handler/upload.go
index 8c66a09..dd376ba 100644
--- a/pepic/handler/upload.go
+++ b/pepic/handler/upload.go
@@ -1,17 +1,16 @@
package handler
import (
- "errors"
- "fmt"
- "github.com/labstack/echo/v4"
- "github.com/vas3k/pepic/pepic/config"
- "github.com/vas3k/pepic/pepic/entity"
- "github.com/vas3k/pepic/pepic/utils"
- "io/ioutil"
+ "io"
"log"
"mime/multipart"
"net/http"
"strings"
+
+ "github.com/labstack/echo/v4"
+ "github.com/vas3k/pepic/pepic/config"
+ "github.com/vas3k/pepic/pepic/entity"
+ "github.com/vas3k/pepic/pepic/utils"
)
type UploadResult struct {
@@ -68,7 +67,7 @@ func (h *PepicHandler) UploadBodyBytes(c echo.Context) error {
var bytes []byte
if c.Request().Body != nil {
- bytes, _ = ioutil.ReadAll(c.Request().Body)
+ bytes, _ = io.ReadAll(c.Request().Body)
}
result, err := h.uploadBytes("", bytes)
@@ -82,62 +81,71 @@ func (h *PepicHandler) UploadBodyBytes(c echo.Context) error {
}
func (h *PepicHandler) uploadBytes(filename string, bytes []byte) (*entity.ProcessingFile, error) {
+ var err error
+
file := &entity.ProcessingFile{
Filename: filename,
Mime: utils.DetectMimeType(filename, bytes),
Bytes: bytes,
}
- if file.IsImage() {
- log.Printf("Processing image: %s", file.Mime)
- err := utils.CalculateHashName(file)
- if err != nil {
- return file, err
- }
+ log.Printf("Processing %s file", file.Mime)
- if !config.App.Images.StoreOriginals {
- err = h.Processing.Image.AutoRotate(file)
+ hashedFilename, err := utils.CalculateHashName(file)
+ if err != nil {
+ return file, err
+ }
+
+ file.Filename = hashedFilename
+
+ if !config.App.Images.StoreOriginals {
+ if file.IsGIF() {
+ log.Printf("Converting GIF to video...")
+ err = h.Processing.Video.Convert(file, config.App.Images.GIFConvert)
if err != nil {
return file, err
}
+ }
+
+ if file.IsVideo() || file.IsGIF() {
+ log.Printf("Processing video...")
- err := h.Processing.Image.Resize(file, config.App.Images.OriginalLength)
+ err = h.Processing.Video.Transcode(file, config.App.Videos.OriginalLength)
if err != nil {
return file, err
}
- if config.App.Images.AutoConvert != "false" {
- err := h.Processing.Image.Convert(file, config.App.Images.AutoConvert)
+ if config.App.Videos.AutoConvert != "false" {
+ err = h.Processing.Video.Convert(file, config.App.Videos.AutoConvert)
if err != nil {
return file, err
}
}
}
- } else if file.IsVideo() {
- log.Printf("Processing video: %s", file.Mime)
- err := utils.CalculateHashName(file)
- if err != nil {
- return file, err
- }
- if !config.App.Videos.StoreOriginals {
- err := h.Processing.Video.Transcode(file, config.App.Videos.OriginalLength)
+ if file.IsImage() {
+ log.Printf("Processing image...")
+
+ err = h.Processing.Image.AutoRotate(file)
if err != nil {
return file, err
}
- if config.App.Videos.AutoConvert != "false" {
- err := h.Processing.Video.Convert(file, config.App.Videos.AutoConvert)
+ err = h.Processing.Image.Resize(file, config.App.Images.OriginalLength)
+ if err != nil {
+ return file, err
+ }
+
+ if config.App.Images.AutoConvert != "false" {
+ err := h.Processing.Image.Convert(file, config.App.Images.AutoConvert)
if err != nil {
return file, err
}
}
}
- } else {
- return nil, errors.New(fmt.Sprintf("unsupported file type: %s", file.Mime))
}
- err := h.Storage.StoreFile(file, "orig")
+ err = h.Storage.StoreFile(file, "orig")
if err != nil {
return file, err
}
@@ -151,7 +159,7 @@ func multipartToBytes(multipartFile *multipart.FileHeader) ([]byte, error) {
return nil, err
}
defer src.Close()
- return ioutil.ReadAll(src)
+ return io.ReadAll(src)
}
func renderUploadResults(results []UploadResult, c echo.Context) error {
diff --git a/pepic/processing/image.go b/pepic/processing/image.go
index 35519c6..8a0cedd 100644
--- a/pepic/processing/image.go
+++ b/pepic/processing/image.go
@@ -4,16 +4,17 @@ import (
"bytes"
"errors"
"fmt"
- "github.com/h2non/bimg"
- "github.com/vas3k/pepic/pepic/config"
- "github.com/vas3k/pepic/pepic/entity"
- "github.com/vas3k/pepic/pepic/utils"
"image"
"image/color"
"image/draw"
"image/jpeg"
_ "image/png"
"log"
+
+ "github.com/h2non/bimg"
+ "github.com/vas3k/pepic/pepic/config"
+ "github.com/vas3k/pepic/pepic/entity"
+ "github.com/vas3k/pepic/pepic/utils"
)
type ImageBackend interface {
@@ -101,6 +102,9 @@ func (i *imageBackend) Convert(file *entity.ProcessingFile, newMimeType string)
Quality: config.App.Images.JPEGQuality,
Compression: config.App.Images.PNGCompression,
})
+ if err != nil {
+ return err
+ }
newExt, _ := utils.ExtensionByMimeType(newMimeType)
file.Bytes = convertedImg
@@ -149,6 +153,6 @@ func (i imageBackend) mimeTypeToImageType(mimeType string) (bimg.ImageType, erro
if imageType, ok := mapping[mimeType]; ok {
return imageType, nil
} else {
- return bimg.UNKNOWN, errors.New(fmt.Sprintf("'%s' is not supported", mimeType))
+ return bimg.UNKNOWN, fmt.Errorf("'%s' is not supported", mimeType)
}
}
diff --git a/pepic/processing/video.go b/pepic/processing/video.go
index d2cda51..be4df33 100644
--- a/pepic/processing/video.go
+++ b/pepic/processing/video.go
@@ -3,14 +3,14 @@ package processing
import (
"errors"
"fmt"
+ "log"
+ "os"
+ "path"
+
"github.com/vas3k/pepic/pepic/config"
"github.com/vas3k/pepic/pepic/entity"
"github.com/vas3k/pepic/pepic/utils"
"github.com/xfrr/goffmpeg/transcoder"
- "io/ioutil"
- "log"
- "os"
- "path"
)
type VideoBackend interface {
@@ -71,7 +71,7 @@ func (v *videoBackend) Transcode(file *entity.ProcessingFile, maxLength int) err
}
// load transcoded video back to memory and remove temp files (deferred)
- file.Bytes, err = ioutil.ReadFile(tempTransFile)
+ file.Bytes, err = os.ReadFile(tempTransFile)
if err != nil {
return err
}
@@ -85,10 +85,6 @@ func (v *videoBackend) Convert(file *entity.ProcessingFile, newMimeType string)
return errors.New("file data is empty, try reading it first")
}
- if !file.IsVideo() {
- return errors.New(fmt.Sprintf("'%s' is not supported video type", newMimeType))
- }
-
// save bytes to disc because ffmpeg works with filenames
tempOrigFile := path.Join(config.App.Videos.FFmpeg.TempDir, file.Filename)
dst, err := os.Create(tempOrigFile)
@@ -128,7 +124,7 @@ func (v *videoBackend) Convert(file *entity.ProcessingFile, newMimeType string)
}
// load transcoded video back to memory and remove temp files (deferred)
- file.Bytes, err = ioutil.ReadFile(tempTransFile)
+ file.Bytes, err = os.ReadFile(tempTransFile)
if err != nil {
return err
}
diff --git a/pepic/storage/storage.go b/pepic/storage/storage.go
index 1d141c4..d0f346b 100644
--- a/pepic/storage/storage.go
+++ b/pepic/storage/storage.go
@@ -2,12 +2,13 @@ package storage
import (
"errors"
- "github.com/labstack/echo/v4"
- "github.com/vas3k/pepic/pepic/entity"
- "github.com/vas3k/pepic/pepic/utils"
"log"
"mime"
"path"
+
+ "github.com/labstack/echo/v4"
+ "github.com/vas3k/pepic/pepic/entity"
+ "github.com/vas3k/pepic/pepic/utils"
)
type Backend interface {
diff --git a/pepic/template/renderer.go b/pepic/template/renderer.go
index ffc1376..1a86048 100644
--- a/pepic/template/renderer.go
+++ b/pepic/template/renderer.go
@@ -3,13 +3,14 @@ package template
import (
"errors"
"fmt"
- "github.com/flosch/pongo2"
- "github.com/labstack/echo/v4"
- "github.com/vas3k/pepic/pepic/entity"
"io"
"os"
"path"
"path/filepath"
+
+ "github.com/flosch/pongo2"
+ "github.com/labstack/echo/v4"
+ "github.com/vas3k/pepic/pepic/entity"
)
type Renderer struct {
@@ -30,6 +31,14 @@ func (t *Renderer) Render(w io.Writer, name string, data interface{}, c echo.Con
result, _ := tpl.Execute(pongo2.Context{"file": file})
return result
}
+ viewContext["renderMultipleFileTemplate"] = func(text string, files []*entity.ProcessingFile) string {
+ tpl, err := pongo2.FromString(text)
+ if err != nil {
+ return "ERROR"
+ }
+ result, _ := tpl.Execute(pongo2.Context{"files": files})
+ return result
+ }
viewContext["bytesHumanize"] = func(b int64) string {
const unit = 1000
if b < unit {
diff --git a/pepic/utils/file.go b/pepic/utils/file.go
index eba7e64..738f822 100644
--- a/pepic/utils/file.go
+++ b/pepic/utils/file.go
@@ -3,12 +3,13 @@ package utils
import (
"crypto/sha256"
"encoding/hex"
- "github.com/vas3k/pepic/pepic/config"
- "github.com/vas3k/pepic/pepic/entity"
"log"
"mime"
"path"
"strings"
+
+ "github.com/vas3k/pepic/pepic/config"
+ "github.com/vas3k/pepic/pepic/entity"
)
var canonicalExtensions = map[string]string{
@@ -60,17 +61,16 @@ func ReplaceExt(filename string, newExt string) string {
return filename[:len(filename)-len(ext)] + newExt
}
-func CalculateHashName(file *entity.ProcessingFile) error {
+func CalculateHashName(file *entity.ProcessingFile) (string, error) {
log.Printf("Calculating file name: %s", file.Filename)
ext, err := ExtensionByMimeType(file.Mime)
if err != nil {
- return err
+ return "", err
}
sum := sha256.Sum256(file.Bytes)
- file.Filename = strings.ToLower(hex.EncodeToString(sum[:]) + ext)
- return nil
+ return strings.ToLower(hex.EncodeToString(sum[:]) + ext), nil
}
func CanonizeFileName(filename string) string {