Impression Mitsubishi K60 + cadres + interface admin distante
- Intégration selphy_print (backend CUPS dyesub Mitsubishi CP-K60DW-S) - Formats impression : 15x20, 10x15, 10x15 2-strips avec cadre et orientation portrait/paysage - Cadres d'impression par format (strip/10x15/15x20) : upload, sélection, aperçu live - Cadres démo générés : pellicule noir/vintage/couleurs + bordures classique/doré/rose gold - Écran de choix de cadre avant capture (clic direct, aperçu grand format) - Filtres réduits à 3 (Couleur, N&B, Sépia) et appliqués sur tous les strips - Port 80 via authbind, route /admin avec redirection auto depuis poste distant - Autologin LightDM corrigé (pam-autologin-service) - Bouton évacuer bourrage papier dans admin - Fix : _gp_lock manquant dans Camera.__init__ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ async function chargerAdmin() {
|
||||
chargerCompteur();
|
||||
chargerDestinations();
|
||||
chargerCadres();
|
||||
chargerCadresImpression();
|
||||
chargerAnimations();
|
||||
chargerFonctionnalites();
|
||||
chargerEmailAdmin();
|
||||
@@ -57,6 +58,10 @@ async function chargerMateriel() {
|
||||
// Imprimantes
|
||||
await rafraichirImprimantes();
|
||||
if (imp.imprimante) document.getElementById('admin-imprimante').value = imp.imprimante;
|
||||
if (imp.format) document.getElementById('admin-format-papier').value = imp.format;
|
||||
const fmt = (imp.format || '15x20').replace('-2up', '');
|
||||
const orient = (imp.orientations || {})[fmt] || 'portrait';
|
||||
document.querySelector(`input[name="admin-orientation"][value="${orient}"]`).checked = true;
|
||||
setValue('admin-copies-max', imp.copies_max || 5);
|
||||
}
|
||||
|
||||
@@ -78,6 +83,19 @@ async function reconnecterCamera() {
|
||||
chargerMateriel();
|
||||
}
|
||||
|
||||
function _getOrientations() {
|
||||
const fmt = (getValue('admin-format-papier') || '15x20').replace('-2up', '');
|
||||
const orient = document.querySelector('input[name="admin-orientation"]:checked')?.value || 'portrait';
|
||||
// On conserve les orientations des autres formats depuis la config courante
|
||||
const existing = (config.impression || {}).orientations || {};
|
||||
return { ...existing, [fmt]: orient };
|
||||
}
|
||||
|
||||
async function evacuerBourrage() {
|
||||
const r = await apiPost('/api/imprimante/evacuer', {});
|
||||
afficherStatut(r.message || 'Imprimante réactivée', r.succes ? 'succes' : 'erreur');
|
||||
}
|
||||
|
||||
async function sauvegarderMateriel() {
|
||||
await apiPost('/api/config', {
|
||||
camera: {
|
||||
@@ -86,7 +104,9 @@ async function sauvegarderMateriel() {
|
||||
},
|
||||
impression: {
|
||||
imprimante: getValue('admin-imprimante'),
|
||||
format: getValue('admin-format-papier'),
|
||||
copies_max: parseInt(getValue('admin-copies-max')) || 5,
|
||||
orientations: _getOrientations(),
|
||||
},
|
||||
});
|
||||
afficherStatut('Materiel sauvegarde', 'succes');
|
||||
@@ -208,7 +228,108 @@ async function sauvegarderDestinations() {
|
||||
afficherStatut('Destinations sauvegardees', 'succes');
|
||||
}
|
||||
|
||||
// === CADRES ===
|
||||
// === CADRES IMPRESSION (par format) ===
|
||||
|
||||
const FORMATS_IMPRESSION = ['strip', '10x15', '15x20'];
|
||||
|
||||
async function chargerCadresImpression() {
|
||||
for (const fmt of FORMATS_IMPRESSION) {
|
||||
await chargerCadresFormat(fmt);
|
||||
}
|
||||
}
|
||||
|
||||
async function chargerCadresFormat(fmt) {
|
||||
const data = await apiGet(`/api/cadres-impression/${fmt}`);
|
||||
const liste = document.getElementById(`cadres-liste-${fmt}`);
|
||||
liste.innerHTML = '';
|
||||
|
||||
// Option "Aucun cadre"
|
||||
const divAucun = _creerItemCadreImpression(fmt, null, data.actif);
|
||||
liste.appendChild(divAucun);
|
||||
|
||||
if (data.disponibles.length === 0) {
|
||||
const p = document.createElement('p');
|
||||
p.className = 'texte-secondaire';
|
||||
p.textContent = 'Aucun cadre importe';
|
||||
liste.appendChild(p);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const nom of data.disponibles) {
|
||||
const div = _creerItemCadreImpression(fmt, nom, data.actif);
|
||||
liste.appendChild(div);
|
||||
}
|
||||
}
|
||||
|
||||
function _creerItemCadreImpression(fmt, nom, actif) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'cadre-imp-item';
|
||||
|
||||
const rb = document.createElement('input');
|
||||
rb.type = 'radio';
|
||||
rb.name = `cadre-imp-${fmt}`;
|
||||
rb.value = nom || '';
|
||||
rb.checked = (actif === nom);
|
||||
rb.onchange = () => activerCadreFormat(fmt, nom);
|
||||
|
||||
if (nom) {
|
||||
const img = document.createElement('img');
|
||||
img.src = `/assets/cadres/${fmt}/${nom}`;
|
||||
img.alt = nom;
|
||||
img.className = 'cadre-preview';
|
||||
|
||||
const btnDel = document.createElement('button');
|
||||
btnDel.className = 'btn-danger btn-icone';
|
||||
btnDel.title = 'Supprimer';
|
||||
btnDel.textContent = '✕';
|
||||
btnDel.onclick = () => supprimerCadreFormat(fmt, nom);
|
||||
|
||||
const span = document.createElement('span');
|
||||
span.textContent = nom.replace('.png', '');
|
||||
|
||||
div.appendChild(rb);
|
||||
div.appendChild(img);
|
||||
div.appendChild(span);
|
||||
div.appendChild(btnDel);
|
||||
} else {
|
||||
const span = document.createElement('span');
|
||||
span.textContent = 'Aucun cadre';
|
||||
div.appendChild(rb);
|
||||
div.appendChild(span);
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
async function activerCadreFormat(fmt, nom) {
|
||||
await apiPost(`/api/cadres-impression/${fmt}/actif`, { nom: nom || null });
|
||||
afficherStatut(`Cadre ${fmt} mis a jour`, 'succes');
|
||||
}
|
||||
|
||||
async function supprimerCadreFormat(fmt, nom) {
|
||||
await fetch(`/api/cadres-impression/${fmt}/${encodeURIComponent(nom)}`, { method: 'DELETE' });
|
||||
await chargerCadresFormat(fmt);
|
||||
afficherStatut('Cadre supprime', 'succes');
|
||||
}
|
||||
|
||||
async function uploaderCadreFormat(fmt) {
|
||||
const input = document.getElementById(`input-cadre-${fmt}`);
|
||||
if (!input.files.length) return;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('fichier', input.files[0]);
|
||||
|
||||
const r = await fetch(`/api/upload/cadre/${fmt}`, { method: 'POST', body: formData });
|
||||
if (r.ok) {
|
||||
input.value = '';
|
||||
await chargerCadresFormat(fmt);
|
||||
afficherStatut(`Cadre ${fmt} importe`, 'succes');
|
||||
} else {
|
||||
afficherStatut('Erreur import cadre', 'erreur');
|
||||
}
|
||||
}
|
||||
|
||||
// === CADRES OVERLAYS PHOTO ===
|
||||
|
||||
async function chargerCadres() {
|
||||
const data = await apiGet('/api/cadres');
|
||||
|
||||
Reference in New Issue
Block a user