2023-03-07 01:19:05 -05:00
|
|
|
import { Router, Response, Request, NextFunction } from 'express'
|
2023-03-17 21:19:48 -04:00
|
|
|
import { TwitchAPI } from '../util/scraping/extractor';
|
2023-03-17 22:58:06 -04:00
|
|
|
import ws, { WebSocket } from 'ws';
|
|
|
|
import { TwitchChat } from '../util/scraping/chat/chat';
|
2023-03-07 01:19:05 -05:00
|
|
|
|
|
|
|
const proxyRouter = Router();
|
2023-03-17 21:19:48 -04:00
|
|
|
const twitch = new TwitchAPI()
|
2023-03-14 17:06:36 -04:00
|
|
|
|
2023-03-07 01:19:05 -05:00
|
|
|
proxyRouter.get('/img', async (req: Request, res: Response, next: NextFunction) => {
|
|
|
|
const imageUrl = req.query.imageUrl?.toString()
|
|
|
|
if(!imageUrl) return;
|
|
|
|
|
|
|
|
fetch(imageUrl).then((response) => {
|
|
|
|
response.body!.pipeTo(
|
|
|
|
new WritableStream({
|
|
|
|
start() {
|
|
|
|
response.headers.forEach((v, n) => res.setHeader(n, v));
|
|
|
|
},
|
|
|
|
write(chunk) {
|
|
|
|
res.write(chunk);
|
|
|
|
},
|
|
|
|
close() {
|
|
|
|
res.end();
|
|
|
|
},
|
|
|
|
})
|
|
|
|
);
|
|
|
|
})
|
|
|
|
.catch((err) => next(err))
|
|
|
|
})
|
|
|
|
|
2023-03-13 21:07:25 -04:00
|
|
|
|
2023-03-17 21:19:48 -04:00
|
|
|
proxyRouter.get('/stream/:username/hls.m3u8', async (req: Request, res: Response, next: NextFunction) => {
|
|
|
|
console.log(req.params.username)
|
|
|
|
let m3u8Data = await twitch.getStream(req.params.username)
|
|
|
|
const urlRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
|
|
|
|
const matches = m3u8Data.match(urlRegex)
|
|
|
|
if (!matches) return next(new Error('Error proxying HLS'));
|
2023-03-13 21:07:25 -04:00
|
|
|
|
2023-03-17 21:19:48 -04:00
|
|
|
for (let url of matches) {
|
|
|
|
const base64data = Buffer.from(url).toString('base64url')
|
|
|
|
m3u8Data = m3u8Data.replace(url, `${process.env.URL}/proxy/hls/${base64data}`)
|
|
|
|
}
|
2023-03-13 21:07:25 -04:00
|
|
|
|
2023-03-17 21:19:48 -04:00
|
|
|
res.setHeader('Content-type','application/vnd.apple.mpegurl')
|
|
|
|
res.send(m3u8Data)
|
2023-03-13 21:07:25 -04:00
|
|
|
})
|
|
|
|
|
2023-03-17 21:19:48 -04:00
|
|
|
proxyRouter.get('/hls/:encodedUrl' , async (req: Request, res: Response, next: NextFunction) => {
|
|
|
|
console.log('hi')
|
2023-03-13 21:07:25 -04:00
|
|
|
const unencodedUrl = Buffer.from(req.params.encodedUrl, 'base64url').toString()
|
2023-03-17 21:19:48 -04:00
|
|
|
const m3u8Fetch = await fetch(unencodedUrl)
|
|
|
|
var m3u8Data = await m3u8Fetch.text()
|
|
|
|
|
|
|
|
res.send(m3u8Data)
|
2023-03-13 21:07:25 -04:00
|
|
|
})
|
|
|
|
|
2023-03-14 17:06:36 -04:00
|
|
|
|
|
|
|
|
|
|
|
// IRC PROXY
|
2023-03-17 22:58:06 -04:00
|
|
|
interface ExtWebSocket extends WebSocket {
|
|
|
|
id: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const chat = new TwitchChat({
|
|
|
|
login: {
|
|
|
|
username: 'justinfan23423',
|
|
|
|
password: 'none'
|
|
|
|
},
|
|
|
|
channels: []
|
|
|
|
})
|
|
|
|
chat.connect()
|
|
|
|
|
|
|
|
const clients : { [k:string]: ExtWebSocket[] } = {}
|
|
|
|
import { randomUUID } from 'crypto';
|
|
|
|
|
|
|
|
const findClientsForStreamer = async (streamerName: string) => {
|
|
|
|
if(!clients[streamerName]) return Promise.reject(new Error('No clients following streamer'))
|
|
|
|
|
|
|
|
return clients[streamerName]
|
|
|
|
}
|
|
|
|
|
2023-03-14 17:06:36 -04:00
|
|
|
export const wsServer = new ws.Server({ noServer: true });
|
2023-03-17 22:58:06 -04:00
|
|
|
wsServer.on('connection', (ws: ExtWebSocket) => {
|
|
|
|
const socket = ws as ExtWebSocket
|
|
|
|
socket.on('message', (message) => {
|
|
|
|
const data = message.toString()
|
|
|
|
const splitted = data.split(' ')
|
|
|
|
|
|
|
|
if(splitted.length > 2) socket.close()
|
|
|
|
if(splitted[0] !== 'JOIN') socket.close()
|
|
|
|
|
|
|
|
const streamersToJoin = splitted[1].split(',')
|
|
|
|
if(streamersToJoin.length > 1) socket.close()
|
|
|
|
|
|
|
|
const id = randomUUID()
|
|
|
|
for (let streamer of streamersToJoin) {
|
|
|
|
chat.addStreamer(streamer)
|
|
|
|
|
|
|
|
if(clients[streamer]) {
|
|
|
|
clients[streamer].push(socket)
|
|
|
|
} else {
|
|
|
|
clients[streamer] = [socket]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
socket.id = id
|
|
|
|
socket.send('OK')
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('close', () => {
|
|
|
|
if(socket.id) {
|
|
|
|
}
|
|
|
|
})
|
2023-03-14 17:06:36 -04:00
|
|
|
});
|
|
|
|
|
2023-03-17 22:58:06 -04:00
|
|
|
chat.on('PRIVMSG', async (username, type, channel, message) => {
|
|
|
|
const socketsToSend = await findClientsForStreamer(channel)
|
|
|
|
for(let socket of socketsToSend) {
|
|
|
|
let payload = {
|
|
|
|
username,
|
|
|
|
type,
|
|
|
|
channel,
|
|
|
|
message
|
|
|
|
}
|
|
|
|
socket.send(JSON.stringify(payload))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-03-14 17:06:36 -04:00
|
|
|
|
|
|
|
|
2023-03-07 01:19:05 -05:00
|
|
|
export default proxyRouter
|