Refactored piwigo context to a object oriented implementation

Optimized http calls to reuse more code and DRY
Interface for PiwigoContext WIP
Naming WIP
This commit is contained in:
Philipp Häfelfinger 2019-03-20 00:14:10 +01:00
parent f3f864ec49
commit 8068d829fe
11 changed files with 351 additions and 414 deletions

View File

@ -3,7 +3,6 @@ package app
import ( import (
"flag" "flag"
"git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/pkg/localFileStructure" "git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/pkg/localFileStructure"
"git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/pkg/piwigo"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"os" "os"
) )
@ -23,7 +22,7 @@ func Run() {
logErrorAndExit(err, 1) logErrorAndExit(err, 1)
} }
err = context.piwigo.LoginToPiwigoAndConfigureContext() err = context.piwigo.Login()
if err != nil { if err != nil {
logErrorAndExit(err, 2) logErrorAndExit(err, 2)
} }
@ -58,7 +57,7 @@ func Run() {
// logErrorAndExit(err, 8) // logErrorAndExit(err, 8)
//} //}
_ = piwigo.Logout(context.piwigo) _ = context.piwigo.Logout()
} }
func logErrorAndExit(err error, exitCode int) { func logErrorAndExit(err error, exitCode int) {

View File

@ -12,7 +12,7 @@ import (
func getAllCategoriesFromServer(context *appContext) (map[string]*piwigo.PiwigoCategory, error) { func getAllCategoriesFromServer(context *appContext) (map[string]*piwigo.PiwigoCategory, error) {
logrus.Debugln("Starting GetAllCategories") logrus.Debugln("Starting GetAllCategories")
categories, err := piwigo.GetAllCategories(context.piwigo) categories, err := context.piwigo.GetAllCategories()
return categories, err return categories, err
} }
@ -66,7 +66,7 @@ func createMissingCategories(context *appContext, missingCategories []string, ex
} }
// create category on piwigo // create category on piwigo
id, err := piwigo.CreateCategory(context.piwigo, parentId, name) id, err := context.piwigo.CreateCategory(parentId, name)
if err != nil { if err != nil {
return errors.New(fmt.Sprintf("Could not create category on piwigo: %s", err)) return errors.New(fmt.Sprintf("Could not create category on piwigo: %s", err))
} }

View File

@ -29,7 +29,7 @@ func (img *ImageMetaData) String() string {
type ImageMetadataProvider interface { type ImageMetadataProvider interface {
ImageMetadata(relativePath string) (ImageMetaData, error) ImageMetadata(relativePath string) (ImageMetaData, error)
ImageMetadataToUpload() ([]*ImageMetaData, error) ImageMetadataToUpload() ([]ImageMetaData, error)
SaveImageMetadata(m ImageMetaData) error SaveImageMetadata(m ImageMetaData) error
SavePiwigoIdAndUpdateUploadFlag(md5Sum string, piwigoId int) error SavePiwigoIdAndUpdateUploadFlag(md5Sum string, piwigoId int) error
} }
@ -90,7 +90,7 @@ func (d *localDataStore) ImageMetadata(relativePath string) (ImageMetaData, erro
return img, err return img, err
} }
func (d *localDataStore) ImageMetadataToUpload() ([]*ImageMetaData, error) { func (d *localDataStore) ImageMetadataToUpload() ([]ImageMetaData, error) {
logrus.Tracef("Query all image metadata that represent files queued to upload") logrus.Tracef("Query all image metadata that represent files queued to upload")
db, err := d.openDatabase() db, err := d.openDatabase()
@ -105,14 +105,14 @@ func (d *localDataStore) ImageMetadataToUpload() ([]*ImageMetaData, error) {
} }
defer rows.Close() defer rows.Close()
images := []*ImageMetaData{} images := []ImageMetaData{}
for rows.Next() { for rows.Next() {
img := &ImageMetaData{} img := &ImageMetaData{}
err = ReadImageMetadataFromRow(rows, img) err = ReadImageMetadataFromRow(rows, img)
if err != nil { if err != nil {
return nil, err return nil, err
} }
images = append(images, img) images = append(images, *img)
} }
err = rows.Err() err = rows.Err()
@ -233,10 +233,10 @@ func (d *localDataStore) createTablesIfNeeded(db *sql.DB) error {
} }
func (d *localDataStore) updateImageMetaData(tx *sql.Tx, data ImageMetaData) error { func (d *localDataStore) updateImageMetaData(tx *sql.Tx, data ImageMetaData) error {
stmt, err := tx.Prepare("UPDATE image SET piwigoId = ?, relativePath = ?, fileName = ?, md5sum = ?, lastChanged = ?, categoryPath = ?, categoryId = ? WHERE imageId = ?") stmt, err := tx.Prepare("UPDATE image SET piwigoId = ?, relativePath = ?, fileName = ?, md5sum = ?, lastChanged = ?, categoryPath = ?, categoryId = ?, uploadRequired = ? WHERE imageId = ?")
if err != nil { if err != nil {
return err return err
} }
_, err = stmt.Exec(data.PiwigoId, data.RelativeImagePath, data.Filename, data.Md5Sum, data.LastChange, data.CategoryPath, data.CategoryId, data.ImageId) _, err = stmt.Exec(data.PiwigoId, data.RelativeImagePath, data.Filename, data.Md5Sum, data.LastChange, data.CategoryPath, data.CategoryId, data.UploadRequired, data.ImageId)
return err return err
} }

View File

@ -57,12 +57,12 @@ func TestSaveAndQueryForUploadRecords(t *testing.T) {
t.Fatalf("Could not query images to upload! %s", err) t.Fatalf("Could not query images to upload! %s", err)
} }
if len(images)<1 { if len(images) < 1 {
t.Fatal("Did not get any saved images to upload!") t.Fatal("Did not get any saved images to upload!")
} }
imgLoad := images[0] imgLoad := images[0]
EnsureMetadataAreEqual("toupload", img, *imgLoad, t) EnsureMetadataAreEqual("toupload", img, imgLoad, t)
cleanupDatabase(t) cleanupDatabase(t)
} }

