Backend: Refactor package structure
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
1a3966e798
commit
aa220a06fe
|
@ -13,24 +13,11 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var imp *photoprism.Import
|
||||
|
||||
func initImport(conf *config.Config) {
|
||||
if imp != nil {
|
||||
return
|
||||
}
|
||||
|
||||
initIndex(conf)
|
||||
|
||||
convert := photoprism.NewConvert(conf)
|
||||
|
||||
imp = photoprism.NewImport(conf, ind, convert)
|
||||
}
|
||||
|
||||
// POST /api/v1/import*
|
||||
func StartImport(router *gin.RouterGroup, conf *config.Config) {
|
||||
router.POST("/import/*path", func(c *gin.Context) {
|
||||
|
@ -64,7 +51,7 @@ func StartImport(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
path = filepath.Clean(path)
|
||||
|
||||
initImport(conf)
|
||||
imp := service.Import()
|
||||
|
||||
var opt photoprism.ImportOptions
|
||||
|
||||
|
@ -105,7 +92,7 @@ func CancelImport(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
initImport(conf)
|
||||
imp := service.Import()
|
||||
|
||||
imp.Cancel()
|
||||
|
||||
|
|
|
@ -7,38 +7,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var ind *photoprism.Index
|
||||
var nd *nsfw.Detector
|
||||
|
||||
func initIndex(conf *config.Config) {
|
||||
if ind != nil {
|
||||
return
|
||||
}
|
||||
|
||||
initNsfwDetector(conf)
|
||||
|
||||
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
|
||||
|
||||
ind = photoprism.NewIndex(conf, tf, nd)
|
||||
}
|
||||
|
||||
func initNsfwDetector(conf *config.Config) {
|
||||
if nd != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nd = nsfw.New(conf.NSFWModelPath())
|
||||
}
|
||||
|
||||
// POST /api/v1/index
|
||||
func StartIndexing(router *gin.RouterGroup, conf *config.Config) {
|
||||
router.POST("/index", func(c *gin.Context) {
|
||||
|
@ -67,7 +43,7 @@ func StartIndexing(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
|
||||
if f.ConvertRaw && !conf.ReadOnly() {
|
||||
convert := photoprism.NewConvert(conf)
|
||||
convert := service.Convert()
|
||||
|
||||
if err := convert.Start(conf.OriginalsPath()); err != nil {
|
||||
cancel(err)
|
||||
|
@ -76,7 +52,7 @@ func StartIndexing(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
|
||||
if f.CreateThumbs {
|
||||
rs := photoprism.NewResample(conf)
|
||||
rs := service.Resample()
|
||||
|
||||
if err := rs.Start(false); err != nil {
|
||||
cancel(err)
|
||||
|
@ -84,7 +60,7 @@ func StartIndexing(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
initIndex(conf)
|
||||
ind := service.Index()
|
||||
|
||||
if f.SkipUnchanged {
|
||||
ind.Start(photoprism.IndexOptionsNone())
|
||||
|
@ -110,7 +86,7 @@ func CancelIndexing(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
initIndex(conf)
|
||||
ind := service.Index()
|
||||
|
||||
ind.Cancel()
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -65,7 +66,7 @@ func Upload(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
|
||||
if !conf.UploadNSFW() {
|
||||
initNsfwDetector(conf)
|
||||
nd := service.NsfwDetector()
|
||||
|
||||
containsNSFW := false
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -20,6 +20,7 @@ func convertAction(ctx *cli.Context) error {
|
|||
start := time.Now()
|
||||
|
||||
conf := config.NewConfig(ctx)
|
||||
service.SetConfig(conf)
|
||||
|
||||
if conf.ReadOnly() {
|
||||
return config.ErrReadOnly
|
||||
|
@ -31,9 +32,11 @@ func convertAction(ctx *cli.Context) error {
|
|||
|
||||
log.Infof("converting RAW images in %s to JPEG", conf.OriginalsPath())
|
||||
|
||||
convert := photoprism.NewConvert(conf)
|
||||
convert := service.Convert()
|
||||
|
||||
convert.Start(conf.OriginalsPath())
|
||||
if err := convert.Start(conf.OriginalsPath()); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
|
||||
|
|
|
@ -7,10 +7,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -27,6 +26,7 @@ func copyAction(ctx *cli.Context) error {
|
|||
start := time.Now()
|
||||
|
||||
conf := config.NewConfig(ctx)
|
||||
service.SetConfig(conf)
|
||||
|
||||
// very if copy directory exist and is writable
|
||||
if conf.ReadOnly() {
|
||||
|
@ -66,14 +66,7 @@ func copyAction(ctx *cli.Context) error {
|
|||
|
||||
log.Infof("copying media files from %s to %s", sourcePath, conf.OriginalsPath())
|
||||
|
||||
tensorFlow := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
|
||||
nsfwDetector := nsfw.New(conf.NSFWModelPath())
|
||||
|
||||
ind := photoprism.NewIndex(conf, tensorFlow, nsfwDetector)
|
||||
|
||||
convert := photoprism.NewConvert(conf)
|
||||
|
||||
imp := photoprism.NewImport(conf, ind, convert)
|
||||
imp := service.Import()
|
||||
opt := photoprism.ImportOptionsCopy(sourcePath)
|
||||
|
||||
imp.Start(opt)
|
||||
|
|
|
@ -7,10 +7,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -27,6 +26,7 @@ func importAction(ctx *cli.Context) error {
|
|||
start := time.Now()
|
||||
|
||||
conf := config.NewConfig(ctx)
|
||||
service.SetConfig(conf)
|
||||
|
||||
// very if copy directory exist and is writable
|
||||
if conf.ReadOnly() {
|
||||
|
@ -66,14 +66,7 @@ func importAction(ctx *cli.Context) error {
|
|||
|
||||
log.Infof("moving media files from %s to %s", sourcePath, conf.OriginalsPath())
|
||||
|
||||
tensorFlow := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
|
||||
nsfwDetector := nsfw.New(conf.NSFWModelPath())
|
||||
|
||||
ind := photoprism.NewIndex(conf, tensorFlow, nsfwDetector)
|
||||
|
||||
convert := photoprism.NewConvert(conf)
|
||||
|
||||
imp := photoprism.NewImport(conf, ind, convert)
|
||||
imp := service.Import()
|
||||
opt := photoprism.ImportOptionsMove(sourcePath)
|
||||
|
||||
imp.Start(opt)
|
||||
|
|
|
@ -4,10 +4,9 @@ import (
|
|||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -31,6 +30,7 @@ func indexAction(ctx *cli.Context) error {
|
|||
start := time.Now()
|
||||
|
||||
conf := config.NewConfig(ctx)
|
||||
service.SetConfig(conf)
|
||||
|
||||
if err := conf.CreateDirectories(); err != nil {
|
||||
return err
|
||||
|
@ -49,9 +49,7 @@ func indexAction(ctx *cli.Context) error {
|
|||
log.Infof("read-only mode enabled")
|
||||
}
|
||||
|
||||
tf := classify.New(conf.ResourcesPath(), conf.TensorFlowDisabled())
|
||||
nd := nsfw.New(conf.NSFWModelPath())
|
||||
ind := photoprism.NewIndex(conf, tf, nd)
|
||||
ind := service.Index()
|
||||
|
||||
var opt photoprism.IndexOptions
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/server"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/sevlyar/go-daemon"
|
||||
"github.com/urfave/cli"
|
||||
|
@ -41,6 +42,7 @@ var startFlags = []cli.Flag{
|
|||
// startAction start the web server and initializes the daemon
|
||||
func startAction(ctx *cli.Context) error {
|
||||
conf := config.NewConfig(ctx)
|
||||
service.SetConfig(conf)
|
||||
|
||||
if err := conf.CreateDirectories(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
|
@ -26,6 +26,7 @@ func thumbsAction(ctx *cli.Context) error {
|
|||
start := time.Now()
|
||||
|
||||
conf := config.NewConfig(ctx)
|
||||
service.SetConfig(conf)
|
||||
|
||||
if err := conf.CreateDirectories(); err != nil {
|
||||
return err
|
||||
|
@ -33,7 +34,7 @@ func thumbsAction(ctx *cli.Context) error {
|
|||
|
||||
log.Infof("creating thumbnails in \"%s\"", conf.ThumbnailsPath())
|
||||
|
||||
rs := photoprism.NewResample(conf)
|
||||
rs := service.Resample()
|
||||
|
||||
if err := rs.Start(ctx.Bool("force")); err != nil {
|
||||
log.Error(err)
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/service/webdav"
|
||||
"github.com/photoprism/photoprism/internal/remote"
|
||||
"github.com/photoprism/photoprism/internal/remote/webdav"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/ulule/deepcopier"
|
||||
)
|
||||
|
@ -73,7 +73,7 @@ func (m *Account) Save(form form.Account, db *gorm.DB) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if m.AccType != string(service.TypeWebDAV) {
|
||||
if m.AccType != string(remote.ServiceWebDAV) {
|
||||
// TODO: Only WebDAV supported at the moment
|
||||
m.AccShare = false
|
||||
m.AccSync = false
|
||||
|
@ -97,7 +97,7 @@ func (m *Account) Delete(db *gorm.DB) error {
|
|||
|
||||
// Directories returns a list of directories or albums in an account.
|
||||
func (m *Account) Directories() (result fs.FileInfos, err error) {
|
||||
if m.AccType == service.TypeWebDAV {
|
||||
if m.AccType == remote.ServiceWebDAV {
|
||||
c := webdav.New(m.AccURL, m.AccUser, m.AccPass)
|
||||
result, err = c.Directories("/", true)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/remote"
|
||||
"github.com/ulule/deepcopier"
|
||||
)
|
||||
|
||||
|
@ -38,7 +38,7 @@ func NewAccount(m interface{}) (f Account, err error) {
|
|||
}
|
||||
|
||||
func (f *Account) ServiceDiscovery() error {
|
||||
acc, err := service.Discover(f.AccURL, f.AccUser, f.AccPass)
|
||||
acc, err := remote.Discover(f.AccURL, f.AccUser, f.AccPass)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/service/webdav"
|
||||
"github.com/photoprism/photoprism/internal/remote"
|
||||
"github.com/photoprism/photoprism/internal/remote/webdav"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
)
|
||||
|
||||
|
@ -49,7 +49,7 @@ func (s *Share) Start() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
if a.AccType != service.TypeWebDAV {
|
||||
if a.AccType != remote.ServiceWebDAV {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ func (s *Share) Start() (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
if a.AccType != service.TypeWebDAV {
|
||||
if a.AccType != remote.ServiceWebDAV {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/service/webdav"
|
||||
"github.com/photoprism/photoprism/internal/remote"
|
||||
"github.com/photoprism/photoprism/internal/remote/webdav"
|
||||
)
|
||||
|
||||
// Sync represents a sync worker.
|
||||
|
@ -43,7 +43,7 @@ func (s *Sync) Start() (err error) {
|
|||
accounts, err := q.Accounts(f)
|
||||
|
||||
for _, a := range accounts {
|
||||
if a.AccType != service.TypeWebDAV {
|
||||
if a.AccType != remote.ServiceWebDAV {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ func (s *Sync) Start() (err error) {
|
|||
}
|
||||
|
||||
func (s *Sync) getRemoteFiles(a entity.Account) (complete bool, err error) {
|
||||
if a.AccType != service.TypeWebDAV {
|
||||
if a.AccType != remote.ServiceWebDAV {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
@ -221,5 +221,6 @@ func (s *Sync) download(a entity.Account) (complete bool, err error) {
|
|||
}
|
||||
|
||||
func (s *Sync) upload(a entity.Account) (complete bool, err error) {
|
||||
// TODO: Not implemented yet
|
||||
return false, nil
|
||||
}
|
||||
|
|
176
internal/remote/service.go
Normal file
176
internal/remote/service.go
Normal file
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
Package service implements a remote service abstraction.
|
||||
|
||||
Additional information can be found in our Developer Guide:
|
||||
|
||||
https://github.com/photoprism/photoprism/wiki
|
||||
*/
|
||||
package remote
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
var client = &http.Client{}
|
||||
|
||||
const (
|
||||
ServiceWeb = "web"
|
||||
ServiceWebDAV = "webdav"
|
||||
ServiceFacebook = "facebook"
|
||||
ServiceTwitter = "twitter"
|
||||
ServiceFlickr = "flickr"
|
||||
ServiceInstagram = "instagram"
|
||||
ServiceEyeEm = "eyeem"
|
||||
ServiceTelegram = "telegram"
|
||||
ServiceWhatsApp = "whatsapp"
|
||||
ServiceGPhotos = "gphotos"
|
||||
ServiceGDrive = "gdrive"
|
||||
ServiceOneDrive = "onedrive"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
AccName string
|
||||
AccURL string
|
||||
AccType string
|
||||
AccKey string
|
||||
AccUser string
|
||||
AccPass string
|
||||
}
|
||||
|
||||
type Heuristic struct {
|
||||
ServiceType string
|
||||
Domains []string
|
||||
Paths []string
|
||||
Method string
|
||||
}
|
||||
|
||||
var Heuristics = []Heuristic{
|
||||
{ServiceFacebook, []string{"facebook.com", "www.facebook.com"}, []string{}, "GET"},
|
||||
{ServiceTwitter, []string{"twitter.com"}, []string{}, "GET"},
|
||||
{ServiceFlickr, []string{"flickr.com", "www.flickr.com"}, []string{}, "GET"},
|
||||
{ServiceInstagram, []string{"instagram.com", "www.instagram.com"}, []string{}, "GET"},
|
||||
{ServiceEyeEm, []string{"eyeem.com", "www.eyeem.com"}, []string{}, "GET"},
|
||||
{ServiceTelegram, []string{"web.telegram.org", "www.telegram.org", "telegram.org"}, []string{}, "GET"},
|
||||
{ServiceWhatsApp, []string{"web.whatsapp.com", "www.whatsapp.com", "whatsapp.com"}, []string{}, "GET"},
|
||||
{ServiceOneDrive, []string{"onedrive.live.com"}, []string{}, "GET"},
|
||||
{ServiceGDrive, []string{"drive.google.com"}, []string{}, "GET"},
|
||||
{ServiceGPhotos, []string{"photos.google.com"}, []string{}, "GET"},
|
||||
{ServiceWebDAV, []string{}, []string{"/", "/webdav", "/remote.php/dav/files/{user}", "/remote.php/webdav", "/dav/files/{user}", "/servlet/webdav.infostore/"}, "PROPFIND"},
|
||||
{ServiceWeb, []string{}, []string{}, "GET"},
|
||||
}
|
||||
|
||||
func HttpOk(method, rawUrl string) bool {
|
||||
req, err := http.NewRequest(method, rawUrl, nil)
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
return false
|
||||
} else if resp.StatusCode < 400 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (h Heuristic) MatchDomain(match string) bool {
|
||||
if len(h.Domains) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, m := range h.Domains {
|
||||
if m == match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (h Heuristic) Discover(rawUrl, user string) *url.URL {
|
||||
u, err := url.Parse(rawUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if HttpOk(h.Method, u.String()) {
|
||||
return u
|
||||
}
|
||||
|
||||
for _, p := range h.Paths {
|
||||
strings.Replace(p, "{user}", user, -1)
|
||||
u.Path = p
|
||||
|
||||
if HttpOk(h.Method, u.String()) {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Discover(rawUrl, user, pass string) (result Account, err error) {
|
||||
if rawUrl == "" {
|
||||
return result, errors.New("service URL is empty")
|
||||
}
|
||||
|
||||
u, err := url.Parse(rawUrl)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
u.Host = strings.ToLower(u.Host)
|
||||
|
||||
result.AccUser = u.User.Username()
|
||||
result.AccPass, _ = u.User.Password()
|
||||
|
||||
// Extract user info
|
||||
if user != "" {
|
||||
result.AccUser = user
|
||||
}
|
||||
|
||||
if pass != "" {
|
||||
result.AccPass = pass
|
||||
}
|
||||
|
||||
if user != "" || pass != "" {
|
||||
u.User = url.UserPassword(result.AccUser, result.AccPass)
|
||||
}
|
||||
|
||||
// Set default scheme
|
||||
if u.Scheme == "" {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
|
||||
for _, h := range Heuristics {
|
||||
if !h.MatchDomain(u.Host) {
|
||||
continue
|
||||
}
|
||||
|
||||
if serviceUrl := h.Discover(u.String(), result.AccUser); serviceUrl != nil {
|
||||
serviceUrl.User = nil
|
||||
|
||||
if w := txt.Keywords(serviceUrl.Host); len(w) > 0 {
|
||||
result.AccName = strings.Title(w[0])
|
||||
} else {
|
||||
result.AccName = serviceUrl.Host
|
||||
}
|
||||
|
||||
result.AccType = h.ServiceType
|
||||
result.AccURL = serviceUrl.String()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
return result, errors.New("could not connect")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package service
|
||||
package remote
|
||||
|
||||
import (
|
||||
"testing"
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
19
internal/service/classify.go
Normal file
19
internal/service/classify.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
)
|
||||
|
||||
var onceClassify sync.Once
|
||||
|
||||
func initClassify() {
|
||||
services.Classify = classify.New(Config().ResourcesPath(), Config().TensorFlowDisabled())
|
||||
}
|
||||
|
||||
func Classify() *classify.TensorFlow {
|
||||
onceClassify.Do(initClassify)
|
||||
|
||||
return services.Classify
|
||||
}
|
19
internal/service/convert.go
Normal file
19
internal/service/convert.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
)
|
||||
|
||||
var onceConvert sync.Once
|
||||
|
||||
func initConvert() {
|
||||
services.Convert = photoprism.NewConvert(Config())
|
||||
}
|
||||
|
||||
func Convert() *photoprism.Convert {
|
||||
onceConvert.Do(initConvert)
|
||||
|
||||
return services.Convert
|
||||
}
|
19
internal/service/import.go
Normal file
19
internal/service/import.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
)
|
||||
|
||||
var onceImport sync.Once
|
||||
|
||||
func initImport() {
|
||||
services.Import = photoprism.NewImport(Config(), Index(), Convert())
|
||||
}
|
||||
|
||||
func Import() *photoprism.Import {
|
||||
onceImport.Do(initImport)
|
||||
|
||||
return services.Import
|
||||
}
|
19
internal/service/index.go
Normal file
19
internal/service/index.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
)
|
||||
|
||||
var onceIndex sync.Once
|
||||
|
||||
func initIndex() {
|
||||
services.Index = photoprism.NewIndex(Config(), Classify(), NsfwDetector())
|
||||
}
|
||||
|
||||
func Index() *photoprism.Index {
|
||||
onceIndex.Do(initIndex)
|
||||
|
||||
return services.Index
|
||||
}
|
19
internal/service/nsfw.go
Normal file
19
internal/service/nsfw.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
)
|
||||
|
||||
var onceNsfwDetector sync.Once
|
||||
|
||||
func initNsfwDetector() {
|
||||
services.Nsfw = nsfw.New(conf.NSFWModelPath())
|
||||
}
|
||||
|
||||
func NsfwDetector() *nsfw.Detector {
|
||||
onceNsfwDetector.Do(initNsfwDetector)
|
||||
|
||||
return services.Nsfw
|
||||
}
|
19
internal/service/resample.go
Normal file
19
internal/service/resample.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
)
|
||||
|
||||
var onceResample sync.Once
|
||||
|
||||
func initResample() {
|
||||
services.Resample = photoprism.NewResample(Config())
|
||||
}
|
||||
|
||||
func Resample() *photoprism.Resample {
|
||||
onceResample.Do(initResample)
|
||||
|
||||
return services.Resample
|
||||
}
|
|
@ -1,178 +1,35 @@
|
|||
/*
|
||||
Package service implements a remote service abstraction.
|
||||
|
||||
Additional information can be found in our Developer Guide:
|
||||
|
||||
https://github.com/photoprism/photoprism/wiki
|
||||
*/
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
"github.com/photoprism/photoprism/internal/classify"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
var client = &http.Client{}
|
||||
var conf *config.Config
|
||||
|
||||
const (
|
||||
TypeWeb = "web"
|
||||
TypeWebDAV = "webdav"
|
||||
TypeFacebook = "facebook"
|
||||
TypeTwitter = "twitter"
|
||||
TypeFlickr = "flickr"
|
||||
TypeInstagram = "instagram"
|
||||
TypeEyeEm = "eyeem"
|
||||
TypeTelegram = "telegram"
|
||||
TypeWhatsApp = "whatsapp"
|
||||
TypeGooglePhotos = "gphotos"
|
||||
TypeGoogleDrive = "gdrive"
|
||||
TypeOneDrive = "onedrive"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
AccName string
|
||||
AccURL string
|
||||
AccType string
|
||||
AccKey string
|
||||
AccUser string
|
||||
AccPass string
|
||||
var services struct {
|
||||
Import *photoprism.Import
|
||||
Index *photoprism.Index
|
||||
Nsfw *nsfw.Detector
|
||||
Convert *photoprism.Convert
|
||||
Resample *photoprism.Resample
|
||||
Classify *classify.TensorFlow
|
||||
}
|
||||
|
||||
type Heuristic struct {
|
||||
ServiceType string
|
||||
Domains []string
|
||||
Paths []string
|
||||
Method string
|
||||
func SetConfig(c *config.Config) {
|
||||
if c == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
conf = c
|
||||
}
|
||||
|
||||
var Heuristics = []Heuristic{
|
||||
{TypeFacebook, []string{"facebook.com", "www.facebook.com"}, []string{}, "GET"},
|
||||
{TypeTwitter, []string{"twitter.com"}, []string{}, "GET"},
|
||||
{TypeFlickr, []string{"flickr.com", "www.flickr.com"}, []string{}, "GET"},
|
||||
{TypeInstagram, []string{"instagram.com", "www.instagram.com"}, []string{}, "GET"},
|
||||
{TypeEyeEm, []string{"eyeem.com", "www.eyeem.com"}, []string{}, "GET"},
|
||||
{TypeTelegram, []string{"web.telegram.org", "www.telegram.org", "telegram.org"}, []string{}, "GET"},
|
||||
{TypeWhatsApp, []string{"web.whatsapp.com", "www.whatsapp.com", "whatsapp.com"}, []string{}, "GET"},
|
||||
{TypeOneDrive, []string{"onedrive.live.com"}, []string{}, "GET"},
|
||||
{TypeGoogleDrive, []string{"drive.google.com"}, []string{}, "GET"},
|
||||
{TypeGooglePhotos, []string{"photos.google.com"}, []string{}, "GET"},
|
||||
{TypeWebDAV, []string{}, []string{"/", "/webdav", "/remote.php/dav/files/{user}", "/remote.php/webdav", "/dav/files/{user}", "/servlet/webdav.infostore/"}, "PROPFIND"},
|
||||
{TypeWeb, []string{}, []string{}, "GET"},
|
||||
}
|
||||
|
||||
func HttpOk(method, rawUrl string) bool {
|
||||
req, err := http.NewRequest(method, rawUrl, nil)
|
||||
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
return false
|
||||
} else if resp.StatusCode < 400 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (h Heuristic) MatchDomain(match string) bool {
|
||||
if len(h.Domains) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, m := range h.Domains {
|
||||
if m == match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (h Heuristic) Discover(rawUrl, user string) *url.URL {
|
||||
u, err := url.Parse(rawUrl)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if HttpOk(h.Method, u.String()) {
|
||||
return u
|
||||
}
|
||||
|
||||
for _, p := range h.Paths {
|
||||
strings.Replace(p, "{user}", user, -1)
|
||||
u.Path = p
|
||||
|
||||
if HttpOk(h.Method, u.String()) {
|
||||
return u
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Discover(rawUrl, user, pass string) (result Account, err error) {
|
||||
if rawUrl == "" {
|
||||
return result, errors.New("service URL is empty")
|
||||
}
|
||||
|
||||
u, err := url.Parse(rawUrl)
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
u.Host = strings.ToLower(u.Host)
|
||||
|
||||
result.AccUser = u.User.Username()
|
||||
result.AccPass, _ = u.User.Password()
|
||||
|
||||
// Extract user info
|
||||
if user != "" {
|
||||
result.AccUser = user
|
||||
}
|
||||
|
||||
if pass != "" {
|
||||
result.AccPass = pass
|
||||
}
|
||||
|
||||
if user != "" || pass != "" {
|
||||
u.User = url.UserPassword(result.AccUser, result.AccPass)
|
||||
}
|
||||
|
||||
// Set default scheme
|
||||
if u.Scheme == "" {
|
||||
u.Scheme = "https"
|
||||
}
|
||||
|
||||
for _, h := range Heuristics {
|
||||
if !h.MatchDomain(u.Host) {
|
||||
continue
|
||||
}
|
||||
|
||||
if serviceUrl := h.Discover(u.String(), result.AccUser); serviceUrl != nil {
|
||||
serviceUrl.User = nil
|
||||
|
||||
if w := txt.Keywords(serviceUrl.Host); len(w) > 0 {
|
||||
result.AccName = strings.Title(w[0])
|
||||
} else {
|
||||
result.AccName = serviceUrl.Host
|
||||
}
|
||||
|
||||
result.AccType = h.ServiceType
|
||||
result.AccURL = serviceUrl.String()
|
||||
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
return result, errors.New("could not connect")
|
||||
func Config() *config.Config {
|
||||
if conf == nil {
|
||||
panic("config is nil")
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue