feat: dodanie mechanizmu limitu odtworzeń (TTL) w kolejce oraz konfiguracji w UI i na Discordzie
This commit is contained in:
5 files changed
+218
-9
No files matched your search
@@ -25,6 +25,7 @@ class MusicBot(commands.Cog):
|
|||||||
self.history = {} # guild_id -> list of dicts
|
self.history = {} # guild_id -> list of dicts
|
||||||
self.queue_indices = {} # guild_id -> int
|
self.queue_indices = {} # guild_id -> int
|
||||||
self.jump_flag = {} # guild_id -> bool
|
self.jump_flag = {} # guild_id -> bool
|
||||||
|
self.default_plays = self.get_default_plays()
|
||||||
self.on_state_change_callback = None
|
self.on_state_change_callback = None
|
||||||
|
|
||||||
def load_history(self, guild_id: int):
|
def load_history(self, guild_id: int):
|
||||||
@@ -126,15 +127,21 @@ class MusicBot(commands.Cog):
|
|||||||
|
|
||||||
async def get_fresh_player(self, track):
|
async def get_fresh_player(self, track):
|
||||||
if track.data.get('is_local'):
|
if track.data.get('is_local'):
|
||||||
return await YTDLSource.from_file(track.data['filepath'], loop=self.bot.loop, title=track.title)
|
new_player = await YTDLSource.from_file(track.data['filepath'], loop=self.bot.loop, title=track.title)
|
||||||
else:
|
else:
|
||||||
url = track.data.get('webpage_url') or track.url
|
url = track.data.get('webpage_url') or track.url
|
||||||
return await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
|
new_player = await YTDLSource.from_url(url, loop=self.bot.loop, stream=True)
|
||||||
|
new_player.id = track.id
|
||||||
|
new_player.plays_left = getattr(track, 'plays_left', self.get_default_plays())
|
||||||
|
return new_player
|
||||||
|
|
||||||
async def enqueue_track(self, guild_id: int, player, channel=None, from_web=False):
|
async def enqueue_track(self, guild_id: int, player, channel=None, from_web=False):
|
||||||
if channel:
|
if channel:
|
||||||
self.last_channels[guild_id] = channel
|
self.last_channels[guild_id] = channel
|
||||||
|
|
||||||
|
if getattr(player, 'plays_left', None) is None:
|
||||||
|
player.plays_left = self.get_default_plays()
|
||||||
|
|
||||||
queue = self.get_queue(guild_id)
|
queue = self.get_queue(guild_id)
|
||||||
queue.append(player)
|
queue.append(player)
|
||||||
|
|
||||||
@@ -216,6 +223,36 @@ class MusicBot(commands.Cog):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error saving config: {e}")
|
print(f"Error saving config: {e}")
|
||||||
|
|
||||||
|
def get_default_plays(self) -> int:
|
||||||
|
config_path = "config/config.json"
|
||||||
|
if os.path.exists(config_path):
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
with open(config_path, "r") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
val = data.get("default_plays")
|
||||||
|
if val is not None:
|
||||||
|
return int(val)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading default_plays: {e}")
|
||||||
|
return 5
|
||||||
|
|
||||||
|
def set_default_plays(self, val: int):
|
||||||
|
config_path = "config/config.json"
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
||||||
|
data = {}
|
||||||
|
if os.path.exists(config_path):
|
||||||
|
with open(config_path, "r") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
data["default_plays"] = int(val)
|
||||||
|
with open(config_path, "w") as f:
|
||||||
|
json.dump(data, f, indent=4)
|
||||||
|
self.default_plays = int(val)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving default_plays: {e}")
|
||||||
|
|
||||||
def get_notification_channel(self, guild_id: int):
|
def get_notification_channel(self, guild_id: int):
|
||||||
if self.last_channels.get(guild_id):
|
if self.last_channels.get(guild_id):
|
||||||
return self.last_channels[guild_id]
|
return self.last_channels[guild_id]
|
||||||
@@ -394,7 +431,17 @@ class MusicBot(commands.Cog):
|
|||||||
player = track
|
player = track
|
||||||
|
|
||||||
player.played = True
|
player.played = True
|
||||||
queue[next_idx] = player
|
current_plays = getattr(player, 'plays_left', self.get_default_plays())
|
||||||
|
player.plays_left = current_plays - 1
|
||||||
|
|
||||||
|
if player.plays_left <= 0:
|
||||||
|
queue.pop(next_idx)
|
||||||
|
self.set_queue_index(guild_id, next_idx - 1)
|
||||||
|
if self.get_loop_current(guild_id):
|
||||||
|
self.set_loop_current(guild_id, False)
|
||||||
|
else:
|
||||||
|
queue[next_idx] = player
|
||||||
|
|
||||||
self.set_current(guild_id, player)
|
self.set_current(guild_id, player)
|
||||||
self.add_to_history(guild_id, player)
|
self.add_to_history(guild_id, player)
|
||||||
await self.trigger_state_change()
|
await self.trigger_state_change()
|
||||||
@@ -445,10 +492,15 @@ class MusicBot(commands.Cog):
|
|||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
current_idx = self.get_queue_index(guild_id)
|
current_idx = self.get_queue_index(guild_id)
|
||||||
|
current_playing = self.get_current(guild_id)
|
||||||
|
if current_playing and (current_idx < 0 or current_idx >= len(queue)):
|
||||||
|
plays = getattr(current_playing, 'plays_left', 0)
|
||||||
|
lines.append(f"▶️ **{discord.utils.escape_markdown(current_playing.title)}** (obecnie odtwarzane - ostatnie odtworzenie) [🔁 {plays}]\n---")
|
||||||
for i, item in enumerate(queue):
|
for i, item in enumerate(queue):
|
||||||
prefix = "▶️ **" if i == current_idx else f"{i + 1}. "
|
prefix = "▶️ **" if i == current_idx else f"{i + 1}. "
|
||||||
suffix = "** (obecnie odtwarzane)" if i == current_idx else ""
|
suffix = "** (obecnie odtwarzane)" if i == current_idx else ""
|
||||||
lines.append(f"{prefix}{discord.utils.escape_markdown(item.title)}{suffix}")
|
plays = getattr(item, 'plays_left', self.get_default_plays())
|
||||||
|
lines.append(f"{prefix}{discord.utils.escape_markdown(item.title)}{suffix} [🔁 {plays}]")
|
||||||
queue_list = "\n".join(lines[:30])
|
queue_list = "\n".join(lines[:30])
|
||||||
if len(lines) > 30:
|
if len(lines) > 30:
|
||||||
queue_list += f"\n... i {len(lines) - 30} więcej."
|
queue_list += f"\n... i {len(lines) - 30} więcej."
|
||||||
@@ -694,6 +746,32 @@ class MusicBot(commands.Cog):
|
|||||||
ephemeral=False
|
ephemeral=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@app_commands.command(name="defaultplays", description="Ustaw domyślną liczbę odtworzeń dla nowych utworów w kolejce")
|
||||||
|
@app_commands.describe(count="Domyślna liczba odtworzeń (1-999)")
|
||||||
|
async def default_plays_cmd(self, interaction: discord.Interaction, count: int):
|
||||||
|
if count < 1 or count > 999:
|
||||||
|
await interaction.response.send_message("❌ Liczba odtworzeń musi być z przedziału 1-999.", ephemeral=True)
|
||||||
|
return
|
||||||
|
self.set_default_plays(count)
|
||||||
|
await self.trigger_state_change()
|
||||||
|
await interaction.response.send_message(f"✅ Domyślna liczba odtworzeń dla nowych utworów została ustawiona na: **{count}**.")
|
||||||
|
|
||||||
|
@app_commands.command(name="trackplays", description="Zmień liczbę odtworzeń dla utworu w kolejce")
|
||||||
|
@app_commands.describe(index="Pozycja w kolejce (od 1)", count="Nowa liczba odtworzeń (1-999)")
|
||||||
|
async def track_plays_cmd(self, interaction: discord.Interaction, index: int, count: int):
|
||||||
|
guild_id = interaction.guild.id
|
||||||
|
queue = self.get_queue(guild_id)
|
||||||
|
if index < 1 or index > len(queue):
|
||||||
|
await interaction.response.send_message("❌ Nieprawidłowy numer utworu w kolejce.", ephemeral=True)
|
||||||
|
return
|
||||||
|
if count < 1 or count > 999:
|
||||||
|
await interaction.response.send_message("❌ Liczba odtworzeń musi być z przedziału 1-999.", ephemeral=True)
|
||||||
|
return
|
||||||
|
track = queue[index - 1]
|
||||||
|
track.plays_left = count
|
||||||
|
await self.trigger_state_change()
|
||||||
|
await interaction.response.send_message(f"✅ Ustawiono liczbę odtworzeń dla utworu **{discord.utils.escape_markdown(track.title)}** na **{count}**.")
|
||||||
|
|
||||||
|
|
||||||
class MyBot(commands.Bot):
|
class MyBot(commands.Bot):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|||||||
+79
-2
@@ -175,6 +175,30 @@ function bindEvents() {
|
|||||||
if (e.target === settingsModal) closeSettingsModal();
|
if (e.target === settingsModal) closeSettingsModal();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const btnSaveDefaultPlays = document.getElementById('btn-save-default-plays');
|
||||||
|
if (btnSaveDefaultPlays) {
|
||||||
|
btnSaveDefaultPlays.addEventListener('click', async () => {
|
||||||
|
const inputDefaultPlays = document.getElementById('setting-default-plays');
|
||||||
|
if (!inputDefaultPlays) return;
|
||||||
|
const val = parseInt(inputDefaultPlays.value, 10);
|
||||||
|
if (isNaN(val) || val < 1 || val > 999) {
|
||||||
|
showToast('Wartość musi być z przedziału 1-999', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/settings/default_plays', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ default_plays: val })
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Failed to save setting');
|
||||||
|
showToast(`Domyślna liczba odtworzeń: ${val}`, 'success');
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Błąd zapisu ustawienia', 'error');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Uploads Modal Events
|
// Uploads Modal Events
|
||||||
if (btnOpenAllUploads) btnOpenAllUploads.addEventListener('click', openUploadsModal);
|
if (btnOpenAllUploads) btnOpenAllUploads.addEventListener('click', openUploadsModal);
|
||||||
@@ -271,6 +295,7 @@ function updateProgressBar() {
|
|||||||
|
|
||||||
// Update UI States
|
// Update UI States
|
||||||
function updateUI(status) {
|
function updateUI(status) {
|
||||||
|
currentStatus = status;
|
||||||
// 1. Connection Status & Available Channels
|
// 1. Connection Status & Available Channels
|
||||||
if (status.active_voice_channel) {
|
if (status.active_voice_channel) {
|
||||||
statusDot.className = 'status-indicator online';
|
statusDot.className = 'status-indicator online';
|
||||||
@@ -320,6 +345,12 @@ function updateUI(status) {
|
|||||||
if (status.guilds) {
|
if (status.guilds) {
|
||||||
renderSettingsGuilds(status.guilds);
|
renderSettingsGuilds(status.guilds);
|
||||||
}
|
}
|
||||||
|
if (status.default_plays !== undefined) {
|
||||||
|
const inputDefaultPlays = document.getElementById('setting-default-plays');
|
||||||
|
if (inputDefaultPlays && document.activeElement !== inputDefaultPlays) {
|
||||||
|
inputDefaultPlays.value = status.default_plays;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Playback State
|
// 2. Playback State
|
||||||
if (status.current) {
|
if (status.current) {
|
||||||
@@ -422,8 +453,8 @@ function updateUI(status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Queue render (if not currently dragging)
|
// 6. Queue render (if not currently dragging and not editing track plays input)
|
||||||
if (!isDragging) {
|
if (!isDragging && (!document.activeElement || !document.activeElement.classList.contains('track-plays-input'))) {
|
||||||
renderQueue(status.queue, status.queue_index);
|
renderQueue(status.queue, status.queue_index);
|
||||||
renderHistory(status.history || []);
|
renderHistory(status.history || []);
|
||||||
}
|
}
|
||||||
@@ -471,6 +502,13 @@ function renderQueue(queue, queueIndex = -1) {
|
|||||||
${indexHtml}
|
${indexHtml}
|
||||||
<div class="queue-track-info">
|
<div class="queue-track-info">
|
||||||
<div class="queue-track-title" title="${track.title}">${track.title}</div>
|
<div class="queue-track-title" title="${track.title}">${track.title}</div>
|
||||||
|
<div class="track-plays-control" style="display: flex; align-items: center; gap: 6px; font-size: 0.75rem; background: rgba(255,255,255,0.06); padding: 3px 10px; border-radius: 12px; margin-top: 6px; width: fit-content; border: 1px solid rgba(255,255,255,0.1);">
|
||||||
|
<i data-lucide="repeat" style="width: 13px; height: 13px; opacity: 0.9; color: var(--primary);"></i>
|
||||||
|
<span title="Pozostało odtworzeń z kolejki" style="color: var(--text-muted);">Pozostało:</span>
|
||||||
|
<button class="btn-tiny-action" onclick="changeTrackPlays('${track.id}', -1)" title="Zmniejsz o 1" style="background: rgba(255,255,255,0.12); border: none; color: inherit; cursor: pointer; padding: 2px 6px; border-radius: 6px; margin-left: 2px; transition: background 0.2s;"><i data-lucide="minus" style="width: 11px; height: 11px;"></i></button>
|
||||||
|
<input type="number" class="track-plays-input" min="1" max="999" value="${track.plays_left !== undefined ? track.plays_left : (currentStatus && currentStatus.default_plays ? currentStatus.default_plays : 5)}" onchange="changeTrackPlaysDirect('${track.id}', this.value)" style="width: 44px; text-align: center; background: rgba(0,0,0,0.35); border: 1px solid rgba(255,255,255,0.2); color: #fff; border-radius: 6px; padding: 2px 4px; font-size: 0.8rem; font-weight: bold;">
|
||||||
|
<button class="btn-tiny-action" onclick="changeTrackPlays('${track.id}', 1)" title="Zwiększ o 1" style="background: rgba(255,255,255,0.12); border: none; color: inherit; cursor: pointer; padding: 2px 6px; border-radius: 6px; transition: background 0.2s;"><i data-lucide="plus" style="width: 11px; height: 11px;"></i></button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="queue-item-actions">
|
<div class="queue-item-actions">
|
||||||
<button class="btn-action-icon" onclick="jumpToTrack(${idx})" title="Odtwarzaj od tej pozycji">
|
<button class="btn-action-icon" onclick="jumpToTrack(${idx})" title="Odtwarzaj od tej pozycji">
|
||||||
@@ -851,6 +889,45 @@ async function saveGuildLogChannel(guildId, channelId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function changeTrackPlays(trackId, delta) {
|
||||||
|
if (!currentStatus || !currentStatus.queue) return;
|
||||||
|
const track = currentStatus.queue.find(t => t.id === trackId);
|
||||||
|
if (!track) return;
|
||||||
|
const currentPlays = track.plays_left !== undefined ? track.plays_left : (currentStatus.default_plays || 5);
|
||||||
|
const newPlays = Math.max(1, Math.min(999, currentPlays + delta));
|
||||||
|
if (newPlays === currentPlays) return;
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/queue/plays', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id: trackId, plays: newPlays })
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Failed to update plays');
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Błąd aktualizacji odtworzeń', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function changeTrackPlaysDirect(trackId, val) {
|
||||||
|
const newPlays = parseInt(val, 10);
|
||||||
|
if (isNaN(newPlays) || newPlays < 1 || newPlays > 999) {
|
||||||
|
showToast('Wartość musi być z przedziału 1-999', 'error');
|
||||||
|
if (currentStatus) renderQueue(currentStatus.queue, currentStatus.queue_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/queue/plays', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ id: trackId, plays: newPlays })
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Failed to update plays');
|
||||||
|
} catch (err) {
|
||||||
|
showToast('Błąd aktualizacji odtworzeń', 'error');
|
||||||
|
if (currentStatus) renderQueue(currentStatus.queue, currentStatus.queue_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch Uploaded Files
|
// Fetch Uploaded Files
|
||||||
async function fetchUploads() {
|
async function fetchUploads() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -222,6 +222,14 @@
|
|||||||
<button class="btn-icon" id="btn-close-settings" title="Zamknij"><i data-lucide="x"></i></button>
|
<button class="btn-icon" id="btn-close-settings" title="Zamknij"><i data-lucide="x"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<div class="settings-section" style="margin-bottom: 24px; padding-bottom: 24px; border-bottom: 1px solid rgba(255,255,255,0.08);">
|
||||||
|
<h3><i data-lucide="repeat"></i> Domyślna liczba odtworzeń w kolejce</h3>
|
||||||
|
<p class="settings-desc">Ustaw domyślną liczbę odtworzeń dla każdego nowo dodawanego utworu do kolejki. Przy każdym odtworzeniu utworu wartość jest pomniejszana o 1, a po spadku do 0 utwór usuwany jest z kolejki.</p>
|
||||||
|
<div style="display: flex; align-items: center; gap: 12px; margin-top: 12px;">
|
||||||
|
<input type="number" id="setting-default-plays" class="input-text" min="1" max="999" value="5" style="width: 100px; text-align: center; font-weight: bold; font-size: 1.1rem; padding: 8px 12px;">
|
||||||
|
<button id="btn-save-default-plays" class="btn btn-primary" style="display: flex; align-items: center; gap: 8px; padding: 8px 16px;"><i data-lucide="save"></i> Zapisz</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="settings-section">
|
<div class="settings-section">
|
||||||
<h3><i data-lucide="server"></i> Konfiguracja Serwerów Discord (Kanały Logów)</h3>
|
<h3><i data-lucide="server"></i> Konfiguracja Serwerów Discord (Kanały Logów)</h3>
|
||||||
<p class="settings-desc">Wybierz kanał tekstowy na każdym serwerze Discord, na który bot ma wysyłać powiadomienia o odtwarzanych utworach i zmianach w kolejce.</p>
|
<p class="settings-desc">Wybierz kanał tekstowy na każdym serwerze Discord, na który bot ma wysyłać powiadomienia o odtwarzanych utworach i zmianach w kolejce.</p>
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ class TextChannelRequest(BaseModel):
|
|||||||
class PlayFileRequest(BaseModel):
|
class PlayFileRequest(BaseModel):
|
||||||
filename: str
|
filename: str
|
||||||
|
|
||||||
|
class DefaultPlaysRequest(BaseModel):
|
||||||
|
default_plays: int
|
||||||
|
|
||||||
|
class TrackPlaysRequest(BaseModel):
|
||||||
|
id: str
|
||||||
|
plays: int
|
||||||
|
|
||||||
# Connection Manager for WebSockets
|
# Connection Manager for WebSockets
|
||||||
class ConnectionManager:
|
class ConnectionManager:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -123,7 +130,8 @@ async def get_status():
|
|||||||
"active_voice_channel": None,
|
"active_voice_channel": None,
|
||||||
"text_channels": [],
|
"text_channels": [],
|
||||||
"active_text_channel_id": None,
|
"active_text_channel_id": None,
|
||||||
"guilds": all_guilds
|
"guilds": all_guilds,
|
||||||
|
"default_plays": music_cog.get_default_plays() if music_cog else 5
|
||||||
}
|
}
|
||||||
|
|
||||||
guild_id = guild.id
|
guild_id = guild.id
|
||||||
@@ -172,16 +180,19 @@ async def get_status():
|
|||||||
"url": current_track.data.get("webpage_url") or current_track.url,
|
"url": current_track.data.get("webpage_url") or current_track.url,
|
||||||
"id": current_track.id,
|
"id": current_track.id,
|
||||||
"duration": current_track.duration,
|
"duration": current_track.duration,
|
||||||
"elapsed": round(current_track.elapsed, 1)
|
"elapsed": round(current_track.elapsed, 1),
|
||||||
|
"plays_left": getattr(current_track, 'plays_left', music_cog.get_default_plays())
|
||||||
} if current_track else None,
|
} if current_track else None,
|
||||||
"queue": [
|
"queue": [
|
||||||
{
|
{
|
||||||
"title": item.title,
|
"title": item.title,
|
||||||
"url": item.data.get("webpage_url") or item.url,
|
"url": item.data.get("webpage_url") or item.url,
|
||||||
"id": item.id
|
"id": item.id,
|
||||||
|
"plays_left": getattr(item, 'plays_left', music_cog.get_default_plays())
|
||||||
} for item in queue
|
} for item in queue
|
||||||
],
|
],
|
||||||
"queue_index": music_cog.get_queue_index(guild_id),
|
"queue_index": music_cog.get_queue_index(guild_id),
|
||||||
|
"default_plays": music_cog.get_default_plays(),
|
||||||
"voice_channels": all_voice_channels,
|
"voice_channels": all_voice_channels,
|
||||||
"active_voice_channel": active_vc,
|
"active_voice_channel": active_vc,
|
||||||
"text_channels": text_channels,
|
"text_channels": text_channels,
|
||||||
@@ -664,6 +675,40 @@ async def api_set_text_channel(req: TextChannelRequest):
|
|||||||
await broadcast_status()
|
await broadcast_status()
|
||||||
return {"status": "success", "channel_id": req.channel_id, "guild_id": str(guild_id)}
|
return {"status": "success", "channel_id": req.channel_id, "guild_id": str(guild_id)}
|
||||||
|
|
||||||
|
@app.post("/api/settings/default_plays")
|
||||||
|
async def api_set_default_plays(req: DefaultPlaysRequest):
|
||||||
|
music_cog = bot.get_cog("MusicBot")
|
||||||
|
if not music_cog:
|
||||||
|
raise HTTPException(status_code=500, detail="MusicBot cog not loaded")
|
||||||
|
if req.default_plays < 1 or req.default_plays > 999:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid default plays value")
|
||||||
|
music_cog.set_default_plays(req.default_plays)
|
||||||
|
await broadcast_status()
|
||||||
|
return {"status": "success", "default_plays": req.default_plays}
|
||||||
|
|
||||||
|
@app.post("/api/queue/plays")
|
||||||
|
async def api_set_track_plays(req: TrackPlaysRequest):
|
||||||
|
music_cog = bot.get_cog("MusicBot")
|
||||||
|
if not music_cog:
|
||||||
|
raise HTTPException(status_code=500, detail="MusicBot cog not loaded")
|
||||||
|
guild = get_active_guild()
|
||||||
|
if not guild:
|
||||||
|
raise HTTPException(status_code=400, detail="Bot has no active server context")
|
||||||
|
guild_id = guild.id
|
||||||
|
queue = music_cog.get_queue(guild_id)
|
||||||
|
if req.plays < 1 or req.plays > 999:
|
||||||
|
raise HTTPException(status_code=400, detail="Invalid plays value")
|
||||||
|
found = False
|
||||||
|
for item in queue:
|
||||||
|
if item.id == req.id:
|
||||||
|
item.plays_left = req.plays
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
raise HTTPException(status_code=404, detail="Track not found in queue")
|
||||||
|
await broadcast_status()
|
||||||
|
return {"status": "success", "id": req.id, "plays": req.plays}
|
||||||
|
|
||||||
# WebSocket Background Loop and Startup Hook
|
# WebSocket Background Loop and Startup Hook
|
||||||
async def status_broadcast_loop():
|
async def status_broadcast_loop():
|
||||||
while True:
|
while True:
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class YTDLSource(discord.PCMVolumeTransformer):
|
|||||||
self.id = uuid.uuid4().hex # Unique track identifier
|
self.id = uuid.uuid4().hex # Unique track identifier
|
||||||
self.duration = data.get('duration') # Duration in seconds
|
self.duration = data.get('duration') # Duration in seconds
|
||||||
self.elapsed = 0.0 # Elapsed time in seconds
|
self.elapsed = 0.0 # Elapsed time in seconds
|
||||||
|
self.plays_left = data.get('plays_left', None) # Remaining plays in queue
|
||||||
|
|
||||||
def read(self):
|
def read(self):
|
||||||
data = super().read()
|
data = super().read()
|
||||||
|
|||||||
Reference in new issue
Block a user