Added start and end position for each track

This commit is contained in:
Choco
2024-06-26 15:06:18 +08:00
parent f87f3f2152
commit 33ad427670
13 changed files with 117 additions and 43 deletions

View File

@@ -30,7 +30,6 @@ from function import (
settings,
send,
time as ctime,
formatTime,
format_time,
get_source,
get_user,
@@ -122,10 +121,14 @@ class Basic(commands.Cog):
await send(ctx, 'connect', player.channel)
@commands.hybrid_command(name="play", aliases=get_aliases("play"))
@app_commands.describe(query="Input a query or a searchable link.")
@app_commands.describe(
query="Input a query or a searchable link.",
start="Specify a time you would like to start, e.g. 1:00",
end="Specify a time you would like to end, e.g. 4:00"
)
@app_commands.autocomplete(query=play_autocomplete)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
async def play(self, ctx: commands.Context, *, query: str) -> None:
async def play(self, ctx: commands.Context, *, query: str, start: str = "0", end: str = "0") -> None:
"Loads your input and added it to the queue."
player: voicelink.Player = ctx.guild.voice_client
if not player:
@@ -140,10 +143,10 @@ class Basic(commands.Cog):
try:
if isinstance(tracks, voicelink.Playlist):
index = await player.add_track(tracks.tracks)
index = await player.add_track(tracks.tracks, start_time=format_time(start), end_time=format_time(end))
await send(ctx, "playlistLoad", tracks.name, index)
else:
position = await player.add_track(tracks[0])
position = await player.add_track(tracks[0], start_time=format_time(start), end_time=format_time(end))
texts = await get_lang(ctx.guild.id, "live", "trackLoad_pos", "trackLoad")
await ctx.send((f"`{texts[0]}`" if tracks[0].is_stream else "") + (texts[1].format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length, position) if position >= 1 and player.is_playing else texts[2].format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length)), allowed_mentions=False)
except voicelink.QueueFull as e:
@@ -250,10 +253,14 @@ class Basic(commands.Cog):
await player.do_next()
@commands.hybrid_command(name="playtop", aliases=get_aliases("playtop"))
@app_commands.describe(query="Input a query or a searchable link.")
@app_commands.describe(
query="Input a query or a searchable link.",
start="Specify a time you would like to start, e.g. 1:00",
end="Specify a time you would like to end, e.g. 4:00"
)
@app_commands.autocomplete(query=play_autocomplete)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
async def playtop(self, ctx: commands.Context, *, query: str):
async def playtop(self, ctx: commands.Context, *, query: str, start: str = "0", end: str = "0"):
"Adds a song with the given url or query on the top of the queue."
player: voicelink.Player = ctx.guild.voice_client
if not player:
@@ -268,10 +275,10 @@ class Basic(commands.Cog):
try:
if isinstance(tracks, voicelink.Playlist):
index = await player.add_track(tracks.tracks, at_font=True)
index = await player.add_track(tracks.tracks, start_time=format_time(start), end_time=format_time(end), at_font=True)
await send(ctx, "playlistLoad", tracks.name, index)
else:
position = await player.add_track(tracks[0], at_font=True)
position = await player.add_track(tracks[0], start_time=format_time(start), end_time=format_time(end), at_font=True)
texts = await get_lang(ctx.guild.id, "live", "trackLoad_pos", "trackLoad")
await ctx.send((f"`{texts[0]}`" if tracks[0].is_stream else "") + (texts[1].format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length, position) if position >= 1 and player.is_playing else texts[2].format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length)), allowed_mentions=False)
@@ -283,9 +290,13 @@ class Basic(commands.Cog):
await player.do_next()
@commands.hybrid_command(name="forceplay", aliases=get_aliases("forceplay"))
@app_commands.describe(query="Input a query or a searchable link.")
@app_commands.describe(
query="Input a query or a searchable link.",
start="Specify a time you would like to start, e.g. 1:00",
end="Specify a time you would like to end, e.g. 4:00"
)
@commands.dynamic_cooldown(cooldown_check, commands.BucketType.guild)
async def forceplay(self, ctx: commands.Context, query: str):
async def forceplay(self, ctx: commands.Context, *, query: str, start: str = "0", end: str = "0"):
"Enforce playback using the given URL or query."
player: voicelink.Player = ctx.guild.voice_client
if not player:
@@ -300,11 +311,11 @@ class Basic(commands.Cog):
try:
if isinstance(tracks, voicelink.Playlist):
index = await player.add_track(tracks.tracks, at_font=True)
index = await player.add_track(tracks.tracks, start_time=format_time(start), end_time=format_time(end), at_font=True)
await send(ctx, "playlistLoad", tracks.name, index)
else:
texts = await get_lang(ctx.guild.id, "live", "trackLoad")
await player.add_track(tracks[0], at_font=True)
await player.add_track(tracks[0], start_time=format_time(start), end_time=format_time(end), at_font=True)
await ctx.send((f"`{texts[0]}`" if tracks[0].is_stream else "") + texts[1].format(tracks[0].title, tracks[0].uri, tracks[0].author, tracks[0].formatted_length), allowed_mentions=False)
except voicelink.QueueFull as e:
@@ -404,7 +415,7 @@ class Basic(commands.Cog):
return await send(ctx, "noPlayer", ephemeral=True)
if not player.node._available:
return await send(ctx, "nnodeReconnectode")
return await send(ctx, "nodeReconnectode")
if not player.is_privileged(ctx.author):
if ctx.author in player.previous_votes:

View File

@@ -1,4 +1,5 @@
import time
import time, re
import function as func
from datetime import timezone
@@ -8,6 +9,7 @@ from typing import List, Dict, Union, Optional
from discord import User, Member, VoiceChannel
from discord.ext import commands
from voicelink import Player, Track, Playlist, NodePool, decode, LoopType
from addons import lyricsPlatform
RATELIMIT_COUNTER: Dict[int, Dict[str, float]] = {}
SCOPES = {
@@ -560,6 +562,21 @@ async def getSettings(bot: commands.Bot, data: Dict) -> Dict:
"user_id": str(user_id)
}
async def getLyrics(bot: commands.Bot, data: Dict) -> Dict:
title, artist, platform = data.get("title", ""), data.get("artist", ""), data.get("platform", "")
if not platform or platform not in lyricsPlatform:
platform = func.settings.lyrics_platform
song: dict[str, str] = await lyricsPlatform.get(platform)().get_lyrics(title, artist)
payload = {
"op": "getLyrics",
"user_id": data.get("user_id"),
"lyrics": {_: re.findall(r'.*\n(?:.*\n){,22}', v) for _, v in song.items()} if song else {},
"callback": data.get("callback")
}
return payload
async def updateSettings(bot: commands.Bot, data: Dict) -> None:
user_id = int(data.get("user_id"))
guild_id = int(data.get("guild_id"))
@@ -588,22 +605,23 @@ async def updateSettings(bot: commands.Bot, data: Dict) -> None:
await func.update_settings(guild.id, {"$set": data})
methods: Dict[str, Union[SystemMethod, PlayerMethod]] = {
METHODS: Dict[str, Union[SystemMethod, PlayerMethod]] = {
"initBot": SystemMethod(initBot, credit=0),
"initUser": SystemMethod(initUser, credit=0),
"getPlaylist": SystemMethod(getPlaylist),
"updatePlaylist": SystemMethod(updatePlaylist),
"getMutualGuilds": SystemMethod(getMutualGuilds),
"getSettings": SystemMethod(getSettings),
"getLyrics": SystemMethod(getLyrics),
"updateSettings": SystemMethod(updateSettings),
"getRecommendation": SystemMethod(getRecommendation, credit=5),
"closeConnection": SystemMethod(closeConnection, credit=0),
"getTracks": SystemMethod(getTracks, credit=5),
"initPlayer": PlayerMethod(initPlayer),
"skipTo": PlayerMethod(skipTo),
"backTo": PlayerMethod(backTo),
"moveTrack": PlayerMethod(moveTrack),
"addTracks": PlayerMethod(addTracks, auto_connect=True),
"getTracks": PlayerMethod(getTracks, auto_connect=True),
"shuffleTrack": PlayerMethod(shuffleTrack, credit=3),
"repeatTrack": PlayerMethod(repeatTrack),
"removeTrack": PlayerMethod(removeTrack),
@@ -615,7 +633,7 @@ methods: Dict[str, Union[SystemMethod, PlayerMethod]] = {
async def process_methods(ipc_client, bot: commands.Bot, data: Dict) -> None:
op: str = data.get("op", "")
method = methods.get(op)
method = METHODS.get(op)
if not method or not (user_id := data.get("user_id")):
return

View File

@@ -187,5 +187,8 @@
"voicelinkOutofList": "請提供有效的歌曲索引!",
"voicelinkDuplicateTrack": "抱歉,此歌曲已在隊列中。",
"deocdeError": "解碼文件時出現問題!"
"deocdeError": "解碼文件時出現問題!",
"invalidStartTime": "無效的開始時間! 時間必須在 `00:00` 和 `{0}` 之間。",
"invalidEndTime": "無效的結束時間! 時間必須在 `00:00` 和 `{0}` 之間。",
"invalidTimeOrder": "結束時間不能小於或等於開始時間。"
}

View File

@@ -187,5 +187,8 @@
"voicelinkOutofList": "Bitte gib einen gültigen Track-Index an!",
"voicelinkDuplicateTrack": "Entschuldigung, dieser Track ist bereits in der Warteschlange.",
"deocdeError": "Beim Dekodieren der Datei ist etwas schief gelaufen!"
"deocdeError": "Beim Dekodieren der Datei ist etwas schief gelaufen!",
"invalidStartTime": "Ungültiger Startzeit! Die Zeit muss innerhalb von `00:00` und `{0}` liegen.",
"invalidEndTime": "Ungültiger Endzeit! Die Zeit muss innerhalb von `00:00` und `{0}` liegen.",
"invalidTimeOrder": "Der Endzeit darf nicht kleiner oder gleich dem Startzeit sein."
}

View File

@@ -187,5 +187,8 @@
"voicelinkOutofList": "Please provide a valid track index!",
"voicelinkDuplicateTrack": "Sorry, this track is already in the queue.",
"deocdeError": "Something went wrong while decoding the file!"
"deocdeError": "Something went wrong while decoding the file!",
"invalidStartTime": "Invalid start time, it must be between `00:00` and `{0}`",
"invalidEndTime": "Invalid end time, it must be between `00:00` and `{0}`",
"invalidTimeOrder": "End time cannot be less than or equal to start time"
}

