Sharing: Render preview image & site info

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2019-12-11 14:10:20 +01:00
parent a24bf5611a
commit dac272468e
53 changed files with 5316 additions and 5053 deletions

View file

@ -1,3 +1,9 @@
url: "https://demo.photoprism.org/"
title: "PhotoPrism"
subtitle: "Browse your life"
description: "Personal Photo Management"
author: "Anonymous"
twitter: "@browseyourlife"
debug: false
read-only: false
public: false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

View file

@ -5,24 +5,21 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ .clientConfig.name }}</title>
<title>{{ .clientConfig.title }}</title>
<meta property="og:title" content="PhotoPrism: Try our demo! 🌈"/>
<meta property="og:image" content="https://dl.photoprism.org/assets/img/preview.jpg"/>
<meta property="og:url" content="https://demo.photoprism.org/"/>
<meta property="og:description"
content="Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."/>
<meta property="og:title" content="{{ .clientConfig.title }}: {{ .clientConfig.subtitle }}"/>
<meta property="og:image" content="{{ .clientConfig.url }}api/v1/preview"/>
<meta property="og:url" content="{{ .clientConfig.url }}"/>
<meta property="og:description" content="{{ .clientConfig.description }}"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:title" content="PhotoPrism: Try our demo! 🌈"/>
<meta name="twitter:description"
content="Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."/>
<meta name="twitter:image" content="https://dl.photoprism.org/assets/img/preview.jpg"/>
<meta name="twitter:site" content="@browseyourlife"/>
<meta name="twitter:title" content="{{ .clientConfig.title }}: {{ .clientConfig.subtitle }}"/>
<meta name="twitter:description" content="{{ .clientConfig.description }}"/>
<meta name="twitter:image" content="{{ .clientConfig.url }}api/v1/preview"/>
<meta name="twitter:site" content="{{ .clientConfig.twitter }}"/>
<meta name="author" content="PhotoPrism.org">
<meta name="description"
content="Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."/>
<meta name="author" content="{{ .clientConfig.author }}">
<meta name="description" content="{{ .clientConfig.description }}"/>
<link rel="shortcut icon" href="/favicon.ico">
<link rel="apple-touch-icon" href="/static/favicons/favicon.png">

View file

@ -11,6 +11,12 @@ services:
- "~/.cache/npm:/root/.cache/npm"
- "~/.cache/go-mod:/go/pkg/mod"
environment:
PHOTOPRISM_URL: "http://localhost:2342/"
PHOTOPRISM_TITLE: "PhotoPrism"
PHOTOPRISM_SUBTITLE: "Browse your life"
PHOTOPRISM_DESCRIPTION: "Personal Photo Management tested by Travis CI."
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
PHOTOPRISM_TWITTER: "@browseyourlife"
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
PHOTOPRISM_HTTP_PORT: 2342
PHOTOPRISM_SQL_HOST: "0.0.0.0"

View file

@ -13,6 +13,12 @@ services:
- ".:/go/src/github.com/photoprism/photoprism"
shm_size: "2gb"
environment:
PHOTOPRISM_URL: "http://localhost:2342/"
PHOTOPRISM_TITLE: "PhotoPrism"
PHOTOPRISM_SUBTITLE: "Browse your life"
PHOTOPRISM_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
PHOTOPRISM_TWITTER: "@browseyourlife"
PHOTOPRISM_DEBUG: "true"
PHOTOPRISM_SERVER_MODE: "debug"
PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets"

View file

@ -11,6 +11,16 @@ services:
image: photoprism/photoprism:latest
ports:
- 2342:2342 # [local port]:[container port]
environment: # Run "photoprism help" and "photoprism config" too see all config options and current values
PHOTOPRISM_URL: "https://demo.photoprism.org/"
PHOTOPRISM_TITLE: "PhotoPrism"
PHOTOPRISM_SUBTITLE: "Browse your life"
PHOTOPRISM_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."
PHOTOPRISM_AUTHOR: "Anonymous"
PHOTOPRISM_TWITTER: "@browseyourlife"
PHOTOPRISM_IMPORT_PATH: "/home/photoprism/Pictures/Import"
PHOTOPRISM_EXPORT_PATH: "/home/photoprism/Pictures/Export"
PHOTOPRISM_ORIGINALS_PATH: "/home/photoprism/Pictures/Originals"
volumes:
- "~/Pictures/Originals:/home/photoprism/Pictures/Originals" # [local path]:[container path]
- "~/Pictures/Import:/home/photoprism/Pictures/Import" # [local path]:[container path] (optional)

