1
0
Fork 0

Working web render for public albums

main
Ambrose Chua 2020-05-31 21:51:09 +08:00
parent 6070c64827
commit 787e025a1f
Signed by: ambrose
GPG Key ID: BC367D33F140B5C2
14 changed files with 828 additions and 105 deletions

View File

@ -69,6 +69,20 @@ func update(w http.ResponseWriter, req *http.Request) {
return
}
width, height, err := readsize(original)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
metadata, _ := client.GetPhotoMetadata(bucket, photo)
metadata.Width = width
metadata.Height = height
err = client.PutPhotoMetadata(bucket, photo, metadata)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
resizes := make([]resizeOperation, len(previewOptions))
for i := range resizes {
resizes[i] = resizeOperation{
@ -138,6 +152,14 @@ func update(w http.ResponseWriter, req *http.Request) {
w.Header().Add("X-Debug-Resize-Timings", fmt.Sprintf("%v", timings))
}
func readsize(buf []byte) (int, int, error) {
i, err := vips.NewImageFromBuffer(buf)
if err != nil {
return 0, 0, err
}
return i.Width(), i.Height(), nil
}
func resize(buf []byte, height int, format vips.ImageType, quality int) ([]byte, error) {
i, err := vips.NewImageFromBuffer(buf)
if err != nil {

9
cmd/web/README.md Normal file
View File

@ -0,0 +1,9 @@
# Creating embedded data
```bash
go get -u github.com/go-bindata/go-bindata/v3/...
go generate
```
<!-- vim: set conceallevel=2 et ts=2 sw=2: -->

502
cmd/web/bindata.go Normal file

File diff suppressed because one or more lines are too long

45
cmd/web/templates.go Normal file
View File

@ -0,0 +1,45 @@
package main
import (
"html/template"
lib "git.makerforce.io/photos/photos/pkg/bucket"
)
var funcs = template.FuncMap{
"ar": func(p Photo) float64 { return float64(p.Width) / float64(p.Height) },
"mul": func(a, b float64) float64 { return a * b },
"preview": preview,
}
type IndexTemplateData struct {
Assets string
Metadata lib.BucketMetadata
Photos []Photo
}
type Photo struct {
lib.Photo
lib.PhotoMetadata
}
var indexTemplate = mustTemplateAsset("index")
func mustTemplateAsset(name string) *template.Template {
buf, err := Asset("view/" + name + ".tmpl")
if err != nil {
panic(err)
}
return template.Must(
template.New(name).Funcs(funcs).Parse(string(buf)),
)
}
func preview(p Photo, height int, format lib.PhotoFormat, quality int) template.Srcset {
preview := p.GetPreview(lib.PreviewOption{
Height: height,
Format: format,
Quality: quality,
})
return template.Srcset(preview.String())
}

92
cmd/web/web.go Normal file
View File

@ -0,0 +1,92 @@
package main
//go:generate go-bindata -fs -prefix "../../web/" "../../web/..."
import (
"bytes"
"fmt"
"net/http"
"os"
"time"
"git.makerforce.io/photos/photos/internal/httphelpers"
lib "git.makerforce.io/photos/photos/pkg/bucket"
)
var client *lib.Client
var endpoint string
func main() {
// Read configuration
endpoint = os.Getenv("WEB_ENDPOINT")
// Setup bucket client
var err error
client, err = lib.NewClientFromEnv()
if err != nil {
panic(err)
}
server := &http.Server{
Addr: ":8004",
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
http.Handle("/shared/", http.FileServer(AssetFile()))
http.HandleFunc("/update", update)
err = server.ListenAndServe()
if err != nil {
panic(err)
}
}
func update(w http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
err := fmt.Errorf("%w: %v", httphelpers.ErrorMethodNotAllowed, req.Method)
httphelpers.ErrorResponse(w, err)
return
}
bucket := lib.Bucket(req.FormValue("bucket"))
/*
bucketMetadata, err := client.GetBucketMetadata(bucket)
previewOptions := bucketMetadata.PreviewOptions
*/
metadata, err := client.GetBucketMetadata(bucket)
photos, err := client.GetPhotos(bucket)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
detailedPhotos := make([]Photo, 0)
for _, photo := range photos {
photoMetadata, err := client.GetPhotoMetadata(bucket, photo)
if err != nil {
err := fmt.Errorf("%w: %v", err, photo)
httphelpers.ErrorResponse(w, err)
return
}
detailedPhotos = append(detailedPhotos, Photo{photo, photoMetadata})
}
data := IndexTemplateData{
Assets: endpoint + "/shared/",
Metadata: metadata,
Photos: detailedPhotos,
}
indexBuf := new(bytes.Buffer)
err = indexTemplate.Execute(indexBuf, data)
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
err = client.PutBucketWeb(bucket, indexBuf.Bytes())
if err != nil {
httphelpers.ErrorResponse(w, err)
return
}
}

5
go.mod
View File

@ -4,6 +4,11 @@ go 1.14
require (
github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a
github.com/elazarl/go-bindata-assetfs v1.0.0
github.com/go-bindata/go-bindata v1.0.0 // indirect
github.com/go-bindata/go-bindata/v3 v3.1.3 // indirect
github.com/miekg/dns v1.1.29
github.com/minio/minio-go/v6 v6.0.55
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/tools v0.0.0-20200530233709-52effbd89c51 // indirect
)

27
go.sum
View File

@ -4,6 +4,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a h1:g1X1c43wACmPtvFo+szBy/0coLkRwVJ0+i2f6YJITto=
github.com/davidbyttow/govips v0.0.0-20200412130214-cbefdd8c639a/go.mod h1:a3qO525EPfJNYa0NXBcNtXzJvyQsJAxphEDa7OOHPBk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/go-bindata/go-bindata v1.0.0 h1:DZ34txDXWn1DyWa+vQf7V9ANc2ILTtrEjtlsdJRF26M=
github.com/go-bindata/go-bindata v1.0.0/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
github.com/go-bindata/go-bindata/v3 v3.1.3 h1:F0nVttLC3ws0ojc7p60veTurcOm//D4QBODNM7EGrCI=
github.com/go-bindata/go-bindata/v3 v3.1.3/go.mod h1:1/zrpXsLD8YDIbhZRqXzm1Ghc7NhEvIN9+Z6R5/xH4I=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -11,6 +17,8 @@ github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGn
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
@ -35,12 +43,18 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
@ -48,7 +62,10 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
@ -57,8 +74,18 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPT
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200530233709-52effbd89c51 h1:Wec8/IO8hAraBf0it7/dPQYOslIrgM938wZYNkLnOYc=
golang.org/x/tools v0.0.0-20200530233709-52effbd89c51/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -78,6 +78,42 @@ func (c *Client) PutBucketMetadata(b Bucket, m BucketMetadata) error {
return nil
}
func (c *Client) PutBucketWeb(b Bucket, buf []byte) error {
err := b.Validate()
if err != nil {
return err
}
_, err = c.PutObject(b.String(), "index.html", bytes.NewBuffer(buf), int64(len(buf)), minio.PutObjectOptions{
ContentType: "text/html",
StorageClass: "REDUCED_REDUNDANCY",
})
if err != nil {
return err
}
return nil
}
func (c *Client) GetPhotos(b Bucket) ([]Photo, error) {
err := b.Validate()
if err != nil {
return nil, err
}
keys := make([]Photo, 0)
doneCh := make(chan struct{})
objCh := c.ListObjectsV2(b.String(), photoPrefix, true, doneCh)
for obj := range objCh {
if obj.Err != nil {
return nil, obj.Err
}
keys = append(keys, Photo(obj.Key))
}
return keys, nil
}
func (c *Client) GetPhoto(b Bucket, p Photo) ([]byte, error) {
err := b.Validate()
if err != nil {
@ -96,6 +132,58 @@ func (c *Client) GetPhoto(b Bucket, p Photo) ([]byte, error) {
return ioutil.ReadAll(o)
}
func (c *Client) GetPhotoMetadata(b Bucket, p Photo) (PhotoMetadata, error) {
err := b.Validate()
if err != nil {
return PhotoMetadata{}, err
}
err = p.Validate()
if err != nil {
return PhotoMetadata{}, err
}
o, err := c.GetObject(b.String(), p.MetadataString(), minio.GetObjectOptions{})
if err != nil {
return PhotoMetadata{}, err
}
defer o.Close()
buf, err := ioutil.ReadAll(o)
if err != nil {
return PhotoMetadata{}, err
}
m := PhotoMetadata{}
err = json.Unmarshal(buf, &m)
if err != nil {
return PhotoMetadata{}, err
}
return m, nil
}
func (c *Client) PutPhotoMetadata(b Bucket, p Photo, m PhotoMetadata) error {
err := b.Validate()
if err != nil {
return err
}
err = p.Validate()
if err != nil {
return err
}
buf, err := json.MarshalIndent(m, "", "\t")
if err != nil {
return err
}
_, err = c.PutObject(b.String(), p.MetadataString(), bytes.NewBuffer(buf), int64(len(buf)), minio.PutObjectOptions{
ContentType: "application/json",
})
if err != nil {
return err
}
return nil
}
func (c *Client) PutPreview(b Bucket, p Preview, buf []byte) error {
err := b.Validate()
if err != nil {

View File

@ -26,3 +26,17 @@ func (p Photo) String() string {
func (p Photo) Path() string {
return "/" + string(p)
}
const photoMetadataPrefix = "photometa/"
func (p Photo) MetadataString() string {
objectBase := strings.Replace(string(p), photoPrefix, photoMetadataPrefix, 1)
objectBase += ".metadata.json"
return objectBase
}
type PhotoMetadata struct {
Title string `json:"title"`
Width int `json:"width"`
Height int `json:"height"`
}

View File

@ -95,7 +95,7 @@ func (p Photo) GetPreview(option PreviewOption) Preview {
}
var defaultSizes = []int{640, 320, 160, 80}
var defaultSizesThumb = []int{120, 60, 30}
var defaultSizesThumb = []int{60, 30}
var defaultFormats = []PhotoFormat{
PhotoFormatWEBP,
PhotoFormatJPEG,

View File

@ -1,12 +0,0 @@
local1:2020 {
root * ./shared
file_server browse
header Access-Control-Allow-Origin "*"
tls internal
}
local2:2020 {
root * ./view
file_server browse
tls internal
}

View File

@ -3,10 +3,10 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Photos</title>
<link rel="stylesheet" href="https://local1:2020/css/base.css">
<link rel="stylesheet" href="https://local1:2020/css/gallery.css">
<link rel="stylesheet" href="https://local1:2020/css/enlarge.css">
<title>{{ .Metadata.Title }}</title>
<link rel="stylesheet" href="{{ .Assets }}css/base.css">
<link rel="stylesheet" href="{{ .Assets }}css/gallery.css">
<link rel="stylesheet" href="{{ .Assets }}css/enlarge.css">
</head>
<body>
<nav>
@ -16,65 +16,40 @@
</ul>
</nav>
<header>
<h1>James Birthday 2020</h1>
<h1>{{ .Metadata.Title }}</h1>
</header>
<main class="gallery">
{{ range (datasource "samples") }}
<div class="gallery-item preloaded-thumbnail" data-ar="{{ div (index . 1) (index . 2) }}">
<a href="sample/{{ index . 0 }}" data-enlarge="sample/{{ index . 0 }}">
{{ range .Photos }}
<div class="gallery-item preloaded-thumbnail" data-ar="{{ ar . }}">
<a href="{{ . }}" data-enlarge="{{ . }}">
<picture>
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_320.webp"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_160.webp"}} 1x,
" type="image/webp" media="(max-width: 900px)">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_640.webp"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_320.webp"}} 1x,
" type="image/webp">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_320.jpg"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_160.jpg"}} 1x,
" media="(max-width: 900px)">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_640.jpg"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_320.jpg"}} 1x,
">
<img src="sample/{{ index . 0 | replaceAll ".jpg" "_320.jpg"}}"
<source srcset="{{ preview . 320 "image/webp" 70 }} 2x, {{ preview . 160 "image/webp" 70 }} 1x" type="image/webp" media="(max-width: 900px)">
<source srcset="{{ preview . 640 "image/webp" 70 }} 2x, {{ preview . 320 "image/webp" 70 }} 1x" type="image/webp">
<source srcset="{{ preview . 320 "image/jpeg" 70 }} 2x, {{ preview . 160 "image/jpeg" 70 }} 1x" type="image/jpeg" media="(max-width: 900px)">
<source srcset="{{ preview . 640 "image/jpeg" 70 }} 2x, {{ preview . 320 "image/jpeg" 70 }} 1x" type="image/jpeg">
<img src="{{ preview . 320 "image/jpeg" 70 }}"
alt=""
width="{{ div (index . 1) (index . 2) | mul 320 }}"
width="{{ ar . | mul 320 }}"
height="320"
loading="lazy"
data-enlarge-preload-for="sample/{{ index . 0 }}">
data-enlarge-preload-for="{{ . }}">
</picture>
<picture class="preloaded-thumbnail-image">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_pre60.webp"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_pre30.webp"}} 1x,
" type="image/webp" media="(max-width: 900px)">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_pre120.webp"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_pre60.webp"}} 1x,
" type="image/webp">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_pre60.jpg"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_pre30.jpg"}} 1x,
" media="(max-width: 900px)">
<source srcset="
sample/{{ index . 0 | replaceAll ".jpg" "_pre120.jpg"}} 2x,
sample/{{ index . 0 | replaceAll ".jpg" "_pre60.jpg"}} 1x,
">
<img src="sample/{{ index . 0 | replaceAll ".jpg" "_pre30.jpg"}}"
<source srcset="{{ preview . 30 "image/webp" 34 }} 1x" type="image/webp" media="(max-width: 900px)">
<source srcset="{{ preview . 60 "image/webp" 34 }} 1x" type="image/webp">
<source srcset="{{ preview . 30 "image/jpeg" 34 }} 1x" type="image/jpeg" media="(max-width: 900px)">
<source srcset="{{ preview . 60 "image/jpeg" 34 }} 1x" type="image/jpeg">
<img src="{{ preview . 30 "image/jpeg" 34 }}"
alt=""
width="{{ div (index . 1) (index . 2) | mul 320 }}"
height="320"
>
width="{{ ar . | mul 320 }}"
height="320">
</picture>
</a>
</div>
{{ end }}
</main>
<!-- This gallery works without JavaScript! -->
<script type="module" src="https://local1:2020/js/view.js" async></script>
<script type="module" src="{{ .Assets }}js/view.js" async></script>
</body>
</html>
<!-- vim: set ft=html: -->

View File

@ -1,34 +0,0 @@
#!/bin/sh
set -e
sizes=(640 320 160 80)
presizes=(120 60 30)
rm samples.csv
echo "Getting file dimensions..."
for f in sample/*unsplash.jpg; do
gm identify -format %f,%w,%h $f >> samples.csv
done
for size in ${presizes[*]}; do
for f in sample/*unsplash.jpg; do
echo "Compressing thumbnail $f at $size"
if [ -f "${f%.jpg}_pre$size.jpg" ]; then
continue
fi
gm convert $f -resize x$size -compress jpeg -quality 40 ${f%.jpg}_pre$size.jpg
gm convert $f -resize x$size -compress webp -quality 35 ${f%.jpg}_pre$size.webp
done
done
for size in ${sizes[*]}; do
for f in sample/*unsplash.jpg; do
echo "Compressing $f at $size"
if [ -f "${f%.jpg}_$size.jpg" ]; then
continue
fi
gm convert $f -resize x$size -compress jpeg -quality 75 ${f%.jpg}_$size.jpg
gm convert $f -resize x$size -compress webp -quality 70 ${f%.jpg}_$size.webp
done
done

View File

@ -1,10 +0,0 @@
#!/bin/sh
set -e
while true; do
gotemplate -f index.tmpl -d samples.csv -o index.html 2>&1 > /dev/null
date
sleep 5
done