Adding docs and ready for 1.0.0

Signed-off-by: Kris Nóva <kris@nivenly.com>
This commit is contained in:
Kris Nóva 2021-02-11 18:04:53 -08:00
parent c78e3d199e
commit 94dde03103
63 changed files with 394 additions and 5616 deletions

40
api/v1/README.md Normal file
View file

@ -0,0 +1,40 @@
# V1 SDK
This is the main SDK code. Here be dragons.
The V1 Client ships with the following HTTP methods
- GET
- POST
- PUT
- DELETE
The client can also be chained using the following two chain methods.
#### JSON Marshal
To send a GET request to `/api/v1/photos/:uuid` and marshal the results on to a Photo struct
```go
uuid := "123"
photo := Photo{
UUID: uuid,
}
err := v1.GET("/api/v1/photos/%s", uuid).JSON(&object)
//
fmt.Println(err)
fmt.Println(photo)
```
#### String
Sometimes it is helpful to just see what the Photoprism API returns.
The `String()` method implements the Go idiomatic `String()` and will
return the body of the response for debugging.
To send a GET request to `/api/v1/photos/:uuid` and see the raw JSON output
```go
uuid := "123"
fmt.Println(v1.GET("/api/v1/photos/%s", uuid).String())
```

View file

@ -2,6 +2,9 @@ package api
import "fmt"
// AlbumOptions are the parameters passed to get
// albums by various fields. Populate these as needed
// to pass to the SDK
type AlbumOptions struct {
ParamType string
Q string
@ -18,6 +21,8 @@ const (
DefaultAlbumOptionsCategory = ""
)
// GetAlbums is used to list albums by query fields.
//
// GET /api/v1/albums
//
// Example Params: http://localhost:8080/api/v1/albums?count=24&offset=0&q=&category=&type=album
@ -28,11 +33,11 @@ func (v1 *V1Client) GetAlbums(options *AlbumOptions) ([]Album, error) {
// Default to sane options for query
options = &AlbumOptions{
ParamType: "album",
Q: "",
Count: 24,
Offset: 0,
Category: "",
ParamType: DefaultAlbumOptionsParamType,
Q: DefaultAlbumOptionsQ,
Count: DefaultAlbumOptionsCount,
Offset: DefaultAlbumOptionsOffset,
Category: DefaultAlbumOptionsCategory,
}
}
@ -50,6 +55,8 @@ func (v1 *V1Client) GetAlbums(options *AlbumOptions) ([]Album, error) {
return albums, err
}
// GetAlbum is used to get an album by an UUID.
//
// GET /api/v1/albums/:uuid
func (v1 *V1Client) GetAlbum(uuid string) (Album, error) {
album := Album{}
@ -66,32 +73,38 @@ func (v1 *V1Client) GetAlbum(uuid string) (Album, error) {
// such that an empty Album{} object will still
// create a new album.
//
//POST /api/v1/albums
func (v1 *V1Client) CreateAlbum(object Album) (Album, error) {
err := v1.POST(&object, "/api/v1/albums").JSON(&object)
return object, err
// POST /api/v1/albums
func (v1 *V1Client) CreateAlbum(album Album) (Album, error) {
err := v1.POST(&album, "/api/v1/albums").JSON(&album)
return album, err
}
// UpdateAlbum will update meta information about an album.
//
// PUT /api/v1/albums/:uid
func (v1 *V1Client) UpdateAlbum(object Album) (Album, error) {
if object.AlbumUID == "" {
return object, fmt.Errorf("missing album.AlbumUID in album")
func (v1 *V1Client) UpdateAlbum(album Album) (Album, error) {
if album.AlbumUID == "" {
return album, fmt.Errorf("missing album.AlbumUID in album")
}
err := v1.PUT(&object, "/api/v1/albums/%s", object.AlbumUID).JSON(&object)
return object, err
err := v1.PUT(&album, "/api/v1/albums/%s", album.AlbumUID).JSON(&album)
return album, err
}
// DeleteAlbums will batch delete a set of albums by ID.
//
// POST /api/v1/batch/albums/delete
func (v1 *V1Client) DeleteAlbums(uuids []string) error {
func (v1 *V1Client) DeleteAlbums(albumUUIDs []string) error {
payload := struct {
Albums []string `json:"albums"`
}{
Albums: uuids,
Albums: albumUUIDs,
}
resp := v1.POST(payload, "/api/v1/batch/albums/delete")
return resp.Error
}
// LikeAlbum can be used to like an album.
//
// POST /api/v1/albums/:uid/like
//
// Parameters:
@ -101,6 +114,8 @@ func (v1 *V1Client) LikeAlbum(uuid string) error {
return resp.Error
}
// DislikeAlbum can be used to dislike an album.
//
// DELETE /api/v1/albums/:uid/like
//
// Parameters:
@ -110,16 +125,21 @@ func (v1 *V1Client) DislikeAlbum(uuid string) error {
return resp.Error
}
// CloneAlbum can be used to clone an album and will
// return the newly cloned album on success.
//
// POST /api/v1/albums/:uid/clone
func (v1 *V1Client) CloneAlbum(object Album) (Album, error) {
if object.AlbumUID == "" {
return object, fmt.Errorf("missing album.AlbumUID in album")
func (v1 *V1Client) CloneAlbum(album Album) (Album, error) {
if album.AlbumUID == "" {
return album, fmt.Errorf("missing album.AlbumUID in album")
}
newAlbum := Album{}
err := v1.POST(&object, "/api/v1/albums/%s/clone", object.AlbumUID).JSON(&newAlbum)
err := v1.POST(&album, "/api/v1/albums/%s/clone", album.AlbumUID).JSON(&newAlbum)
return newAlbum, err
}
// AddPhotosToAlbum will associate a set of photos by UUID with an album by UUID
//
// POST /api/v1/albums/:uid/photos
func (v1 *V1Client) AddPhotosToAlbum(albumUUID string, photoIDs []string) error {
payload := struct {
@ -131,6 +151,8 @@ func (v1 *V1Client) AddPhotosToAlbum(albumUUID string, photoIDs []string) error
return resp.Error
}
// DeletePhotosFromAlbum will disassociate a set of photos by UUID from an album by UUID
//
// DELETE /api/v1/albums/:uid/photos
func (v1 *V1Client) DeletePhotosFromAlbum(albumUUID string, photoIDs []string) error {
payload := struct {
@ -142,6 +164,9 @@ func (v1 *V1Client) DeletePhotosFromAlbum(albumUUID string, photoIDs []string) e
return resp.Error
}
// GetAlbumDownload will return a .zip file of the album's content
// and can be used to download an album from the API.
//
// GET /api/v1/albums/:uid/dl
func (v1 *V1Client) GetAlbumDownload(uuid string) ([]byte, error) {
// NOTE: Even though this method is singular GetAlbum

View file

@ -17,6 +17,7 @@ const (
DefaultContentType string = "application/json; charset=utf-8"
)
// V1Client is used to access the V1 Photoprism API.
type V1Client struct {
downloadToken string
token string
@ -25,7 +26,7 @@ type V1Client struct {
}
// New will only accept a url.URL so that we know
// all errors have been handled up until this point
// all errors have been handled up until this point.
func New(connURL *url.URL, token, downloadToken string) *V1Client {
return &V1Client{
client: http.Client{},
@ -35,6 +36,8 @@ func New(connURL *url.URL, token, downloadToken string) *V1Client {
}
}
// V1Response is the master HTTP Response object
// for all transactions with the Photoprism API.
type V1Response struct {
HTTPResponse *http.Response
StatusCode int

View file

@ -1,11 +1,17 @@
package api
// Index is used to sync the backend storage with
// the database meta information
//
// POST /api/v1/index
func (v1 *V1Client) Index() error {
resp := v1.POST(nil, "/api/v1/index")
return resp.Error
}
// CancelIndex can be used to attempt to cancel a running index
// operation
//
// DELETE /api/v1/index
func (v1 *V1Client) CancelIndex() error {
resp := v1.DELETE(nil, "/api/v1/index")

View file

@ -1,21 +0,0 @@
package api
import "net/http"
type Meta struct {
requested bool
response *http.Response
}
// TODO We need an http.Client and an object interface{}
func (m *Meta) Request() error {
m.requested = true
return nil
}
func (m *Meta) Response() *http.Response {
if !m.requested {
return nil
}
return m.response
}

View file

@ -1,17 +1,22 @@
package api
// GET /api/v1/photos/:uuid
// GetPhoto can be used to get a photo by UUID
//
//GET /api/v1/photos/:uuid
//
// Parameters:
// uuid: string PhotoUID as returned by the API
func (v1 *V1Client) GetPhoto(uuid string) (Photo, error) {
object := Photo{
photo := Photo{
UUID: uuid,
}
err := v1.GET("/api/v1/photos/%s", uuid).JSON(&object)
return object, err
err := v1.GET("/api/v1/photos/%s", uuid).JSON(&photo)
return photo, err
}
// PhotoOptions is used while listing photos. These
// fields can be optionally set to query for specific
// photos.
type PhotoOptions struct {
Count int
Offset int
@ -62,9 +67,9 @@ func (v1 *V1Client) GetPhotos(options *PhotoOptions) ([]Photo, error) {
//
// Parameters:
// uuid: string PhotoUUID as returned by the API
func (v1 *V1Client) UpdatePhoto(object Photo) (Photo, error) {
err := v1.PUT(&object, "/api/v1/photos/%s", object.UUID).JSON(&object)
return object, err
func (v1 *V1Client) UpdatePhoto(photo Photo) (Photo, error) {
err := v1.PUT(&photo, "/api/v1/photos/%s", photo.UUID).JSON(&photo)
return photo, err
}
// GET /api/v1/photos/:uuid/dl

View file

@ -9,64 +9,62 @@ type Photos []Photo
// Photo represents a photo, all its properties, and link to all its images and sidecar files.
type Photo struct {
Meta
//ID uint `gorm:"primary_key" yaml:"-"`
UUID string `gorm:"type:VARBINARY(42);index;" json:"DocumentID,omitempty" yaml:"DocumentID,omitempty"`
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uid;" json:"TakenAt" yaml:"TakenAt"`
TakenAtLocal time.Time `gorm:"type:datetime;" yaml:"-"`
TakenSrc string `gorm:"type:VARBINARY(8);" json:"TakenSrc" yaml:"TakenSrc,omitempty"`
PhotoUID string `gorm:"type:VARBINARY(42);unique_index;index:idx_photos_taken_uid;" json:"UID" yaml:"UID"`
PhotoType string `gorm:"type:VARBINARY(8);default:'image';" json:"Type" yaml:"Type"`
TypeSrc string `gorm:"type:VARBINARY(8);" json:"TypeSrc" yaml:"TypeSrc,omitempty"`
PhotoTitle string `gorm:"type:VARCHAR(255);" json:"Title" yaml:"Title"`
TitleSrc string `gorm:"type:VARBINARY(8);" json:"TitleSrc" yaml:"TitleSrc,omitempty"`
PhotoDescription string `gorm:"type:TEXT;" json:"Description" yaml:"Description,omitempty"`
DescriptionSrc string `gorm:"type:VARBINARY(8);" json:"DescriptionSrc" yaml:"DescriptionSrc,omitempty"`
PhotoPath string `gorm:"type:VARBINARY(500);index:idx_photos_path_name;" json:"Path" yaml:"-"`
PhotoName string `gorm:"type:VARBINARY(255);index:idx_photos_path_name;" json:"Name" yaml:"-"`
OriginalName string `gorm:"type:VARBINARY(755);" json:"OriginalName" yaml:"OriginalName,omitempty"`
PhotoStack int8 `json:"Stack" yaml:"Stack,omitempty"`
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`
PhotoScan bool `json:"Scan" yaml:"Scan,omitempty"`
PhotoPanorama bool `json:"Panorama" yaml:"Panorama,omitempty"`
TimeZone string `gorm:"type:VARBINARY(64);" json:"TimeZone" yaml:"-"`
PlaceID string `gorm:"type:VARBINARY(42);index;default:'zz'" json:"PlaceID" yaml:"-"`
PlaceSrc string `gorm:"type:VARBINARY(8);" json:"PlaceSrc" yaml:"PlaceSrc,omitempty"`
CellID string `gorm:"type:VARBINARY(42);index;default:'zz'" json:"CellID" yaml:"-"`
CellAccuracy int `json:"CellAccuracy" yaml:"CellAccuracy,omitempty"`
PhotoAltitude int `json:"Altitude" yaml:"Altitude,omitempty"`
PhotoLat float32 `gorm:"type:FLOAT;index;" json:"Lat" yaml:"Lat,omitempty"`
PhotoLng float32 `gorm:"type:FLOAT;index;" json:"Lng" yaml:"Lng,omitempty"`
PhotoCountry string `gorm:"type:VARBINARY(2);index:idx_photos_country_year_month;default:'zz'" json:"Country" yaml:"-"`
PhotoYear int `gorm:"index:idx_photos_country_year_month;" json:"Year" yaml:"Year"`
PhotoMonth int `gorm:"index:idx_photos_country_year_month;" json:"Month" yaml:"Month"`
PhotoDay int `json:"Day" yaml:"Day"`
PhotoIso int `json:"Iso" yaml:"ISO,omitempty"`
PhotoExposure string `gorm:"type:VARBINARY(64);" json:"Exposure" yaml:"Exposure,omitempty"`
PhotoFNumber float32 `gorm:"type:FLOAT;" json:"FNumber" yaml:"FNumber,omitempty"`
PhotoFocalLength int `json:"FocalLength" yaml:"FocalLength,omitempty"`
PhotoQuality int `gorm:"type:SMALLINT" json:"Quality" yaml:"-"`
PhotoResolution int `gorm:"type:SMALLINT" json:"Resolution" yaml:"-"`
PhotoColor uint8 `json:"Color" yaml:"-"`
CameraID uint `gorm:"index:idx_photos_camera_lens;default:1" json:"CameraID" yaml:"-"`
CameraSerial string `gorm:"type:VARBINARY(255);" json:"CameraSerial" yaml:"CameraSerial,omitempty"`
CameraSrc string `gorm:"type:VARBINARY(8);" json:"CameraSrc" yaml:"-"`
LensID uint `gorm:"index:idx_photos_camera_lens;default:1" json:"LensID" yaml:"-"`
Details *Details `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Details" yaml:"Details"`
Camera *Camera `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Camera" yaml:"-"`
Lens *Lens `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Lens" yaml:"-"`
Cell *Cell `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Cell" yaml:"-"`
Place *Place `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Place" yaml:"-"`
Keywords []Keyword `json:"-" yaml:"-"`
Albums []Album `json:"-" yaml:"-"`
Files []File `yaml:"-"`
//Labels []PhotoLabel `yaml:"-"`
CreatedAt time.Time `yaml:"CreatedAt,omitempty"`
UpdatedAt time.Time `yaml:"UpdatedAt,omitempty"`
EditedAt *time.Time `yaml:"EditedAt,omitempty"`
CheckedAt *time.Time `sql:"index" yaml:"-"`
DeletedAt *time.Time `sql:"index" yaml:"DeletedAt,omitempty"`
UUID string `gorm:"type:VARBINARY(42);index;" json:"DocumentID,omitempty" yaml:"DocumentID,omitempty"`
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uid;" json:"TakenAt" yaml:"TakenAt"`
TakenAtLocal time.Time `gorm:"type:datetime;" yaml:"-"`
TakenSrc string `gorm:"type:VARBINARY(8);" json:"TakenSrc" yaml:"TakenSrc,omitempty"`
PhotoUID string `gorm:"type:VARBINARY(42);unique_index;index:idx_photos_taken_uid;" json:"UID" yaml:"UID"`
PhotoType string `gorm:"type:VARBINARY(8);default:'image';" json:"Type" yaml:"Type"`
TypeSrc string `gorm:"type:VARBINARY(8);" json:"TypeSrc" yaml:"TypeSrc,omitempty"`
PhotoTitle string `gorm:"type:VARCHAR(255);" json:"Title" yaml:"Title"`
TitleSrc string `gorm:"type:VARBINARY(8);" json:"TitleSrc" yaml:"TitleSrc,omitempty"`
PhotoDescription string `gorm:"type:TEXT;" json:"Description" yaml:"Description,omitempty"`
DescriptionSrc string `gorm:"type:VARBINARY(8);" json:"DescriptionSrc" yaml:"DescriptionSrc,omitempty"`
PhotoPath string `gorm:"type:VARBINARY(500);index:idx_photos_path_name;" json:"Path" yaml:"-"`
PhotoName string `gorm:"type:VARBINARY(255);index:idx_photos_path_name;" json:"Name" yaml:"-"`
OriginalName string `gorm:"type:VARBINARY(755);" json:"OriginalName" yaml:"OriginalName,omitempty"`
PhotoStack int8 `json:"Stack" yaml:"Stack,omitempty"`
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`
PhotoScan bool `json:"Scan" yaml:"Scan,omitempty"`
PhotoPanorama bool `json:"Panorama" yaml:"Panorama,omitempty"`
TimeZone string `gorm:"type:VARBINARY(64);" json:"TimeZone" yaml:"-"`
PlaceID string `gorm:"type:VARBINARY(42);index;default:'zz'" json:"PlaceID" yaml:"-"`
PlaceSrc string `gorm:"type:VARBINARY(8);" json:"PlaceSrc" yaml:"PlaceSrc,omitempty"`
CellID string `gorm:"type:VARBINARY(42);index;default:'zz'" json:"CellID" yaml:"-"`
CellAccuracy int `json:"CellAccuracy" yaml:"CellAccuracy,omitempty"`
PhotoAltitude int `json:"Altitude" yaml:"Altitude,omitempty"`
PhotoLat float32 `gorm:"type:FLOAT;index;" json:"Lat" yaml:"Lat,omitempty"`
PhotoLng float32 `gorm:"type:FLOAT;index;" json:"Lng" yaml:"Lng,omitempty"`
PhotoCountry string `gorm:"type:VARBINARY(2);index:idx_photos_country_year_month;default:'zz'" json:"Country" yaml:"-"`
PhotoYear int `gorm:"index:idx_photos_country_year_month;" json:"Year" yaml:"Year"`
PhotoMonth int `gorm:"index:idx_photos_country_year_month;" json:"Month" yaml:"Month"`
PhotoDay int `json:"Day" yaml:"Day"`
PhotoIso int `json:"Iso" yaml:"ISO,omitempty"`
PhotoExposure string `gorm:"type:VARBINARY(64);" json:"Exposure" yaml:"Exposure,omitempty"`
PhotoFNumber float32 `gorm:"type:FLOAT;" json:"FNumber" yaml:"FNumber,omitempty"`
PhotoFocalLength int `json:"FocalLength" yaml:"FocalLength,omitempty"`
PhotoQuality int `gorm:"type:SMALLINT" json:"Quality" yaml:"-"`
PhotoResolution int `gorm:"type:SMALLINT" json:"Resolution" yaml:"-"`
PhotoColor uint8 `json:"Color" yaml:"-"`
CameraID uint `gorm:"index:idx_photos_camera_lens;default:1" json:"CameraID" yaml:"-"`
CameraSerial string `gorm:"type:VARBINARY(255);" json:"CameraSerial" yaml:"CameraSerial,omitempty"`
CameraSrc string `gorm:"type:VARBINARY(8);" json:"CameraSrc" yaml:"-"`
LensID uint `gorm:"index:idx_photos_camera_lens;default:1" json:"LensID" yaml:"-"`
Details *Details `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Details" yaml:"Details"`
Camera *Camera `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Camera" yaml:"-"`
Lens *Lens `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Lens" yaml:"-"`
Cell *Cell `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Cell" yaml:"-"`
Place *Place `gorm:"association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Place" yaml:"-"`
Keywords []Keyword `json:"-" yaml:"-"`
Albums []Album `json:"-" yaml:"-"`
Files []File `yaml:"-"`
Labels []PhotoLabel `yaml:"-"`
CreatedAt time.Time `yaml:"CreatedAt,omitempty"`
UpdatedAt time.Time `yaml:"UpdatedAt,omitempty"`
EditedAt *time.Time `yaml:"EditedAt,omitempty"`
CheckedAt *time.Time `sql:"index" yaml:"-"`
DeletedAt *time.Time `sql:"index" yaml:"DeletedAt,omitempty"`
}
// Details stores additional metadata fields for each photo to improve search performance.
@ -195,6 +193,8 @@ type PhotoAlbum struct {
Album *Album `gorm:"PRELOAD:true" yaml:"-"`
}
type Files []File
// File represents an image or sidecar file that belongs to a photo.
type File struct {
ID uint `gorm:"primary_key" json:"-" yaml:"-"`
@ -298,8 +298,7 @@ type Label struct {
New bool `gorm:"-" json:"-" yaml:"-"`
}
type Files []File
// FileInfos represents meta data about a file
type FileInfos struct {
FileWidth int
FileHeight int