import { Router, Response, Request, NextFunction } from 'express' import { TwitchAPI } from '../util/scraping/extractor'; import ws, { WebSocket } from 'ws'; import { TwitchChat } from '../util/scraping/chat/chat'; import { logger } from '../util/logger'; const proxyRouter = Router(); const twitch = new TwitchAPI() proxyRouter.get('/img/:base64Url', async (req: Request, res: Response, next: NextFunction) => { const imageUrl = Buffer.from(req.params.base64Url, 'base64url').toString('utf-8') console.log(imageUrl) if(!imageUrl) return; const imageRes = await fetch(imageUrl) .catch(next) if(!imageRes) return; if(imageRes.status !== 200) { res.status(imageRes.status).send() const error = new Error('Image proxy fetch was not status 200') logger.warn(error) } const arrayBuffer = await imageRes.arrayBuffer() const buf = Buffer.from(arrayBuffer) res.send(buf) }) proxyRouter.get('/stream/:username/hls.m3u8', async (req: Request, res: Response, next: NextFunction) => { 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')); for (let url of matches) { const base64data = Buffer.from(url).toString('base64url') m3u8Data = m3u8Data.replace(url, `${process.env.URL}/proxy/hls/${base64data}`) } res.setHeader('Content-type','application/vnd.apple.mpegurl') res.send(m3u8Data) }) proxyRouter.get('/hls/:encodedUrl' , async (req: Request, res: Response, next: NextFunction) => { const unencodedUrl = Buffer.from(req.params.encodedUrl, 'base64url').toString() const m3u8Fetch = await fetch(unencodedUrl) var m3u8Data = await m3u8Fetch.text() res.send(m3u8Data) }) // IRC PROXY 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] } export const wsServer = new ws.Server({ noServer: true }); 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) { } }) }); 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)) } }) export default proxyRouter