View file

@ -45,7 +45,7 @@ class Config {
}
onCount(ev, data) {
const type = ev.split('.')[1];
const type = ev.split(".")[1];
switch (type) {
case "favorites":
@ -64,10 +64,10 @@ class Config {
this.values.count.labels += data.count;
break;
default:
console.warn("unknown count type", ev, data)
console.warn("unknown count type", ev, data);
}
this.values.count
this.values.count;
}
updateSettings(values, $vuetify) {

View file

@ -228,7 +228,7 @@
this.$nextTick(() => this.$emit("scrollRefresh"));
}
});
}).catch(() => this.loading = false);
},
findAlbum() {
this.model.find(this.uuid).then(m => {

View file

@ -219,7 +219,7 @@
this.$nextTick(() => this.$emit("scrollRefresh"));
}
});
}).catch(() => this.loading = false);
},
},
created() {

View file

@ -1,9 +1,10 @@
package api
import (
"github.com/stretchr/testify/assert"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetAlbums(t *testing.T) {

View file

@ -1,9 +1,10 @@
package api
import (
"github.com/stretchr/testify/assert"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetLabels(t *testing.T) {

112
internal/api/preview.go Normal file
View file

@ -0,0 +1,112 @@
package api
import (
"fmt"
"image"
"image/color"
"net/http"
"os"
"time"
"github.com/disintegration/imaging"
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/util"
)
// GET /api/v1/preview
func GetPreview(router *gin.RouterGroup, conf *config.Config) {
router.GET("/preview", func(c *gin.Context) {
// TODO: proof of concept - code needs refactoring!
t := time.Now().Format("20060102")
path := fmt.Sprintf("%s/preview/%s/%s", conf.ThumbnailsPath(), t[0:4], t[4:6])
if err := os.MkdirAll(path, os.ModePerm); err != nil {
log.Error(err)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
return
}
previewFilename := fmt.Sprintf("%s/%s.jpg", path, t[6:8])
if util.Exists(previewFilename) {
c.File(previewFilename)
return
}
var f form.PhotoSearch
f.Public = true
f.Safe = true
f.Count = 12
f.Order = "relevance"
r := repo.New(conf.OriginalsPath(), conf.Db())
p, err := r.Photos(f)
if err != nil {
log.Error(err)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
return
}
width := 908
height := 680
x := 0
y := 0
preview := imaging.New(width, height, color.NRGBA{255, 255, 255, 255})
thumbType, _ := photoprism.ThumbnailTypes["tile_224"]
for _, file := range p {
fileName := fmt.Sprintf("%s/%s", conf.OriginalsPath(), file.FileName)
if !util.Exists(fileName) {
log.Errorf("could not find original for thumbnail: %s", fileName)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
// Set missing flag so that the file doesn't show up in search results anymore
file.FileMissing = true
conf.Db().Save(&file)
return
}
thumbnail, err := photoprism.ThumbnailFromFile(fileName, file.FileHash, conf.ThumbnailsPath(), thumbType.Width, thumbType.Height, thumbType.Options...)
if err != nil {
log.Error(err)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
}
src, err := imaging.Open(thumbnail)
if err != nil {
log.Error(err)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
}
preview = imaging.Paste(preview, src, image.Pt(x, y))
x += 228
if x > width {
x = 0
y += 228
}
}
// Save the resulting image as JPEG.
err = imaging.Save(preview, previewFilename)
if err != nil {
log.Error(err)
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
return
}
c.File(previewFilename)
})
}

View file

@ -31,7 +31,7 @@ func CreateSession(router *gin.RouterGroup, conf *config.Config) {
gc := conf.Cache()
gc.Set(token, 1, cache.DefaultExpiration);
gc.Set(token, 1, cache.DefaultExpiration)
s := gin.H{"token": token, "user": gin.H{"ID": 1, "FirstName": "Admin", "LastName": "", "Role": "admin", "Email": "photoprism@localhost"}}

View file

@ -1,9 +1,10 @@
package api
import (
"github.com/stretchr/testify/assert"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetThumbnail(t *testing.T) {

View file

@ -19,6 +19,12 @@ func configAction(ctx *cli.Context) error {
fmt.Printf("NAME VALUE\n")
fmt.Printf("name %s\n", conf.Name())
fmt.Printf("url %s\n", conf.Url())
fmt.Printf("title %s\n", conf.Title())
fmt.Printf("subtitle %s\n", conf.Subtitle())
fmt.Printf("description %s\n", conf.Description())
fmt.Printf("author %s\n", conf.Author())
fmt.Printf("twitter %s\n", conf.Twitter())
fmt.Printf("version %s\n", conf.Version())
fmt.Printf("copyright %s\n", conf.Copyright())
fmt.Printf("debug %t\n", conf.Debug())

View file

@ -186,6 +186,44 @@ func (c *Config) Name() string {
return c.config.Name
}
// Url returns the public server URL (default is "http://localhost:2342/").
func (c *Config) Url() string {
if c.config.Url == "" {
return "http://localhost:2342/"
}
return c.config.Url
}
// Title returns the site title (default is application name).
func (c *Config) Title() string {
if c.config.Title == "" {
return c.Name()
}
return c.config.Title
}
// Subtitle returns the site title.
func (c *Config) Subtitle() string {
return c.config.Subtitle
}
// Description returns the site title.
func (c *Config) Description() string {
return c.config.Description
}
// Author returns the site author / copyright.
func (c *Config) Author() string {
return c.config.Author
}
// Description returns the twitter handle for sharing.
func (c *Config) Twitter() string {
return c.config.Twitter
}
// Version returns the application version.
func (c *Config) Version() string {
return c.config.Version
@ -571,6 +609,12 @@ func (c *Config) ClientConfig() ClientConfig {
result := ClientConfig{
"name": c.Name(),
"url": c.Url(),
"title": c.Title(),
"subtitle": c.Subtitle(),
"description": c.Description(),
"author": c.Author(),
"twitter": c.Twitter(),
"version": c.Version(),
"copyright": c.Copyright(),
"debug": c.Debug(),

View file

@ -21,6 +21,42 @@ var GlobalFlags = []cli.Flag{
Usage: "no authentication required",
EnvVar: "PHOTOPRISM_PUBLIC",
},
cli.StringFlag{
Name: "url",
Usage: "canonical site URL",
Value: "http://localhost:2342/",
EnvVar: "PHOTOPRISM_URL",
},
cli.StringFlag{
Name: "title",
Usage: "site title",
Value: "PhotoPrism",
EnvVar: "PHOTOPRISM_TITLE",
},
cli.StringFlag{
Name: "subtitle",
Usage: "site subtitle",
Value: "Browse your life",
EnvVar: "PHOTOPRISM_SUBTITLE",
},
cli.StringFlag{
Name: "description",
Usage: "site description",
Value: "Personal Photo Management",
EnvVar: "PHOTOPRISM_DESCRIPTION",
},
cli.StringFlag{
Name: "author",
Usage: "site owner / copyright",
Value: "Anonymous",
EnvVar: "PHOTOPRISM_AUTHOR",
},
cli.StringFlag{
Name: "twitter",
Usage: "twitter handle for sharing",
Value: "@browseyourlife",
EnvVar: "PHOTOPRISM_TWITTER",
},
cli.StringFlag{
Name: "admin-password",
Usage: "admin password",

View file

@ -28,6 +28,12 @@ const (
// See https://github.com/photoprism/photoprism/issues/50#issuecomment-433856358
type Params struct {
Name string
Url string `yaml:"url" flag:"url"`
Title string `yaml:"title" flag:"title"`
Subtitle string `yaml:"subtitle" flag:"subtitle"`
Description string `yaml:"description" flag:"description"`
Author string `yaml:"author" flag:"author"`
Twitter string `yaml:"twitter" flag:"twitter"`
Version string
Copyright string
Debug bool `yaml:"debug" flag:"debug"`

View file

@ -47,4 +47,3 @@ func (s *Settings) WriteValuesToFile(fileName string) error {
return ioutil.WriteFile(fileName, data, os.ModePerm)
}

View file

@ -11,7 +11,6 @@ type Message = hub.Message
var channelCap = 10
var sharedHub = NewHub()
func NewHub() *Hub {
return hub.New()
}

View file

@ -9,7 +9,6 @@ import (
var Log *logrus.Logger
type Hook struct {
hub *hub.Hub
}

View file

@ -1,9 +1,10 @@
package form
import (
"testing"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"testing"
)
func TestAlbumSearchForm(t *testing.T) {

View file

@ -1,9 +1,10 @@
package form
import (
"testing"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"testing"
)
func TestLabelSearchForm(t *testing.T) {

View file

@ -40,6 +40,9 @@ type PhotoSearch struct {
Before time.Time `form:"before" time_format:"2006-01-02"`
After time.Time `form:"after" time_format:"2006-01-02"`
Favorites bool `form:"favorites"`
Public bool `form:"public"`
Story bool `form:"story"`
Safe bool `form:"safe"`
Count int `form:"count" binding:"required"`
Offset int `form:"offset"`

View file

@ -1,10 +1,11 @@
package models
import (
"github.com/gosimple/slug"
"github.com/stretchr/testify/assert"
"testing"
"time"
"github.com/gosimple/slug"
"github.com/stretchr/testify/assert"
)
func TestNewAlbum(t *testing.T) {

View file

@ -1,8 +1,9 @@
package models
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewCamera(t *testing.T) {

View file

@ -1,8 +1,9 @@
package models
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewCountry(t *testing.T) {

View file

@ -36,7 +36,6 @@ type File struct {
FileNotes string `gorm:"type:text"`
}
func FindFileByHash(db *gorm.DB, fileHash string) (File, error) {
var file File

View file

@ -1,8 +1,9 @@
package models
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewLabel(t *testing.T) {

View file

@ -1,8 +1,9 @@
package models
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewLens(t *testing.T) {

View file

@ -1,8 +1,9 @@
package models
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewPhotoAlbum(t *testing.T) {

View file

@ -1,8 +1,9 @@
package models
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewPhotoLabel(t *testing.T) {

View file

@ -338,7 +338,6 @@ func (i *Indexer) indexLocation (mediaFile *MediaFile, photo *models.Photo, keyw
// Sort by priority and uncertainty
sort.Sort(labels)
if (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false {
if len(labels) > 0 && labels[0].Priority >= -1 && labels[0].Uncertainty <= 60 && labels[0].Name != "" { // TODO: User defined title format
log.Infof("index: using label %s to create photo title (%d%% uncertainty)", labels[0].Name, labels[0].Uncertainty)

View file

@ -1,8 +1,9 @@
package photoprism
import (
"github.com/photoprism/photoprism/internal/config"
"testing"
"github.com/photoprism/photoprism/internal/config"
)
func TestIndexer_IndexAll(t *testing.T) {

View file

@ -1,8 +1,9 @@
package photoprism
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestLabel_NewLocationLabel(t *testing.T) {

View file

@ -1,10 +1,11 @@
package photoprism
import (
"github.com/photoprism/photoprism/internal/util"
"os"
"testing"
"github.com/photoprism/photoprism/internal/util"
"github.com/photoprism/photoprism/internal/config"
"github.com/stretchr/testify/assert"
)

View file

@ -1,9 +1,10 @@
package photoprism
import (
"testing"
"github.com/photoprism/photoprism/internal/config"
"github.com/stretchr/testify/assert"
"testing"
)
func TestMediaFile_Location(t *testing.T) {

View file

@ -1,10 +1,11 @@
package photoprism
import (
tensorflow "github.com/tensorflow/tensorflow/tensorflow/go"
"io/ioutil"
"testing"
tensorflow "github.com/tensorflow/tensorflow/tensorflow/go"
"github.com/photoprism/photoprism/internal/config"
"github.com/stretchr/testify/assert"
)

View file

@ -1,11 +1,12 @@
package photoprism
import (
"github.com/disintegration/imaging"
"github.com/photoprism/photoprism/internal/models"
"os"
"testing"
"github.com/disintegration/imaging"
"github.com/photoprism/photoprism/internal/models"
"github.com/photoprism/photoprism/internal/config"
"github.com/stretchr/testify/assert"
)

View file

@ -85,10 +85,6 @@ type PhotoResult struct {
FileHeight int
FileOrientation int
FileAspectRatio float64
// List of matching labels and keywords
Labels string
Keywords string
}
func (m *PhotoResult) DownloadFileName() string {
@ -128,18 +124,13 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
lenses.lens_make, lenses.lens_model,
countries.country_name,
locations.loc_display_name, locations.loc_name, locations.loc_city, locations.loc_postcode, locations.loc_county,
locations.loc_state, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type,
GROUP_CONCAT(DISTINCT labels.label_name) AS labels,
GROUP_CONCAT(DISTINCT keywords.keyword) AS keywords`).
locations.loc_state, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type`).
Joins("JOIN files ON files.photo_id = photos.id AND files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN cameras ON cameras.id = photos.camera_id").
Joins("JOIN lenses ON lenses.id = photos.lens_id").
Joins("LEFT JOIN countries ON countries.id = photos.country_id").
Joins("LEFT JOIN locations ON locations.id = photos.location_id").
Joins("LEFT JOIN photos_labels ON photos_labels.photo_id = photos.id").
Joins("LEFT JOIN labels ON photos_labels.label_id = labels.id").
Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id").
Where("photos.deleted_at IS NULL AND files.file_missing = 0").
Group("photos.id, files.id")
var categories []models.Category
@ -159,7 +150,7 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
labelIds = append(labelIds, category.LabelID)
}
q = q.Where("labels.id IN (?)", labelIds)
q = q.Where("photos_labels.label_id IN (?)", labelIds)
}
}
@ -167,18 +158,27 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
q = q.Where("location_id > 0")
if f.Query != "" {
likeString := "%" + strings.ToLower(f.Query) + "%"
q = q.Where("LOWER(locations.loc_display_name) LIKE ?", likeString)
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id").
Where("keywords.keyword LIKE ?", strings.ToLower(f.Query)+"%")
}
} else if f.Query != "" {
if len(f.Query) < 2 {
return results, fmt.Errorf("query too short")
}
slugString := slug.Make(f.Query)
lowerString := strings.ToLower(f.Query)
likeString := lowerString + "%"
if result := s.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
log.Infof("search: label \"%s\" not found", f.Query)
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id")
q = q.Where("labels.label_slug = ? OR keywords.keyword LIKE ? OR files.file_main_color = ?", slugString, likeString, lowerString)
if result := s.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
log.Infof("search: label \"%s\" not found, using fuzzy search", f.Query)
q = q.Joins("LEFT JOIN labels ON photos_labels.label_id = labels.id").
Where("labels.label_name LIKE ? OR keywords.keyword LIKE ? OR files.file_main_color = ?", likeString, likeString, lowerString)
} else {
labelIds = append(labelIds, label.ID)
@ -190,7 +190,7 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
log.Infof("search: label \"%s\" includes %d categories", label.LabelName, len(labelIds))
q = q.Where("labels.id IN (?) OR keywords.keyword LIKE ? OR files.file_main_color = ?", labelIds, likeString, lowerString)
q = q.Where("photos_labels.label_id IN (?) OR keywords.keyword LIKE ? OR files.file_main_color = ?", labelIds, likeString, lowerString)
}
}
@ -210,6 +210,18 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
q = q.Where("photos.photo_favorite = 1")
}
if f.Public {
q = q.Where("photos.photo_private = 0")
}
if f.Safe {
q = q.Where("photos.photo_nsfw = 0")
}
if f.Story {
q = q.Where("photos.photo_story = 1")
}
if f.Country != "" {
q = q.Where("locations.loc_country_code = ?", f.Country)
}
@ -282,6 +294,8 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
}
switch f.Order {
case "relevance":
q = q.Order("photo_story DESC, photo_favorite DESC, taken_at DESC")
case "newest":
q = q.Order("taken_at DESC")
case "oldest":

View file

@ -1,9 +1,10 @@
package repo
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/form"
)

View file

@ -21,6 +21,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
api.CreateSession(v1, conf)
api.DeleteSession(v1, conf)
api.GetPreview(v1, conf)
api.GetThumbnail(v1, conf)
api.GetDownload(v1, conf)
api.CreateZip(v1, conf)

View file

@ -5,8 +5,8 @@ package main
import (
"bufio"
"os"
"io"
"os"
"text/template"
)

View file

@ -1,8 +1,9 @@
package util
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsSeparator(t *testing.T) {

View file

@ -2,9 +2,10 @@ package util
import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestProfileTime(t *testing.T) {