From bc286dfb908d89c5638e6c6c8aa165c59688e637 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Thu, 19 Mar 2026 16:59:50 +0100 Subject: [PATCH 1/3] Download pretixkiosk_screensaver_image during Settings-Sync --- .../sync/BaseSingleObjectSyncAdapter.kt | 1 + .../libpretixsync/sync/EventSyncAdapter.kt | 2 + .../sync/InvoiceSettingsSyncAdapter.kt | 2 + .../pretix/libpretixsync/sync/OrderCleanup.kt | 2 +- .../libpretixsync/sync/SettingsSyncAdapter.kt | 82 +++++++++++++++++++ .../libpretixsync/sync/SubEventSyncAdapter.kt | 2 + .../libpretixsync/sync/SyncManager.java | 6 +- .../check/AsyncCheckProviderTest.kt | 6 +- 8 files changed, 96 insertions(+), 7 deletions(-) diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/BaseSingleObjectSyncAdapter.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/BaseSingleObjectSyncAdapter.kt index 7e6f268e..92dc53cb 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/BaseSingleObjectSyncAdapter.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/BaseSingleObjectSyncAdapter.kt @@ -15,6 +15,7 @@ import org.json.JSONObject abstract class BaseSingleObjectSyncAdapter( protected var db: SyncDatabase, protected var eventSlug: String, + protected var fileStorage: FileStorage, protected var key: String, protected var api: PretixApi, protected var syncCycleId: String, diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/EventSyncAdapter.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/EventSyncAdapter.kt index 365cae0e..aa659c90 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/EventSyncAdapter.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/EventSyncAdapter.kt @@ -13,6 +13,7 @@ import org.json.JSONObject class EventSyncAdapter( db: SyncDatabase, + fileStorage: FileStorage, eventSlug: String, key: String, api: PretixApi, @@ -20,6 +21,7 @@ class EventSyncAdapter( feedback: ProgressFeedback? = null, ) : BaseSingleObjectSyncAdapter( db = db, + fileStorage = fileStorage, eventSlug = eventSlug, key = key, api = api, diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/InvoiceSettingsSyncAdapter.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/InvoiceSettingsSyncAdapter.kt index bb729f2c..a4b7bc44 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/InvoiceSettingsSyncAdapter.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/InvoiceSettingsSyncAdapter.kt @@ -6,6 +6,7 @@ import eu.pretix.libpretixsync.sync.SyncManager.ProgressFeedback class InvoiceSettingsSyncAdapter( db: SyncDatabase, + fileStorage: FileStorage, eventSlug: String, key: String, api: PretixApi, @@ -13,6 +14,7 @@ class InvoiceSettingsSyncAdapter( feedback: ProgressFeedback? = null, ) : SettingsSyncAdapter( db = db, + fileStorage = fileStorage, eventSlug = eventSlug, key = key, api = api, diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/OrderCleanup.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/OrderCleanup.kt index 88b3034b..af5d963b 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/OrderCleanup.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/OrderCleanup.kt @@ -16,7 +16,7 @@ class OrderCleanup(val db: SyncDatabase, val fileStorage: FileStorage, val api: return subeventsDeletionDate[sid] } try { - SubEventSyncAdapter(db, eventSlug, sid.toString(), api, syncCycleId) { }.download() + SubEventSyncAdapter(db, fileStorage, eventSlug, sid.toString(), api, syncCycleId) { }.download() } catch (e: JSONException) { subeventsDeletionDate[sid] = null return null diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt index 0fb1ec0f..939ed25b 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt @@ -1,14 +1,19 @@ package eu.pretix.libpretixsync.sync import app.cash.sqldelight.TransactionWithoutReturn +import eu.pretix.libpretixsync.api.ApiException import eu.pretix.libpretixsync.api.PretixApi import eu.pretix.libpretixsync.sqldelight.Settings import eu.pretix.libpretixsync.sqldelight.SyncDatabase import eu.pretix.libpretixsync.sync.SyncManager.ProgressFeedback +import eu.pretix.libpretixsync.utils.HashUtils import org.json.JSONObject +import java.io.IOException +import java.io.OutputStream open class SettingsSyncAdapter( db: SyncDatabase, + fileStorage: FileStorage, eventSlug: String, key: String, api: PretixApi, @@ -16,6 +21,7 @@ open class SettingsSyncAdapter( feedback: ProgressFeedback? = null, ) : BaseSingleObjectSyncAdapter( db = db, + fileStorage = fileStorage, eventSlug = eventSlug, key = key, api = api, @@ -43,6 +49,11 @@ open class SettingsSyncAdapter( override fun getJSON(obj: Settings): JSONObject = JSONObject(obj.json_data!!) override fun insert(jsonobj: JSONObject) { + var jsonobj = jsonobj + listOf("pretixkiosk_screensaver_image").forEach { fieldName -> + jsonobj = processAndUpdateJSONdataWithPicutre(jsonobj, fieldName, null) + } + db.settingsQueries.insert( slug = eventSlug, address = jsonobj.optString("invoice_address_from"), @@ -58,6 +69,12 @@ open class SettingsSyncAdapter( } override fun update(obj: Settings, jsonobj: JSONObject) { + var jsonobj = jsonobj + val objjsondata = JSONObject(obj.json_data) + listOf("pretixkiosk_screensaver_image").forEach { fieldName -> + jsonobj = processAndUpdateJSONdataWithPicutre(jsonobj, fieldName, if (objjsondata.isNull(fieldName)) null else objjsondata.getString(fieldName)) + } + db.settingsQueries.updateFromJson( address = jsonobj.optString("invoice_address_from"), city = jsonobj.optString("invoice_address_from_city"), @@ -75,4 +92,69 @@ open class SettingsSyncAdapter( override fun runInTransaction(body: TransactionWithoutReturn.() -> Unit) { db.settingsQueries.transaction(false, body) } + + private fun processAndUpdateJSONdataWithPicutre(jsonobj: JSONObject, fieldName: String, oldFilename: String?): JSONObject { + if (jsonobj.has(fieldName)) { + val pictureFilename = processPicture(jsonobj, fieldName, oldFilename) + + jsonobj.put(fieldName, pictureFilename) + } + + return jsonobj + } + + private fun processPicture(jsonobj: JSONObject, fieldName: String, oldFilename: String?): String? { + val remote_filename: String = jsonobj.optString(fieldName) + var result: String? = null; + + if (remote_filename.startsWith("http")) { + val hash = HashUtils.toSHA1(remote_filename.toByteArray()) + val local_filename = + "settings_" + fieldName + "_" + hash + remote_filename.substring( + remote_filename.lastIndexOf(".") + ) + if (oldFilename != null && oldFilename != local_filename) { + fileStorage.delete(oldFilename) + result = null + } + if (!fileStorage.contains(local_filename)) { + try { + val file = api.downloadFile(remote_filename) + + val outStream: OutputStream = fileStorage.writeStream(local_filename) + val inStream = file.response.body?.byteStream() + + if (inStream == null) { + outStream.close() + throw IOException() + } + + val buffer = ByteArray(1444) + var byteread: Int + while (inStream.read(buffer).also { byteread = it } != -1) { + outStream.write(buffer, 0, byteread) + } + inStream.close() + outStream.close() + result = local_filename + } catch (e: ApiException) { + // TODO: What to do? + e.printStackTrace() + } catch (e: IOException) { + // TODO: What to do? + e.printStackTrace() + fileStorage.delete(local_filename) + } + } else { + result = local_filename + } + } else { + if (oldFilename != null) { + fileStorage.delete(oldFilename) + result = null + } + } + + return result + } } diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SubEventSyncAdapter.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SubEventSyncAdapter.kt index b25e0b84..94668fbb 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SubEventSyncAdapter.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SubEventSyncAdapter.kt @@ -13,6 +13,7 @@ import org.json.JSONObject class SubEventSyncAdapter( db: SyncDatabase, + fileStorage: FileStorage, eventSlug: String, key: String, api: PretixApi, @@ -20,6 +21,7 @@ class SubEventSyncAdapter( feedback: ProgressFeedback? = null, ) : BaseSingleObjectSyncAdapter( db = db, + fileStorage = fileStorage, eventSlug = eventSlug, key = key, api = api, diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SyncManager.java b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SyncManager.java index 4cfb987b..ada47bee 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SyncManager.java +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SyncManager.java @@ -403,7 +403,7 @@ protected void downloadData(ProgressFeedback feedback, Boolean skip_orders, Stri subEvent = overrideSubeventId; } try { - download(new EventSyncAdapter(db, eventSlug, eventSlug, api, configStore.getSyncCycleId(), feedback)); + download(new EventSyncAdapter(db, fileStorage, eventSlug, eventSlug, api, configStore.getSyncCycleId(), feedback)); } catch (PermissionDeniedApiException e) { e.eventSlug = eventSlug; throw e; @@ -444,12 +444,12 @@ protected void downloadData(ProgressFeedback feedback, Boolean skip_orders, Stri } try { - download(new SettingsSyncAdapter(db, eventSlug, eventSlug, api, configStore.getSyncCycleId(), feedback)); + download(new SettingsSyncAdapter(db, fileStorage, eventSlug, eventSlug, api, configStore.getSyncCycleId(), feedback)); } catch (ApiException e) { // Older pretix installations // We don't need these on pretixSCAN, so we can save some traffic if (profile == Profile.PRETIXPOS) { - download(new InvoiceSettingsSyncAdapter(db, eventSlug, eventSlug, api, configStore.getSyncCycleId(), feedback)); + download(new InvoiceSettingsSyncAdapter(db, fileStorage, eventSlug, eventSlug, api, configStore.getSyncCycleId(), feedback)); } } } diff --git a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt index bd22efe0..d1ca1ee7 100644 --- a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt +++ b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt @@ -34,8 +34,8 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { fakeApi = FakePretixApi() p = AsyncCheckProvider(configStore!!, db) - EventSyncAdapter(db, "demo", "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/event1.json")) - EventSyncAdapter(db, "demo2", "demo2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/event2.json")) + EventSyncAdapter(db, FakeFileStorage(), "demo", "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/event1.json")) + EventSyncAdapter(db, FakeFileStorage(), "demo2", "demo2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/event2.json")) ItemSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/item1.json")) ItemSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/item2.json")) ItemSyncAdapter(db, FakeFileStorage(), "demo", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/item3.json")) @@ -64,7 +64,7 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { CheckInListSyncAdapter(db, FakeFileStorage(), "demo2", fakeApi!!, "", null, 0).standaloneRefreshFromJSON( jsonResource("checkinlists/event2-list7.json") ) - SubEventSyncAdapter(db, "demo", "14", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("subevents/subevent1.json")) + SubEventSyncAdapter(db, FakeFileStorage(), "demo", "14", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("subevents/subevent1.json")) val osa = OrderSyncAdapter(db, FakeFileStorage(), "demo", 0, true, false, fakeApi!!, "", null) osa.standaloneRefreshFromJSON(jsonResource("orders/order1.json")) From 539958aef4af5f43b27d80fd05d85e9efeaf9aad Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Wed, 25 Mar 2026 13:14:18 +0100 Subject: [PATCH 2/3] Fix spelling in function name Co-authored-by: robbi5 --- .../eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt index 939ed25b..e152096a 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/sync/SettingsSyncAdapter.kt @@ -51,7 +51,7 @@ open class SettingsSyncAdapter( override fun insert(jsonobj: JSONObject) { var jsonobj = jsonobj listOf("pretixkiosk_screensaver_image").forEach { fieldName -> - jsonobj = processAndUpdateJSONdataWithPicutre(jsonobj, fieldName, null) + jsonobj = processAndUpdateJSONdataWithPicture(jsonobj, fieldName, null) } db.settingsQueries.insert( @@ -72,7 +72,7 @@ open class SettingsSyncAdapter( var jsonobj = jsonobj val objjsondata = JSONObject(obj.json_data) listOf("pretixkiosk_screensaver_image").forEach { fieldName -> - jsonobj = processAndUpdateJSONdataWithPicutre(jsonobj, fieldName, if (objjsondata.isNull(fieldName)) null else objjsondata.getString(fieldName)) + jsonobj = processAndUpdateJSONdataWithPicture(jsonobj, fieldName, if (objjsondata.isNull(fieldName)) null else objjsondata.getString(fieldName)) } db.settingsQueries.updateFromJson( @@ -93,7 +93,7 @@ open class SettingsSyncAdapter( db.settingsQueries.transaction(false, body) } - private fun processAndUpdateJSONdataWithPicutre(jsonobj: JSONObject, fieldName: String, oldFilename: String?): JSONObject { + private fun processAndUpdateJSONdataWithPicture(jsonobj: JSONObject, fieldName: String, oldFilename: String?): JSONObject { if (jsonobj.has(fieldName)) { val pictureFilename = processPicture(jsonobj, fieldName, oldFilename) From 48dcfd85760fd5ca19a43280d89f9e4f489a6cd4 Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Thu, 18 Jun 2026 17:08:55 +0200 Subject: [PATCH 3/3] Update AsyncCheckProviderReusableMediumTest.kt to also use FakeFileStorage --- .../check/AsyncCheckProviderReusableMediumTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderReusableMediumTest.kt b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderReusableMediumTest.kt index 1269b2bf..003d8e5c 100644 --- a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderReusableMediumTest.kt +++ b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderReusableMediumTest.kt @@ -27,8 +27,8 @@ class AsyncCheckProviderReusableMediumTest : BaseDatabaseTest() { fakeApi = FakePretixApi("mtrmt") p = AsyncCheckProvider(configStore!!, db) - EventSyncAdapter(db, "event1", "event1", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/rmevent1.json")) - EventSyncAdapter(db, "event2", "event2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/rmevent2.json")) + EventSyncAdapter(db, FakeFileStorage(),"event1", "event1", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/rmevent1.json")) + EventSyncAdapter(db, FakeFileStorage(),"event2", "event2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("events/rmevent2.json")) ItemSyncAdapter(db, FakeFileStorage(), "event1", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/rmevent1-item1.json")) ItemSyncAdapter(db, FakeFileStorage(), "event2", fakeApi!!, "", null).standaloneRefreshFromJSON(jsonResource("items/rmevent2-item1.json")) CheckInListSyncAdapter(db, FakeFileStorage(), "event1", fakeApi!!, "", null, 0).standaloneRefreshFromJSON(