diff --git a/cmd/photoprism/photoprism.go b/cmd/photoprism/photoprism.go index 673abe61c..757b117bd 100644 --- a/cmd/photoprism/photoprism.go +++ b/cmd/photoprism/photoprism.go @@ -1,12 +1,8 @@ package main import ( - "fmt" - "github.com/araddon/dateparse" - "github.com/photoprism/photoprism/internal/photoprism" - "github.com/photoprism/photoprism/internal/server" + "github.com/photoprism/photoprism/internal/commands" "github.com/urfave/cli" - "log" "os" ) @@ -17,352 +13,18 @@ func main() { app.Version = "0.0.0" app.Copyright = "Copyright (c) 2018 Michael Mayer and contributors" app.EnableBashCompletion = true - app.Flags = globalCliFlags + app.Flags = commands.GlobalFlags app.Commands = []cli.Command{ - { - Name: "config", - Usage: "Displays global configuration values", - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - fmt.Printf("NAME VALUE\n") - fmt.Printf("debug %t\n", conf.Debug) - fmt.Printf("config-file %s\n", conf.ConfigFile) - fmt.Printf("assets-path %s\n", conf.AssetsPath) - fmt.Printf("originals-path %s\n", conf.OriginalsPath) - fmt.Printf("thumbnails-path %s\n", conf.ThumbnailsPath) - fmt.Printf("import-path %s\n", conf.ImportPath) - fmt.Printf("export-path %s\n", conf.ExportPath) - fmt.Printf("darktable-cli %s\n", conf.DarktableCli) - fmt.Printf("database-driver %s\n", conf.DatabaseDriver) - fmt.Printf("database-dsn %s\n", conf.DatabaseDsn) - - return nil - }, - }, - { - Name: "start", - Usage: "Starts web server", - Flags: []cli.Flag{ - cli.IntFlag{ - Name: "server-port, p", - Usage: "HTTP server port", - Value: 80, - EnvVar: "PHOTOPRISM_SERVER_PORT", - }, - cli.StringFlag{ - Name: "server-host, i", - Usage: "HTTP server host", - Value: "", - EnvVar: "PHOTOPRISM_SERVER_HOST", - }, - cli.StringFlag{ - Name: "server-mode, m", - Usage: "debug, release or test", - Value: "", - EnvVar: "PHOTOPRISM_SERVER_MODE", - }, - }, - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - if context.IsSet("server-host") || conf.ServerIP == "" { - conf.ServerIP = context.String("server-host") - } - - if context.IsSet("server-port") || conf.ServerPort == 0 { - conf.ServerPort = context.Int("server-port") - } - - if context.IsSet("server-mode") || conf.ServerMode == "" { - conf.ServerMode = context.String("server-mode") - } - - if conf.ServerPort < 1 { - log.Fatal("Server port must be a positive integer") - } - - if err := conf.CreateDirectories(); err != nil { - log.Fatal(err) - } - - conf.MigrateDb() - - fmt.Printf("Starting web server at %s:%d...\n", context.String("server-host"), context.Int("server-port")) - - server.Start(conf) - - fmt.Println("Done.") - - return nil - }, - }, - { - Name: "migrate", - Usage: "Automatically migrates / initializes database", - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - fmt.Println("Migrating database...") - - conf.MigrateDb() - - fmt.Println("Done.") - - return nil - }, - }, - { - Name: "import", - Usage: "Imports photos", - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - if err := conf.CreateDirectories(); err != nil { - log.Fatal(err) - } - - conf.MigrateDb() - - fmt.Printf("Importing photos from %s...\n", conf.ImportPath) - - tensorFlow := photoprism.NewTensorFlow(conf.GetTensorFlowModelPath()) - - indexer := photoprism.NewIndexer(conf.OriginalsPath, tensorFlow, conf.GetDb()) - - converter := photoprism.NewConverter(conf.DarktableCli) - - importer := photoprism.NewImporter(conf.OriginalsPath, indexer, converter) - - importer.ImportPhotosFromDirectory(conf.ImportPath) - - fmt.Println("Done.") - - return nil - }, - }, - { - Name: "index", - Usage: "Re-indexes all originals", - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - if err := conf.CreateDirectories(); err != nil { - log.Fatal(err) - } - - conf.MigrateDb() - - fmt.Printf("Indexing photos in %s...\n", conf.OriginalsPath) - - tensorFlow := photoprism.NewTensorFlow(conf.GetTensorFlowModelPath()) - - indexer := photoprism.NewIndexer(conf.OriginalsPath, tensorFlow, conf.GetDb()) - - indexer.IndexAll() - - fmt.Println("Done.") - - return nil - }, - }, - { - Name: "convert", - Usage: "Converts RAW originals to JPEG", - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - if err := conf.CreateDirectories(); err != nil { - log.Fatal(err) - } - - fmt.Printf("Converting RAW images in %s to JPEG...\n", conf.OriginalsPath) - - converter := photoprism.NewConverter(conf.DarktableCli) - - converter.ConvertAll(conf.OriginalsPath) - - fmt.Println("Done.") - - return nil - }, - }, - { - Name: "thumbnails", - Usage: "Creates thumbnails", - Flags: []cli.Flag{ - cli.IntSliceFlag{ - Name: "size, s", - Usage: "Thumbnail size in pixels", - }, - cli.BoolFlag{ - Name: "default, d", - Usage: "Render default sizes: 320, 500, 640, 1280, 1920 and 2560px", - }, - cli.BoolFlag{ - Name: "square, q", - Usage: "Square aspect ratio", - }, - }, - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - if err := conf.CreateDirectories(); err != nil { - log.Fatal(err) - } - - fmt.Printf("Creating thumbnails in %s...\n", conf.ThumbnailsPath) - - sizes := context.IntSlice("size") - - if context.Bool("default") { - sizes = []int{320, 500, 640, 1280, 1920, 2560} - } - - if len(sizes) == 0 { - fmt.Println("No sizes selected. Nothing to do.") - return nil - } - - for _, size := range sizes { - photoprism.CreateThumbnailsFromOriginals(conf.OriginalsPath, conf.ThumbnailsPath, size, context.Bool("square")) - } - - fmt.Println("Done.") - - return nil - }, - }, - { - Name: "export", - Usage: "Exports photos as JPEG", - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "name, n", - Usage: "Sub-directory name", - }, - cli.StringFlag{ - Name: "after, a", - Usage: "Start date e.g. 2017/04/15", - }, - cli.StringFlag{ - Name: "before, b", - Usage: "End date e.g. 2018/05/02", - }, - cli.IntFlag{ - Name: "size, s", - Usage: "Image size in pixels", - Value: 2560, - }, - }, - Action: func(context *cli.Context) error { - conf := photoprism.NewConfig(context) - - if err := conf.CreateDirectories(); err != nil { - log.Fatal(err) - } - - before := context.String("before") - after := context.String("after") - - if before == "" || after == "" { - fmt.Println("You need to provide before and after dates for export, e.g.\n\nphotoprism export --after 2018/04/10 --before '2018/04/15 23:00:00'") - - return nil - } - - afterDate, _ := dateparse.ParseAny(after) - beforeDate, _ := dateparse.ParseAny(before) - afterDateFormatted := afterDate.Format("20060102") - beforeDateFormatted := beforeDate.Format("20060102") - - name := context.String("name") - - if name == "" { - if afterDateFormatted == beforeDateFormatted { - name = beforeDateFormatted - } else { - name = fmt.Sprintf("%s - %s", afterDateFormatted, beforeDateFormatted) - } - } - - exportPath := fmt.Sprintf("%s/%s", conf.ExportPath, name) - size := context.Int("size") - originals := photoprism.FindOriginalsByDate(conf.OriginalsPath, afterDate, beforeDate) - - fmt.Printf("Exporting photos to %s...\n", exportPath) - - photoprism.ExportPhotosFromOriginals(originals, conf.ThumbnailsPath, exportPath, size) - - fmt.Println("Done.") - - return nil - }, - }, + commands.ConfigCommand, + commands.StartCommand, + commands.MigrateCommand, + commands.ImportCommand, + commands.IndexCommand, + commands.ConvertCommand, + commands.ThumbnailsCommand, + commands.ExportCommand, } app.Run(os.Args) -} - -var globalCliFlags = []cli.Flag{ - cli.BoolFlag{ - Name: "debug", - Usage: "run in debug mode", - EnvVar: "PHOTOPRISM_DEBUG", - }, - cli.StringFlag{ - Name: "config-file, c", - Usage: "load configuration from `FILENAME`", - Value: "/etc/photoprism/photoprism.yml", - EnvVar: "PHOTOPRISM_CONFIG_FILE", - }, - cli.StringFlag{ - Name: "darktable-cli", - Usage: "darktable command-line executable `FILENAME`", - Value: "/usr/bin/darktable-cli", - EnvVar: "PHOTOPRISM_DARKTABLE_CLI", - }, - cli.StringFlag{ - Name: "originals-path", - Usage: "originals `PATH`", - Value: "/var/photoprism/photos/originals", - EnvVar: "PHOTOPRISM_ORIGINALS_PATH", - }, - cli.StringFlag{ - Name: "thumbnails-path", - Usage: "thumbnails `PATH`", - Value: "/var/photoprism/photos/thumbnails", - EnvVar: "PHOTOPRISM_THUMBNAILS_PATH", - }, - cli.StringFlag{ - Name: "import-path", - Usage: "import `PATH`", - Value: "/var/photoprism/photos/import", - EnvVar: "PHOTOPRISM_IMPORT_PATH", - }, - cli.StringFlag{ - Name: "export-path", - Usage: "export `PATH`", - Value: "/var/photoprism/photos/export", - EnvVar: "PHOTOPRISM_EXPORT_PATH", - }, - cli.StringFlag{ - Name: "assets-path", - Usage: "assets `PATH`", - Value: "/var/photoprism", - EnvVar: "PHOTOPRISM_ASSETS_PATH", - }, - cli.StringFlag{ - Name: "database-driver", - Usage: "database `DRIVER` (mysql, mssql, postgres or sqlite)", - Value: "mysql", - EnvVar: "PHOTOPRISM_DATABASE_DRIVER", - }, - cli.StringFlag{ - Name: "database-dsn", - Usage: "database data source name (`DSN`)", - Value: "photoprism:photoprism@tcp(localhost:3306)/photoprism", - EnvVar: "PHOTOPRISM_DATABASE_DSN", - }, -} +} \ No newline at end of file diff --git a/internal/commands/config.go b/internal/commands/config.go new file mode 100644 index 000000000..da8a249a4 --- /dev/null +++ b/internal/commands/config.go @@ -0,0 +1,31 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" +) + +var ConfigCommand = cli.Command{ + Name: "config", + Usage: "Displays global configuration values", + Action: configAction, +} + +func configAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + fmt.Printf("NAME VALUE\n") + fmt.Printf("debug %t\n", conf.Debug) + fmt.Printf("config-file %s\n", conf.ConfigFile) + fmt.Printf("assets-path %s\n", conf.AssetsPath) + fmt.Printf("originals-path %s\n", conf.OriginalsPath) + fmt.Printf("thumbnails-path %s\n", conf.ThumbnailsPath) + fmt.Printf("import-path %s\n", conf.ImportPath) + fmt.Printf("export-path %s\n", conf.ExportPath) + fmt.Printf("darktable-cli %s\n", conf.DarktableCli) + fmt.Printf("database-driver %s\n", conf.DatabaseDriver) + fmt.Printf("database-dsn %s\n", conf.DatabaseDsn) + + return nil +} diff --git a/internal/commands/convert.go b/internal/commands/convert.go new file mode 100644 index 000000000..4a7967c7e --- /dev/null +++ b/internal/commands/convert.go @@ -0,0 +1,32 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" + "log" +) + +var ConvertCommand = cli.Command{ + Name: "convert", + Usage: "Converts RAW originals to JPEG", + Action: convertAction, +} + +func convertAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + if err := conf.CreateDirectories(); err != nil { + log.Fatal(err) + } + + fmt.Printf("Converting RAW images in %s to JPEG...\n", conf.OriginalsPath) + + converter := photoprism.NewConverter(conf.DarktableCli) + + converter.ConvertAll(conf.OriginalsPath) + + fmt.Println("Done.") + + return nil +} diff --git a/internal/commands/export.go b/internal/commands/export.go new file mode 100644 index 000000000..1fd4ae3a6 --- /dev/null +++ b/internal/commands/export.go @@ -0,0 +1,80 @@ +package commands + +import ( + "fmt" + "github.com/araddon/dateparse" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" + "log" +) + +var ExportCommand = cli.Command{ + Name: "export", + Usage: "Exports photos as JPEG", + Flags: exportFlags, + Action: exportAction, +} + +var exportFlags = []cli.Flag{ + cli.StringFlag{ + Name: "name, n", + Usage: "Sub-directory name", + }, + cli.StringFlag{ + Name: "after, a", + Usage: "Start date e.g. 2017/04/15", + }, + cli.StringFlag{ + Name: "before, b", + Usage: "End date e.g. 2018/05/02", + }, + cli.IntFlag{ + Name: "size, s", + Usage: "Image size in pixels", + Value: 2560, + }, +} + +func exportAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + if err := conf.CreateDirectories(); err != nil { + log.Fatal(err) + } + + before := context.String("before") + after := context.String("after") + + if before == "" || after == "" { + fmt.Println("You need to provide before and after dates for export, e.g.\n\nphotoprism export --after 2018/04/10 --before '2018/04/15 23:00:00'") + + return nil + } + + afterDate, _ := dateparse.ParseAny(after) + beforeDate, _ := dateparse.ParseAny(before) + afterDateFormatted := afterDate.Format("20060102") + beforeDateFormatted := beforeDate.Format("20060102") + + name := context.String("name") + + if name == "" { + if afterDateFormatted == beforeDateFormatted { + name = beforeDateFormatted + } else { + name = fmt.Sprintf("%s - %s", afterDateFormatted, beforeDateFormatted) + } + } + + exportPath := fmt.Sprintf("%s/%s", conf.ExportPath, name) + size := context.Int("size") + originals := photoprism.FindOriginalsByDate(conf.OriginalsPath, afterDate, beforeDate) + + fmt.Printf("Exporting photos to %s...\n", exportPath) + + photoprism.ExportPhotosFromOriginals(originals, conf.ThumbnailsPath, exportPath, size) + + fmt.Println("Done.") + + return nil +} diff --git a/internal/commands/flags.go b/internal/commands/flags.go new file mode 100644 index 000000000..368c52ee5 --- /dev/null +++ b/internal/commands/flags.go @@ -0,0 +1,65 @@ +package commands + +import "github.com/urfave/cli" + +var GlobalFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "run in debug mode", + EnvVar: "PHOTOPRISM_DEBUG", + }, + cli.StringFlag{ + Name: "config-file, c", + Usage: "load configuration from `FILENAME`", + Value: "/etc/photoprism/photoprism.yml", + EnvVar: "PHOTOPRISM_CONFIG_FILE", + }, + cli.StringFlag{ + Name: "darktable-cli", + Usage: "darktable command-line executable `FILENAME`", + Value: "/usr/bin/darktable-cli", + EnvVar: "PHOTOPRISM_DARKTABLE_CLI", + }, + cli.StringFlag{ + Name: "originals-path", + Usage: "originals `PATH`", + Value: "/var/photoprism/photos/originals", + EnvVar: "PHOTOPRISM_ORIGINALS_PATH", + }, + cli.StringFlag{ + Name: "thumbnails-path", + Usage: "thumbnails `PATH`", + Value: "/var/photoprism/photos/thumbnails", + EnvVar: "PHOTOPRISM_THUMBNAILS_PATH", + }, + cli.StringFlag{ + Name: "import-path", + Usage: "import `PATH`", + Value: "/var/photoprism/photos/import", + EnvVar: "PHOTOPRISM_IMPORT_PATH", + }, + cli.StringFlag{ + Name: "export-path", + Usage: "export `PATH`", + Value: "/var/photoprism/photos/export", + EnvVar: "PHOTOPRISM_EXPORT_PATH", + }, + cli.StringFlag{ + Name: "assets-path", + Usage: "assets `PATH`", + Value: "/var/photoprism", + EnvVar: "PHOTOPRISM_ASSETS_PATH", + }, + cli.StringFlag{ + Name: "database-driver", + Usage: "database `DRIVER` (mysql, mssql, postgres or sqlite)", + Value: "mysql", + EnvVar: "PHOTOPRISM_DATABASE_DRIVER", + }, + cli.StringFlag{ + Name: "database-dsn", + Usage: "database data source name (`DSN`)", + Value: "photoprism:photoprism@tcp(localhost:3306)/photoprism", + EnvVar: "PHOTOPRISM_DATABASE_DSN", + }, +} diff --git a/internal/commands/import.go b/internal/commands/import.go new file mode 100644 index 000000000..888bf7082 --- /dev/null +++ b/internal/commands/import.go @@ -0,0 +1,40 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" + "log" +) + +var ImportCommand = cli.Command{ + Name: "import", + Usage: "Imports photos", + Action: importAction, +} + +func importAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + if err := conf.CreateDirectories(); err != nil { + log.Fatal(err) + } + + conf.MigrateDb() + + fmt.Printf("Importing photos from %s...\n", conf.ImportPath) + + tensorFlow := photoprism.NewTensorFlow(conf.GetTensorFlowModelPath()) + + indexer := photoprism.NewIndexer(conf.OriginalsPath, tensorFlow, conf.GetDb()) + + converter := photoprism.NewConverter(conf.DarktableCli) + + importer := photoprism.NewImporter(conf.OriginalsPath, indexer, converter) + + importer.ImportPhotosFromDirectory(conf.ImportPath) + + fmt.Println("Done.") + + return nil +} diff --git a/internal/commands/index.go b/internal/commands/index.go new file mode 100644 index 000000000..aaf6d5138 --- /dev/null +++ b/internal/commands/index.go @@ -0,0 +1,36 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" + "log" +) + +var IndexCommand = cli.Command{ + Name: "index", + Usage: "Re-indexes all originals", + Action: indexAction, +} + +func indexAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + if err := conf.CreateDirectories(); err != nil { + log.Fatal(err) + } + + conf.MigrateDb() + + fmt.Printf("Indexing photos in %s...\n", conf.OriginalsPath) + + tensorFlow := photoprism.NewTensorFlow(conf.GetTensorFlowModelPath()) + + indexer := photoprism.NewIndexer(conf.OriginalsPath, tensorFlow, conf.GetDb()) + + indexer.IndexAll() + + fmt.Println("Done.") + + return nil +} diff --git a/internal/commands/migrate.go b/internal/commands/migrate.go new file mode 100644 index 000000000..65a4f96d1 --- /dev/null +++ b/internal/commands/migrate.go @@ -0,0 +1,25 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" +) + +var MigrateCommand = cli.Command{ + Name: "migrate", + Usage: "Automatically migrates / initializes database", + Action: migrateAction, +} + +func migrateAction (context *cli.Context) error { + conf := photoprism.NewConfig(context) + + fmt.Println("Migrating database...") + + conf.MigrateDb() + + fmt.Println("Done.") + + return nil +} diff --git a/internal/commands/start.go b/internal/commands/start.go new file mode 100644 index 000000000..24ceae229 --- /dev/null +++ b/internal/commands/start.go @@ -0,0 +1,71 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/photoprism/photoprism/internal/server" + "github.com/urfave/cli" + "log" +) + +var StartCommand = cli.Command{ + Name: "start", + Usage: "Starts web server", + Flags: startFlags, + Action: startAction, +} + +var startFlags = []cli.Flag{ + cli.IntFlag{ + Name: "server-port, p", + Usage: "HTTP server port", + Value: 80, + EnvVar: "PHOTOPRISM_SERVER_PORT", + }, + cli.StringFlag{ + Name: "server-host, i", + Usage: "HTTP server host", + Value: "", + EnvVar: "PHOTOPRISM_SERVER_HOST", + }, + cli.StringFlag{ + Name: "server-mode, m", + Usage: "debug, release or test", + Value: "", + EnvVar: "PHOTOPRISM_SERVER_MODE", + }, +} + +func startAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + if context.IsSet("server-host") || conf.ServerIP == "" { + conf.ServerIP = context.String("server-host") + } + + if context.IsSet("server-port") || conf.ServerPort == 0 { + conf.ServerPort = context.Int("server-port") + } + + if context.IsSet("server-mode") || conf.ServerMode == "" { + conf.ServerMode = context.String("server-mode") + } + + if conf.ServerPort < 1 { + log.Fatal("Server port must be a positive integer") + } + + if err := conf.CreateDirectories(); err != nil { + log.Fatal(err) + } + + conf.MigrateDb() + + fmt.Printf("Starting web server at %s:%d...\n", context.String("server-host"), context.Int("server-port")) + + server.Start(conf) + + fmt.Println("Done.") + + return nil +} diff --git a/internal/commands/thumbnails.go b/internal/commands/thumbnails.go new file mode 100644 index 000000000..494723202 --- /dev/null +++ b/internal/commands/thumbnails.go @@ -0,0 +1,57 @@ +package commands + +import ( + "fmt" + "github.com/photoprism/photoprism/internal/photoprism" + "github.com/urfave/cli" + "log" +) + +var ThumbnailsCommand = cli.Command{ + Name: "thumbnails", + Usage: "Creates thumbnails", + Flags: []cli.Flag{ + cli.IntSliceFlag{ + Name: "size, s", + Usage: "Thumbnail size in pixels", + }, + cli.BoolFlag{ + Name: "default, d", + Usage: "Render default sizes: 320, 500, 640, 1280, 1920 and 2560px", + }, + cli.BoolFlag{ + Name: "square, q", + Usage: "Square aspect ratio", + }, + }, + Action: thumbnailsAction, +} + +func thumbnailsAction(context *cli.Context) error { + conf := photoprism.NewConfig(context) + + if err := conf.CreateDirectories(); err != nil { + log.Fatal(err) + } + + fmt.Printf("Creating thumbnails in %s...\n", conf.ThumbnailsPath) + + sizes := context.IntSlice("size") + + if context.Bool("default") { + sizes = []int{320, 500, 640, 1280, 1920, 2560} + } + + if len(sizes) == 0 { + fmt.Println("No sizes selected. Nothing to do.") + return nil + } + + for _, size := range sizes { + photoprism.CreateThumbnailsFromOriginals(conf.OriginalsPath, conf.ThumbnailsPath, size, context.Bool("square")) + } + + fmt.Println("Done.") + + return nil +}