View File

@ -90,7 +90,7 @@ func checkPiwigoForChangedImages(provider ImageMetadataProvider, piwigoCtx *piwi
if img.PiwigoId == 0 { if img.PiwigoId == 0 {
continue continue
} }
state, err := piwigo.ImageCheckFile(piwigoCtx, img.PiwigoId, img.Md5Sum) state, err := piwigoCtx.ImageCheckFile(img.PiwigoId, img.Md5Sum)
if err != nil { if err != nil {
logrus.Warnf("Error during file change check of file %s", img.RelativeImagePath) logrus.Warnf("Error during file change check of file %s", img.RelativeImagePath)
continue continue
@ -99,7 +99,7 @@ func checkPiwigoForChangedImages(provider ImageMetadataProvider, piwigoCtx *piwi
if state == piwigo.ImageStateUptodate { if state == piwigo.ImageStateUptodate {
logrus.Debugf("File %s - %d has not changed", img.RelativeImagePath, img.PiwigoId) logrus.Debugf("File %s - %d has not changed", img.RelativeImagePath, img.PiwigoId)
img.UploadRequired = false img.UploadRequired = false
err = provider.SaveImageMetadata(*img) err = provider.SaveImageMetadata(img)
if err != nil { if err != nil {
logrus.Warnf("Could not save image data of image %s", img.RelativeImagePath) logrus.Warnf("Could not save image data of image %s", img.RelativeImagePath)
} }
@ -111,7 +111,7 @@ func checkPiwigoForChangedImages(provider ImageMetadataProvider, piwigoCtx *piwi
// This function calls piwigo and checks if the given md5sum is already present. // This function calls piwigo and checks if the given md5sum is already present.
// Only files without a piwigo id are used to query the server. // Only files without a piwigo id are used to query the server.
func updatePiwigoIdIfAlreadyUploaded(provider ImageMetadataProvider, piwiCtx *piwigo.PiwigoContext) error { func updatePiwigoIdIfAlreadyUploaded(provider ImageMetadataProvider, piwigoCtx *piwigo.PiwigoContext) error {
logrus.Infof("checking for pending files that are already on piwigo and updating piwigoids...") logrus.Infof("checking for pending files that are already on piwigo and updating piwigoids...")
images, err := provider.ImageMetadataToUpload() images, err := provider.ImageMetadataToUpload()
if err != nil { if err != nil {
@ -125,7 +125,7 @@ func updatePiwigoIdIfAlreadyUploaded(provider ImageMetadataProvider, piwiCtx *pi
files = append(files, img.Md5Sum) files = append(files, img.Md5Sum)
} }
} }
missingResults, err := piwigo.ImagesExistOnPiwigo(piwiCtx, files) missingResults, err := piwigoCtx.ImagesExistOnPiwigo(files)
if err != nil { if err != nil {
return err return err
} }

View File

@ -171,7 +171,6 @@ func TestSynchronizePiwigoMetadata(t *testing.T) {
Filename: "abc.jpg", Filename: "abc.jpg",
} }
// execute the sync metadata based on the file system results // execute the sync metadata based on the file system results
//err := synchronizeLocalImageMetadata( db) //err := synchronizeLocalImageMetadata( db)
//if err != nil { //if err != nil {
@ -202,7 +201,7 @@ func (s *testStore) SaveImageMetadata(m ImageMetaData) error {
return nil return nil
} }
func (d *testStore) ImageMetadataToUpload() ([]*ImageMetaData, error) { func (d *testStore) ImageMetadataToUpload() ([]ImageMetaData, error) {
return nil, errors.New("N/A") return nil, errors.New("N/A")
} }

View File

@ -1,19 +1,18 @@
package piwigo package piwigo
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"net/http" "net/http"
"net/http/cookiejar" "net/http/cookiejar"
"net/url" "net/url"
"os"
"strconv"
"strings"
) )
type PiwigoFormPoster interface {
getChunkSizeInKB() int
postForm(formData url.Values) (resp *http.Response, err error)
}
type PiwigoContext struct { type PiwigoContext struct {
url string url string
username string username string
@ -47,29 +46,186 @@ func (context *PiwigoContext) Initialize(baseUrl string, username string, passwo
return nil return nil
} }
func (context *PiwigoContext) LoginToPiwigoAndConfigureContext() error { func (context *PiwigoContext) Login() error {
logrus.Infoln("Logging in to piwigo and getting chunk size configuration for uploads") logrus.Infoln("Logging in to piwigo and getting chunk size configuration for uploads")
err := Login(context) logrus.Debugf("Logging in to %s using user %s", context.url, context.username)
if !strings.HasPrefix(context.url, "https") {
logrus.Warnf("The server url %s does not use https! Credentials are not encrypted!", context.url)
}
formData := url.Values{}
formData.Set("method", "pwg.session.login")
formData.Set("username", context.username)
formData.Set("password", context.password)
var loginResponse loginResponse
err := context.executePiwigoRequest(formData, &loginResponse)
if err != nil { if err != nil {
errorMessage := fmt.Sprintf("Login failed: %d - %s", loginResponse.ErrorNumber, loginResponse.Message)
logrus.Errorln(errorMessage)
return errors.New(errorMessage)
}
logrus.Infof("Login succeeded: %s", loginResponse.Status)
return context.initializeUploadChunkSize()
}
func (context *PiwigoContext) Logout() error {
logrus.Debugf("Logging out from %s", context.url)
formData := url.Values{}
formData.Set("method", "pwg.session.logout")
var logoutResponse logoutResponse
err := context.executePiwigoRequest(formData, &logoutResponse)
if err != nil {
logrus.Errorf("Logout from %s failed", context.url)
return err return err
} }
return initializeUploadChunkSize(context) logrus.Infof("Successfully logged out from %s", context.url)
return nil
} }
func (context *PiwigoContext) getChunkSizeInKB() int { func (context *PiwigoContext) GetStatus() (*getStatusResponse, error) {
return context.chunkSizeInKB logrus.Debugln("Getting current login state...")
}
func (context *PiwigoContext) postForm(formData url.Values) (resp *http.Response, err error) { formData := url.Values{}
context.initializeCookieJarIfRequired() formData.Set("method", "pwg.session.getStatus")
client := http.Client{Jar: context.cookies} var getStatusResponse getStatusResponse
response, err := client.PostForm(context.url, formData) err := context.executePiwigoRequest(formData, &getStatusResponse)
if err != nil {
errorMessage := fmt.Sprintln("Could not get session state from server")
logrus.Errorln(errorMessage)
return nil, errors.New(errorMessage)
}
return &getStatusResponse, nil
}
func (context *PiwigoContext) GetAllCategories() (map[string]*PiwigoCategory, error) {
formData := url.Values{}
formData.Set("method", "pwg.categories.getList")
formData.Set("recursive", "true")
var getCategoryListResponse getCategoryListResponse
err := context.executePiwigoRequest(formData, &getCategoryListResponse)
if err != nil {
logrus.Errorf("Got error while loading categories: %s", err)
return nil, errors.New("Could not load categories")
}
logrus.Infof("Successfully got all categories")
categories := buildCategoryMap(&getCategoryListResponse)
buildCategoryKeys(categories)
categoryLookups := buildLookupMap(categories)
return categoryLookups, nil
}
func (context *PiwigoContext) CreateCategory(parentId int, name string) (int, error) {
formData := url.Values{}
formData.Set("method", "pwg.categories.add")
formData.Set("name", name)
// we only submit the parentid if there is one.
if parentId > 0 {
formData.Set("parent", fmt.Sprint(parentId))
}
var createCategoryResponse createCategoryResponse
err := context.executePiwigoRequest(formData, &createCategoryResponse)
if err != nil {
logrus.Errorln(err)
return 0, err
}
logrus.Infof("Successfully created category %s with id %d", name, createCategoryResponse.Result.ID)
return createCategoryResponse.Result.ID, nil
}
func (context *PiwigoContext) ImageCheckFile(piwigoId int, md5sum string) (int, error) {
formData := url.Values{}
formData.Set("method", "pwg.images.checkFiles")
formData.Set("image_id", strconv.Itoa(piwigoId))
formData.Set("file_sum", md5sum)
logrus.Tracef("Checking if file %s - %d needs to be uploaded", md5sum, piwigoId)
var checkFilesResponse checkFilesResponse
err := context.executePiwigoRequest(formData, &checkFilesResponse)
if err != nil {
return ImageStateInvalid, err
}
if checkFilesResponse.Result["file"] == "equals" {
return ImageStateUptodate, nil
}
return ImageStateDifferent, nil
}
func (context *PiwigoContext) ImagesExistOnPiwigo(md5sums []string) (map[string]int, error) {
//TODO: make sure to split to multiple queries -> to honor max upload queries
md5sumList := strings.Join(md5sums, ",")
formData := url.Values{}
formData.Set("method", "pwg.images.exist")
formData.Set("md5sum_list", md5sumList)
logrus.Tracef("Looking up if files exist: %s", md5sumList)
var imageExistResponse imageExistResponse
err := context.executePiwigoRequest(formData, &imageExistResponse)
if err != nil { if err != nil {
logrus.Errorf("The HTTP request failed with error %s", err)
return nil, err return nil, err
} }
return response, nil
existResults := make(map[string]int, len(imageExistResponse.Result))
for key, value := range imageExistResponse.Result {
if value == "" {
logrus.Tracef("Missing file with md5sum: %s", key)
existResults[key] = 0
} else {
piwigoId, err := strconv.Atoi(value)
if err != nil {
logrus.Warnf("could not parse piwigoid of file %s", key)
continue
}
logrus.Tracef("Found piwigo id %d for md5sum %s", piwigoId, key)
existResults[key] = piwigoId
}
}
return existResults, nil
}
func (context *PiwigoContext) UploadImage(filePath string, md5sum string, category int) (int, error) {
if context.chunkSizeInKB <= 0 {
return 0, errors.New("Uploadchunk size is less or equal to zero. 512 is a recommendet value to begin with.")
}
fileInfo, err := os.Stat(filePath)
if err != nil {
return 0, err
}
fileSizeInKB := fileInfo.Size() / 1024
logrus.Infof("Uploading %s using chunksize of %d KB and total size of %d KB", filePath, context.chunkSizeInKB, fileSizeInKB)
err = uploadImageChunks(filePath, context, fileSizeInKB, md5sum)
if err != nil {
return 0, err
}
imageId, err := uploadImageFinal(context, fileInfo.Name(), md5sum, category)
if err != nil {
return 0, err
}
return imageId, nil
} }
func (context *PiwigoContext) initializeCookieJarIfRequired() { func (context *PiwigoContext) initializeCookieJarIfRequired() {
@ -82,8 +238,8 @@ func (context *PiwigoContext) initializeCookieJarIfRequired() {
context.cookies = jar context.cookies = jar
} }
func initializeUploadChunkSize(context *PiwigoContext) error { func (context *PiwigoContext) initializeUploadChunkSize() error {
userStatus, err := GetStatus(context) userStatus, err := context.GetStatus()
if err != nil { if err != nil {
return err return err
} }
@ -91,3 +247,26 @@ func initializeUploadChunkSize(context *PiwigoContext) error {
logrus.Debugf("Got chunksize of %d KB from server.", context.chunkSizeInKB) logrus.Debugf("Got chunksize of %d KB from server.", context.chunkSizeInKB)
return nil return nil
} }
func (context *PiwigoContext) executePiwigoRequest(formData url.Values, decodedResponse responseStatuser) error {
context.initializeCookieJarIfRequired()
client := http.Client{Jar: context.cookies}
response, err := client.PostForm(context.url, formData)
if err != nil {
return err
}
defer response.Body.Close()
if err := json.NewDecoder(response.Body).Decode(decodedResponse); err != nil {
logrus.Errorln(err)
return err
}
if decodedResponse.responseStatus() != "ok" {
errorMessage := fmt.Sprintf("Error on handling piwigo response: %s", decodedResponse)
logrus.Error(errorMessage)
return errors.New(errorMessage)
}
return nil
}

View File

@ -1,126 +0,0 @@
package piwigo
import (
"encoding/json"
"errors"
"fmt"
"github.com/sirupsen/logrus"
"net/url"
"strings"
)
type LoginResponse struct {
Status string `json:"stat"`
Result bool `json:"result"`
ErrorNumber int `json:"err"`
Message string `json:"message"`
}
type GetStatusResponse struct {
Status string `json:"stat"`
Result struct {
Username string `json:"username"`
Status string `json:"status"`
Theme string `json:"theme"`
Language string `json:"language"`
PwgToken string `json:"pwg_token"`
Charset string `json:"charset"`
CurrentDatetime string `json:"current_datetime"`
Version string `json:"version"`
AvailableSizes []string `json:"available_sizes"`
UploadFileTypes string `json:"upload_file_types"`
UploadFormChunkSize int `json:"upload_form_chunk_size"`
} `json:"result"`
}
type LogoutResponse struct {
Status string `json:"stat"`
Result bool `json:"result"`
}
func Login(context *PiwigoContext) error {
logrus.Debugf("Logging in to %s using user %s", context.url, context.username)
if !strings.HasPrefix(context.url, "https") {
logrus.Warnf("The server url %s does not use https! Credentials are not encrypted!", context.url)
}
formData := url.Values{}
formData.Set("method", "pwg.session.login")
formData.Set("username", context.username)
formData.Set("password", context.password)
response, err := context.postForm(formData)
if err != nil {
return err
}
defer response.Body.Close()
var loginResponse LoginResponse
if err := json.NewDecoder(response.Body).Decode(&loginResponse); err != nil {
logrus.Errorln(err)
return err
}
if loginResponse.Status != "ok" {
errorMessage := fmt.Sprintf("Login failed: %d - %s", loginResponse.ErrorNumber, loginResponse.Message)
logrus.Errorln(errorMessage)
return errors.New(errorMessage)
}
logrus.Infof("Login succeeded: %s", loginResponse.Status)
return nil
}
func Logout(context *PiwigoContext) error {
logrus.Debugf("Logging out from %s", context.url)
formData := url.Values{}
formData.Set("method", "pwg.session.logout")
response, err := context.postForm(formData)
if err != nil {
return err
}
defer response.Body.Close()
var statusResponse LogoutResponse
if err := json.NewDecoder(response.Body).Decode(&statusResponse); err != nil {
logrus.Errorln(err)
}
if statusResponse.Status != "ok" {
logrus.Errorf("Logout from %s failed", context.url)
} else {
logrus.Infof("Successfully logged out from %s", context.url)
}
return nil
}
func GetStatus(context PiwigoFormPoster) (*GetStatusResponse, error) {
logrus.Debugln("Getting current login state...")
formData := url.Values{}
formData.Set("method", "pwg.session.getStatus")
response, err := context.postForm(formData)
if err != nil {
return nil, err
}
defer response.Body.Close()
var statusResponse GetStatusResponse
if err := json.NewDecoder(response.Body).Decode(&statusResponse); err != nil {
logrus.Errorln(err)
return nil, err
}
if statusResponse.Status != "ok" {
errorMessage := fmt.Sprintln("Could not get session state from server")
logrus.Errorln(errorMessage)
return nil, errors.New(errorMessage)
}
return &statusResponse, nil
}

View File

@ -1,11 +1,8 @@
package piwigo package piwigo
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"net/url"
"os" "os"
) )
@ -16,71 +13,6 @@ type PiwigoCategory struct {
Key string Key string
} }
type getCategoryListResponse struct {
Status string `json:"stat"`
Result struct {
Categories []struct {
ID int `json:"id"`
Name string `json:"name"`
Comment string `json:"comment,omitempty"`
Permalink string `json:"permalink,omitempty"`
Status string `json:"status,omitempty"`
Uppercats string `json:"uppercats,omitempty"`
GlobalRank string `json:"global_rank,omitempty"`
IDUppercat int `json:"id_uppercat,string,omitempty"`
NbImages int `json:"nb_images,omitempty"`
TotalNbImages int `json:"total_nb_images,omitempty"`
RepresentativePictureID string `json:"representative_picture_id,omitempty"`
DateLast string `json:"date_last,omitempty"`
MaxDateLast string `json:"max_date_last,omitempty"`
NbCategories int `json:"nb_categories,omitempty"`
URL string `json:"url,omitempty"`
TnURL string `json:"tn_url,omitempty"`
} `json:"categories"`
} `json:"result"`
}
type createCategoryResponse struct {
Status string `json:"stat"`
Err int `json:"err"`
Message string `json:"message"`
Result struct {
Info string `json:"info"`
ID int `json:"id"`
} `json:"result"`
}
func GetAllCategories(context PiwigoFormPoster) (map[string]*PiwigoCategory, error) {
formData := url.Values{}
formData.Set("method", "pwg.categories.getList")
formData.Set("recursive", "true")
response, err := context.postForm(formData)
if err != nil {
return nil, err
}
defer response.Body.Close()
var statusResponse getCategoryListResponse
if err := json.NewDecoder(response.Body).Decode(&statusResponse); err != nil {
logrus.Errorln(err)
return nil, err
}
if statusResponse.Status != "ok" {
logrus.Errorf("Got state %s while loading categories", statusResponse.Status)
return nil, errors.New("Could not load categories")
}
logrus.Infof("Successfully got all categories")
categories := buildCategoryMap(&statusResponse)
buildCategoryKeys(categories)
categoryLookups := buildLookupMap(categories)
return categoryLookups, nil
}
func buildLookupMap(categories map[int]*PiwigoCategory) map[string]*PiwigoCategory { func buildLookupMap(categories map[int]*PiwigoCategory) map[string]*PiwigoCategory {
categoryLookups := map[string]*PiwigoCategory{} categoryLookups := map[string]*PiwigoCategory{}
for _, category := range categories { for _, category := range categories {
@ -118,35 +50,3 @@ func buildCategoryKeys(categories map[int]*PiwigoCategory) {
category.Key = key category.Key = key
} }
} }
func CreateCategory(context PiwigoFormPoster, parentId int, name string) (int, error) {
formData := url.Values{}
formData.Set("method", "pwg.categories.add")
formData.Set("name", name)
// we only submit the parentid if there is one.
if parentId > 0 {
formData.Set("parent", fmt.Sprint(parentId))
}
response, err := context.postForm(formData)
if err != nil {
return 0, err
}
defer response.Body.Close()
var createResponse createCategoryResponse
if err := json.NewDecoder(response.Body).Decode(&createResponse); err != nil {
logrus.Errorln(err)
return 0, err
}
if createResponse.Status != "ok" {
logrus.Errorf("Got state %s while loading categories", createResponse.Status)
return 0, errors.New("Could not create category")
}
logrus.Infof("Successfully got all categories from server...")
return createResponse.Result.ID, nil
}

View File

@ -3,7 +3,6 @@ package piwigo
import ( import (
"bufio" "bufio"
"encoding/base64" "encoding/base64"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -11,135 +10,15 @@ import (
"net/url" "net/url"
"os" "os"
"strconv" "strconv"
"strings"
) )
type uploadChunkResponse struct {
Status string `json:"stat"`
Result interface{} `json:"result"`
}
type fileAddResponse struct {
Status string `json:"stat"`
Result struct {
ImageID int `json:"image_id"`
URL string `json:"url"`
} `json:"result"`
}
type imageExistResponse struct {
Status string `json:"stat"`
Result map[string]string `json:"result"`
}
type checkFilesResponse struct {
Status string `json:"stat"`
Result struct {
File string `json:"file"`
} `json:"result"`
}
const ( const (
ImageStateInvalid = -1 ImageStateInvalid = -1
ImageStateUptodate = 0 ImageStateUptodate = 0
ImageStateDifferent = 1 ImageStateDifferent = 1
) )
func ImageCheckFile(context PiwigoFormPoster, piwigoId int, md5sum string) (int, error) { func uploadImageChunks(filePath string, context *PiwigoContext, fileSizeInKB int64, md5sum string) error {
formData := url.Values{}
formData.Set("method", "pwg.images.exist")
formData.Set("image_id", strconv.Itoa(piwigoId))
formData.Set("file_sum", md5sum)
logrus.Tracef("Checking if file %s - %d needs to be uploaded", md5sum, piwigoId)
response, err := context.postForm(formData)
if err != nil {
return ImageStateInvalid, err
}
defer response.Body.Close()
var checkFilesResponse checkFilesResponse
if err := json.NewDecoder(response.Body).Decode(&checkFilesResponse); err != nil {
logrus.Errorln(err)
return ImageStateInvalid, err
}
if checkFilesResponse.Result.File == "equals" {
return ImageStateUptodate, nil
}
return ImageStateDifferent, nil
}
func ImagesExistOnPiwigo(context PiwigoFormPoster, md5sums []string) (map[string]int, error) {
//TODO: make sure to split to multiple queries -> to honor max upload queries
md5sumList := strings.Join(md5sums, ",")
formData := url.Values{}
formData.Set("method", "pwg.images.exist")
formData.Set("md5sum_list", md5sumList)
logrus.Tracef("Looking up if files exist: %s", md5sumList)
response, err := context.postForm(formData)
if err != nil {
return nil, err
}
defer response.Body.Close()
var imageExistResponse imageExistResponse
if err := json.NewDecoder(response.Body).Decode(&imageExistResponse); err != nil {
logrus.Errorln(err)
return nil, err
}
existResults := make(map[string]int, len(imageExistResponse.Result))
for key, value := range imageExistResponse.Result {
if value == "" {
logrus.Tracef("Missing file with md5sum: %s", key)
existResults[key] = 0
} else {
piwigoId, err := strconv.Atoi(value)
if err != nil {
logrus.Warnf("could not parse piwigoid of file %s", key)
continue
}
logrus.Tracef("Found piwigo id %d for md5sum %s", piwigoId, key)
existResults[key] = piwigoId
}
}
return existResults, nil
}
func UploadImage(context PiwigoFormPoster, filePath string, md5sum string, category int) (int, error) {
if context.getChunkSizeInKB() <= 0 {
return 0, errors.New("Uploadchunk size is less or equal to zero. 512 is a recommendet value to begin with.")
}
fileInfo, err := os.Stat(filePath)
if err != nil {
return 0, err
}
fileSizeInKB := fileInfo.Size() / 1024
logrus.Infof("Uploading %s using chunksize of %d KB and total size of %d KB", filePath, context.getChunkSizeInKB(), fileSizeInKB)
err = uploadImageChunks(filePath, context, fileSizeInKB, md5sum)
if err != nil {
return 0, err
}
imageId, err := uploadImageFinal(context, fileInfo.Name(), md5sum, category)
if err != nil {
return 0, err
}
return imageId, nil
}
func uploadImageChunks(filePath string, context PiwigoFormPoster, fileSizeInKB int64, md5sum string) error {
file, err := os.Open(filePath) file, err := os.Open(filePath)
if err != nil { if err != nil {
return err return err
@ -147,9 +26,9 @@ func uploadImageChunks(filePath string, context PiwigoFormPoster, fileSizeInKB i
defer file.Close() defer file.Close()
reader := bufio.NewReader(file) reader := bufio.NewReader(file)
bufferSize := 1024 * context.getChunkSizeInKB() bufferSize := 1024 * context.chunkSizeInKB
buffer := make([]byte, bufferSize) buffer := make([]byte, bufferSize)
numberOfChunks := (fileSizeInKB / int64(context.getChunkSizeInKB())) + 1 numberOfChunks := (fileSizeInKB / int64(context.chunkSizeInKB)) + 1
currentChunk := int64(0) currentChunk := int64(0)
for { for {
@ -176,7 +55,7 @@ func uploadImageChunks(filePath string, context PiwigoFormPoster, fileSizeInKB i
return nil return nil
} }
func uploadImageChunk(context PiwigoFormPoster, base64chunk string, md5sum string, position int64) error { func uploadImageChunk(context *PiwigoContext, base64chunk string, md5sum string, position int64) error {
formData := url.Values{} formData := url.Values{}
formData.Set("method", "pwg.images.addChunk") formData.Set("method", "pwg.images.addChunk")
formData.Set("data", base64chunk) formData.Set("data", base64chunk)
@ -187,19 +66,9 @@ func uploadImageChunk(context PiwigoFormPoster, base64chunk string, md5sum strin
logrus.Tracef("Uploading chunk %d of file with sum %s", position, md5sum) logrus.Tracef("Uploading chunk %d of file with sum %s", position, md5sum)
response, err := context.postForm(formData)
if err != nil {
return err
}
defer response.Body.Close()
var uploadChunkResponse uploadChunkResponse var uploadChunkResponse uploadChunkResponse
if err := json.NewDecoder(response.Body).Decode(&uploadChunkResponse); err != nil { err := context.executePiwigoRequest(formData, &uploadChunkResponse)
logrus.Errorln(err) if err != nil {
return err
}
if uploadChunkResponse.Status != "ok" {
logrus.Errorf("Got state %s while uploading chunk %d of %s", uploadChunkResponse.Status, position, md5sum) logrus.Errorf("Got state %s while uploading chunk %d of %s", uploadChunkResponse.Status, position, md5sum)
return errors.New(fmt.Sprintf("Got state %s while uploading chunk %d of %s", uploadChunkResponse.Status, position, md5sum)) return errors.New(fmt.Sprintf("Got state %s while uploading chunk %d of %s", uploadChunkResponse.Status, position, md5sum))
} }
@ -207,7 +76,7 @@ func uploadImageChunk(context PiwigoFormPoster, base64chunk string, md5sum strin
return nil return nil
} }
func uploadImageFinal(context PiwigoFormPoster, originalFilename string, md5sum string, categoryId int) (int, error) { func uploadImageFinal(context *PiwigoContext, originalFilename string, md5sum string, categoryId int) (int, error) {
formData := url.Values{} formData := url.Values{}
formData.Set("method", "pwg.images.add") formData.Set("method", "pwg.images.add")
formData.Set("original_sum", md5sum) formData.Set("original_sum", md5sum)
@ -217,19 +86,9 @@ func uploadImageFinal(context PiwigoFormPoster, originalFilename string, md5sum
logrus.Debugf("Finalizing upload of file %s with sum %s to category %d", originalFilename, md5sum, categoryId) logrus.Debugf("Finalizing upload of file %s with sum %s to category %d", originalFilename, md5sum, categoryId)
response, err := context.postForm(formData)
if err != nil {
return 0, err
}
defer response.Body.Close()
var fileAddResponse fileAddResponse var fileAddResponse fileAddResponse
if err := json.NewDecoder(response.Body).Decode(&fileAddResponse); err != nil { err := context.executePiwigoRequest(formData, &fileAddResponse)
logrus.Errorln(err) if err != nil {
return 0, err
}
if fileAddResponse.Status != "ok" {
logrus.Errorf("Got state %s while adding image %s", fileAddResponse.Status, originalFilename) logrus.Errorf("Got state %s while adding image %s", fileAddResponse.Status, originalFilename)
return 0, errors.New(fmt.Sprintf("Got state %s while adding image %s", fileAddResponse.Status, originalFilename)) return 0, errors.New(fmt.Sprintf("Got state %s while adding image %s", fileAddResponse.Status, originalFilename))
} }

View File

@ -0,0 +1,127 @@
package piwigo
type responseStatuser interface {
responseStatus() string
}
type loginResponse struct {
Status string `json:"stat"`
Result bool `json:"result"`
ErrorNumber int `json:"err"`
Message string `json:"message"`
}
func (r loginResponse) responseStatus() string {
return r.Status
}
type getStatusResponse struct {
Status string `json:"stat"`
Result struct {
Username string `json:"username"`
Status string `json:"status"`
Theme string `json:"theme"`
Language string `json:"language"`
PwgToken string `json:"pwg_token"`
Charset string `json:"charset"`
CurrentDatetime string `json:"current_datetime"`
Version string `json:"version"`
AvailableSizes []string `json:"available_sizes"`
UploadFileTypes string `json:"upload_file_types"`
UploadFormChunkSize int `json:"upload_form_chunk_size"`
} `json:"result"`
}
func (r getStatusResponse) responseStatus() string {
return r.Status
}
type logoutResponse struct {
Status string `json:"stat"`
Result bool `json:"result"`
}
func (r logoutResponse) responseStatus() string {
return r.Status
}
type getCategoryListResponse struct {
Status string `json:"stat"`
Result struct {
Categories []struct {
ID int `json:"id"`
Name string `json:"name"`
Comment string `json:"comment,omitempty"`
Permalink string `json:"permalink,omitempty"`
Status string `json:"status,omitempty"`
Uppercats string `json:"uppercats,omitempty"`
GlobalRank string `json:"global_rank,omitempty"`
IDUppercat int `json:"id_uppercat,string,omitempty"`
NbImages int `json:"nb_images,omitempty"`
TotalNbImages int `json:"total_nb_images,omitempty"`
RepresentativePictureID string `json:"representative_picture_id,omitempty"`
DateLast string `json:"date_last,omitempty"`
MaxDateLast string `json:"max_date_last,omitempty"`
NbCategories int `json:"nb_categories,omitempty"`
URL string `json:"url,omitempty"`
TnURL string `json:"tn_url,omitempty"`
} `json:"categories"`
} `json:"result"`
}
func (r getCategoryListResponse) responseStatus() string {
return r.Status
}
type createCategoryResponse struct {
Status string `json:"stat"`
Err int `json:"err"`
Message string `json:"message"`
Result struct {
Info string `json:"info"`
ID int `json:"id"`
} `json:"result"`
}
func (r createCategoryResponse) responseStatus() string {
return r.Status
}
type uploadChunkResponse struct {
Status string `json:"stat"`
Result interface{} `json:"result"`
}
func (r uploadChunkResponse) responseStatus() string {
return r.Status
}
type fileAddResponse struct {
Status string `json:"stat"`
Result struct {
ImageID int `json:"image_id"`
URL string `json:"url"`
} `json:"result"`
}
func (r fileAddResponse) responseStatus() string {
return r.Status
}
type imageExistResponse struct {
Status string `json:"stat"`
Result map[string]string `json:"result"`
}
func (r imageExistResponse) responseStatus() string {
return r.Status
}
type checkFilesResponse struct {
Status string `json:"stat"`
Result map[string]string `json:"result"`
}
func (r checkFilesResponse) responseStatus() string {
return r.Status
}