View File

@@ -187,5 +187,8 @@
"voicelinkOutofList": "¡Proporcione un índice de pista válido!",
"voicelinkDuplicateTrack": "Lo siento, esta canción ya está en la cola.",
"deocdeError": "¡Algo salió mal al decodificar el archivo!"
"deocdeError": "¡Algo salió mal al decodificar el archivo!",
"invalidStartTime": "Tiempo de inicio inválido! El tiempo debe estar entre `00:00` y `{0}`.",
"invalidEndTime": "Tiempo de finalización inválido! El tiempo debe estar entre `00:00` y `{0}`.",
"invalidTimeOrder": "El tiempo final no puede ser menor o igual que el tiempo de inicio."
}

View File

@@ -187,5 +187,8 @@
"voicelinkOutofList": "有効なトラックインデックスを指定してください!",
"voicelinkDuplicateTrack": "申し訳ありませんが、このトラックは既にキューに存在します。",
"deocdeError": "ファイルのデコード中に問題が発生しました!"
"deocdeError": "ファイルのデコード中に問題が発生しました!",
"invalidStartTime": "無効な開始時間!時間は `00:00` と `{0}` の間に設定する必要があります。",
"invalidEndTime": "無効な終了時間!時間は `00:00` と `{0}` の間に設定する必要があります。",
"invalidTimeOrder": "終了時間は開始時間より大きくない必要があります。"
}

View File

@@ -187,5 +187,8 @@
"voicelinkOutofList": "유효한 트랙 인덱스를 제공해주세요!",
"voicelinkDuplicateTrack": "죄송합니다. 이 트랙은 이미 큐에 있습니다.",
"deocdeError": "파일 디코딩 중 문제가 발생했습니다!"
"deocdeError": "파일 디코딩 중 문제가 발생했습니다!",
"invalidStartTime": "효력 없는 시작 시간! 시간은 `00:00` 과 `{0}` 사이에 설정해야 합니다.",
"invalidEndTime": "효력 없는 종료 시간! 시간은 `00:00` 과 `{0}` 사이에 설정해야 합니다.",
"invalidTimeOrder": "종료 시간은 시작 시간보다 클수 있어야 합니다."
}

View File

@@ -186,5 +186,8 @@
"voicelinkOutofList": "Пожалуйста, предоставьте действительный индекс трека!",
"voicelinkDuplicateTrack": "Извините, этот трек уже есть в очереди.",
"deocdeError": "Что-то пошло не так при декодировании файла!"
"deocdeError": "Что-то пошло не так при декодировании файла!",
"invalidStartTime": "Hевозможное время начала! Вход времени должен быть внутри `00:00` и `{0}`.",
"invalidEndTime": "Невозможное время конца! Вход времени должен быть внутри `00:00` и `{0}`.",
"invalidTimeOrder": "Время конца не может быть меньше или равно времени начала."
}

View File

