1+ --[[
2+ UpdateChecker.lua
3+
4+ Auto-update functionality for Piwigo Publisher plugin
5+ Checks GitHub Releases API for new versions
6+
7+ Copyright (C) 2024 Fiona Boston <fiona@fbphotography.uk>.
8+ Copyright (C) 2026 Julien Moreau <contact@julien-moreau.fr>.
9+
10+ This file is part of PiwigoPublish
11+
12+ This program is free software: you can redistribute it and/or modify
13+ it under the terms of the GNU General Public License as published by
14+ the Free Software Foundation; either version 3 of the License, or
15+ (at your option) any later version.
16+
17+ This program is distributed in the hope that it will be useful,
18+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+ GNU General Public License for more details.
21+
22+ You should have received a copy of the GNU General Public License
23+ along with this program. If not, see <http://www.gnu.org/licenses/>.
24+ ]]
25+
26+ local LrHttp = import ' LrHttp'
27+ local LrTasks = import ' LrTasks'
28+ local LrDialogs = import ' LrDialogs'
29+ local LrDate = import ' LrDate'
30+
31+ local UpdateChecker = {}
32+
33+ -- Configuration
34+ UpdateChecker .GITHUB_OWNER = " Piwigo"
35+ UpdateChecker .GITHUB_REPO = " PiwigoPublish-lrc-plugin"
36+ UpdateChecker .CHECK_INTERVAL_DAYS = 1
37+
38+ -- *************************************************
39+ function UpdateChecker .parseVersion (versionStr )
40+ -- Converts version string like "20260111.26" to comparable number
41+ -- Format: YYYYMMDD.revision
42+ if not versionStr or versionStr == " " then
43+ return 0
44+ end
45+
46+ -- Remove 'v' prefix if present
47+ versionStr = versionStr :gsub (" ^[vV]" , " " )
48+
49+ local major , minor = versionStr :match (" ^(%d+)%.?(%d*)" )
50+ if major then
51+ minor = minor or " 0"
52+ return tonumber (major ) * 1000 + tonumber (minor )
53+ end
54+ return 0
55+ end
56+
57+ -- *************************************************
58+ function UpdateChecker .shouldCheckForUpdates ()
59+ -- Returns true if enough time has passed since last check
60+ local lastCheck = prefs .lastUpdateCheck or 0
61+ local now = LrDate .currentTime ()
62+ local daysSinceCheck = (now - lastCheck ) / (24 * 60 * 60 )
63+
64+ log :info (" UpdateChecker.shouldCheckForUpdates - days since last check: " .. string.format (" %.1f" , daysSinceCheck ))
65+
66+ return daysSinceCheck >= UpdateChecker .CHECK_INTERVAL_DAYS
67+ end
68+
69+ -- *************************************************
70+ function UpdateChecker .checkForUpdates (silent )
71+ log :info (" UpdateChecker.checkForUpdates - silent: " .. tostring (silent ))
72+
73+ LrTasks .startAsyncTask (function ()
74+ -- Build GitHub API URL
75+ local url = string.format (
76+ " https://api.github.com/repos/%s/%s/releases/latest" ,
77+ UpdateChecker .GITHUB_OWNER ,
78+ UpdateChecker .GITHUB_REPO
79+ )
80+
81+ log :info (" UpdateChecker.checkForUpdates - fetching: " .. url )
82+
83+ -- Make HTTP request
84+ local response , headers = LrHttp .get (url , {
85+ { field = " Accept" , value = " application/vnd.github.v3+json" },
86+ { field = " User-Agent" , value = " PiwigoPublish-Lightroom-Plugin" }
87+ })
88+
89+ log :info (" UpdateChecker.checkForUpdates - HTTP status: " .. tostring (headers and headers .status ))
90+
91+ -- Update last check timestamp
92+ prefs .lastUpdateCheck = LrDate .currentTime ()
93+
94+ -- Handle errors
95+ if not response or (headers and headers .status ~= 200 ) then
96+ log :info (" UpdateChecker.checkForUpdates - failed to fetch release info" )
97+ if not silent then
98+ LrDialogs .message (
99+ " Update Check Failed" ,
100+ " Could not connect to GitHub to check for updates.\n Please check your internet connection." ,
101+ " warning"
102+ )
103+ end
104+ return
105+ end
106+
107+ -- Parse JSON response
108+ local ok , data = pcall (function () return JSON :decode (response ) end )
109+ if not ok or not data then
110+ log :info (" UpdateChecker.checkForUpdates - failed to parse JSON response" )
111+ if not silent then
112+ LrDialogs .message (
113+ " Update Check Failed" ,
114+ " Could not parse update information from GitHub." ,
115+ " warning"
116+ )
117+ end
118+ return
119+ end
120+
121+ -- Extract version info
122+ local remoteVersion = data .tag_name
123+ if not remoteVersion then
124+ log :info (" UpdateChecker.checkForUpdates - no tag_name in response" )
125+ if not silent then
126+ LrDialogs .message (
127+ " Update Check Failed" ,
128+ " No release information found on GitHub." ,
129+ " warning"
130+ )
131+ end
132+ return
133+ end
134+
135+ log :info (" UpdateChecker.checkForUpdates - remote version: " .. remoteVersion )
136+ log :info (" UpdateChecker.checkForUpdates - current version: " .. pluginVersion )
137+
138+ local currentNum = UpdateChecker .parseVersion (pluginVersion )
139+ local remoteNum = UpdateChecker .parseVersion (remoteVersion )
140+
141+ log :info (" UpdateChecker.checkForUpdates - currentNum: " .. currentNum .. " , remoteNum: " .. remoteNum )
142+
143+ if remoteNum > currentNum then
144+ -- New version available
145+ local changelog = data .body or " No changelog available."
146+ -- Truncate changelog if too long
147+ if # changelog > 500 then
148+ changelog = changelog :sub (1 , 500 ) .. " ..."
149+ end
150+
151+ local downloadUrl = data .html_url or
152+ string.format (" https://github.com/%s/%s/releases/latest" ,
153+ UpdateChecker .GITHUB_OWNER , UpdateChecker .GITHUB_REPO )
154+
155+ -- Store for later use
156+ prefs .latestVersion = remoteVersion
157+ prefs .latestVersionUrl = downloadUrl
158+
159+ log :info (" UpdateChecker.checkForUpdates - new version available!" )
160+
161+ local result = LrDialogs .confirm (
162+ " Update Available" ,
163+ string.format (
164+ " A new version of Piwigo Publisher is available!\n\n " ..
165+ " Current version: %s\n " ..
166+ " New version: %s\n\n " ..
167+ " Changes:\n %s\n\n " ..
168+ " Would you like to download it now?" ,
169+ pluginVersion ,
170+ remoteVersion ,
171+ changelog
172+ ),
173+ " Download" ,
174+ " Later"
175+ )
176+
177+ if result == " ok" then
178+ UpdateChecker .openDownloadPage (downloadUrl )
179+ end
180+ else
181+ log :info (" UpdateChecker.checkForUpdates - already up to date" )
182+ if not silent then
183+ LrDialogs .message (
184+ " No Updates Available" ,
185+ string.format (" You are running the latest version (%s)." , pluginVersion ),
186+ " info"
187+ )
188+ end
189+ end
190+ end )
191+ end
192+
193+ -- *************************************************
194+ function UpdateChecker .openDownloadPage (url )
195+ log :info (" UpdateChecker.openDownloadPage - opening: " .. url )
196+ LrHttp .openUrlInBrowser (url )
197+
198+ LrDialogs .message (
199+ " Download Started" ,
200+ " The download page has been opened in your browser.\n\n " ..
201+ " After downloading:\n " ..
202+ " 1. Quit Lightroom Classic\n " ..
203+ " 2. Replace the plugin folder with the new version\n " ..
204+ " 3. Restart Lightroom Classic" ,
205+ " info"
206+ )
207+ end
208+
209+ -- *************************************************
210+ function UpdateChecker .getUpdateStatus ()
211+ -- Returns a status string for display in Plugin Manager
212+ local latestVersion = prefs .latestVersion
213+ local currentNum = UpdateChecker .parseVersion (pluginVersion )
214+ local latestNum = UpdateChecker .parseVersion (latestVersion or " " )
215+
216+ if latestNum > currentNum then
217+ return string.format (" Update available: %s" , latestVersion )
218+ else
219+ return " Up to date"
220+ end
221+ end
222+
223+ return UpdateChecker
0 commit comments