2019-03-23 22:41:04 +01:00
/ *
* Copyright ( C ) 2019 Philipp Haefelfinger ( http : //www.haefelfinger.ch/). All Rights Reserved.
* This application is licensed under GPLv2 . See the LICENSE file in the root directory of the project .
* /
2019-04-02 21:45:46 +02:00
package images
2019-02-25 23:36:18 +01:00
2019-02-25 23:51:18 +01:00
import (
2019-04-02 21:30:39 +02:00
"git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/pkg/datastore"
2019-03-03 23:44:13 +01:00
"git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/pkg/localFileStructure"
2019-03-11 21:42:20 +01:00
"git.haefelfinger.net/piwigo/PiwigoDirectoryUploader/internal/pkg/piwigo"
2019-03-02 00:21:01 +01:00
"github.com/sirupsen/logrus"
2019-03-24 00:09:20 +01:00
"os"
2019-03-17 23:05:17 +01:00
"path/filepath"
2019-02-25 23:51:18 +01:00
)
2019-02-25 23:36:18 +01:00
2019-03-17 23:05:17 +01:00
// to make use of the new local data store, we have to rethink and refactor the whole local detection process
// extend the storage of the images to keep track of upload state
// TBD: How to deal with updates -> delete / upload all based on md5 sums
2019-03-19 23:08:28 +01:00
type fileChecksumCalculator func ( filePath string ) ( string , error )
// Update the local image metadata by walking through all found files and check if the modification date has changed
// or if they are new to the local database. If the files is new or changed, the md5sum will be rebuilt as well.
2019-04-02 21:45:46 +02:00
func SynchronizeLocalImageMetadata ( metadataStorage datastore . ImageMetadataProvider , fileSystemNodes map [ string ] * localFileStructure . FilesystemNode , categories map [ string ] * piwigo . PiwigoCategory , checksumCalculator fileChecksumCalculator ) error {
logrus . Debug ( "Starting SynchronizeLocalImageMetadata" )
defer logrus . Debug ( "Leaving SynchronizeLocalImageMetadata" )
2019-03-24 00:09:20 +01:00
2019-03-17 23:05:17 +01:00
logrus . Info ( "Synchronizing local image metadata database with local available images" )
2019-02-27 23:26:18 +01:00
2019-03-23 23:33:44 +01:00
err := synchronizeLocalImageMetadataScanNewFiles ( fileSystemNodes , metadataStorage , categories , checksumCalculator )
if err != nil {
return err
}
2019-03-24 00:09:20 +01:00
err = synchronizeLocalImageMetadataFindFilesToDelete ( metadataStorage )
if err != nil {
return err
}
2019-03-23 23:33:44 +01:00
return nil
}
2019-04-02 21:30:39 +02:00
func synchronizeLocalImageMetadataScanNewFiles ( fileSystemNodes map [ string ] * localFileStructure . FilesystemNode , metadataStorage datastore . ImageMetadataProvider , categories map [ string ] * piwigo . PiwigoCategory , checksumCalculator fileChecksumCalculator ) error {
2019-03-24 00:09:20 +01:00
logrus . Debug ( "Entering synchronizeLocalImageMetadataScanNewFiles" )
defer logrus . Debug ( "Leaving synchronizeLocalImageMetadataScanNewFiles" )
2019-03-17 23:05:17 +01:00
for _ , file := range fileSystemNodes {
if file . IsDir {
// we are only interested in files not directories
continue
}
2019-02-25 23:36:18 +01:00
2019-03-20 23:15:41 +01:00
metadata , err := metadataStorage . ImageMetadata ( file . Path )
2019-04-02 21:30:39 +02:00
if err == datastore . ErrorRecordNotFound {
2019-03-25 23:36:25 +01:00
logrus . Debugf ( "Creating new metadata entry for %s." , file . Path )
2019-04-02 21:30:39 +02:00
metadata = datastore . ImageMetaData { }
2019-03-17 23:05:17 +01:00
metadata . Filename = file . Name
2019-03-20 23:15:41 +01:00
metadata . FullImagePath = file . Path
2019-03-17 23:05:17 +01:00
metadata . CategoryPath = filepath . Dir ( file . Key )
2019-03-20 23:15:41 +01:00
category , exist := categories [ metadata . CategoryPath ]
if exist {
metadata . CategoryId = category . Id
} else {
logrus . Warnf ( "No category found for image %s" , file . Path )
}
2019-03-17 23:05:17 +01:00
} else if err != nil {
logrus . Errorf ( "Could not get metadata due to trouble. Cancelling - %s" , err )
return err
}
2019-03-02 00:32:15 +01:00
2019-03-25 23:36:25 +01:00
if fileDidNotChange ( & metadata , file ) {
logrus . Debugf ( "No changes found for file %s" , file . Path )
continue
}
2019-03-25 00:09:56 +01:00
metadata . UploadRequired = ! metadata . LastChange . Equal ( file . ModTime ) || metadata . PiwigoId == 0
2019-03-24 23:46:23 +01:00
metadata . DeleteRequired = false
2019-03-17 23:05:17 +01:00
metadata . LastChange = file . ModTime
metadata . Md5Sum , err = checksumCalculator ( file . Path )
if err != nil {
logrus . Warnf ( "Could not calculate checksum for file %s. Skipping..." , file . Path )
continue
}
2019-03-01 00:14:52 +01:00
2019-03-17 23:05:17 +01:00
err = metadataStorage . SaveImageMetadata ( metadata )
2019-03-01 00:14:52 +01:00
if err != nil {
return err
}
}
return nil
2019-02-25 23:36:18 +01:00
}
2019-03-17 23:05:17 +01:00
2019-04-02 21:30:39 +02:00
func synchronizeLocalImageMetadataFindFilesToDelete ( provider datastore . ImageMetadataProvider ) error {
2019-04-02 21:45:46 +02:00
logrus . Debug ( "Entering SynchronizeLocalImageMetadataFindFilesToDelete" )
defer logrus . Debug ( "Leaving SynchronizeLocalImageMetadataFindFilesToDelete" )
2019-03-24 00:09:20 +01:00
images , err := provider . ImageMetadataAll ( )
if err != nil {
return err
}
for _ , img := range images {
if _ , err := os . Stat ( img . FullImagePath ) ; os . IsNotExist ( err ) {
img . UploadRequired = false
img . DeleteRequired = true
err := provider . SaveImageMetadata ( img )
if err != nil {
return err
}
}
}
return nil
}
2019-03-20 23:15:41 +01:00
// Uploads the pending images to the piwigo gallery and assign the category of to the image.
// Update local metadata and set upload flag to false. Also updates the piwigo image id if there was a difference.
2019-04-02 21:45:46 +02:00
func UploadImages ( piwigoCtx piwigo . PiwigoImageApi , metadataProvider datastore . ImageMetadataProvider ) error {
2019-03-24 00:09:20 +01:00
logrus . Debug ( "Starting uploadImages" )
defer logrus . Debug ( "Finished uploadImages successfully" )
2019-03-20 23:15:41 +01:00
images , err := metadataProvider . ImageMetadataToUpload ( )
if err != nil {
return err
}
logrus . Infof ( "Uploading %d images to piwigo" , len ( images ) )
for _ , img := range images {
imgId , err := piwigoCtx . UploadImage ( img . PiwigoId , img . FullImagePath , img . Md5Sum , img . CategoryId )
if err != nil {
logrus . Warnf ( "could not upload image %s. Continuing with the next image." , img . FullImagePath )
continue
}
if imgId > 0 && imgId != img . PiwigoId {
img . PiwigoId = imgId
logrus . Debugf ( "Updating image %d with piwigo id %d" , img . ImageId , img . PiwigoId )
}
logrus . Infof ( "Successfully uploaded %s" , img . FullImagePath )
img . UploadRequired = false
err = metadataProvider . SaveImageMetadata ( img )
if err != nil {
logrus . Warnf ( "could not save uploaded image %s. Continuing with the next image." , img . FullImagePath )
continue
}
}
return nil
}
2019-04-02 21:45:46 +02:00
func DeleteImages ( piwigoCtx piwigo . PiwigoImageApi , metadataProvider datastore . ImageMetadataProvider ) error {
2019-03-24 23:46:23 +01:00
logrus . Debug ( "Starting deleteImages" )
defer logrus . Debug ( "Finished deleteImages successfully" )
images , err := metadataProvider . ImageMetadataToDelete ( )
if err != nil {
return err
}
if len ( images ) == 0 {
logrus . Info ( "There are not images scheduled for deletion." )
return nil
}
logrus . Infof ( "Deleting %d images from piwigo" , len ( images ) )
2019-03-25 23:36:25 +01:00
var piwigoIds [ ] int = nil
2019-03-24 23:46:23 +01:00
for _ , img := range images {
if img . PiwigoId > 0 {
logrus . Tracef ( "Adding %d to deletable list" , img . PiwigoId )
piwigoIds = append ( piwigoIds , img . PiwigoId )
}
}
if len ( piwigoIds ) > 0 {
err = piwigoCtx . DeleteImages ( piwigoIds )
if err != nil {
return err
}
} else {
logrus . Info ( "Only local images found to delete. No call to piwigo is made." )
}
return metadataProvider . DeleteMarkedImages ( )
}
2019-03-20 23:15:41 +01:00
// This method aggregates the check for files with missing piwigoids and if changed files need to be uploaded again.
2019-04-02 21:45:46 +02:00
func SynchronizePiwigoMetadata ( piwigoCtx piwigo . PiwigoImageApi , metadataProvider datastore . ImageMetadataProvider ) error {
logrus . Debug ( "Entering SynchronizePiwigoMetadata" )
defer logrus . Debug ( "Leaving SynchronizePiwigoMetadata" )
2019-03-24 00:09:20 +01:00
2019-03-19 23:08:28 +01:00
// TODO: check if category has to be assigned (image possibly added to two albums -> only uploaded once but assigned multiple times) -> implement later
2019-03-20 23:15:41 +01:00
err := updatePiwigoIdIfAlreadyUploaded ( metadataProvider , piwigoCtx )
2019-03-19 23:08:28 +01:00
if err != nil {
2019-03-17 23:57:28 +01:00
return err
}
2019-03-20 23:15:41 +01:00
err = checkPiwigoForChangedImages ( metadataProvider , piwigoCtx )
2019-03-19 23:08:28 +01:00
if err != nil {
return err
2019-03-17 23:57:28 +01:00
}
2019-03-19 23:08:28 +01:00
return nil
2019-03-17 23:57:28 +01:00
}
2019-03-17 23:05:17 +01:00
2019-03-19 23:08:28 +01:00
// Check all images with upload required if they are really changed and need to be uploaded to the server.
2019-04-02 21:30:39 +02:00
func checkPiwigoForChangedImages ( provider datastore . ImageMetadataProvider , piwigoCtx piwigo . PiwigoImageApi ) error {
2019-03-24 00:09:20 +01:00
logrus . Info ( "Checking pending files if they really differ from the version in piwigo..." )
defer logrus . Info ( "Finished checking pending files if they really differ from the version in piwigo..." )
2019-03-17 23:05:17 +01:00
2019-03-19 23:08:28 +01:00
images , err := provider . ImageMetadataToUpload ( )
if err != nil {
return err
}
2019-03-20 00:31:23 +01:00
if len ( images ) == 0 {
logrus . Info ( "There are no existing images to check for modification on the server." )
return nil
}
2019-03-19 23:08:28 +01:00
for _ , img := range images {
if img . PiwigoId == 0 {
continue
}
2019-03-20 00:14:10 +01:00
state , err := piwigoCtx . ImageCheckFile ( img . PiwigoId , img . Md5Sum )
2019-03-19 23:08:28 +01:00
if err != nil {
2019-03-20 23:15:41 +01:00
logrus . Warnf ( "Error during file change check of file %s" , img . FullImagePath )
2019-03-19 23:08:28 +01:00
continue
}
if state == piwigo . ImageStateUptodate {
2019-03-20 23:15:41 +01:00
logrus . Debugf ( "File %s - %d has not changed" , img . FullImagePath , img . PiwigoId )
2019-03-19 23:08:28 +01:00
img . UploadRequired = false
2019-03-20 00:14:10 +01:00
err = provider . SaveImageMetadata ( img )
2019-03-19 23:08:28 +01:00
if err != nil {
2019-03-20 23:15:41 +01:00
logrus . Warnf ( "Could not save image data of image %s" , img . FullImagePath )
2019-03-19 23:08:28 +01:00
}
}
}
2019-03-17 23:05:17 +01:00
return nil
}
2019-03-19 23:08:28 +01:00
// 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.
2019-04-02 21:30:39 +02:00
func updatePiwigoIdIfAlreadyUploaded ( provider datastore . ImageMetadataProvider , piwigoCtx piwigo . PiwigoImageApi ) error {
2019-03-24 00:09:20 +01:00
logrus . Info ( "checking for pending files that are already on piwigo and updating piwigoids..." )
defer logrus . Info ( "finshed checking for pending files that are already on piwigo and updating piwigoids..." )
2019-03-19 23:08:28 +01:00
images , err := provider . ImageMetadataToUpload ( )
if err != nil {
return err
}
2019-03-20 00:31:23 +01:00
if len ( images ) == 0 {
logrus . Info ( "There are no existing images to check for modification on the server." )
return nil
}
2019-03-19 23:08:28 +01:00
logrus . Debugln ( "Preparing lookuplist for missing piwigo ids..." )
files := make ( [ ] string , 0 , len ( images ) )
for _ , img := range images {
if img . PiwigoId == 0 {
files = append ( files , img . Md5Sum )
}
}
2019-03-20 00:31:23 +01:00
2019-03-22 00:14:34 +01:00
if len ( files ) == 0 {
logrus . Info ( "There are no images without piwigo id to check for modification on the server." )
return nil
}
2019-03-20 00:14:10 +01:00
missingResults , err := piwigoCtx . ImagesExistOnPiwigo ( files )
2019-03-19 23:08:28 +01:00
if err != nil {
return err
}
2019-03-25 23:36:25 +01:00
2019-03-19 23:08:28 +01:00
for md5sum , piwigoId := range missingResults {
2019-03-25 22:40:19 +01:00
if piwigoId > 0 {
2019-03-25 22:54:30 +01:00
logrus . Debugf ( "Setting piwigo id of %s to %d" , md5sum , piwigoId )
2019-03-25 22:40:19 +01:00
err = provider . SavePiwigoIdAndUpdateUploadFlag ( md5sum , piwigoId )
if err != nil {
logrus . Warnf ( "Could not save piwigo id %d for file %s" , piwigoId , md5sum )
}
} else {
logrus . Tracef ( "Image %s not found on server" , md5sum )
2019-03-19 23:08:28 +01:00
}
}
2019-03-22 00:14:34 +01:00
2019-03-19 23:08:28 +01:00
return nil
}
2019-03-25 23:36:25 +01:00
2019-04-02 21:30:39 +02:00
func fileDidNotChange ( metadata * datastore . ImageMetaData , file * localFileStructure . FilesystemNode ) bool {
2019-03-25 23:36:25 +01:00
return metadata . LastChange . Equal ( file . ModTime ) && ! metadata . DeleteRequired
}