summaryrefslogtreecommitdiffstats
path: root/yta
diff options
context:
space:
mode:
authorGravatar piotrruss <mail@pruss.it> 2021-05-30 19:20:42 +0200
committerGravatar piotrruss <mail@pruss.it> 2021-05-30 19:20:42 +0200
commitda4c8c2fda07812e643363114c67df3ec0d3925e (patch)
tree02694c94ec64b667d799ec13f32a39427dc4d125 /yta
downloadyt_audio_player_api-da4c8c2fda07812e643363114c67df3ec0d3925e.tar.gz
yt_audio_player_api-da4c8c2fda07812e643363114c67df3ec0d3925e.tar.bz2
yt_audio_player_api-da4c8c2fda07812e643363114c67df3ec0d3925e.zip
init commit
Diffstat (limited to 'yta')
-rwxr-xr-xyta170
1 files changed, 170 insertions, 0 deletions
diff --git a/yta b/yta
new file mode 100755
index 0000000..851e849
--- /dev/null
+++ b/yta
@@ -0,0 +1,170 @@
+#!/usr/bin/env node
+const spawn = require('child_process').spawn;
+const colors = require('colors');
+const prompt = require('prompt');
+const fetch = require('node-fetch');
+const { YTKEY } = require('./config');
+
+let data = {ids: [], titles: [], descriptions: [], channels: [], durations: []};
+
+const clearOutput = () => console.clear();
+
+const showError = (msg) => console.log(colors.red('Error'), ' - ', msg);
+
+const showMsg = (msg) => console.log(colors.green(msg));
+
+const showOsd = () => {
+ data.ids.forEach((id, i) => {
+ console.log(`${colors.yellow(i+1)}. ${colors.cyan(data.titles[i])} ${colors.green('['+data.durations[i]+']')} ${colors.red(data.channels[i])}`);
+ });
+ console.log('');
+};
+
+const showPlaying = (n) => console.log(`${colors.green('Currently playing:\n------------------')}\n${colors.yellow('Title:')} ${colors.cyan(data.titles[n])}\n${colors.yellow('Description:')} ${colors.cyan(data.descriptions[n])}\n${colors.yellow('Channel:')} ${colors.cyan(data.channels[n])}\n${colors.yellow('Length:')} ${colors.cyan(data.durations[n])}\n`);
+
+const showPlaylist = (r,n) => console.log(r.map(x=>x===n?(colors.yellow(x+'. '+data.titles[x-1])):(colors.cyan(x+'. '+data.titles[x-1]))).join('\n'),'\n');
+
+const showHelp = () => console.log(colors.green('YT audio player\n---------------\n1. Type song name and press [ENTER] to search it in YT\n2. Type result number and press [ENTER] to play the song\n3. Type a range of songs (ex. [2-5]) to play them in a row\n4. Type "h" and press [ENTER] to see this help page\n5. Type "q" and press [ENTER] to quit YT audio player\n'));
+
+const searchUrl = (q) => `https://www.googleapis.com/youtube/v3/search?part=snippet&partcontentDetails&maxResults=20&q=${q}&type=video&key=${YTKEY}`;
+
+const detailsUrl = (a) => `https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=${a.join(',')}&key=${YTKEY}`;
+
+const isSongRange = q => q.match(/^[0-9]+-[0-9]+$/);
+
+const isSongName = q => q.match(/.{3,}/);
+
+const isSongNumber = q => q.match(/^[0-9]+$/) && parseInt(q) <= data.ids.length;
+
+const playNr = (n) => spawn('mpv', ['--no-video', '--msg-level=all=no,statusline=status', `https://www.youtube.com/watch?v=${data.ids[parseInt(n)]}`], { stdio: 'inherit' });
+
+const playNext = (r, n) => {
+ clearOutput();
+ showMsg('Playlist:\n---------');
+ showPlaylist(r, n);
+ showPlaying(n-1);
+
+ const player = playNr(n-1);
+
+ player.on('close', () => {
+ if (n < r[r.length-1]){
+ playNext(r, n+1);
+ } else {
+ clearOutput();
+ showOsd();
+ showPrompt();
+ }
+ });
+};
+
+const handleSongRange = (q) => {
+ if (data.ids.length > 0) {
+ const [a,b] = q.split('-').map(n=>parseInt(n));
+ if (a<b){
+ const range = [...Array(b-a+1).keys()].map(n=>n+a);
+ playNext(range, range[0]);
+ } else {
+ clearOutput();
+ showOsd();
+ showError('invalid range, try again');
+ showPrompt();
+ }
+ } else {
+ clearOutput();
+ showError('first search for the videos');
+ showPrompt();
+ }
+};
+
+const handleSongName = (q) => (
+ fetch(searchUrl(q))
+ .then(res => res.json())
+ .then(json => presentResults(json))
+ .catch(e => console.log(e))
+);
+
+const handleSongNumber = (q) => {
+ const nr = parseInt(q) - 1;
+ clearOutput();
+ showPlaying(nr);
+ const player = playNr(nr);
+ player.on('close', () => {
+ clearOutput();
+ showOsd();
+ showPrompt();
+ });
+};
+
+const handleHelp = () => {
+ clearOutput();
+ showHelp();
+ showPrompt();
+};
+
+const handleQuit = () => {
+ clearOutput();
+ showMsg('Quiting YT audio player...');
+ return 0;
+};
+
+const handleWrongInput = () => {
+ clearOutput();
+ showOsd();
+ showError('wrong input');
+ showPrompt();
+};
+
+const getDurations = (ids) => (
+ fetch(detailsUrl(ids))
+ .then(res => res.json())
+ .then(json => json.items.map(i => i.contentDetails.duration.replace(/(PT|S)/g,'').split(/[A-Z]/g).map(d => d.padStart(2, '0')).join(':')))
+ .catch(e => console.log(e))
+);
+
+const presentResults = (results) => {
+ data.titles = results.items.map(result => result.snippet.title);
+ data.descriptions = results.items.map(result => result.snippet.description);
+ data.channels = results.items.map(result => result.snippet.channelTitle);
+ data.ids = results.items.map(result => result.id.videoId);
+ getDurations(data.ids).then(durations => {
+ data.durations = durations;
+ clearOutput();
+ showOsd();
+ showPrompt();
+ });
+};
+
+const handlePrompt = (err, { query }) => {
+ if (err) {
+ console.log(err);
+ return 1;
+ }
+
+ if (isSongRange(query)) {
+ handleSongRange(query);
+ } else if (isSongName(query)) {
+ handleSongName(query);
+ } else if (isSongNumber(query)) {
+ handleSongNumber(query);
+ } else if (query === 'h') {
+ handleHelp();
+ } else if (query === 'q') {
+ handleQuit();
+ } else {
+ handleWrongInput();
+ }
+};
+
+const showPrompt = () => {
+ prompt.message = "";
+ prompt.delimiter = "";
+ prompt.start();
+ prompt.get({
+ name: 'query',
+ description: 'YT Search ([h]elp, [q]uit):',
+ required: true,
+ }, handlePrompt);
+}
+
+clearOutput();
+showPrompt();