💬 Add playlists feature

This commit is contained in:
KaKi87 2024-01-28 03:20:29 +01:00
parent 165d095185
commit 60ceb2b18a
5 changed files with 160 additions and 7 deletions

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
.idea
node_modules
config.js
config.js
data/*

0
data/.gitkeep Normal file
View file

130
main.js
View file

@ -1,9 +1,16 @@
import { exec } from 'node:child_process';
import {
readFile,
writeFile
} from 'node:fs/promises';
import { overrideConsole } from 'nodejs-better-console';
import Eris from 'eris';
import { Command, Argument } from 'commander';
import KeyEvent from 'keyevent.json' assert { type: 'json' };
import { M3uParser } from 'm3u-parser-generator';
import { parse as parseXspf } from 'xspf-js';
import outdent from 'outdent';
import AsciiTable from 'ascii-table';
import { parse } from 'shell-quote';
@ -34,7 +41,20 @@ const
resolve(stdout.replace(/\r/g, '').trim());
}
)),
handleCommand = async command => {
getConfig = async id => {
try {
return JSON.parse(await readFile(`./data/${id}.json`, 'utf8'));
}
catch {
return {};
}
},
setConfig = async (id, config) => await writeFile(`./data/${id}.json`, JSON.stringify(config)),
handleCommand = async ({
content,
authorID,
attachments
}) => {
const program = new Command('');
let response;
program.exitOverride();
@ -107,6 +127,92 @@ const
.description('Play media on MPV')
.argument('<url>', 'Media URL')
.action(url => adb(`am start -a android.intent.action.VIEW -d ${url} -t video/any is.xyz.mpv`));
program
.command('playlist')
.alias('pl')
.description('Manage playlists & play on MPV')
.argument('[name]', 'Playlist name')
.argument('[item]', 'Playlist item index/name/partial name')
.action(async (name, item) => {
const
config = await getConfig(authorID) || {},
newPlaylistUrl = attachments[0]?.url;
let { playlists } = config;
if(!playlists)
config.playlists = playlists = [];
if(!name){
if(!playlists.length)
return response = { embed: { title: `❌ No existing playlist` } };
return response = {
embed: {
title: ` Your playlists :`,
description: playlists.map(playlist => `- [${playlist.name}](${playlist.url})`).join('\n')
}
};
}
let playlist = playlists.find(playlist => playlist.name === name);
if(newPlaylistUrl){
if(!/\.(m3u8?|xspf)$/i.test(new URL(newPlaylistUrl).pathname))
return response = { embed: { title: `❌ Invalid playlist` } };
response = { embed: { title: `✅ Playlist updated` } };
if(!playlist){
playlists.push(playlist = { name });
response = { embed: { title: `✅ Playlist created` } };
}
playlist.url = newPlaylistUrl;
return setConfig(authorID, config);
}
if(!playlist)
return response = { embed: { title: `❌ Playlist not found` } };
const { pathname: playlistPathname } = new URL(playlist.url);
let
rawPlaylistData = await (await fetch(playlist.url)).text(),
playlistItems;
if(/\.m3u8?$/i.test(playlistPathname)){
if(!rawPlaylistData.startsWith('#EXTM3U'))
rawPlaylistData = `#EXTM3U\n${rawPlaylistData}`;
playlistItems = M3uParser.parse(rawPlaylistData).medias.map(item => ({
url: item['location'],
title: item['name'] || new URL(item['location']).pathname.split('/').slice(-1)[0]
}));
}
if(/\.xspf?$/i.test(playlistPathname)){
playlistItems = parseXspf(rawPlaylistData)['playlist']['tracks'].map(item => ({
url: item['location'],
title: item['title'] || new URL(item['location']).pathname.split('/').slice(-1)[0]
}));
}
if(!item)
return response = {
output: outdent `
📀 ${playlist.name}
${playlistItems.map((item, index) => `${index + 1}. \`${item.title}\``).join('\n')}
`
};
const playlistItem = /^\d+$/.test(item)
? playlistItems[parseInt(item) - 1]
: playlistItems.find(({ title }) => title.toLowerCase().includes(item.toLowerCase()));
if(!playlistItem)
return response = { embed: { title: `❌ Item not found` } };
await adb(`am start -a android.intent.action.VIEW -d ${playlistItem.url} -t video/any is.xyz.mpv`);
return response = { embed: { title: `▶️ \`${playlistItem.title}\`` } };
});
program
.command('playlist.rm')
.alias('pl.rm')
.description('Remove playlist')
.argument('<name>', 'Playlist name')
.action(async name => {
const
config = await getConfig(authorID) || {},
{ playlists } = config,
playlistIndex = playlists?.findIndex(playlist => playlist.name === name);
if([undefined, -1].includes(playlistIndex))
return response = { embed: { title: `❌ No existing playlist by that name` } };
playlists.splice(playlistIndex, 1);
await setConfig(authorID, config);
response = { embed: { title: `✅ Playlist removed` } };
});
program
.command('kiwi')
.description('Launch Kiwi browser')
@ -146,7 +252,7 @@ const
});
try {
await program.parseAsync(
parse(command),
parse(content),
{ from: 'user' }
);
}
@ -171,7 +277,8 @@ eris.on(
author: { id: authorID },
channel: { id: channelID },
id: messageID,
content
content,
attachments
} = message,
reply = async (content, isCodeBlock) => {
const lines = content.split('\n');
@ -206,10 +313,23 @@ eris.on(
try {
const {
output,
isRaw
} = await handleCommand(content) || {};
isRaw,
embed
} = await handleCommand({
content,
authorID,
attachments
}) || {};
if(output)
await reply(output, isRaw);
if(embed)
await eris.createMessage(
channelID,
{
embed,
messageReference: { messageID }
}
);
await message.addReaction('✅');
}
catch(error){

View file

@ -10,8 +10,11 @@
"commander": "^11.1.0",
"eris": "^0.17.1",
"keyevent.json": "^0.1.0",
"m3u-parser-generator": "^1.6.0",
"nodejs-better-console": "^1.0.2",
"shell-quote": "^1.8.1"
"outdent": "^0.8.0",
"shell-quote": "^1.8.1",
"xspf-js": "^0.1.1"
},
"scripts": {
"start": "node ./main.js"

View file

@ -27,6 +27,11 @@ keyevent.json@^0.1.0:
resolved "https://registry.yarnpkg.com/keyevent.json/-/keyevent.json-0.1.0.tgz#598ef46218f53350ac4c5ce845dcaadcf2b6f2bb"
integrity sha512-1SVBT4dTNggvKXOk8etHiMngBTfo2RaRwcNSugx94IvYNr2Yhlypoq/eerQyPY79+Ew4PyxLnKw2duRHx9n1Kw==
m3u-parser-generator@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/m3u-parser-generator/-/m3u-parser-generator-1.6.0.tgz#d65f5a68dce75a0d022d1833be352d87b07a0638"
integrity sha512-PXKVY7TcAraOvlMVYgC2XCLkV6DFFvYZfnjPeON3tBEvOTUJkt/FoDwKcfNJu0SCHVFIUE5yobZ2LEueA+3YvA==
nodejs-better-console@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/nodejs-better-console/-/nodejs-better-console-1.0.2.tgz#bf6da52a3bf6ddbc61a414b1d83739c86117f207"
@ -37,6 +42,16 @@ opusscript@^0.0.8:
resolved "https://registry.yarnpkg.com/opusscript/-/opusscript-0.0.8.tgz#00b49e81281b4d99092d013b1812af8654bd0a87"
integrity sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==
outdent@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.8.0.tgz#2ebc3e77bf49912543f1008100ff8e7f44428eb0"
integrity sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==
sax@^1.2.4:
version "1.3.0"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0"
integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==
shell-quote@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680"
@ -51,3 +66,17 @@ ws@^8.2.3:
version "8.8.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769"
integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==
xml-js@^1.6.11:
version "1.6.11"
resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9"
integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==
dependencies:
sax "^1.2.4"
xspf-js@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/xspf-js/-/xspf-js-0.1.1.tgz#af54fc9514556a36d3a2e0186a3ae46f401853da"
integrity sha512-/2lUkOvMkjBLrHqk9zts0Hn589nfG07MakYIGhI4lseDsXi8YDvsFDCX8iAE2Dsm7lkZnyS7CkaYfRSlkS+oyw==
dependencies:
xml-js "^1.6.11"