Merge pull request #488 from photoview/feature-env-vars
Add environment variables to disable optional features
This commit is contained in:
commit
4af3d11db4
|
@ -45,6 +45,7 @@ type ResolverRoot interface {
|
|||
Mutation() MutationResolver
|
||||
Query() QueryResolver
|
||||
ShareToken() ShareTokenResolver
|
||||
SiteInfo() SiteInfoResolver
|
||||
Subscription() SubscriptionResolver
|
||||
User() UserResolver
|
||||
}
|
||||
|
@ -220,6 +221,7 @@ type ComplexityRoot struct {
|
|||
|
||||
SiteInfo struct {
|
||||
ConcurrentWorkers func(childComplexity int) int
|
||||
FaceDetectionEnabled func(childComplexity int) int
|
||||
InitialSetup func(childComplexity int) int
|
||||
PeriodicScanInterval func(childComplexity int) int
|
||||
}
|
||||
|
@ -338,6 +340,9 @@ type QueryResolver interface {
|
|||
type ShareTokenResolver interface {
|
||||
HasPassword(ctx context.Context, obj *models.ShareToken) (bool, error)
|
||||
}
|
||||
type SiteInfoResolver interface {
|
||||
FaceDetectionEnabled(ctx context.Context, obj *models.SiteInfo) (bool, error)
|
||||
}
|
||||
type SubscriptionResolver interface {
|
||||
Notification(ctx context.Context) (<-chan *models.Notification, error)
|
||||
}
|
||||
|
@ -1369,6 +1374,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.SiteInfo.ConcurrentWorkers(childComplexity), true
|
||||
|
||||
case "SiteInfo.faceDetectionEnabled":
|
||||
if e.complexity.SiteInfo.FaceDetectionEnabled == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.SiteInfo.FaceDetectionEnabled(childComplexity), true
|
||||
|
||||
case "SiteInfo.initialSetup":
|
||||
if e.complexity.SiteInfo.InitialSetup == nil {
|
||||
break
|
||||
|
@ -1821,7 +1833,10 @@ type ShareToken {
|
|||
|
||||
"General information about the site"
|
||||
type SiteInfo {
|
||||
"Whether or not the initial setup wizard should be shown"
|
||||
initialSetup: Boolean!
|
||||
"Whether or not face detection is enabled and working"
|
||||
faceDetectionEnabled: Boolean!
|
||||
"How often automatic scans should be initiated in seconds"
|
||||
periodicScanInterval: Int! @isAdmin
|
||||
"How many max concurrent scanner jobs that should run at once"
|
||||
|
@ -7862,6 +7877,41 @@ func (ec *executionContext) _SiteInfo_initialSetup(ctx context.Context, field gr
|
|||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _SiteInfo_faceDetectionEnabled(ctx context.Context, field graphql.CollectedField, obj *models.SiteInfo) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "SiteInfo",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.SiteInfo().FaceDetectionEnabled(rctx, obj)
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _SiteInfo_periodicScanInterval(ctx context.Context, field graphql.CollectedField, obj *models.SiteInfo) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -11133,17 +11183,31 @@ func (ec *executionContext) _SiteInfo(ctx context.Context, sel ast.SelectionSet,
|
|||
case "initialSetup":
|
||||
out.Values[i] = ec._SiteInfo_initialSetup(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "faceDetectionEnabled":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._SiteInfo_faceDetectionEnabled(ctx, field, obj)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "periodicScanInterval":
|
||||
out.Values[i] = ec._SiteInfo_periodicScanInterval(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
case "concurrentWorkers":
|
||||
out.Values[i] = ec._SiteInfo_concurrentWorkers(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
|
|
|
@ -32,6 +32,10 @@ func (r imageFaceResolver) FaceGroup(ctx context.Context, obj *models.ImageFace)
|
|||
return obj.FaceGroup, nil
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
var faceGroup models.FaceGroup
|
||||
if err := r.Database.Model(&obj).Association("FaceGroup").Find(&faceGroup); err != nil {
|
||||
return nil, err
|
||||
|
@ -48,6 +52,10 @@ func (r faceGroupResolver) ImageFaces(ctx context.Context, obj *models.FaceGroup
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
if err := user.FillAlbums(r.Database); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -78,6 +86,10 @@ func (r faceGroupResolver) ImageFaceCount(ctx context.Context, obj *models.FaceG
|
|||
return -1, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return -1, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
if err := user.FillAlbums(r.Database); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
@ -107,6 +119,10 @@ func (r *queryResolver) FaceGroup(ctx context.Context, id int) (*models.FaceGrou
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
if err := user.FillAlbums(r.Database); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -135,6 +151,10 @@ func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagin
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
if err := user.FillAlbums(r.Database); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -168,6 +188,10 @@ func (r *mutationResolver) SetFaceGroupLabel(ctx context.Context, faceGroupID in
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
faceGroup, err := userOwnedFaceGroup(r.Database, user, faceGroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -186,6 +210,10 @@ func (r *mutationResolver) CombineFaceGroups(ctx context.Context, destinationFac
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
destinationFaceGroup, err := userOwnedFaceGroup(r.Database, user, destinationFaceGroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -223,6 +251,10 @@ func (r *mutationResolver) MoveImageFaces(ctx context.Context, imageFaceIDs []in
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
userOwnedImageFaceIDs := make([]int, 0)
|
||||
var destFaceGroup *models.FaceGroup
|
||||
|
||||
|
@ -290,6 +322,10 @@ func (r *mutationResolver) RecognizeUnlabeledFaces(ctx context.Context) ([]*mode
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
var updatedImageFaces []*models.ImageFace
|
||||
|
||||
transactionError := r.Database.Transaction(func(tx *gorm.DB) error {
|
||||
|
@ -312,6 +348,10 @@ func (r *mutationResolver) DetachImageFaces(ctx context.Context, imageFaceIDs []
|
|||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return nil, errors.New("face detector not initialized")
|
||||
}
|
||||
|
||||
userOwnedImageFaceIDs := make([]int, 0)
|
||||
newFaceGroup := models.FaceGroup{}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
api "github.com/photoview/photoview/api/graphql"
|
||||
"github.com/photoview/photoview/api/graphql/auth"
|
||||
"github.com/photoview/photoview/api/graphql/models"
|
||||
"github.com/photoview/photoview/api/scanner/face_detection"
|
||||
"github.com/pkg/errors"
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
@ -228,6 +229,10 @@ func (r *mutationResolver) FavoriteMedia(ctx context.Context, mediaID int, favor
|
|||
}
|
||||
|
||||
func (r *mediaResolver) Faces(ctx context.Context, media *models.Media) ([]*models.ImageFace, error) {
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return []*models.ImageFace{}, nil
|
||||
}
|
||||
|
||||
if media.Faces != nil {
|
||||
return media.Faces, nil
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/photoview/photoview/api/graphql"
|
||||
"github.com/photoview/photoview/api/graphql/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
@ -35,7 +32,3 @@ type queryResolver struct{ *Resolver }
|
|||
type subscriptionResolver struct {
|
||||
Resolver *Resolver
|
||||
}
|
||||
|
||||
func (r *queryResolver) SiteInfo(ctx context.Context) (*models.SiteInfo, error) {
|
||||
return models.GetSiteInfo(r.Database)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
api "github.com/photoview/photoview/api/graphql"
|
||||
"github.com/photoview/photoview/api/graphql/models"
|
||||
"github.com/photoview/photoview/api/scanner/face_detection"
|
||||
)
|
||||
|
||||
func (r *queryResolver) SiteInfo(ctx context.Context) (*models.SiteInfo, error) {
|
||||
return models.GetSiteInfo(r.Database)
|
||||
}
|
||||
|
||||
type SiteInfoResolver struct {
|
||||
*Resolver
|
||||
}
|
||||
|
||||
func (r *Resolver) SiteInfo() api.SiteInfoResolver {
|
||||
return &SiteInfoResolver{r}
|
||||
}
|
||||
|
||||
func (SiteInfoResolver) FaceDetectionEnabled(ctx context.Context, obj *models.SiteInfo) (bool, error) {
|
||||
return face_detection.GlobalFaceDetector != nil, nil
|
||||
}
|
|
@ -343,8 +343,10 @@ func (r *mutationResolver) UserRemoveRootAlbum(ctx context.Context, userID int,
|
|||
}
|
||||
|
||||
// Reload faces as media might have been deleted
|
||||
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(r.Database); err != nil {
|
||||
return nil, err
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(r.Database); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ type Query {
|
|||
"Get media owned by the logged in user, returned in GeoJson format"
|
||||
myMediaGeoJson: Any! @isAuthorized
|
||||
"Get the mapbox api token, returns null if mapbox is not enabled"
|
||||
mapboxToken: String
|
||||
mapboxToken: String @isAuthorized
|
||||
|
||||
shareToken(credentials: ShareTokenCredentials!): ShareToken!
|
||||
shareTokenValidatePassword(credentials: ShareTokenCredentials!): Boolean!
|
||||
|
@ -201,7 +201,10 @@ type ShareToken {
|
|||
|
||||
"General information about the site"
|
||||
type SiteInfo {
|
||||
"Whether or not the initial setup wizard should be shown"
|
||||
initialSetup: Boolean!
|
||||
"Whether or not face detection is enabled and working"
|
||||
faceDetectionEnabled: Boolean!
|
||||
"How often automatic scans should be initiated in seconds"
|
||||
periodicScanInterval: Int! @isAdmin
|
||||
"How many max concurrent scanner jobs that should run at once"
|
||||
|
|
|
@ -53,8 +53,10 @@ func CleanupMedia(db *gorm.DB, albumId int, albumMedia []*models.Media) []error
|
|||
}
|
||||
|
||||
// Reload faces after deleting media
|
||||
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(db); err != nil {
|
||||
deleteErrors = append(deleteErrors, errors.Wrap(err, "reload faces from database"))
|
||||
if face_detection.GlobalFaceDetector != nil {
|
||||
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(db); err != nil {
|
||||
deleteErrors = append(deleteErrors, errors.Wrap(err, "reload faces from database"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,8 +126,10 @@ func deleteOldUserAlbums(db *gorm.DB, scannedAlbums []*models.Album, user *model
|
|||
}
|
||||
|
||||
// Reload faces after deleting albums
|
||||
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(db); err != nil {
|
||||
deleteErrors = append(deleteErrors, err)
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
if err := face_detection.GlobalFaceDetector.ReloadFacesFromDatabase(db); err != nil {
|
||||
deleteErrors = append(deleteErrors, err)
|
||||
}
|
||||
}
|
||||
|
||||
return deleteErrors
|
||||
|
|
|
@ -19,9 +19,13 @@ type FaceDetector struct {
|
|||
imageFaceIDs []int
|
||||
}
|
||||
|
||||
var GlobalFaceDetector FaceDetector
|
||||
var GlobalFaceDetector *FaceDetector = nil
|
||||
|
||||
func InitializeFaceDetector(db *gorm.DB) error {
|
||||
if utils.EnvDisableFaceRecognition.GetBool() {
|
||||
log.Printf("Face detection disabled (%s=1)\n", utils.EnvDisableFaceRecognition.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Println("Initializing face detector")
|
||||
|
||||
|
@ -35,7 +39,7 @@ func InitializeFaceDetector(db *gorm.DB) error {
|
|||
return errors.Wrap(err, "get face detection samples from database")
|
||||
}
|
||||
|
||||
GlobalFaceDetector = FaceDetector{
|
||||
GlobalFaceDetector = &FaceDetector{
|
||||
rec: rec,
|
||||
faceDescriptors: faceDescriptors,
|
||||
faceGroupIDs: faceGroupIDs,
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/photoview/photoview/api/utils"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/vansante/go-ffprobe.v2"
|
||||
)
|
||||
|
@ -33,6 +34,11 @@ type FfmpegWorker struct {
|
|||
}
|
||||
|
||||
func newDarktableWorker() *DarktableWorker {
|
||||
if utils.EnvDisableRawProcessing.GetBool() {
|
||||
log.Printf("Executable worker disabled (%s=1): darktable\n", utils.EnvDisableRawProcessing.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
path, err := exec.LookPath("darktable-cli")
|
||||
if err != nil {
|
||||
log.Println("Executable worker not found: darktable")
|
||||
|
@ -54,6 +60,11 @@ func newDarktableWorker() *DarktableWorker {
|
|||
}
|
||||
|
||||
func newFfmpegWorker() *FfmpegWorker {
|
||||
if utils.EnvDisableVideoEncoding.GetBool() {
|
||||
log.Printf("Executable worker disabled (%s=1): ffmpeg\n", utils.EnvDisableVideoEncoding.GetName())
|
||||
return nil
|
||||
}
|
||||
|
||||
path, err := exec.LookPath("ffmpeg")
|
||||
if err != nil {
|
||||
log.Println("Executable worker not found: ffmpeg")
|
||||
|
|
|
@ -141,6 +141,9 @@ func scanAlbum(album *models.Album, cache *scanner_cache.AlbumScannerCache, db *
|
|||
|
||||
if processing_was_needed && media.Type == models.MediaTypePhoto {
|
||||
go func(media *models.Media) {
|
||||
if face_detection.GlobalFaceDetector == nil {
|
||||
return
|
||||
}
|
||||
if err := face_detection.GlobalFaceDetector.DetectFaces(db, media); err != nil {
|
||||
scanner_utils.ScannerError("Error detecting faces in image (%s): %s", media.Path, err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package utils
|
||||
|
||||
import "os"
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// EnvironmentVariable represents the name of an environment variable used to configure Photoview
|
||||
type EnvironmentVariable string
|
||||
|
@ -30,6 +33,13 @@ const (
|
|||
EnvSqlitePath EnvironmentVariable = "PHOTOVIEW_SQLITE_PATH"
|
||||
)
|
||||
|
||||
// Feature related
|
||||
const (
|
||||
EnvDisableFaceRecognition EnvironmentVariable = "PHOTOVIEW_DISABLE_FACE_RECOGNITION"
|
||||
EnvDisableVideoEncoding EnvironmentVariable = "PHOTOVIEW_DISABLE_VIDEO_ENCODING"
|
||||
EnvDisableRawProcessing EnvironmentVariable = "PHOTOVIEW_DISABLE_RAW_PROCESSING"
|
||||
)
|
||||
|
||||
// GetName returns the name of the environment variable itself
|
||||
func (v EnvironmentVariable) GetName() string {
|
||||
return string(v)
|
||||
|
@ -40,6 +50,20 @@ func (v EnvironmentVariable) GetValue() string {
|
|||
return os.Getenv(string(v))
|
||||
}
|
||||
|
||||
// GetBool returns the environment variable as a boolean (defaults to false if not defined)
|
||||
func (v EnvironmentVariable) GetBool() bool {
|
||||
value := strings.ToLower(os.Getenv(string(v)))
|
||||
trueValues := []string{"1", "true"}
|
||||
|
||||
for _, x := range trueValues {
|
||||
if value == x {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ShouldServeUI whether or not the "serve ui" option is enabled
|
||||
func ShouldServeUI() bool {
|
||||
return EnvServeUI.GetValue() == "1"
|
||||
|
|
|
@ -3,6 +3,7 @@ import { NavLink } from 'react-router-dom'
|
|||
import { useQuery, gql } from '@apollo/client'
|
||||
import { authToken } from '../../helpers/authentication'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { mapboxEnabledQuery } from '../../__generated__/mapboxEnabledQuery'
|
||||
|
||||
export const MAPBOX_QUERY = gql`
|
||||
query mapboxEnabledQuery {
|
||||
|
@ -10,6 +11,14 @@ export const MAPBOX_QUERY = gql`
|
|||
}
|
||||
`
|
||||
|
||||
export const FACE_DETECTION_ENABLED_QUERY = gql`
|
||||
query faceDetectionEnabled {
|
||||
siteInfo {
|
||||
faceDetectionEnabled
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type MenuButtonProps = {
|
||||
to: string
|
||||
exact: boolean
|
||||
|
@ -56,9 +65,16 @@ const MenuSeparator = () => (
|
|||
export const MainMenu = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const mapboxQuery = authToken() ? useQuery(MAPBOX_QUERY) : null
|
||||
const mapboxQuery = authToken()
|
||||
? useQuery<mapboxEnabledQuery>(MAPBOX_QUERY)
|
||||
: null
|
||||
const faceDetectionEnabledQuery = authToken()
|
||||
? useQuery(FACE_DETECTION_ENABLED_QUERY)
|
||||
: null
|
||||
|
||||
const mapboxEnabled = !!mapboxQuery?.data?.mapboxToken
|
||||
const faceDetectionEnabled =
|
||||
!!faceDetectionEnabledQuery?.data?.siteInfo?.faceDetectionEnabled
|
||||
|
||||
return (
|
||||
<div className="fixed w-full bottom-0 lg:bottom-auto lg:top-[84px] z-30 bg-white shadow-separator lg:shadow-none lg:w-[240px] lg:ml-8 lg:mr-5 flex-shrink-0">
|
||||
|
@ -104,19 +120,21 @@ export const MainMenu = () => {
|
|||
}
|
||||
/>
|
||||
) : null}
|
||||
<MenuButton
|
||||
to="/people"
|
||||
exact
|
||||
label={t('sidemenu.people', 'People')}
|
||||
background="#fbcd78"
|
||||
activeClasses="ring-[#fff7e4] bg-[#fff7e4]"
|
||||
className="outline-none focus:ring-2 focus:ring-yellow-100 focus:ring-offset-2"
|
||||
icon={
|
||||
<svg viewBox="0 0 24 24" fill="white">
|
||||
<path d="M15.713873,14.2127622 C17.4283917,14.8986066 18.9087267,16.0457918 20.0014344,17.5008819 C20,19.1568542 18.6568542,20.5 17,20.5 L7,20.5 C5.34314575,20.5 4,19.1568542 4,17.5 L4.09169034,17.3788798 C5.17486154,15.981491 6.62020934,14.878942 8.28693513,14.2120314 C9.30685583,15.018595 10.5972088,15.5 12,15.5 C13.3092718,15.5 14.5205974,15.0806428 15.5069849,14.3689203 L15.713873,14.2127622 L15.713873,14.2127622 Z M12,4 C15.0375661,4 17.5,6.46243388 17.5,9.5 C17.5,12.5375661 15.0375661,15 12,15 C8.96243388,15 6.5,12.5375661 6.5,9.5 C6.5,6.46243388 8.96243388,4 12,4 Z"></path>
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
{faceDetectionEnabled ? (
|
||||
<MenuButton
|
||||
to="/people"
|
||||
exact
|
||||
label={t('sidemenu.people', 'People')}
|
||||
background="#fbcd78"
|
||||
activeClasses="ring-[#fff7e4] bg-[#fff7e4]"
|
||||
className="outline-none focus:ring-2 focus:ring-yellow-100 focus:ring-offset-2"
|
||||
icon={
|
||||
<svg viewBox="0 0 24 24" fill="white">
|
||||
<path d="M15.713873,14.2127622 C17.4283917,14.8986066 18.9087267,16.0457918 20.0014344,17.5008819 C20,19.1568542 18.6568542,20.5 17,20.5 L7,20.5 C5.34314575,20.5 4,19.1568542 4,17.5 L4.09169034,17.3788798 C5.17486154,15.981491 6.62020934,14.878942 8.28693513,14.2120314 C9.30685583,15.018595 10.5972088,15.5 12,15.5 C13.3092718,15.5 14.5205974,15.0806428 15.5069849,14.3689203 L15.713873,14.2127622 L15.713873,14.2127622 Z M12,4 C15.0375661,4 17.5,6.46243388 17.5,9.5 C17.5,12.5375661 15.0375661,15 12,15 C8.96243388,15 6.5,12.5375661 6.5,9.5 C6.5,6.46243388 8.96243388,4 12,4 Z"></path>
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
<MenuSeparator />
|
||||
<MenuButton
|
||||
to="/settings"
|
||||
|
|
|
@ -330,18 +330,6 @@ const flashLookup = (t: TranslationFn): { [key: number]: string } => {
|
|||
}
|
||||
}
|
||||
|
||||
// From https://exiftool.org/TagNames/EXIF.html
|
||||
// const orientation = {
|
||||
// 1: 'Horizontal (normal)',
|
||||
// 2: 'Mirror horizontal',
|
||||
// 3: 'Rotate 180',
|
||||
// 4: 'Mirror vertical',
|
||||
// 5: 'Mirror horizontal and rotate 270 CW',
|
||||
// 6: 'Rotate 90 CW',
|
||||
// 7: 'Mirror horizontal and rotate 90 CW',
|
||||
// 8: 'Rotate 270 CW',
|
||||
// }
|
||||
|
||||
type SidebarContentProps = {
|
||||
media: MediaSidebarMedia
|
||||
hidePreview?: boolean
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Billeder"
|
||||
"title": "Tidslinje"
|
||||
},
|
||||
"places_page": {
|
||||
"title": "Kort"
|
||||
|
@ -285,13 +285,13 @@
|
|||
"sidemenu": {
|
||||
"albums": "Albums",
|
||||
"people": "Personer",
|
||||
"photos": "Billeder",
|
||||
"photos": "Tidslinje",
|
||||
"places": "Kort",
|
||||
"settings": "Indstillinger"
|
||||
},
|
||||
"title": {
|
||||
"loading_album": "Loader album",
|
||||
"login": "Logind",
|
||||
"login": "Log ind",
|
||||
"people": "Personer",
|
||||
"settings": "Indstillinger"
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
}
|
||||
},
|
||||
"photos_page": {
|
||||
"title": "Photos"
|
||||
"title": "Timeline"
|
||||
},
|
||||
"places_page": {
|
||||
"title": "Places"
|
||||
|
@ -285,7 +285,7 @@
|
|||
"sidemenu": {
|
||||
"albums": "Albums",
|
||||
"people": "People",
|
||||
"photos": "Photos",
|
||||
"photos": "Timeline",
|
||||
"places": "Places",
|
||||
"settings": "Settings"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue