CinePlex/js/activityViewer.js

155 lines
6.4 KiB
JavaScript

import { state } from './state.js';
import { getFromDB } from './db.js';
import { fetchPlexSessions } from './api.js';
import { showNotification, _ } from './utils.js';
export class ActivityViewer {
constructor(modalElement) {
this.modalElement = modalElement;
this.modal = new bootstrap.Modal(this.modalElement);
this.dom = {};
this.isChecking = false;
this.cacheDOM();
this.bindEvents();
}
cacheDOM() {
this.dom.serverSelect = this.modalElement.querySelector('#activity-server-select');
this.dom.checkBtn = this.modalElement.querySelector('#check-activity-btn');
this.dom.loader = this.modalElement.querySelector('#activity-loader');
this.dom.resultsContainer = this.modalElement.querySelector('#activity-results');
}
bindEvents() {
this.modalElement.addEventListener('show.bs.modal', () => this.onModalShow());
this.dom.checkBtn.addEventListener('click', () => this.handleCheckActivity());
this.dom.resultsContainer.addEventListener('click', (e) => {
if (e.target.classList.contains('copy-identifier-btn')) {
const identifier = e.target.dataset.identifier;
this.copyToClipboard(identifier, e.target);
}
});
}
async onModalShow() {
this.dom.resultsContainer.innerHTML = '';
await this.populateServerSelect();
}
async populateServerSelect() {
this.dom.serverSelect.innerHTML = `<option>${_('loading')}</option>`;
try {
const servers = await getFromDB('conexiones_locales');
if (servers.length === 0) {
this.dom.serverSelect.innerHTML = `<option>${_('noServersFound')}</option>`;
this.dom.checkBtn.disabled = true;
return;
}
this.dom.serverSelect.innerHTML = '';
servers.forEach((server, index) => {
const option = document.createElement('option');
option.value = index;
option.textContent = server.nombre || server.ip;
this.dom.serverSelect.appendChild(option);
});
this.dom.checkBtn.disabled = false;
} catch (error) {
this.dom.serverSelect.innerHTML = `<option>${_('errorLoadingServers')}</option>`;
this.dom.checkBtn.disabled = true;
}
}
async handleCheckActivity() {
if (this.isChecking) return;
const selectedIndex = this.dom.serverSelect.value;
if (selectedIndex === '') return;
const servers = await getFromDB('conexiones_locales');
const selectedServer = servers[selectedIndex];
if (!selectedServer) return;
this.isChecking = true;
this.dom.checkBtn.disabled = true;
this.dom.loader.style.display = 'block';
this.dom.resultsContainer.innerHTML = '';
try {
const sessions = await fetchPlexSessions(selectedServer);
this.renderSessions(sessions, selectedServer);
} catch (error) {
this.dom.resultsContainer.innerHTML = `<div class="empty-state"><i class="fas fa-exclamation-triangle"></i><p>${_('activityError')}</p><p class="text-muted">${error.message}</p></div>`;
} finally {
this.isChecking = false;
this.dom.checkBtn.disabled = false;
this.dom.loader.style.display = 'none';
}
}
renderSessions(sessions, server) {
if (sessions.length === 0) {
this.dom.resultsContainer.innerHTML = `<div class="empty-state"><i class="fas fa-bed"></i><p class="lead">${_('activityNoSessions')}</p></div>`;
return;
}
const fragment = document.createDocumentFragment();
sessions.forEach(session => {
const card = this.createSessionCard(session, server);
fragment.appendChild(card);
});
this.dom.resultsContainer.appendChild(fragment);
}
createSessionCard(session, server) {
const card = document.createElement('div');
card.className = 'session-card';
const posterUrl = session.thumb ? `${server.protocolo}://${server.ip}:${server.puerto}${session.thumb}?X-Plex-Token=${server.token}` : 'img/no-poster.png';
const contentTitle = session.grandparentTitle ? `${session.grandparentTitle} - ${session.title}` : session.title;
const playerStateIcon = session.Player.state === 'playing' ? 'fa-play' : 'fa-pause';
const playerStateColor = session.Player.state === 'playing' ? 'text-success' : 'text-warning';
card.innerHTML = `
<img src="${posterUrl}" class="session-poster" alt="Poster">
<div class="session-info">
<div class="session-details">
<p><strong>${_('activitySessionUser')}:</strong> ${session.User.title}</p>
<p><strong>${_('activitySessionDevice')}:</strong> ${session.Player.product} (${session.Player.title})</p>
<p><strong>${_('activitySessionContent')}:</strong> ${contentTitle}</p>
<p><strong>${_('activitySessionState')}:</strong> <i class="fas ${playerStateIcon} ${playerStateColor}"></i> ${session.Player.state}</p>
</div>
<div class="session-identifier">
<label>${_('activitySessionIdentifier')}:</label>
<div class="input-group">
<input type="text" class="form-control form-control-sm" value="${session.Player.machineIdentifier}" readonly>
<button class="btn btn-sm btn-outline-secondary copy-identifier-btn" data-identifier="${session.Player.machineIdentifier}" title="${_('activityCopyID')}">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
`;
return card;
}
copyToClipboard(text, button) {
if (!text) return;
navigator.clipboard.writeText(text).then(() => {
const originalIcon = button.innerHTML;
button.innerHTML = '<i class="fas fa-check"></i>';
showNotification(_('activityCopied'), 'success');
setTimeout(() => {
button.innerHTML = originalIcon;
}, 2000);
}).catch(err => {
showNotification(_('activityCopyError'), 'error');
});
}
show() {
this.modal.show();
}
}