better UX and UI on the room page
This commit is contained in:
@@ -10,6 +10,15 @@ interface RoomUser {
|
||||
userId?: number;
|
||||
}
|
||||
|
||||
interface SourceContext {
|
||||
animeId: string;
|
||||
episode: string | number;
|
||||
source: string;
|
||||
extension: string;
|
||||
server: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
export interface QueueItem {
|
||||
uid: string;
|
||||
metadata: RoomMetadata;
|
||||
@@ -23,6 +32,7 @@ interface RoomMetadata {
|
||||
episode: number;
|
||||
image?: string;
|
||||
source?: string;
|
||||
malId?: number;
|
||||
}
|
||||
|
||||
interface RoomData {
|
||||
@@ -38,6 +48,7 @@ interface RoomData {
|
||||
videoData?: any;
|
||||
currentTime: number;
|
||||
isPlaying: boolean;
|
||||
context?: SourceContext;
|
||||
} | null;
|
||||
password?: string;
|
||||
metadata?: RoomMetadata | null;
|
||||
@@ -168,7 +179,10 @@ export function updateRoomVideo(roomId: string, videoData: any): boolean {
|
||||
const room = rooms.get(roomId);
|
||||
if (!room) return false;
|
||||
|
||||
room.currentVideo = videoData;
|
||||
room.currentVideo = {
|
||||
...room.currentVideo,
|
||||
...videoData
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -303,14 +303,34 @@ function handleMessage(roomId: string, userId: string, data: any) {
|
||||
broadcastToRoom(roomId, {
|
||||
type: 'video_update',
|
||||
video: data.video,
|
||||
metadata: data.metadata // ✅ CLAVE
|
||||
metadata: data.metadata
|
||||
});
|
||||
|
||||
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':
|
||||
// Solo el host puede hacer sync
|
||||
if (room.host.id !== userId) return;
|
||||
|
||||
if (room.currentVideo) {
|
||||
|
||||
@@ -78,13 +78,11 @@ const AnimePlayer = (function() {
|
||||
initElements();
|
||||
setupEventListeners();
|
||||
|
||||
// In Room Mode, we show the player immediately and hide extra controls
|
||||
if (_roomMode) {
|
||||
if(els.playerWrapper) {
|
||||
els.playerWrapper.style.display = 'block';
|
||||
els.playerWrapper.classList.add('room-mode');
|
||||
}
|
||||
// Hide extension list loading in room mode
|
||||
} else {
|
||||
loadExtensionsList();
|
||||
}
|
||||
@@ -128,10 +126,8 @@ const AnimePlayer = (function() {
|
||||
els.progressBuffer = document.querySelector('.progress-buffer');
|
||||
els.progressHandle = document.querySelector('.progress-handle');
|
||||
|
||||
// Subtitles canvas
|
||||
els.subtitlesCanvas = document.getElementById('subtitles-canvas');
|
||||
|
||||
// Create skip button if not exists
|
||||
if (!document.getElementById('skip-overlay-btn')) {
|
||||
const btn = document.createElement('button');
|
||||
btn.id = 'skip-overlay-btn';
|
||||
@@ -143,13 +139,11 @@ const AnimePlayer = (function() {
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// Close player
|
||||
if(!_roomMode) {
|
||||
const closeBtn = document.getElementById('close-player-btn');
|
||||
if(closeBtn) closeBtn.addEventListener('click', closePlayer);
|
||||
}
|
||||
|
||||
// Episode navigation
|
||||
if(els.prevBtn) els.prevBtn.onclick = () => playEpisode(_currentEpisode - 1);
|
||||
if(els.nextBtn) els.nextBtn.onclick = () => playEpisode(_currentEpisode + 1);
|
||||
|
||||
@@ -203,6 +197,15 @@ const AnimePlayer = (function() {
|
||||
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 || [];
|
||||
|
||||
if (els.loader) els.loader.style.display = 'none';
|
||||
|
||||
@@ -118,7 +118,7 @@ class CreateRoomModal {
|
||||
|
||||
this.close();
|
||||
|
||||
window.open(`/room?id=${data.room.id}`, '_blank', 'noopener,noreferrer');
|
||||
window.location.href = `/room?id=${data.room.id}`
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
} 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/anime/player.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>
|
||||
</head>
|
||||
<body>
|
||||
@@ -23,7 +24,7 @@
|
||||
<span class="app-title">WaifuBoard</span>
|
||||
</div>
|
||||
<div class="title-right">
|
||||
<button class="min">—</button>
|
||||
<button class="min">−</button>
|
||||
<button class="max">🗖</button>
|
||||
<button class="close">✕</button>
|
||||
</div>
|
||||
@@ -51,23 +52,32 @@
|
||||
<div class="sd-option" data-val="dub">Dub</div>
|
||||
</div>
|
||||
|
||||
<select id="room-ext-select" class="glass-select-sm" title="Extension">
|
||||
<option value="" disabled selected>Ext</option>
|
||||
</select>
|
||||
<div class="control-divider"></div>
|
||||
|
||||
<select id="room-server-select" class="glass-select-sm" title="Server">
|
||||
<option value="" disabled selected>Server</option>
|
||||
</select>
|
||||
<button
|
||||
id="copy-invite-btn"
|
||||
class="btn-icon-glass"
|
||||
title="Copy invite link"
|
||||
style="display:none;"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<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>
|
||||
</select>
|
||||
<div class="select-arrow">▼</div>
|
||||
</div>
|
||||
|
||||
<div class="control-divider"></div>
|
||||
|
||||
<button id="copy-invite-btn" class="btn-quick-action" title="Copy invite link" style="display:none;">
|
||||
<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="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
||||
</svg>
|
||||
<span>Invite</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -80,7 +90,6 @@
|
||||
|
||||
<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>
|
||||
<span>Change Anime</span>
|
||||
</button>
|
||||
|
||||
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
||||
@@ -219,59 +228,62 @@
|
||||
<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>
|
||||
</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 class="config-layout">
|
||||
<div class="config-sidebar">
|
||||
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
||||
|
||||
<div style="width: 100%">
|
||||
<div class="cfg-section-title">Episode</div>
|
||||
<div class="ep-control">
|
||||
<button class="ep-btn" id="ep-dec">−</button>
|
||||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||||
<button class="ep-btn" id="ep-inc">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-main">
|
||||
<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 class="config-main" style="justify-content: center;">
|
||||
|
||||
<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>
|
||||
<div class="cfg-section-title">Audio</div>
|
||||
<div class="cat-toggle" id="modal-sd-toggle">
|
||||
<div class="cat-opt active" data-val="sub">Subtitles</div>
|
||||
<div class="cat-opt" data-val="dub">Dubbed</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">
|
||||
<button class="ep-btn" id="ep-dec">−</button>
|
||||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||||
<button class="ep-btn" id="ep-inc">+</button>
|
||||
</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 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 class="form-actions" style="margin-top:auto; display:flex; gap:10px; padding-top: 20px;">
|
||||
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;">
|
||||
+ Add Selected (<span id="sel-count">0</span>)
|
||||
</button>
|
||||
|
||||
<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>
|
||||
Play First
|
||||
</button>
|
||||
</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 id="btn-launch-stream" class="btn-confirm" style="flex:1;" disabled>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
||||
Play Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -10,6 +10,15 @@ interface RoomUser {
|
||||
userId?: number;
|
||||
}
|
||||
|
||||
interface SourceContext {
|
||||
animeId: string;
|
||||
episode: string | number;
|
||||
source: string;
|
||||
extension: string;
|
||||
server: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
export interface QueueItem {
|
||||
uid: string;
|
||||
metadata: RoomMetadata;
|
||||
@@ -23,6 +32,7 @@ interface RoomMetadata {
|
||||
episode: number;
|
||||
image?: string;
|
||||
source?: string;
|
||||
malId?: number;
|
||||
}
|
||||
|
||||
interface RoomData {
|
||||
@@ -38,6 +48,7 @@ interface RoomData {
|
||||
videoData?: any;
|
||||
currentTime: number;
|
||||
isPlaying: boolean;
|
||||
context?: SourceContext;
|
||||
} | null;
|
||||
password?: string;
|
||||
metadata?: RoomMetadata | null;
|
||||
@@ -168,7 +179,10 @@ export function updateRoomVideo(roomId: string, videoData: any): boolean {
|
||||
const room = rooms.get(roomId);
|
||||
if (!room) return false;
|
||||
|
||||
room.currentVideo = videoData;
|
||||
room.currentVideo = {
|
||||
...room.currentVideo,
|
||||
...videoData
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -303,14 +303,34 @@ function handleMessage(roomId: string, userId: string, data: any) {
|
||||
broadcastToRoom(roomId, {
|
||||
type: 'video_update',
|
||||
video: data.video,
|
||||
metadata: data.metadata // ✅ CLAVE
|
||||
metadata: data.metadata
|
||||
});
|
||||
|
||||
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':
|
||||
// Solo el host puede hacer sync
|
||||
if (room.host.id !== userId) return;
|
||||
|
||||
if (room.currentVideo) {
|
||||
|
||||
@@ -78,13 +78,11 @@ const AnimePlayer = (function() {
|
||||
initElements();
|
||||
setupEventListeners();
|
||||
|
||||
// In Room Mode, we show the player immediately and hide extra controls
|
||||
if (_roomMode) {
|
||||
if(els.playerWrapper) {
|
||||
els.playerWrapper.style.display = 'block';
|
||||
els.playerWrapper.classList.add('room-mode');
|
||||
}
|
||||
// Hide extension list loading in room mode
|
||||
} else {
|
||||
loadExtensionsList();
|
||||
}
|
||||
@@ -128,10 +126,8 @@ const AnimePlayer = (function() {
|
||||
els.progressBuffer = document.querySelector('.progress-buffer');
|
||||
els.progressHandle = document.querySelector('.progress-handle');
|
||||
|
||||
// Subtitles canvas
|
||||
els.subtitlesCanvas = document.getElementById('subtitles-canvas');
|
||||
|
||||
// Create skip button if not exists
|
||||
if (!document.getElementById('skip-overlay-btn')) {
|
||||
const btn = document.createElement('button');
|
||||
btn.id = 'skip-overlay-btn';
|
||||
@@ -143,13 +139,11 @@ const AnimePlayer = (function() {
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
// Close player
|
||||
if(!_roomMode) {
|
||||
const closeBtn = document.getElementById('close-player-btn');
|
||||
if(closeBtn) closeBtn.addEventListener('click', closePlayer);
|
||||
}
|
||||
|
||||
// Episode navigation
|
||||
if(els.prevBtn) els.prevBtn.onclick = () => playEpisode(_currentEpisode - 1);
|
||||
if(els.nextBtn) els.nextBtn.onclick = () => playEpisode(_currentEpisode + 1);
|
||||
|
||||
@@ -203,6 +197,15 @@ const AnimePlayer = (function() {
|
||||
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 || [];
|
||||
|
||||
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>
|
||||
|
||||
<select id="room-ext-select" class="glass-select-sm" title="Extension">
|
||||
<option value="" disabled selected>Ext</option>
|
||||
</select>
|
||||
<div class="control-divider"></div>
|
||||
|
||||
<select id="room-server-select" class="glass-select-sm" title="Server">
|
||||
<option value="" disabled selected>Server</option>
|
||||
</select>
|
||||
<button
|
||||
id="copy-invite-btn"
|
||||
class="btn-icon-glass"
|
||||
title="Copy invite link"
|
||||
style="display:none;"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<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>
|
||||
</select>
|
||||
<div class="select-arrow">▼</div>
|
||||
</div>
|
||||
|
||||
<div class="control-divider"></div>
|
||||
|
||||
<button id="copy-invite-btn" class="btn-quick-action" title="Copy invite link" style="display:none;">
|
||||
<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="M14 11a5 5 0 0 0-7.07 0L5.5 12.41a5 5 0 0 0 7.07 7.07L14 19"/>
|
||||
</svg>
|
||||
<span>Invite</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,7 +77,6 @@
|
||||
|
||||
<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>
|
||||
<span>Change Anime</span>
|
||||
</button>
|
||||
|
||||
<button id="toggle-chat-btn" class="btn-icon-glass" title="Toggle Chat">
|
||||
@@ -207,59 +215,62 @@
|
||||
<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>
|
||||
</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 class="config-layout">
|
||||
<div class="config-sidebar">
|
||||
<img id="config-cover" class="config-cover" src="" alt="Cover">
|
||||
|
||||
<div style="width: 100%">
|
||||
<div class="cfg-section-title">Episode</div>
|
||||
<div class="ep-control">
|
||||
<button class="ep-btn" id="ep-dec">−</button>
|
||||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||||
<button class="ep-btn" id="ep-inc">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="config-main">
|
||||
<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 class="config-main" style="justify-content: center;">
|
||||
|
||||
<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>
|
||||
<div class="cfg-section-title">Audio</div>
|
||||
<div class="cat-toggle" id="modal-sd-toggle">
|
||||
<div class="cat-opt active" data-val="sub">Subtitles</div>
|
||||
<div class="cat-opt" data-val="dub">Dubbed</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">
|
||||
<button class="ep-btn" id="ep-dec">−</button>
|
||||
<input type="number" id="inp-episode" class="ep-input" value="1" min="1">
|
||||
<button class="ep-btn" id="ep-inc">+</button>
|
||||
</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 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 class="form-actions" style="margin-top:auto; display:flex; gap:10px; padding-top: 20px;">
|
||||
<button id="btn-add-queue" class="btn-cancel" style="flex:1; justify-content: center; border-color: var(--brand-color); color: white;">
|
||||
+ Add Selected (<span id="sel-count">0</span>)
|
||||
</button>
|
||||
|
||||
<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>
|
||||
Play First
|
||||
</button>
|
||||
</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 id="btn-launch-stream" class="btn-confirm" style="flex:1;" disabled>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
|
||||
Play Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user