Skip to content

Commit a2ed6de

Browse files
committed
Fix #36 - Ajoute la fonctionnalité d'association d'images à des catégories et améliore la détection des photos déjà publiées dans le service Piwigo.
1 parent daa544b commit a2ed6de

3 files changed

Lines changed: 249 additions & 11 deletions

File tree

piwigoPublish.lrplugin/PiwigoAPI.lua

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,6 +1957,116 @@ function PiwigoAPI.checkPhoto(propertyTable, pwImageID)
19571957
return rtnStatus
19581958
end
19591959

1960+
-- *************************************************
1961+
function PiwigoAPI.associateImageToCategory(propertyTable, imageId, categoryId)
1962+
-- Associates an existing Piwigo image to an additional category without re-uploading
1963+
log:info("PiwigoAPI.associateImageToCategory - imageId: " .. tostring(imageId) .. ", categoryId: " .. tostring(categoryId))
1964+
1965+
local callStatus = { status = false }
1966+
local rv
1967+
1968+
-- Check connection
1969+
if not propertyTable.Connected then
1970+
rv = PiwigoAPI.login(propertyTable)
1971+
if not rv then
1972+
callStatus.statusMsg = "Cannot connect to Piwigo"
1973+
return callStatus
1974+
end
1975+
end
1976+
1977+
local params = {
1978+
{ name = "method", value = "pwg.images.setInfo" },
1979+
{ name = "image_id", value = tostring(imageId) },
1980+
{ name = "categories", value = tostring(categoryId) },
1981+
{ name = "multiple_value_mode", value = "append" },
1982+
{ name = "pwg_token", value = propertyTable.token }
1983+
}
1984+
1985+
local postResponse = PiwigoAPI.httpPostMultiPart(propertyTable, params)
1986+
1987+
if postResponse.status then
1988+
callStatus.status = true
1989+
callStatus.remoteid = imageId
1990+
callStatus.remoteurl = string.format("%s/picture.php?/%s/category/%s", propertyTable.host, imageId, categoryId)
1991+
else
1992+
callStatus.statusMsg = postResponse.statusMsg or "Association failed"
1993+
end
1994+
1995+
return callStatus
1996+
end
1997+
1998+
-- *************************************************
1999+
function PiwigoAPI.dissociateImageFromCategory(propertyTable, imageId, categoryId)
2000+
-- Removes an image from a specific category WITHOUT deleting the image
2001+
-- Uses pwg.images.setInfo to update categories list (excluding the target category)
2002+
log:info("PiwigoAPI.dissociateImageFromCategory - imageId: " .. tostring(imageId) .. ", categoryId: " .. tostring(categoryId))
2003+
2004+
local callStatus = { status = false }
2005+
local rv
2006+
2007+
-- Check connection
2008+
if not propertyTable.Connected then
2009+
rv = PiwigoAPI.login(propertyTable)
2010+
if not rv then
2011+
callStatus.statusMsg = "Cannot connect to Piwigo"
2012+
return callStatus
2013+
end
2014+
end
2015+
2016+
-- First, get current categories for this image
2017+
local checkStatus = PiwigoAPI.checkPhoto(propertyTable, imageId)
2018+
if not checkStatus.status then
2019+
callStatus.statusMsg = "Cannot find image " .. tostring(imageId) .. " on Piwigo"
2020+
return callStatus
2021+
end
2022+
2023+
local imageDets = checkStatus.imageDets
2024+
local currentCategories = imageDets.categories or {}
2025+
2026+
log:info("PiwigoAPI.dissociateImageFromCategory - image currently in " .. #currentCategories .. " categories")
2027+
2028+
-- Build new categories list excluding the target category
2029+
local newCategoryIds = {}
2030+
for _, cat in ipairs(currentCategories) do
2031+
if tostring(cat.id) ~= tostring(categoryId) then
2032+
table.insert(newCategoryIds, tostring(cat.id))
2033+
end
2034+
end
2035+
2036+
log:info("PiwigoAPI.dissociateImageFromCategory - remaining categories: " .. #newCategoryIds)
2037+
2038+
-- If image would be orphaned (no remaining categories), delete it entirely
2039+
if #newCategoryIds == 0 then
2040+
log:info("PiwigoAPI.dissociateImageFromCategory - image would be orphaned, deleting entirely")
2041+
return PiwigoAPI.deletePhoto(propertyTable, categoryId, imageId, callStatus)
2042+
end
2043+
2044+
-- Update image with new categories list (replaces all associations)
2045+
local categoriesStr = table.concat(newCategoryIds, ";")
2046+
2047+
local params = {
2048+
{ name = "method", value = "pwg.images.setInfo" },
2049+
{ name = "image_id", value = tostring(imageId) },
2050+
{ name = "categories", value = categoriesStr },
2051+
{ name = "multiple_value_mode", value = "replace" },
2052+
{ name = "pwg_token", value = propertyTable.token }
2053+
}
2054+
2055+
log:info("PiwigoAPI.dissociateImageFromCategory - new categories string: " .. categoriesStr)
2056+
2057+
local postResponse = PiwigoAPI.httpPostMultiPart(propertyTable, params)
2058+
2059+
if postResponse.status then
2060+
callStatus.status = true
2061+
log:info("PiwigoAPI.dissociateImageFromCategory - success")
2062+
else
2063+
callStatus.statusMsg = postResponse.statusMsg or "Dissociation failed"
2064+
log:info("PiwigoAPI.dissociateImageFromCategory - failed: " .. callStatus.statusMsg)
2065+
end
2066+
2067+
return callStatus
2068+
end
2069+
19602070
-- *************************************************
19612071
function PiwigoAPI.updateGallery(propertyTable, exportFilename, metaData)
19622072
-- update gallery with image via pwg.images.addSimple

piwigoPublish.lrplugin/PublishTask.lua

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,43 @@ function PublishTask.processRenderedPhotos(functionContext, exportContext)
134134

135135
local lrPhoto = rendition.photo
136136
local remoteId = rendition.publishedPhotoId or ""
137+
138+
-- Detect photo already published in this service (multi-album support)
139+
local existingPwImageId = nil
140+
if remoteId == "" then
141+
-- Method 1: Via custom metadata (photos published with plugin >= 20251224.16)
142+
local storedImageUrl = lrPhoto:getPropertyForPlugin(_PLUGIN, "pwImageURL")
143+
local storedHost = lrPhoto:getPropertyForPlugin(_PLUGIN, "pwHostURL")
144+
145+
log:info("DEBUG multi-album: remoteId vide, checking metadata...")
146+
log:info("DEBUG storedHost: " .. tostring(storedHost))
147+
log:info("DEBUG storedImageUrl: " .. tostring(storedImageUrl))
148+
log:info("DEBUG propertyTable.host: " .. tostring(propertyTable.host))
149+
150+
if storedHost == propertyTable.host and storedImageUrl then
151+
existingPwImageId = utils.extractPwImageIdFromUrl(storedImageUrl, propertyTable.host)
152+
end
153+
154+
-- Method 2: Search in other collections of the service (fallback)
155+
if not existingPwImageId then
156+
log:info("DEBUG multi-album: metadata vides, recherche cross-collection...")
157+
local publishService = publishedCollection:getService()
158+
existingPwImageId = utils.findExistingPwImageId(publishService, lrPhoto)
159+
if existingPwImageId then
160+
log:info("DEBUG multi-album: trouvé via cross-collection, ID = " .. tostring(existingPwImageId))
161+
end
162+
end
163+
164+
-- Verify the image still exists on Piwigo
165+
if existingPwImageId then
166+
local checkStatus = PiwigoAPI.checkPhoto(propertyTable, existingPwImageId)
167+
if not checkStatus.status then
168+
log:info("DEBUG multi-album: image " .. existingPwImageId .. " n'existe plus sur Piwigo")
169+
existingPwImageId = nil
170+
end
171+
end
172+
end
173+
137174
-- Wait for next photo to render.
138175
local success, pathOrMessage = rendition:waitForRender()
139176

@@ -149,6 +186,25 @@ function PublishTask.processRenderedPhotos(functionContext, exportContext)
149186
-- photo has been exported to temporary location - upload to piwigo
150187
callStatus = {}
151188
local filePath = pathOrMessage
189+
190+
-- If photo already exists on Piwigo, associate instead of uploading
191+
if existingPwImageId then
192+
log:info("Photo exists on Piwigo (ID " .. existingPwImageId .. "), associating to album " .. albumId)
193+
callStatus = PiwigoAPI.associateImageToCategory(propertyTable, existingPwImageId, albumId)
194+
195+
if callStatus.status then
196+
rendition:recordPublishedPhotoId(callStatus.remoteid)
197+
rendition:recordPublishedPhotoUrl(callStatus.remoteurl)
198+
rendition:renditionIsDone(true)
199+
LrFileUtils.delete(pathOrMessage)
200+
else
201+
log:warn("Association failed: " .. (callStatus.statusMsg or "") .. ", falling back to upload")
202+
existingPwImageId = nil
203+
end
204+
end
205+
206+
if not existingPwImageId then
207+
-- Begin existing upload block (indent all upload code until end of if success)
152208
local metaData = {}
153209
-- build metadata structure
154210
metaData = utils.getPhotoMetadata(propertyTable, lrPhoto)
@@ -202,6 +258,7 @@ function PublishTask.processRenderedPhotos(functionContext, exportContext)
202258
end
203259
-- When done with photo, delete temp file.
204260
LrFileUtils.delete(pathOrMessage)
261+
end -- end if not existingPwImageId
205262
else
206263
rendition:uploadFailed(pathOrMessage or "Render failed")
207264
end
@@ -279,18 +336,32 @@ function PublishTask.deletePhotosFromPublishedCollection(publishSettings, arrayO
279336
local thisLrPhoto = thisPhotoToUnpublish[1]
280337
local thispwImageID = thisPhotoToUnpublish[2]
281338
local thisPubPhoto = thisPhotoToUnpublish[3]
282-
callStatus = PiwigoAPI.deletePhoto(publishSettings, pwCatID, thispwImageID, callStatus)
339+
340+
-- Use dissociate instead of delete to preserve multi-album associations
341+
log:info("PublishTask.deletePhotosFromPublishedCollection - dissociating photo " .. thispwImageID .. " from category " .. pwCatID)
342+
callStatus = PiwigoAPI.dissociateImageFromCategory(publishSettings, thispwImageID, pwCatID)
283343
if callStatus.status then
284-
catalog:withWriteAccessDo("Updating " .. thisLrPhoto:getFormattedMetadata("fileName"),
285-
function()
286-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwHostURL", "")
287-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwAlbumName", "")
288-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwAlbumURL", "")
289-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwImageURL", "")
290-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwUploadDate", "")
291-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwUploadTime", "")
292-
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwCommentSync", "")
293-
end)
344+
-- Only clear metadata if photo is no longer in any other published collection
345+
-- Check if photo exists in other collections of this service
346+
local publishService = publishedCollection:getService()
347+
local stillPublished = utils.findExistingPwImageId(publishService, thisLrPhoto)
348+
349+
if not stillPublished then
350+
-- Photo is no longer published anywhere, clear all metadata
351+
log:info("PublishTask.deletePhotosFromPublishedCollection - photo " .. thispwImageID .. " orphaned, clearing metadata")
352+
catalog:withWriteAccessDo("Updating " .. thisLrPhoto:getFormattedMetadata("fileName"),
353+
function()
354+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwHostURL", "")
355+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwAlbumName", "")
356+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwAlbumURL", "")
357+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwImageURL", "")
358+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwUploadDate", "")
359+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwUploadTime", "")
360+
thisLrPhoto:setPropertyForPlugin(_PLUGIN, "pwCommentSync", "")
361+
end)
362+
else
363+
log:info("PublishTask.deletePhotosFromPublishedCollection - photo " .. thispwImageID .. " still in other collections, keeping metadata")
364+
end
294365
thisPhotoToUnpublish[4] = true
295366
else
296367
PiwigoBusy = false

piwigoPublish.lrplugin/utils.lua

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,4 +1219,61 @@ function utils.pwBusyMessage(callingFunction, displayFunction)
12191219
end
12201220

12211221
-- *************************************************
1222+
function utils.extractPwImageIdFromUrl(url, expectedHost)
1223+
-- Extracts the Piwigo image_id from a URL like "http://host/picture.php?/822/..."
1224+
-- Verifies that the URL matches the expected host
1225+
if not url or url == "" then return nil end
1226+
if expectedHost and not url:find(expectedHost, 1, true) then return nil end
1227+
1228+
local imageId = url:match("picture%.php%?/(%d+)")
1229+
return imageId
1230+
end
1231+
1232+
-- *************************************************
1233+
function utils.findExistingPwImageId(publishService, lrPhoto)
1234+
-- Searches if this LR photo is already published in another collection of the same service
1235+
-- Returns the Piwigo remoteId if found, nil otherwise
1236+
1237+
local foundRemoteId = nil
1238+
1239+
local function searchInCollection(collection)
1240+
if foundRemoteId then return end
1241+
local pubPhotos = collection:getPublishedPhotos()
1242+
for _, pubPhoto in ipairs(pubPhotos) do
1243+
if pubPhoto:getPhoto().localIdentifier == lrPhoto.localIdentifier then
1244+
local rid = pubPhoto:getRemoteId()
1245+
if rid and rid ~= "" then
1246+
foundRemoteId = rid
1247+
return
1248+
end
1249+
end
1250+
end
1251+
end
1252+
1253+
local function searchInSet(collectionSet)
1254+
if foundRemoteId then return end
1255+
-- Search in child collections
1256+
local childColls = collectionSet:getChildCollections()
1257+
if childColls then
1258+
for _, coll in ipairs(childColls) do
1259+
searchInCollection(coll)
1260+
if foundRemoteId then return end
1261+
end
1262+
end
1263+
-- Search in child sets (recursive)
1264+
local childSets = collectionSet:getChildCollectionSets()
1265+
if childSets then
1266+
for _, childSet in ipairs(childSets) do
1267+
searchInSet(childSet)
1268+
if foundRemoteId then return end
1269+
end
1270+
end
1271+
end
1272+
1273+
-- Start search from service root
1274+
searchInSet(publishService)
1275+
1276+
return foundRemoteId
1277+
end
1278+
12221279
return utils

0 commit comments

Comments
 (0)