155 lines
6.4 KiB
JavaScript
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();
|
|
}
|
|
} |