v1.4.3 - persistent device ID via localStorage so refresh reuses same client slot

This commit is contained in:
jessikitty
2026-06-10 07:02:57 +00:00
parent 581f6ee87b
commit 10f9683e81
3 changed files with 19 additions and 20 deletions
+9 -17
View File
@@ -9,30 +9,22 @@ services:
- "3030:3000"
environment:
# REQUIRED
- IMMICH_URL=http://your-immich-server:2283
- IMMICH_API_KEY=your-api-key-here
- IMMICH_URL=http://10.0.0.40:2283
- IMMICH_API_KEY=aQAm76nwCi5I68vFWz18irMmnyrzI5yzcb74rXvt4
- ADMIN_USERNAME=jessikitty
- ADMIN_PASSWORD=23Pinkpr!ncesses
- FRAMBE_API_TOKEN=whosethatgirl-itsjess
# Slideshow
- SLIDESHOW_INTERVAL=30
- TRANSITION_DURATION=2
- SLIDESHOW_INTERVAL=300
- TRANSITION_DURATION=5
- IMAGE_FIT=contain
- SHUFFLE=true
- BACKGROUND_BLUR=true
- REFRESH_INTERVAL=300 # Seconds between album/person refresh checks
- REFRESH_INTERVAL=300 # Seconds between album/person refresh checks
# Overlays
- SHOW_CLOCK=true
- SHOW_DATE=true
- SHOW_EXIF=true
- SHOW_EXIF=false
- SHOW_PROGRESS=true
# Admin Authentication (leave ADMIN_PASSWORD blank to disable login)
- ADMIN_USERNAME=admin
# - ADMIN_PASSWORD=changeme
# API Token for external access (Home Assistant, scripts, etc.)
# - FRAMBE_API_TOKEN=your-secret-token-here
# Auto-start (optional — or use URL params instead)
# - ALBUM_ID=
# - SHOW_FAVORITES_ONLY=false
+2 -1
View File
@@ -7,10 +7,11 @@
var currentVideoPlaying = false, pileCanvas, pileCtx;
var FRAME_PAD_RATIO = 0.03, FRAME_BOTTOM_RATIO = 0.10, FRAME_COLOR = '#ede8df';
var wsConn = null, clientId = null, isSleeping = false;
var persistentId = (function(){ var k='frambe_pid'; var v=localStorage.getItem(k); if(vi docker-compose.yml ){ v='fp-'+Math.random().toString(36).substr(2,9)+'-'+Date.now().toString(36); localStorage.setItem(k,v); } return v; })();
var $setupScreen=document.getElementById('setup-screen'),$slideshowScreen=document.getElementById('slideshow-screen'),$connectionStatus=document.getElementById('connection-status'),$setupContent=document.getElementById('setup-content'),$setupError=document.getElementById('setup-error'),$errorDetail=document.getElementById('error-detail'),$albumsList=document.getElementById('albums-list'),$btnStart=document.getElementById('btn-start'),$bgBlur=document.getElementById('bg-blur'),$mainFrame=document.getElementById('main-frame'),$mainPhoto=document.getElementById('main-photo'),$mainVideo=document.getElementById('main-video'),$clock=document.getElementById('clock'),$dateDisplay=document.getElementById('date-display'),$exifInfo=document.getElementById('exif-info'),$progressFill=document.getElementById('progress-fill'),$overlay=document.getElementById('overlay'),$btnSettings=document.getElementById('btn-settings'),$progressBar=document.getElementById('progress-bar');
// === WEBSOCKET ===
function connectWebSocket(){var proto=location.protocol==='https:'?'wss:':'ws:';wsConn=new WebSocket(proto+'//'+location.host+'/ws');wsConn.onopen=function(){console.log('[Frambe] WebSocket connected');wsConn.send(JSON.stringify({type:'register',role:'frame',status:isRunning?'playing':(isSleeping?'sleeping':'idle'),config:getCurrentConfig()}));};wsConn.onmessage=function(e){try{var msg=JSON.parse(e.data);if(msg.type==='welcome'){clientId=msg.clientId;console.log('[Frambe] Registered as '+clientId);}else if(msg.type==='command'){handleRemoteCommand(msg.action,msg.payload||{});}}catch(err){}};wsConn.onclose=function(){setTimeout(connectWebSocket,5000);};}
function connectWebSocket(){var proto=location.protocol==='https:'?'wss:':'ws:';wsConn=new WebSocket(proto+'//'+location.host+'/ws');wsConn.onopen=function(){console.log('[Frambe] WebSocket connected');wsConn.send(JSON.stringify({type:'register',role:'frame',persistentId:persistentId,status:isRunning?'playing':(isSleeping?'sleeping':'idle'),config:getCurrentConfig()}));};wsConn.onmessage=function(e){try{var msg=JSON.parse(e.data);if(msg.type==='welcome'){clientId=msg.clientId;console.log('[Frambe] Registered as '+clientId);}else if(msg.type==='command'){handleRemoteCommand(msg.action,msg.payload||{});}}catch(err){}};wsConn.onclose=function(){setTimeout(connectWebSocket,5000);};}
function sendStatus(s){if(wsConn&&wsConn.readyState===WebSocket.OPEN)wsConn.send(JSON.stringify({type:'status',status:s,currentAlbum:selectedAlbumId,config:getCurrentConfig()}));}
function getCurrentConfig(){return{slideshowInterval:config.slideshowInterval,showClock:config.showClock,showDate:config.showDate,showExif:config.showExif,showProgress:config.showProgress};}
function handleRemoteCommand(action,payload){console.log('[Frambe] Remote: '+action);switch(action){case'setSource':selectedSource=payload.source;selectedAlbumId=payload.albumId||null;selectedPersonId=payload.personId||null;if(isSleeping)wakeUp();if(isRunning){clearTimeout(slideshowTimer);stopVideo();}doStartSlideshow();break;case'start':if(isSleeping)wakeUp();if(!isRunning&&selectedSource)doStartSlideshow();break;case'stop':if(isRunning)exitSlideshowInternal();sendStatus('idle');break;case'next':if(isRunning)showNextAsset();break;case'prev':if(isRunning)showPrevAsset();break;case'sleep':goToSleep();break;case'wake':wakeUp();break;case'refresh':location.reload();break;case'setConfig':applyConfigChange(payload);break;}}
+8 -2
View File
@@ -90,8 +90,14 @@ wss.on('connection', (ws, req) => {
log('WS admin: ' + ip);
ws.send(JSON.stringify({ type: 'clientList', clients: getClientList() }));
} else {
clients.set(id, { id, ws, name:'', ip, userAgent:ua, connectedAt:now, firstSeen:now, lastSeen:Date.now(), status:msg.status||'idle', config:msg.config||{}, source:msg.source||null });
log('WS frame: ' + id + ' (' + ip + ')');
const pid = msg.persistentId || null;
const existing = pid ? Array.from(clients.values()).find(c => c.persistentId === pid) : null;
const effectiveId = existing ? existing.id : id;
const firstName = existing ? existing.name : '';
const firstSeen = existing ? existing.firstSeen : now;
if (existing) { clients.delete(Array.from(clients.entries()).find(([,c]) => c.persistentId === pid)?.[0]); }
clients.set(effectiveId, { id:effectiveId, persistentId:pid, ws, name:firstName, ip, userAgent:ua, connectedAt:now, firstSeen, lastSeen:Date.now(), status:msg.status||'idle', config:msg.config||{}, source:msg.source||null });
log('WS frame: ' + effectiveId + (pid?' [persistent]':'') + ' (' + ip + ')');
broadcastAdminClients();
}
} else if (msg.type === 'ping') {