@@ -186,5 +186,8 @@
"voicelinkOutofList": "Будь ласка, надайте дійсний індекс треку!",
"voicelinkDuplicateTrack": "Вибачте, цей трек уже є в черзі.",
"deocdeError": "Щось пішло не так під час декодування файлу!"
"deocdeError": "Щось пішло не так під час декодування файлу!",
"invalidStartTime": "Недійснений час початку! Час має бути в межах `00:00` та `{0}`.",
"invalidEndTime": "Недійснений час закінчення! Час має бути в межах `00:00` та `{0}`.",
"invalidTimeOrder": "Час закінчення не може бути меншим або рівним часу початку."
}

View File

@@ -2,7 +2,7 @@ import requests, zipfile, os, shutil, argparse
from io import BytesIO
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
__version__ = "v2.6.9b3"
__version__ = "v2.6.9b4"
GITHUB_API_URL = "https://api.github.com/repos/ChocoMeow/Vocard/releases/latest"
VOCARD_URL = "https://github.com/ChocoMeow/Vocard/archive/"

View File

@@ -62,7 +62,8 @@ class Track:
"requester",
"is_stream",
"is_seekable",
"position"
"position",
"end_time"
)
def __init__(
@@ -102,6 +103,8 @@ class Track:
self.is_seekable: bool = info.get("isSeekable", True)
self.position: int = info.get("position", 0)
self.end_time: Optional[int] = None
def __eq__(self, other) -> bool:
if not isinstance(other, Track):
return False

View File

@@ -496,7 +496,7 @@ class Player(VoiceProtocol):
self._node._players.pop(self.guild.id)
await self.send(method=RequestMethod.delete)
async def play(
self,
track: Track,
@@ -511,41 +511,58 @@ class Player(VoiceProtocol):
if track.spotify:
if not track.original:
search: Track = (await self._node.get_tracks(
f"ytsearch:{track.author} - {track.title}",
requester=track.requester
))
if not search:
raise TrackLoadError("Can't not found a playable source!")
search_results = await self._node.get_tracks(f"ytmsearch:{track.author} - {track.title}", requester=track.requester)
if not search_results:
raise TrackLoadError("Can't find a playable source!")
track.original = search_results[0]
track.original = search[0]
data = {
"encodedTrack": track.original.track_id if track.original else track.track_id,
"position": str(start if start else track.position),
"endTime": str(end if end else track.original.length)
"position": str(start if start else track.position)
}
if end or track.end_time:
data["endTime"] = str(end if end else track.end_time)
await self.send(method=RequestMethod.patch, query=f"noReplace={ignore_if_playing}", data=data)
self._current = track
if self.volume != 100:
await self.set_volume(self.volume)
self._logger.debug(f"Player in {self.guild.name}({self.guild.id}) playing {track.title} from uri {track.uri} with a length of {track.length}")
return self._current
async def add_track(self, raw_tracks: Union[Track, List[Track]], *, at_font: bool = False, duplicate: bool = True) -> int:
def _validate_time(self, track: Track, start_time: int, end_time: int) -> None:
if start_time or end_time:
if not end_time:
end_time = track.length
if start_time >= end_time:
raise VoicelinkException(self.get_msg("invalidStartTime"))
track_length = track.length
if not 0 <= start_time <= track_length:
raise VoicelinkException(self.get_msg("invalidStartTime", func.time(track_length)))
if not 0 <= end_time <= track_length:
raise VoicelinkException(self.get_msg("invalidEndTime", func.time(track_length)))
track.position = start_time
track.end_time = end_time
async def add_track(self, raw_tracks: Union[Track, List[Track]], *, start_time: int = 0, end_time: int = 0, at_front: bool = False, duplicate: bool = True) -> int:
tracks: List[Track] = []
_duplicate_tracks = [] if self.queue._allow_duplicate and duplicate else [track.uri for track in self.queue._queue]
raw_tracks = raw_tracks[0] if isinstance(raw_tracks, List) and len(raw_tracks) == 1 else raw_tracks
try:
if (is_list := isinstance(raw_tracks, List)):
for track in raw_tracks:
if track.uri in _duplicate_tracks:
continue
self._validate_time(track, start_time, end_time)
self.queue.put_at_front(track) if at_front else self.queue.put(track)
tracks.append(track)
_duplicate_tracks.append(track.uri)
@@ -553,7 +570,8 @@ class Player(VoiceProtocol):
if raw_tracks.uri in _duplicate_tracks:
raise DuplicateTrack(self.get_msg("voicelinkDuplicateTrack"))
position = self.queue.put_at_front(raw_tracks) if at_font else self.queue.put(raw_tracks)
self._validate_time(raw_tracks, start_time, end_time)
position = self.queue.put_at_front(raw_tracks) if at_front else self.queue.put(raw_tracks)
tracks.append(raw_tracks)
finally: