179 lines
4.9 KiB
C++
179 lines
4.9 KiB
C++
#include "photo_queue.h"
|
||
#include "immich_client.h"
|
||
#include <ArduinoJson.h>
|
||
#include "config.h"
|
||
|
||
void PhotoQueue::begin(ImmichClient& client, SettingsManager& settings) {
|
||
_client = &client;
|
||
_settings = &settings;
|
||
|
||
Settings s = _settings->get();
|
||
_cursor = s.queue_cursor;
|
||
}
|
||
|
||
bool PhotoQueue::sync() {
|
||
if (_client == nullptr || _settings == nullptr) return false;
|
||
|
||
Settings s = _settings->get();
|
||
std::vector<String> newIds;
|
||
|
||
// Get selected album IDs
|
||
std::vector<String> albumIds = getSelectedAlbumIds();
|
||
|
||
if (albumIds.empty()) {
|
||
// "All photos" mode — fetch from all albums
|
||
auto albums = _client->fetchAlbums();
|
||
for (auto& album : albums) {
|
||
auto ids = _client->fetchAlbumAssetIds(album.id);
|
||
for (auto& id : ids) {
|
||
newIds.push_back(id);
|
||
}
|
||
}
|
||
} else {
|
||
// Fetch from selected albums only
|
||
for (auto& albumId : albumIds) {
|
||
auto ids = _client->fetchAlbumAssetIds(albumId);
|
||
for (auto& id : ids) {
|
||
newIds.push_back(id);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Deduplicate
|
||
std::sort(newIds.begin(), newIds.end());
|
||
newIds.erase(std::unique(newIds.begin(), newIds.end()), newIds.end());
|
||
|
||
if (newIds.empty()) {
|
||
Serial.println("[queue] No assets found");
|
||
return false;
|
||
}
|
||
|
||
_queue = newIds;
|
||
|
||
// Apply cycling mode
|
||
switch (s.cycle_mode) {
|
||
case CycleMode::Random:
|
||
applyFavoritesWeighting();
|
||
shuffle();
|
||
break;
|
||
case CycleMode::Chronological:
|
||
sortChronological(false);
|
||
break;
|
||
case CycleMode::ReverseChronological:
|
||
sortChronological(true);
|
||
break;
|
||
case CycleMode::FavoritesWeighted:
|
||
applyFavoritesWeighting();
|
||
shuffle();
|
||
break;
|
||
}
|
||
|
||
// Clamp cursor
|
||
if (_cursor >= _queue.size()) {
|
||
_cursor = 0;
|
||
}
|
||
|
||
_lastSyncTime = millis();
|
||
Serial.printf("[queue] Synced: %d assets, cursor at %d\n", _queue.size(), _cursor);
|
||
return true;
|
||
}
|
||
|
||
String PhotoQueue::next() {
|
||
if (_queue.empty()) return "";
|
||
if (_cursor >= _queue.size()) {
|
||
_cursor = 0;
|
||
shuffle(); // Reshuffle on wrap for random mode
|
||
}
|
||
String id = _queue[_cursor++];
|
||
|
||
// Persist cursor
|
||
_settings->saveField("queue_cursor", static_cast<uint32_t>(_cursor));
|
||
|
||
return id;
|
||
}
|
||
|
||
String PhotoQueue::random() {
|
||
if (_queue.empty()) return "";
|
||
size_t idx = ::random(0, _queue.size());
|
||
return _queue[idx];
|
||
}
|
||
|
||
String PhotoQueue::current() {
|
||
if (_queue.empty()) return "";
|
||
size_t idx = (_cursor > 0) ? _cursor - 1 : 0;
|
||
return _queue[idx];
|
||
}
|
||
|
||
size_t PhotoQueue::size() {
|
||
return _queue.size();
|
||
}
|
||
|
||
bool PhotoQueue::needsResync() {
|
||
if (_lastSyncTime == 0) return true;
|
||
unsigned long elapsed = millis() - _lastSyncTime;
|
||
return elapsed >= (QUEUE_RESYNC_HOURS * 3600000UL);
|
||
}
|
||
|
||
String PhotoQueue::findPortraitPair(size_t startIndex) {
|
||
// Look ahead up to PORTRAIT_LOOKAHEAD items for another portrait
|
||
for (size_t i = 1; i <= PORTRAIT_LOOKAHEAD && (startIndex + i) < _queue.size(); i++) {
|
||
// We'd need asset info to determine portrait status
|
||
// This will be called by the display task which fetches AssetInfo
|
||
// Return the ID — caller checks isPortrait from AssetInfo
|
||
return _queue[startIndex + i];
|
||
}
|
||
return "";
|
||
}
|
||
|
||
void PhotoQueue::shuffle() {
|
||
for (size_t i = _queue.size() - 1; i > 0; i--) {
|
||
size_t j = ::random(0, i + 1);
|
||
std::swap(_queue[i], _queue[j]);
|
||
}
|
||
}
|
||
|
||
void PhotoQueue::sortChronological(bool reverse) {
|
||
// For chronological sort, we'd need timestamps which we don't store in the queue
|
||
// The IDs from Immich are UUIDs, not sortable by time
|
||
// For now, keep the order returned by Immich (which is chronological within albums)
|
||
if (reverse) {
|
||
std::reverse(_queue.begin(), _queue.end());
|
||
}
|
||
}
|
||
|
||
void PhotoQueue::applyFavoritesWeighting() {
|
||
if (_client == nullptr) return;
|
||
|
||
Settings s = _settings->get();
|
||
if (s.cycle_mode != CycleMode::FavoritesWeighted) return;
|
||
|
||
auto favorites = _client->fetchFavoriteAssetIds();
|
||
// Add favorites 2 more times (total 3× appearance)
|
||
for (auto& fav : favorites) {
|
||
// Only add if already in queue
|
||
bool inQueue = false;
|
||
for (auto& id : _queue) {
|
||
if (id == fav) { inQueue = true; break; }
|
||
}
|
||
if (inQueue) {
|
||
_queue.push_back(fav);
|
||
_queue.push_back(fav);
|
||
}
|
||
}
|
||
}
|
||
|
||
std::vector<String> PhotoQueue::getSelectedAlbumIds() {
|
||
std::vector<String> ids;
|
||
Settings s = _settings->get();
|
||
|
||
JsonDocument doc;
|
||
DeserializationError err = deserializeJson(doc, s.albums_json);
|
||
if (err) return ids;
|
||
|
||
JsonArray arr = doc.as<JsonArray>();
|
||
for (JsonVariant v : arr) {
|
||
ids.push_back(v.as<String>());
|
||
}
|
||
return ids;
|
||
}
|