aboutsummaryrefslogtreecommitdiff
path: root/src/services
diff options
context:
space:
mode:
authorMax Isom <[email protected]>2021-09-18 16:55:50 -0400
committerMax Isom <[email protected]>2021-09-18 16:55:50 -0400
commit9a2ef876d381a646f0d66145d8ed3cfa8da7fac3 (patch)
tree8b801cd52206dab815625c7a42a36a31c3601769 /src/services
parent81bbdb971d4221063b16fc2150ed9f4c56041765 (diff)
downloadmuse-9a2ef876d381a646f0d66145d8ed3cfa8da7fac3.tar.xz
muse-9a2ef876d381a646f0d66145d8ed3cfa8da7fac3.zip
Correctly skip song if unavailable
Also lets user know in text channel that song is unavailable after skipping. Fixes #324
Diffstat (limited to 'src/services')
-rw-r--r--src/services/get-songs.ts19
-rw-r--r--src/services/natural-language-commands.ts30
-rw-r--r--src/services/player.ts22
3 files changed, 57 insertions, 14 deletions
diff --git a/src/services/get-songs.ts b/src/services/get-songs.ts
index 8a040c0..d5ccdef 100644
--- a/src/services/get-songs.ts
+++ b/src/services/get-songs.ts
@@ -7,10 +7,13 @@ import Spotify from 'spotify-web-api-node';
import YouTube, {YoutubePlaylistItem} from 'youtube.ts';
import pLimit from 'p-limit';
import shuffle from 'array-shuffle';
+import {Except} from 'type-fest';
import {QueuedSong, QueuedPlaylist} from '../services/player';
import {TYPES} from '../types';
import {cleanUrl} from '../utils/url';
+type QueuedSongWithoutChannel = Except<QueuedSong, 'addedInChannelId'>;
+
@injectable()
export default class {
private readonly youtube: YouTube;
@@ -23,7 +26,7 @@ export default class {
this.spotify = spotify;
}
- async youtubeVideoSearch(query: string): Promise<QueuedSong|null> {
+ async youtubeVideoSearch(query: string): Promise<QueuedSongWithoutChannel|null> {
try {
const {items: [video]} = await this.youtube.videos.search({q: query, maxResults: 1, type: 'video'});
@@ -33,7 +36,7 @@ export default class {
}
}
- async youtubeVideo(url: string): Promise<QueuedSong|null> {
+ async youtubeVideo(url: string): Promise<QueuedSongWithoutChannel|null> {
try {
const videoDetails = await this.youtube.videos.get(cleanUrl(url));
@@ -50,7 +53,7 @@ export default class {
}
}
- async youtubePlaylist(listId: string): Promise<QueuedSong[]> {
+ async youtubePlaylist(listId: string): Promise<QueuedSongWithoutChannel[]> {
// YouTube playlist
const playlist = await this.youtube.playlists.get(listId);
@@ -93,7 +96,7 @@ export default class {
const queuedPlaylist = {title: playlist.snippet.title, source: playlist.id};
- const songsToReturn: QueuedSong[] = [];
+ const songsToReturn: QueuedSongWithoutChannel[] = [];
for (let video of playlistVideos) {
try {
@@ -115,7 +118,7 @@ export default class {
return songsToReturn;
}
- async spotifySource(url: string): Promise<[QueuedSong[], number, number]> {
+ async spotifySource(url: string): Promise<[QueuedSongWithoutChannel[], number, number]> {
const parsed = spotifyURI.parse(url);
let tracks: SpotifyApi.TrackObjectSimplified[] = [];
@@ -195,7 +198,7 @@ export default class {
let nSongsNotFound = 0;
// Get rid of null values
- songs = songs.reduce((accum: QueuedSong[], song) => {
+ songs = songs.reduce((accum: QueuedSongWithoutChannel[], song) => {
if (song) {
accum.push(song);
} else {
@@ -205,10 +208,10 @@ export default class {
return accum;
}, []);
- return [songs as QueuedSong[], nSongsNotFound, originalNSongs];
+ return [songs as QueuedSongWithoutChannel[], nSongsNotFound, originalNSongs];
}
- private async spotifyToYouTube(track: SpotifyApi.TrackObjectSimplified, _: QueuedPlaylist | null): Promise<QueuedSong | null> {
+ private async spotifyToYouTube(track: SpotifyApi.TrackObjectSimplified, _: QueuedPlaylist | null): Promise<QueuedSongWithoutChannel | null> {
try {
const {items} = await this.youtube.videos.search({q: `"${track.name}" "${track.artists[0].name}"`, maxResults: 10});
const videoResult = items[0]; // Items.find(item => item.type === 'video');
diff --git a/src/services/natural-language-commands.ts b/src/services/natural-language-commands.ts
index 442cbb0..c6348c1 100644
--- a/src/services/natural-language-commands.ts
+++ b/src/services/natural-language-commands.ts
@@ -24,7 +24,15 @@ export default class {
if (msg.content.toLowerCase().includes('packers')) {
await Promise.all([
msg.channel.send('GO PACKERS GO!!!'),
- this.playClip(msg.guild!, msg.member!, {title: 'GO PACKERS!', artist: 'Unknown', url: 'https://www.youtube.com/watch?v=qkdtID7mY3E', length: 204, playlist: null, isLive: false}, 8, 10)
+ this.playClip(msg.guild!, msg.member!, {
+ title: 'GO PACKERS!',
+ artist: 'Unknown',
+ url: 'https://www.youtube.com/watch?v=qkdtID7mY3E',
+ length: 204,
+ playlist: null,
+ isLive: false,
+ addedInChannelId: msg.channel.id
+ }, 8, 10)
]);
return true;
@@ -33,7 +41,15 @@ export default class {
if (msg.content.toLowerCase().includes('bears')) {
await Promise.all([
msg.channel.send('F*** THE BEARS'),
- this.playClip(msg.guild!, msg.member!, {title: 'GO PACKERS!', artist: 'Charlie Berens', url: 'https://www.youtube.com/watch?v=UaqlE9Pyy_Q', length: 385, playlist: null, isLive: false}, 358, 5.5)
+ this.playClip(msg.guild!, msg.member!, {
+ title: 'GO PACKERS!',
+ artist: 'Charlie Berens',
+ url: 'https://www.youtube.com/watch?v=UaqlE9Pyy_Q',
+ length: 385,
+ playlist: null,
+ isLive: false,
+ addedInChannelId: msg.channel.id
+ }, 358, 5.5)
]);
return true;
@@ -42,7 +58,15 @@ export default class {
if (msg.content.toLowerCase().includes('bitconnect')) {
await Promise.all([
msg.channel.send('🌊 🌊 🌊 🌊'),
- this.playClip(msg.guild!, msg.member!, {title: 'BITCONNEEECCT', artist: 'Carlos Matos', url: 'https://www.youtube.com/watch?v=lCcwn6bGUtU', length: 227, playlist: null, isLive: false}, 50, 13)
+ this.playClip(msg.guild!, msg.member!, {
+ title: 'BITCONNEEECCT',
+ artist: 'Carlos Matos',
+ url: 'https://www.youtube.com/watch?v=lCcwn6bGUtU',
+ length: 227,
+ playlist: null,
+ isLive: false,
+ addedInChannelId: msg.channel.id
+ }, 50, 13)
]);
return true;
diff --git a/src/services/player.ts b/src/services/player.ts
index 63f6445..5a9fa83 100644
--- a/src/services/player.ts
+++ b/src/services/player.ts
@@ -1,4 +1,4 @@
-import {VoiceConnection, VoiceChannel, StreamDispatcher} from 'discord.js';
+import {VoiceConnection, VoiceChannel, StreamDispatcher, Snowflake, Client, TextChannel} from 'discord.js';
import {promises as fs, createWriteStream} from 'fs';
import {Readable, PassThrough} from 'stream';
import path from 'path';
@@ -7,6 +7,7 @@ import ytdl from 'ytdl-core';
import {WriteStream} from 'fs-capacitor';
import ffmpeg from 'fluent-ffmpeg';
import shuffle from 'array-shuffle';
+import errorMsg from '../utils/error-msg';
export interface QueuedPlaylist {
title: string;
@@ -20,6 +21,7 @@ export interface QueuedSong {
length: number;
playlist: QueuedPlaylist | null;
isLive: boolean;
+ addedInChannelId: Snowflake;
}
export enum STATUS {
@@ -40,8 +42,11 @@ export default class {
private positionInSeconds = 0;
- constructor(cacheDir: string) {
+ private readonly discordClient: Client;
+
+ constructor(cacheDir: string, client: Client) {
this.cacheDir = cacheDir;
+ this.discordClient = client;
}
async connect(channel: VoiceChannel): Promise<void> {
@@ -142,7 +147,18 @@ export default class {
this.lastSongURL = currentSong.url;
}
} catch (error: unknown) {
- this.removeCurrent();
+ const currentSong = this.getCurrent();
+ await this.forward(1);
+
+ if ((error as {statusCode: number}).statusCode === 410 && currentSong) {
+ const channelId = currentSong.addedInChannelId;
+
+ if (channelId) {
+ await (this.discordClient.channels.cache.get(channelId) as TextChannel).send(errorMsg(`${currentSong.title} is unavailable`));
+ return;
+ }
+ }
+
throw error;
}
}