better UX and UI on the room page
This commit is contained in:
@@ -10,6 +10,15 @@ interface RoomUser {
|
|||||||
userId?: number;
|
userId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SourceContext {
|
||||||
|
animeId: string;
|
||||||
|
episode: string | number;
|
||||||
|
source: string;
|
||||||
|
extension: string;
|
||||||
|
server: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface QueueItem {
|
export interface QueueItem {
|
||||||
uid: string;
|
uid: string;
|
||||||
metadata: RoomMetadata;
|
metadata: RoomMetadata;
|
||||||
@@ -23,6 +32,7 @@ interface RoomMetadata {
|
|||||||
episode: number;
|
episode: number;
|
||||||
image?: string;
|
image?: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
|
malId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RoomData {
|
interface RoomData {
|
||||||
@@ -38,6 +48,7 @@ interface RoomData {
|
|||||||
videoData?: any;
|
videoData?: any;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
|
context?: SourceContext;
|
||||||
} | null;
|
} | null;
|
||||||
password?: string;
|
password?: string;
|
||||||
metadata?: RoomMetadata | null;
|
metadata?: RoomMetadata | null;
|
||||||
@@ -168,7 +179,10 @@ export function updateRoomVideo(roomId: string, videoData: any): boolean {
|
|||||||
const room = rooms.get(roomId);
|
const room = rooms.get(roomId);
|
||||||
if (!room) return false;
|
if (!room) return false;
|
||||||
|
|
||||||
room.currentVideo = videoData;
|
room.currentVideo = {
|
||||||
|
...room.currentVideo,
|
||||||
|
...videoData
|
||||||
|
};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -303,14 +303,34 @@ function handleMessage(roomId: string, userId: string, data: any) {
|
|||||||
broadcastToRoom(roomId, {
|
broadcastToRoom(roomId, {
|
||||||
type: 'video_update',
|
type: 'video_update',
|
||||||
video: data.video,
|
video: data.video,
|
||||||
metadata: data.metadata // ✅ CLAVE
|
metadata: data.metadata
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'queue_add_batch':
|
||||||
|
if (room.host.id !== userId) return;
|
||||||
|
|
||||||
|
if (Array.isArray(data.items)) {
|
||||||
|
// Añadimos el índice (i) al forEach
|
||||||
|
data.items.forEach((item: any, i: number) => {
|
||||||
|
const newItem = {
|
||||||
|
// Añadimos el índice '_${i}' al UID para garantizar unicidad en milisegundos
|
||||||
|
uid: `q_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 5)}`,
|
||||||
|
metadata: item.metadata,
|
||||||
|
videoData: item.video,
|
||||||
|
addedBy: room.users.get(userId)?.username || 'Unknown'
|
||||||
|
};
|
||||||
|
roomService.addQueueItem(roomId, newItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
broadcastToRoom(roomId, {
|
||||||
|
type: 'queue_update',
|
||||||
|
queue: room.queue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'sync':
|
case 'sync':
|
||||||
// Solo el host puede hacer sync
|
|
||||||
if (room.host.id !== userId) return;
|
if (room.host.id !== userId) return;
|
||||||
|
|
||||||
if (room.currentVideo) {
|
if (room.currentVideo) {
|
||||||
|
|||||||
@@ -78,13 +78,11 @@ const AnimePlayer = (function() {
|
|||||||
initElements();
|
initElements();
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
|
|
||||||
// In Room Mode, we show the player immediately and hide extra controls
|
|
||||||
if (_roomMode) {
|
if (_roomMode) {
|
||||||
if(els.playerWrapper) {
|
if(els.playerWrapper) {
|
||||||
els.playerWrapper.style.display = 'block';
|
els.playerWrapper.style.display = 'block';
|
||||||
els.playerWrapper.classList.add('room-mode');
|
els.playerWrapper.classList.add('room-mode');
|
||||||
}
|
}
|
||||||
// Hide extension list loading in room mode
|
|
||||||
} else {
|
} else {
|
||||||
loadExtensionsList();
|
loadExtensionsList();
|
||||||
}
|
}
|
||||||
@@ -128,10 +126,8 @@ const AnimePlayer = (function() {
|
|||||||
els.progressBuffer = document.querySelector('.progress-buffer');
|
els.progressBuffer = document.querySelector('.progress-buffer');
|
||||||
els.progressHandle = document.querySelector('.progress-handle');
|
els.progressHandle = document.querySelector('.progress-handle');
|
||||||
|
|
||||||
// Subtitles canvas
|
|
||||||
els.subtitlesCanvas = document.getElementById('subtitles-canvas');
|
els.subtitlesCanvas = document.getElementById('subtitles-canvas');
|
||||||
|
|
||||||
// Create skip button if not exists
|
|
||||||
if (!document.getElementById('skip-overlay-btn')) {
|
if (!document.getElementById('skip-overlay-btn')) {
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.id = 'skip-overlay-btn';
|
btn.id = 'skip-overlay-btn';
|
||||||
@@ -143,13 +139,11 @@ const AnimePlayer = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
// Close player
|
|
||||||
if(!_roomMode) {
|
if(!_roomMode) {
|
||||||
const closeBtn = document.getElementById('close-player-btn');
|
const closeBtn = document.getElementById('close-player-btn');
|
||||||
if(closeBtn) closeBtn.addEventListener('click', closePlayer);
|
if(closeBtn) closeBtn.addEventListener('click', closePlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Episode navigation
|
|
||||||
if(els.prevBtn) els.prevBtn.onclick = () => playEpisode(_currentEpisode - 1);
|
if(els.prevBtn) els.prevBtn.onclick = () => playEpisode(_currentEpisode - 1);
|
||||||
if(els.nextBtn) els.nextBtn.onclick = () => playEpisode(_currentEpisode + 1);
|
if(els.nextBtn) els.nextBtn.onclick = () => playEpisode(_currentEpisode + 1);
|
||||||
|
|
||||||
@@ -203,6 +197,15 @@ const AnimePlayer = (function() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (videoData.malId) _malId = videoData.malId;
|
||||||
|
if (videoData.episode) _currentEpisode = parseInt(videoData.episode);
|
||||||
|
|
||||||
|
_skipIntervals = [];
|
||||||
|
if (els.progressContainer) {
|
||||||
|
els.progressContainer.querySelectorAll('.skip-range, .skip-cut').forEach(e => e.remove());
|
||||||
|
}
|
||||||
|
if (_skipBtn) _skipBtn.classList.remove('visible');
|
||||||
|
|
||||||
_currentSubtitles = videoData.subtitles || [];
|
_currentSubtitles = videoData.subtitles || [];
|
||||||
|
|
||||||
if (els.loader) els.loader.style.display = 'none';
|
if (els.loader) els.loader.style.display = 'none';
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class CreateRoomModal {
|
|||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
|
|
||||||
window.open(`/room?id=${data.room.id}`, '_blank', 'noopener,noreferrer');
|
window.location.href = `/room?id=${data.room.id}`
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(err.message);
|
alert(err.message);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,8 @@
|
|||||||
<link rel="stylesheet" href="/views/css/globals.css" />
|
<link rel="stylesheet" href="/views/css/globals.css" />
|
||||||
<link rel="stylesheet" href="/views/css/anime/player.css" />
|
<link rel="stylesheet" href="/views/css/anime/player.css" />
|
||||||
<link rel="stylesheet" href="/views/css/room.css" />
|
<link rel="stylesheet" href="/views/css/room.css" />
|
||||||
<link rel="stylesheet" href="/views/css/components/titlebar.css">
|
<link rel="stylesheet" href="/views/css/components/create-room.css"/>
|
||||||
|
<link rel="stylesheet" href="/views/css/components/titlebar.css"/>
|
||||||
<script src="/src/scripts/titlebar.js"></script>
|
<script src="/src/scripts/titlebar.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
<span class="app-title">WaifuBoard</span>
|
<span class="app-title">WaifuBoard</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="title-right">
|
<div class="title-right">
|
||||||
<button class="min">—</button>
|
<button class="min">−</button>
|
||||||
<button class="max">🗖</button>
|
<button class="max">🗖</button>
|
||||||
<button class="close">✕</button>
|
<button class="close">✕</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,23 +52,32 @@
|
|||||||
<div class="sd-option" data-val="dub">Dub</div>
|
<div class="sd-option" data-val="dub">Dub</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select id="room-ext-select" class="glass-select-sm" title="Extension">
|
<div class="control-divider"></div>
|
||||||
<option value="" disabled selected>Ext</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="room-server-select" class="glass-select-sm" title="Server">
|
<div class="quick-select-wrapper">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 7h-9"/><path d="M14 17H5"/><circle cx="17" cy="17" r="3"/><circle cx="7" cy="7" r="3"/></svg>
|
||||||
|
<select id="room-ext-select" class="quick-select" title="Extension">
|
||||||
|
<option value="" disabled selected>Extension</option>
|
||||||
|
</select>
|
||||||
|
<div class="select-arrow">▼</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quick-select-wrapper">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg>
|
||||||
|
<select id="room-server-select" class="quick-select" title="Server">
|
||||||
<option value="" disabled selected>Server</option>
|
<option value="" disabled selected>Server</option>
|
||||||
</select>
|
</select>
|
||||||
<button
|
<div class="select-arrow">▼</div>
|
||||||
id="copy-invite-btn"
|
</div>
|
||||||
class="btn-icon-glass"
|
|
||||||
title="Copy invite link"
|
<div class="control-divider"></div>
|
||||||
style="display:none;"
|
|
||||||
>
|
<button id="copy-invite-btn" class="btn-quick-action" title="Copy invite link" style="display:none;">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M10 13a5 5 0 0 0 7.07 0l1.41-1.41a5 5 0 0 0-7.07-7.07L10 5"/>
|
<path d="M10 13a5 5 0 0 0 7.07 0l1.41-1.41a5 5 0 0 0-7.07-7.07L10 5"/>
|
||||||
<path d="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
<path d="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<span>Invite</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,7 +90,6 @@
|
|||||||
|
|
||||||
<button id="select-anime-btn" class="btn-glass-primary" style="display: none;">
|
<button id="select-anime-btn" class="btn-glass-primary" style="display: none;">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg>
|
||||||
<span>Change Anime</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
||||||
@@ -219,63 +228,66 @@
|
|||||||
<button class="btn-icon-small" id="back-to-search" title="Back">
|
<button class="btn-icon-small" id="back-to-search" title="Back">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<h2 class="modal-title" id="selected-anime-title">Configure Stream</h2>
|
<h2 class="modal-title" id="selected-anime-title">Configure Episode</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-layout">
|
<div class="config-layout">
|
||||||
<div class="config-sidebar">
|
<div class="config-sidebar">
|
||||||
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="width: 100%">
|
<div class="config-main" style="justify-content: center;">
|
||||||
<div class="cfg-section-title">Episode</div>
|
|
||||||
|
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||||
|
|
||||||
|
<div class="cfg-header" style="margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div class="cfg-section-title">Select Episodes</div>
|
||||||
|
<div class="cfg-actions">
|
||||||
|
<button id="select-all-page" class="btn-text-tiny">Select Page</button>
|
||||||
|
<button id="select-none-eps" class="btn-text-tiny">Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="episodes-grid" class="episodes-grid-container">
|
||||||
|
<div class="grid-loader">Loading...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="modal-pagination" class="modal-pagination" style="display: none;">
|
||||||
|
<button id="modal-prev-page" class="modal-page-btn">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
|
||||||
|
</button>
|
||||||
|
<span id="modal-page-info" class="modal-page-info">1 - 50</span>
|
||||||
|
<button id="modal-next-page" class="modal-page-btn">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="manual-ep-input" style="margin-top: 10px; display: none;">
|
||||||
|
<label style="font-size: 0.8rem; color: #aaa;">Manual Entry:</label>
|
||||||
<div class="ep-control">
|
<div class="ep-control">
|
||||||
<button class="ep-btn" id="ep-dec">−</button>
|
<button class="ep-btn" id="ep-dec">−</button>
|
||||||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||||||
<button class="ep-btn" id="ep-inc">+</button>
|
<button class="ep-btn" id="ep-inc">+</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config-main">
|
<div id="config-error" style="color:#ff6b6b; font-size:0.9rem; display:none; background:rgba(255,0,0,0.1); padding:10px; border-radius:8px; margin-top: 10px;"></div>
|
||||||
<div style="display:flex; justify-content:space-between; align-items:end; gap:10px; flex-wrap:wrap;">
|
|
||||||
<div>
|
|
||||||
<div class="cfg-section-title">Source</div>
|
|
||||||
<div class="chips-grid" id="ext-chips-container">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div class="form-actions" style="margin-top:auto; display:flex; gap:10px; padding-top: 20px;">
|
||||||
<div class="cfg-section-title">Audio</div>
|
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;">
|
||||||
<div class="cat-toggle" id="modal-sd-toggle">
|
+ Add Selected (<span id="sel-count">0</span>)
|
||||||
<div class="cat-opt active" data-val="sub">Subtitles</div>
|
|
||||||
<div class="cat-opt" data-val="dub">Dubbed</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="cfg-section-title">Select Server</div>
|
|
||||||
<div class="chips-grid" id="server-chips-container">
|
|
||||||
<div class="grid-loader">Select a source first</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="config-error" style="color:#ff6b6b; font-size:0.9rem; display:none; background:rgba(255,0,0,0.1); padding:10px; border-radius:8px;"></div>
|
|
||||||
|
|
||||||
<div class="form-actions" style="margin-top:auto; display:flex; gap:10px;">
|
|
||||||
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;" disabled>
|
|
||||||
+ Add to Queue
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button id="btn-launch-stream" class="btn-confirm" style="flex:1;" disabled>
|
<button id="btn-launch-stream" class="btn-confirm" style="flex:1;">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
||||||
Play Now
|
Play First
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/src/scripts/utils/auth-utils.js"></script>
|
<script src="/src/scripts/utils/auth-utils.js"></script>
|
||||||
|
|||||||
@@ -10,6 +10,15 @@ interface RoomUser {
|
|||||||
userId?: number;
|
userId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SourceContext {
|
||||||
|
animeId: string;
|
||||||
|
episode: string | number;
|
||||||
|
source: string;
|
||||||
|
extension: string;
|
||||||
|
server: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface QueueItem {
|
export interface QueueItem {
|
||||||
uid: string;
|
uid: string;
|
||||||
metadata: RoomMetadata;
|
metadata: RoomMetadata;
|
||||||
@@ -23,6 +32,7 @@ interface RoomMetadata {
|
|||||||
episode: number;
|
episode: number;
|
||||||
image?: string;
|
image?: string;
|
||||||
source?: string;
|
source?: string;
|
||||||
|
malId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RoomData {
|
interface RoomData {
|
||||||
@@ -38,6 +48,7 @@ interface RoomData {
|
|||||||
videoData?: any;
|
videoData?: any;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
isPlaying: boolean;
|
isPlaying: boolean;
|
||||||
|
context?: SourceContext;
|
||||||
} | null;
|
} | null;
|
||||||
password?: string;
|
password?: string;
|
||||||
metadata?: RoomMetadata | null;
|
metadata?: RoomMetadata | null;
|
||||||
@@ -168,7 +179,10 @@ export function updateRoomVideo(roomId: string, videoData: any): boolean {
|
|||||||
const room = rooms.get(roomId);
|
const room = rooms.get(roomId);
|
||||||
if (!room) return false;
|
if (!room) return false;
|
||||||
|
|
||||||
room.currentVideo = videoData;
|
room.currentVideo = {
|
||||||
|
...room.currentVideo,
|
||||||
|
...videoData
|
||||||
|
};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -303,14 +303,34 @@ function handleMessage(roomId: string, userId: string, data: any) {
|
|||||||
broadcastToRoom(roomId, {
|
broadcastToRoom(roomId, {
|
||||||
type: 'video_update',
|
type: 'video_update',
|
||||||
video: data.video,
|
video: data.video,
|
||||||
metadata: data.metadata // ✅ CLAVE
|
metadata: data.metadata
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'queue_add_batch':
|
||||||
|
if (room.host.id !== userId) return;
|
||||||
|
|
||||||
|
if (Array.isArray(data.items)) {
|
||||||
|
// Añadimos el índice (i) al forEach
|
||||||
|
data.items.forEach((item: any, i: number) => {
|
||||||
|
const newItem = {
|
||||||
|
// Añadimos el índice '_${i}' al UID para garantizar unicidad en milisegundos
|
||||||
|
uid: `q_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 5)}`,
|
||||||
|
metadata: item.metadata,
|
||||||
|
videoData: item.video,
|
||||||
|
addedBy: room.users.get(userId)?.username || 'Unknown'
|
||||||
|
};
|
||||||
|
roomService.addQueueItem(roomId, newItem);
|
||||||
|
});
|
||||||
|
|
||||||
|
broadcastToRoom(roomId, {
|
||||||
|
type: 'queue_update',
|
||||||
|
queue: room.queue
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'sync':
|
case 'sync':
|
||||||
// Solo el host puede hacer sync
|
|
||||||
if (room.host.id !== userId) return;
|
if (room.host.id !== userId) return;
|
||||||
|
|
||||||
if (room.currentVideo) {
|
if (room.currentVideo) {
|
||||||
|
|||||||
@@ -78,13 +78,11 @@ const AnimePlayer = (function() {
|
|||||||
initElements();
|
initElements();
|
||||||
setupEventListeners();
|
setupEventListeners();
|
||||||
|
|
||||||
// In Room Mode, we show the player immediately and hide extra controls
|
|
||||||
if (_roomMode) {
|
if (_roomMode) {
|
||||||
if(els.playerWrapper) {
|
if(els.playerWrapper) {
|
||||||
els.playerWrapper.style.display = 'block';
|
els.playerWrapper.style.display = 'block';
|
||||||
els.playerWrapper.classList.add('room-mode');
|
els.playerWrapper.classList.add('room-mode');
|
||||||
}
|
}
|
||||||
// Hide extension list loading in room mode
|
|
||||||
} else {
|
} else {
|
||||||
loadExtensionsList();
|
loadExtensionsList();
|
||||||
}
|
}
|
||||||
@@ -128,10 +126,8 @@ const AnimePlayer = (function() {
|
|||||||
els.progressBuffer = document.querySelector('.progress-buffer');
|
els.progressBuffer = document.querySelector('.progress-buffer');
|
||||||
els.progressHandle = document.querySelector('.progress-handle');
|
els.progressHandle = document.querySelector('.progress-handle');
|
||||||
|
|
||||||
// Subtitles canvas
|
|
||||||
els.subtitlesCanvas = document.getElementById('subtitles-canvas');
|
els.subtitlesCanvas = document.getElementById('subtitles-canvas');
|
||||||
|
|
||||||
// Create skip button if not exists
|
|
||||||
if (!document.getElementById('skip-overlay-btn')) {
|
if (!document.getElementById('skip-overlay-btn')) {
|
||||||
const btn = document.createElement('button');
|
const btn = document.createElement('button');
|
||||||
btn.id = 'skip-overlay-btn';
|
btn.id = 'skip-overlay-btn';
|
||||||
@@ -143,13 +139,11 @@ const AnimePlayer = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupEventListeners() {
|
function setupEventListeners() {
|
||||||
// Close player
|
|
||||||
if(!_roomMode) {
|
if(!_roomMode) {
|
||||||
const closeBtn = document.getElementById('close-player-btn');
|
const closeBtn = document.getElementById('close-player-btn');
|
||||||
if(closeBtn) closeBtn.addEventListener('click', closePlayer);
|
if(closeBtn) closeBtn.addEventListener('click', closePlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Episode navigation
|
|
||||||
if(els.prevBtn) els.prevBtn.onclick = () => playEpisode(_currentEpisode - 1);
|
if(els.prevBtn) els.prevBtn.onclick = () => playEpisode(_currentEpisode - 1);
|
||||||
if(els.nextBtn) els.nextBtn.onclick = () => playEpisode(_currentEpisode + 1);
|
if(els.nextBtn) els.nextBtn.onclick = () => playEpisode(_currentEpisode + 1);
|
||||||
|
|
||||||
@@ -203,6 +197,15 @@ const AnimePlayer = (function() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (videoData.malId) _malId = videoData.malId;
|
||||||
|
if (videoData.episode) _currentEpisode = parseInt(videoData.episode);
|
||||||
|
|
||||||
|
_skipIntervals = [];
|
||||||
|
if (els.progressContainer) {
|
||||||
|
els.progressContainer.querySelectorAll('.skip-range, .skip-cut').forEach(e => e.remove());
|
||||||
|
}
|
||||||
|
if (_skipBtn) _skipBtn.classList.remove('visible');
|
||||||
|
|
||||||
_currentSubtitles = videoData.subtitles || [];
|
_currentSubtitles = videoData.subtitles || [];
|
||||||
|
|
||||||
if (els.loader) els.loader.style.display = 'none';
|
if (els.loader) els.loader.style.display = 'none';
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -39,23 +39,32 @@
|
|||||||
<div class="sd-option" data-val="dub">Dub</div>
|
<div class="sd-option" data-val="dub">Dub</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select id="room-ext-select" class="glass-select-sm" title="Extension">
|
<div class="control-divider"></div>
|
||||||
<option value="" disabled selected>Ext</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<select id="room-server-select" class="glass-select-sm" title="Server">
|
<div class="quick-select-wrapper">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 7h-9"/><path d="M14 17H5"/><circle cx="17" cy="17" r="3"/><circle cx="7" cy="7" r="3"/></svg>
|
||||||
|
<select id="room-ext-select" class="quick-select" title="Extension">
|
||||||
|
<option value="" disabled selected>Extension</option>
|
||||||
|
</select>
|
||||||
|
<div class="select-arrow">▼</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="quick-select-wrapper">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect><rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect><line x1="6" y1="6" x2="6.01" y2="6"></line><line x1="6" y1="18" x2="6.01" y2="18"></line></svg>
|
||||||
|
<select id="room-server-select" class="quick-select" title="Server">
|
||||||
<option value="" disabled selected>Server</option>
|
<option value="" disabled selected>Server</option>
|
||||||
</select>
|
</select>
|
||||||
<button
|
<div class="select-arrow">▼</div>
|
||||||
id="copy-invite-btn"
|
</div>
|
||||||
class="btn-icon-glass"
|
|
||||||
title="Copy invite link"
|
<div class="control-divider"></div>
|
||||||
style="display:none;"
|
|
||||||
>
|
<button id="copy-invite-btn" class="btn-quick-action" title="Copy invite link" style="display:none;">
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M10 13a5 5 0 0 0 7.07 0l1.41-1.41a5 5 0 0 0-7.07-7.07L10 5"/>
|
<path d="M10 13a5 5 0 0 0 7.07 0l1.41-1.41a5 5 0 0 0-7.07-7.07L10 5"/>
|
||||||
<path d="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
<path d="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<span>Invite</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +77,6 @@
|
|||||||
|
|
||||||
<button id="select-anime-btn" class="btn-glass-primary" style="display: none;">
|
<button id="select-anime-btn" class="btn-glass-primary" style="display: none;">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path></svg>
|
||||||
<span>Change Anime</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
||||||
@@ -207,63 +215,66 @@
|
|||||||
<button class="btn-icon-small" id="back-to-search" title="Back">
|
<button class="btn-icon-small" id="back-to-search" title="Back">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<h2 class="modal-title" id="selected-anime-title">Configure Stream</h2>
|
<h2 class="modal-title" id="selected-anime-title">Configure Episode</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="config-layout">
|
<div class="config-layout">
|
||||||
<div class="config-sidebar">
|
<div class="config-sidebar">
|
||||||
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="width: 100%">
|
<div class="config-main" style="justify-content: center;">
|
||||||
<div class="cfg-section-title">Episode</div>
|
|
||||||
|
<div style="display: flex; flex-direction: column; height: 100%;">
|
||||||
|
|
||||||
|
<div class="cfg-header" style="margin-bottom: 10px; display: flex; justify-content: space-between; align-items: center;">
|
||||||
|
<div class="cfg-section-title">Select Episodes</div>
|
||||||
|
<div class="cfg-actions">
|
||||||
|
<button id="select-all-page" class="btn-text-tiny">Select Page</button>
|
||||||
|
<button id="select-none-eps" class="btn-text-tiny">Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="episodes-grid" class="episodes-grid-container">
|
||||||
|
<div class="grid-loader">Loading...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="modal-pagination" class="modal-pagination" style="display: none;">
|
||||||
|
<button id="modal-prev-page" class="modal-page-btn">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
|
||||||
|
</button>
|
||||||
|
<span id="modal-page-info" class="modal-page-info">1 - 50</span>
|
||||||
|
<button id="modal-next-page" class="modal-page-btn">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="manual-ep-input" style="margin-top: 10px; display: none;">
|
||||||
|
<label style="font-size: 0.8rem; color: #aaa;">Manual Entry:</label>
|
||||||
<div class="ep-control">
|
<div class="ep-control">
|
||||||
<button class="ep-btn" id="ep-dec">−</button>
|
<button class="ep-btn" id="ep-dec">−</button>
|
||||||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||||||
<button class="ep-btn" id="ep-inc">+</button>
|
<button class="ep-btn" id="ep-inc">+</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="config-main">
|
<div id="config-error" style="color:#ff6b6b; font-size:0.9rem; display:none; background:rgba(255,0,0,0.1); padding:10px; border-radius:8px; margin-top: 10px;"></div>
|
||||||
<div style="display:flex; justify-content:space-between; align-items:end; gap:10px; flex-wrap:wrap;">
|
|
||||||
<div>
|
|
||||||
<div class="cfg-section-title">Source</div>
|
|
||||||
<div class="chips-grid" id="ext-chips-container">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div class="form-actions" style="margin-top:auto; display:flex; gap:10px; padding-top: 20px;">
|
||||||
<div class="cfg-section-title">Audio</div>
|
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;">
|
||||||
<div class="cat-toggle" id="modal-sd-toggle">
|
+ Add Selected (<span id="sel-count">0</span>)
|
||||||
<div class="cat-opt active" data-val="sub">Subtitles</div>
|
|
||||||
<div class="cat-opt" data-val="dub">Dubbed</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="cfg-section-title">Select Server</div>
|
|
||||||
<div class="chips-grid" id="server-chips-container">
|
|
||||||
<div class="grid-loader">Select a source first</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="config-error" style="color:#ff6b6b; font-size:0.9rem; display:none; background:rgba(255,0,0,0.1); padding:10px; border-radius:8px;"></div>
|
|
||||||
|
|
||||||
<div class="form-actions" style="margin-top:auto; display:flex; gap:10px;">
|
|
||||||
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;" disabled>
|
|
||||||
+ Add to Queue
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button id="btn-launch-stream" class="btn-confirm" style="flex:1;" disabled>
|
<button id="btn-launch-stream" class="btn-confirm" style="flex:1;">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
||||||
Play Now
|
Play First
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/src/scripts/utils/auth-utils.js"></script>
|
<script src="/src/scripts/utils/auth-utils.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user