hypertube/hypertube.js

179 lines
4.6 KiB
JavaScript

const fs = require('fs');
const b4a = require('b4a');
const Hyperswarm = require('hyperswarm');
const gracefulGoodbye = require('graceful-goodbye');
const crypto = require('hypercore-crypto');
let rand = Math.floor(Math.random() * 99999).toString();
let USERNAME = "annon" + rand
const stream = require('youtube-audio-stream')
const decoder = require('@suldashi/lame').Decoder
const Speaker = require('speaker');
let audioPlayer = new Speaker({
channels: 2,
bitDepth: 16,
sampleRate: 44100,
});
// Flag to keep track of whether the stream is currently playing
let isPlaying = false;
function startStream(URL) {
// Create a speaker instance to play the audio data
// Stream the audio file
const audioStream = stream(URL)
.pipe(decoder());
// Send the audio data to all connected peers
audioStream.on('data', data => {
for (const conn of conns) {
conn.write(data);
}
});
// Check if the audio player has ended
if (!audioPlayer.writable) {
// Create a new audio player if it has ended
audioPlayer = new Speaker({
channels: 2,
bitDepth: 16,
sampleRate: 44100,
});
audioStream.pipe(audioPlayer);
isPlaying = true;
} else {
// Pipe the audio data to the audio player
audioStream.pipe(audioPlayer);
// Update the flag to indicate that the stream is playing
isPlaying = true;
}
}
// Kill the stream
function stopStream() {
// Send an empty message to all connected peers
for (const conn of conns) {
conn.write(Buffer.alloc(0));
}
// Update the flag to indicate that the stream is not playing
isPlaying = false;
}
// Generate a random public key
const publicKey = crypto.randomBytes(32);
// Create the swarm and pass in the public key
const swarm = new Hyperswarm();
gracefulGoodbye(() => swarm.destroy());
// Keep track of all connections and usernames
const conns = [];
swarm.on('connection', conn => {
const name = b4a.toString(conn.remotePublicKey, 'hex');
console.log(`* got a connection from ${name} (${USERNAME}) *`);
// Start the stream if it is currently playing
if (isPlaying) {
startStream();
}
conns.push(conn);
conn.once('close', () => conns.splice(conns.indexOf(conn), 1));
conn.on('data', data => {
// Check if the message is empty
if (data.length === 0) {
for (const conn of conns) {
conn.write(`Stopping on all Peers`)
}
// Stop the stream
audioPlayer.end()
// Update the flag to indicate that the stream is not playing
isPlaying = false;
} else {
// Pipe the data to the speaker instance
console.log(data);
try {
if (!audioPlayer.writable) {
console.log("trying")
// Create a new audio player if it has ended
audioPlayer = new Speaker({
channels: 2,
bitDepth: 16,
sampleRate: 44100,
});
audioStream.pipe(audioPlayer);
isPlaying = true;
} else {
audioPlayer.write(data);
}
} catch (err) {
if (err.code === "ERR_STREAM_WRITE_AFTER_END") {
console.log("The stream has already ended, cannot write data.");
} else {
throw err;
}
}
}
});
});
// Use readline to accept input from the user
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
// When the user inputs a line of text, broadcast it to all connections
rl.on('line', input => {
// Check if the user is issuing the !play command
if (input.startsWith('!play')) {
// Check if the stream is currently playing
let dataInfo = input.split(" ")
let URL = dataInfo[1]
startStream(URL);
}
if (input === '!stop') {
// Check if the stream is currently playing
if (isPlaying) {
audioPlayer.end()
// Stop the stream
stopStream();
audioPlayer = new Speaker({
channels: 2,
bitDepth: 16,
sampleRate: 44100,
});
} else {
console.log("The stream is already stopped.");
}
}
});
// Join a common topic
const topic = process.argv[2] ? b4a.from(process.argv[2], 'hex') : crypto.randomBytes(32);
const discovery = swarm.join(topic, { client: true, server: true });
// The flushed promise will resolve when the topic has been fully announced to the DHT
discovery.flushed().then(() => {
console.log(`joined topic: ${b4a.toString(topic, 'hex')}`);
console.log("(Share this key to others so they may join)")
console.log("To Play a youtube link, use !play LINKHERE to stop !stop")
console.log("All commands are global to all peers")
});