Feature: Custom poster for standby
This commit is contained in:
parent
e7931174de
commit
03474298a9
@ -1,5 +1,5 @@
|
||||
const DB_NAME = 'PartyMediaDB';
|
||||
const DB_VERSION = 1;
|
||||
const DB_VERSION = 2;
|
||||
|
||||
export const MediaStorage = {
|
||||
open: () => {
|
||||
@ -9,6 +9,7 @@ export const MediaStorage = {
|
||||
const db = e.target.result;
|
||||
if (!db.objectStoreNames.contains('music')) db.createObjectStore('music');
|
||||
if (!db.objectStoreNames.contains('tapes')) db.createObjectStore('tapes');
|
||||
if (!db.objectStoreNames.contains('poster')) db.createObjectStore('poster');
|
||||
};
|
||||
request.onsuccess = (e) => resolve(e.target.result);
|
||||
request.onerror = (e) => reject(e);
|
||||
@ -42,10 +43,24 @@ export const MediaStorage = {
|
||||
req.onerror = () => resolve([]);
|
||||
});
|
||||
},
|
||||
savePoster: async (file) => {
|
||||
const db = await MediaStorage.open();
|
||||
const tx = db.transaction('poster', 'readwrite');
|
||||
tx.objectStore('poster').put(file, 'currentPoster');
|
||||
},
|
||||
getPoster: async () => {
|
||||
const db = await MediaStorage.open();
|
||||
return new Promise((resolve) => {
|
||||
const req = db.transaction('poster', 'readonly').objectStore('poster').get('currentPoster');
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => resolve(null);
|
||||
});
|
||||
},
|
||||
clear: async () => {
|
||||
const db = await MediaStorage.open();
|
||||
const tx = db.transaction(['music', 'tapes'], 'readwrite');
|
||||
const tx = db.transaction(['music', 'tapes', 'poster'], 'readwrite');
|
||||
tx.objectStore('music').clear();
|
||||
tx.objectStore('tapes').clear();
|
||||
tx.objectStore('poster').clear();
|
||||
}
|
||||
};
|
||||
@ -136,6 +136,40 @@ export class ConfigUI extends SceneFeature {
|
||||
});
|
||||
statusContainer.appendChild(this.tapeList);
|
||||
|
||||
// Load Poster Button
|
||||
const loadPosterBtn = document.createElement('button');
|
||||
loadPosterBtn.innerText = 'Load Poster';
|
||||
Object.assign(loadPosterBtn.style, {
|
||||
marginTop: '10px',
|
||||
padding: '8px',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: '#555',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
fontSize: '14px'
|
||||
});
|
||||
|
||||
const posterInput = document.createElement('input');
|
||||
posterInput.type = 'file';
|
||||
posterInput.accept = 'image/*';
|
||||
posterInput.style.display = 'none';
|
||||
posterInput.onchange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
if (state.posterImage) URL.revokeObjectURL(state.posterImage);
|
||||
state.posterImage = URL.createObjectURL(file);
|
||||
MediaStorage.savePoster(file);
|
||||
showStandbyScreen();
|
||||
}
|
||||
};
|
||||
document.body.appendChild(posterInput);
|
||||
|
||||
loadPosterBtn.onclick = () => {
|
||||
posterInput.click();
|
||||
};
|
||||
statusContainer.appendChild(loadPosterBtn);
|
||||
|
||||
// Load Tapes Button
|
||||
const loadTapesBtn = document.createElement('button');
|
||||
loadTapesBtn.id = 'loadTapeButton';
|
||||
@ -235,6 +269,11 @@ export class ConfigUI extends SceneFeature {
|
||||
}
|
||||
}
|
||||
|
||||
if (state.posterImage) {
|
||||
URL.revokeObjectURL(state.posterImage);
|
||||
state.posterImage = null;
|
||||
}
|
||||
|
||||
showStandbyScreen();
|
||||
|
||||
const defaults = {
|
||||
@ -259,6 +298,14 @@ export class ConfigUI extends SceneFeature {
|
||||
document.body.appendChild(container);
|
||||
this.container = container;
|
||||
this.updateStatus();
|
||||
|
||||
// Restore poster
|
||||
MediaStorage.getPoster().then(file => {
|
||||
if (file) {
|
||||
state.posterImage = URL.createObjectURL(file);
|
||||
showStandbyScreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateStatus() {
|
||||
|
||||
@ -298,6 +298,11 @@ export class ProjectionScreen extends SceneFeature {
|
||||
export function showStandbyScreen() {
|
||||
if (projectionScreenInstance) projectionScreenInstance.isVisualizerActive = false;
|
||||
|
||||
let texture;
|
||||
|
||||
if (state.posterImage) {
|
||||
texture = new THREE.TextureLoader().load(state.posterImage);
|
||||
} else {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 1024;
|
||||
canvas.height = 576;
|
||||
@ -337,7 +342,8 @@ export function showStandbyScreen() {
|
||||
ctx.fillStyle = '#cccccc';
|
||||
ctx.fillText("WAITING FOR PARTY START", canvas.width / 2, canvas.height / 2 + 50);
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas);
|
||||
texture = new THREE.CanvasTexture(canvas);
|
||||
}
|
||||
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
map: texture,
|
||||
|
||||
@ -44,6 +44,7 @@ export function initState() {
|
||||
videoUrls: [],
|
||||
videoFilenames: [],
|
||||
currentVideoIndex: -1,
|
||||
posterImage: null,
|
||||
|
||||
// Scene constants
|
||||
originalLampIntensity: 0.3,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user