private async Task OnTrackIssue(LavaPlayer ply, ILavaTrack lavaTrack, string error = null) { if (error != null) { this.Logger.Nice("MusicPlayer", ConsoleColor.Red, $"Exception thrown by lavalink for track <{lavaTrack.Title}>\n{error}"); if (lavaTrack.Uri.AbsoluteUri.Contains("soundcloud.com")) { error = $"{error} It is likely that this track is not usable outside of SoundCloud."; } } else { this.Logger.Nice("MusicPlayer", ConsoleColor.Red, $"Track <{lavaTrack.Title}> got stuck"); error = "The track got stuck."; } EmbedBuilder builder = new EmbedBuilder(); builder .WithColorType(EmbedColorType.Warning) .WithFooter("music player") .WithDescription("🎶 Could not play track:") .WithField("Url", $"**{lavaTrack.Uri}**") .WithField("Error", error); await this.MessageSender.SendAsync(ply.TextChannel, builder.Build()); }
private Embed BuildTrackEmbed(ILavaTrack track, int volume, bool paused, bool looping) { EmbedBuilder builder = new EmbedBuilder(); builder .WithLimitedTitle(track.Title) .WithField("Author", track.Author) .WithField("Stream", track.IsStream) .WithField("Volume", $"{volume}%") .WithField("Paused", paused) .WithField("Looping", looping) .WithFooter("music player"); string url = track.Uri.AbsoluteUri; if (url.Length < 1000) { builder.WithDescription($"🎶 Now playing the **[following track]({url})**"); } else { builder.WithDescription("🎶 Now playing the following track"); } builder.WithField("Length", this.FormattedTrack(track), false); return(builder.Build()); }
private async Task <(bool, YoutubeVideo)> TryGetVideoAsync(ILavaTrack lavaTrack) { bool failed = false; YoutubeVideo video = null; if (lavaTrack.Uri.AbsoluteUri.Contains("youtu")) { Match match = YtRegex.Match(lavaTrack.Uri.AbsoluteUri); if (!match.Success) { failed = true; } video = await this.FetchYtRelatedVideoAsync(match.Value); if (video == null) { failed = true; } } else { failed = true; } return(failed, video); }
/// <summary> /// Fetches thumbnail of the specified track. /// </summary> /// <param name="track"><see cref="ILavaTrack"/></param> public static async Task <string> FetchThumbnailAsync(this ILavaTrack track) { var url = string.Empty; switch ($"{track.Uri}".ToLower()) { case var yt when yt.Contains("youtube"): return($"https://img.youtube.com/vi/{track.Id}/maxresdefault.jpg"); case var twich when twich.Contains("twitch"): url = $"https://api.twitch.tv/v4/oembed?url={track.Uri}"; break; case var sc when sc.Contains("soundcloud"): url = $"https://soundcloud.com/oembed?url={track.Uri}&format=json"; break; case var vim when vim.Contains("vimeo"): url = $"https://vimeo.com/api/oembed.json?url={track.Uri}"; break; } var req = await HttpHelper.Instance.GetStringAsync(url); var parse = JObject.Parse(req); return(!parse.TryGetValue("thumbnail_url", out var thumb) ? "https://i.imgur.com/YPCEUDK.gif" : $"{thumb}"); }
private async Task <Embed> GetNewTrackEmbed(ILavaTrack lavaTrack, IMessage msg = null) { string thumbnailUrl = await GetThumbnailAsync(lavaTrack); EmbedBuilder builder = new EmbedBuilder(); if (msg != null) { builder.WithAuthorNickname(msg); } const string desc = "🎶 Added the following track to the queue:"; if (!string.IsNullOrWhiteSpace(thumbnailUrl)) { builder.WithThumbnailUrl(thumbnailUrl); } return(builder .WithDescription(desc) .WithFooter("music player") .WithField("Title", lavaTrack.Title) .WithField("Author", lavaTrack.Author) .WithField("Length", lavaTrack.IsStream ? " - " : lavaTrack.Length.ToString(@"hh\:mm\:ss")) .WithField("Stream", lavaTrack.IsStream) .Build()); }
private string FormattedTrack(ILavaTrack track) { string len, pos, line; if (!track.HasLength) { len = TimeSpan.Zero.ToString(@"hh\:mm\:ss"); pos = len; line = new string('─', 20) + "⚪"; } else { len = track.Length.ToString(@"hh\:mm\:ss"); pos = track.Position.ToString(@"hh\:mm\:ss"); double perc = (double)track.Position.Ticks / track.Length.Ticks * 100.0; int circlepos = Math.Clamp((int)Math.Ceiling(21.0 / 100.0 * perc), 0, 21); //Make sure its clamped if (circlepos > 0) { line = new string('─', circlepos - 1) + "⚪" + new string('─', 21 - circlepos); } else { line = "⚪" + new string('─', 20); } } return($"```http\n▶ {line} {pos} / {len}\n```"); }
private static async Task <string> GetThumbnailAsync(ILavaTrack track) { try { return(await track.FetchThumbnailAsync()); } catch { return(string.Empty); } }
private static async Task <string> GetTrackHash(ILavaTrack track) { string trackHash = track.Hash; if (track is IAsyncLazyLoadTrack async) { trackHash = (await async.GetInnerTrackAsync()).Hash; } return(trackHash); }
public async Task <ILavaTrack> GetInnerTrackAsync() { if (this.InnerTrack != null) { return(this.InnerTrack); } ILavaTrack track = await this.InnerTrackCallback(); this.InnerTrack = track; return(track); }
public RadioTrack(ILavaTrack innerTrack) { this.Genre = "unknown"; this.InnerTrack = innerTrack; foreach ((string key, string value) in StaticData.Instance.RadioSources) { if (value.Equals(innerTrack.Uri.AbsoluteUri)) { this.Genre = key; } } }
public async Task <List <IUserMessage> > AddPlaylistAsync(IVoiceChannel vc, ITextChannel chan, string name, IEnumerable <ILavaTrack> trs) { IEnergizePlayer ply = await this.ConnectAsync(vc, chan); if (ply == null) { return(null); } List <ILavaTrack> tracks = trs.ToList(); if (tracks.Count < 1) { return(new List <IUserMessage> { await this.MessageSender.SendWarningAsync(chan, "music player", "The loaded playlist does not contain any tracks") }); } if (ply.IsPlaying) { foreach (ILavaTrack track in tracks) { ply.Queue.Enqueue(track); } return(new List <IUserMessage> { await this.MessageSender.SendGoodAsync(chan, "music player", $"🎶 Added `{tracks.Count}` tracks from `{name}`") }); } ILavaTrack lavaTrack = tracks[0]; tracks.RemoveAt(0); if (tracks.Count > 0) { foreach (ILavaTrack tr in tracks) { ply.Queue.Enqueue(tr); } } await ply.Lavalink.PlayAsync(lavaTrack); return(new List <IUserMessage> { await this.MessageSender.SendGoodAsync(chan, "music player", $"🎶 Added `{tracks.Count}` tracks from `{name}`"), await this.SendPlayerAsync(ply, lavaTrack, chan) }); }
private static async Task OnPlayReactionAsync(PaginatorSenderService sender, Paginator <object> paginator, Cacheable <IUserMessage, ulong> cache, ISocketMessageChannel chan, SocketReaction reaction) { if (!(chan is IGuildChannel) || reaction.User.Value == null) { return; } IGuildUser guser = (IGuildUser)reaction.User.Value; if (guser.VoiceChannel == null) { return; } ITextChannel textChan = (ITextChannel)chan; IMusicPlayerService music = sender.ServiceManager.GetService <IMusicPlayerService>("Music"); switch (paginator.CurrentValue) { case ILavaTrack track: await music.AddTrackAsync(guser.VoiceChannel, textChan, track); await chan.DeleteMessageAsync(paginator.Message); break; case string url: if (string.IsNullOrWhiteSpace(url)) { return; } SearchResult result = await music.LavaRestClient.SearchTracksAsync(url); List <ILavaTrack> tracks = result.Tracks.ToList(); if (tracks.Count > 0) { ILavaTrack tr = tracks[0]; await music.AddTrackAsync(guser.VoiceChannel, textChan, tr); await chan.DeleteMessageAsync(paginator.Message); } else { await sender.MessageSender.SendWarningAsync(chan, "music player", $"Could not add the following Url to the queue\n{url}"); } break; } }
/// <summary> /// Plays the specified <paramref name="track"/>. /// </summary> /// <param name="track"><see cref="ILavaTrack"/></param> /// <param name="noReplace">If set to true, this operation will be ignored if a track is already playing or paused.</param> public async Task PlayAsync(ILavaTrack track, bool noReplace = false) { this.IsPlaying = true; this.CurrentTrack = track; this.LastUpdate = DateTimeOffset.Now; if (!noReplace) { Volatile.Write(ref this._isPaused, false); } string trackHash = await GetTrackHash(track); var payload = new PlayPayload(this.VoiceChannel.GuildId, trackHash, noReplace); await this._socketHelper.SendPayloadAsync(payload); }
/*private async Task OnSocketClosed(int errorCode, string reason, bool byRemote) * { * await this.DisconnectAllPlayersAsync("Music streaming is unavailable at the moment, disconnecting"); * SocketChannel chan = this.DiscordClient.GetChannel(Config.Instance.Discord.BugReportChannelID); * if (chan != null) * { * EmbedBuilder builder = new EmbedBuilder(); * builder * .WithDescription("Lavalink lost connection to Discord websocket") * .WithField("Error Code", errorCode) * .WithField("Reason", reason) * .WithField("By Remote", byRemote) * .WithColorType(EmbedColorType.Warning) * .WithFooter("lavalink error"); * * await this.MessageSender.SendAsync(chan, builder); * } * }*/ private async Task OnPlayerUpdated(LavaPlayer lply, ILavaTrack track, TimeSpan position) { if (this.Players.TryGetValue(lply.VoiceChannel.GuildId, out IEnergizePlayer ply)) { if (ply.TrackPlayer != null) { if (!track.IsStream && !ply.IsPaused) { await ply.TrackPlayer.Update(track, ply.Volume, ply.IsPaused, ply.IsLooping); } ply.Refresh(); } } IGuild guild = lply.VoiceChannel.Guild; string msg = $"Updated track <{track.Title}> ({position}) for player in guild <{guild.Name}>"; this.Logger.LogTo("victoria.log", msg); }
private async Task OnTrackFinished(LavaPlayer lavalink, ILavaTrack lavaTrack, TrackEndReason reason) { if (!reason.ShouldPlayNext()) { return; } IEnergizePlayer ply = this.Players[lavalink.VoiceChannel.GuildId]; if (ply.IsLooping) { lavaTrack.ResetPosition(); await ply.Lavalink.PlayAsync(lavaTrack); } else { if (ply.Queue.TryDequeue(out IQueueObject obj)) { if (obj is ILavaTrack newTrack) { await ply.Lavalink.PlayAsync(newTrack); await this.SendPlayerAsync(ply, newTrack); } } else { if (ply.Autoplay && ply.Queue.Count == 0) { await this.AddRelatedYtContentAsync(ply.VoiceChannel, ply.TextChannel, lavaTrack); } else { if (ply.TrackPlayer != null) { await ply.TrackPlayer.DeleteMessage(); } } } } }
public async Task SeekTrackAsync(IVoiceChannel vc, ITextChannel chan, int amount) { IEnergizePlayer ply = await this.ConnectAsync(vc, chan); if (ply == null) { return; } if (!ply.IsPlaying) { return; } ILavaTrack lavaTrack = ply.CurrentTrack; TimeSpan total = lavaTrack.Position.Add(TimeSpan.FromSeconds(amount)); if (total < lavaTrack.Length && total >= TimeSpan.Zero) { await ply.Lavalink.SeekAsync(total); } }
/// <summary> /// Plays the specified <paramref name="track"/>. /// </summary> /// <param name="track"></param> /// <param name="startTime">Optional setting that determines the number of milliseconds to offset the track by.</param> /// <param name="stopTime">optional setting that determines at the number of milliseconds at which point the track should stop playing.</param> /// <param name="noReplace">If set to true, this operation will be ignored if a track is already playing or paused.</param> public async Task PlayAsync(ILavaTrack track, TimeSpan startTime, TimeSpan stopTime, bool noReplace = false) { if (startTime.TotalMilliseconds < 0 || stopTime.TotalMilliseconds < 0) { throw new InvalidOperationException("Start and stop must be greater than 0."); } if (startTime <= stopTime) { throw new InvalidOperationException("Stop time must be greater than start time."); } this.IsPlaying = true; this.CurrentTrack = track; if (!noReplace) { Volatile.Write(ref this._isPaused, false); } string trackHash = await GetTrackHash(track); var payload = new PlayPayload(this.VoiceChannel.GuildId, trackHash, startTime, stopTime, noReplace); await this._socketHelper.SendPayloadAsync(payload); }
public async Task SkipTrackAsync(IVoiceChannel vc, ITextChannel chan) { IEnergizePlayer ply = await this.ConnectAsync(vc, chan); if (ply == null) { return; } if (ply.CurrentRadio != null) { ply.CurrentRadio = null; } if (!ply.IsPlaying) { return; } if (ply.Queue.Count > 0) { await ply.Lavalink.SkipAsync(); await this.SendPlayerAsync(ply, ply.CurrentTrack); } else { ILavaTrack oldTrack = ply.CurrentTrack; await ply.Lavalink.StopAsync(); if (ply.Autoplay) { await this.AddRelatedYtContentAsync(vc, chan, oldTrack); } } }
public async Task <IUserMessage> PlayRadioAsync(IVoiceChannel vc, ITextChannel chan, ILavaTrack lavaTrack) { IEnergizePlayer ply = await this.ConnectAsync(vc, chan); if (ply == null) { return(null); } RadioTrack radio = new RadioTrack(lavaTrack); ply.Queue.Clear(); ply.CurrentRadio = radio; await ply.Lavalink.PlayAsync(lavaTrack); return(await this.SendPlayerAsync(ply, radio, chan)); }
private bool OnMessage(string message) { this.ShadowLog?.WriteLog(LogSeverity.Debug, message); JObject json = JObject.Parse(message); ulong guildId = 0; LavaPlayer player; if (json.TryGetValue("guildId", out JToken guildToken)) { guildId = ulong.Parse($"{guildToken}"); } string opCode = $"{json.GetValue("op")}"; switch (opCode) { case "playerUpdate": /*if (!this.Players.TryGetValue(guildId, out player)) * return false; * * PlayerState state = json.GetValue("state").ToObject<PlayerState>(); * player.CurrentTrack.Position = state.Position; * player.LastUpdate = state.Time; * * this.OnPlayerUpdated?.Invoke(player, player.CurrentTrack, state.Position);*/ break; case "stats": this.ServerStats = json.ToObject <ServerStats>(); this.OnServerStats?.Invoke(this.ServerStats); break; case "event": EventType evt = json.GetValue("type").ToObject <EventType>(); if (!this.Players.TryGetValue(guildId, out player)) { return(false); } ILavaTrack track = default; if (json.TryGetValue("track", out JToken hash)) { track = TrackHelper.DecodeTrack($"{hash}"); } switch (evt) { case EventType.TrackEnd: TrackEndReason endReason = json.GetValue("reason").ToObject <TrackEndReason>(); if (endReason != TrackEndReason.Finished) { if (endReason != TrackEndReason.Replaced) { player.IsPlaying = false; player.CurrentTrack = default; } this.OnTrackFinished?.Invoke(player, track, endReason); } break; case EventType.TrackException: string error = json.GetValue("error").ToObject <string>(); player.CurrentTrack = track; this.OnTrackException?.Invoke(player, track, error); break; case EventType.TrackStuck: long timeout = json.GetValue("thresholdMs").ToObject <long>(); player.CurrentTrack = track; this.OnTrackStuck?.Invoke(player, track, timeout); break; case EventType.WebSocketClosed: string reason = json.GetValue("reason").ToObject <string>(); int code = json.GetValue("code").ToObject <int>(); bool byRemote = json.GetValue("byRemote").ToObject <bool>(); this.OnSocketClosed?.Invoke(code, reason, byRemote); break; default: this.ShadowLog?.WriteLog(LogSeverity.Warning, $"Missing implementation of {evt} event."); break; } break; default: this.ShadowLog?.WriteLog(LogSeverity.Warning, $"Missing handling of {opCode} OP code."); break; } return(true); }
/// <summary> /// Searches lyrics for the specified track. /// </summary> /// <param name="track"><see cref="ILavaTrack"/></param> public static Task <string> FetchLyricsAsync(this ILavaTrack track) { return(LyricsHelper.SearchAsync(track.Author, track.Title)); }
public async Task <IUserMessage> SendNewTrackAsync(IMessage msg, ILavaTrack lavaTrack) { Embed embed = await this.GetNewTrackEmbed(lavaTrack, msg); return(await this.MessageSender.SendAsync(msg, embed)); }
public async Task <IUserMessage> SendNewTrackAsync(ITextChannel chan, ILavaTrack lavaTrack) { Embed embed = await this.GetNewTrackEmbed(lavaTrack); return(await this.MessageSender.SendAsync(chan, embed)); }
private async Task AddRelatedYtContentAsync(IVoiceChannel vc, ITextChannel chan, ILavaTrack oldTrack) { (bool failed, YoutubeVideo video) = await this.TryGetVideoAsync(oldTrack); string videoUrl = await this.GetNextTrackVideoUrlAsync(failed, video); SearchResult res = await this.LavaRestClient.SearchTracksAsync(videoUrl); List <ILavaTrack> tracks = res.Tracks.ToList(); if (tracks.Count == 0) { return; } switch (res.LoadType) { case LoadType.SearchResult: case LoadType.TrackLoaded: await this.AddTrackAsync(vc, chan, tracks[0]); break; case LoadType.PlaylistLoaded: await this.AddPlaylistAsync(vc, chan, res.PlaylistInfo.Name, tracks); break; default: await this.MessageSender.SendWarningAsync(chan, "music player", "Failed to get/load the next autoplay track"); break; } }
public async Task <IUserMessage> AddTrackAsync(IVoiceChannel vc, ITextChannel chan, ILavaTrack lavaTrack) { IEnergizePlayer ply = await this.ConnectAsync(vc, chan); if (ply == null) { return(null); } if (ply.IsPlaying) { ply.Queue.Enqueue(lavaTrack); return(await this.SendNewTrackAsync(chan, lavaTrack)); } await ply.Lavalink.PlayAsync(lavaTrack); return(await this.SendPlayerAsync(ply, lavaTrack, chan)); }
/// <summary> /// Initialize SpotifyTrack with an already searched LavaTrack /// </summary> /// <param name="spotifyInfo">Spotify information from the Spotify API</param> /// <param name="innerTrack">Searched LavaTrack</param> public SpotifyTrack(SpotifyTrackInfo spotifyInfo, ILavaTrack innerTrack) { this.SpotifyInfo = spotifyInfo; this.InnerTrack = innerTrack; }