From 5fe4728c1103f7ba51a07536a751cd9793962e41 Mon Sep 17 00:00:00 2001 From: knotteye Date: Sat, 10 Oct 2020 16:35:05 -0500 Subject: [PATCH 1/7] Add migration script and update remove and adduser functions. Needs a UI, API, and functionality. --- src/database.ts | 3 +++ src/db/1.ts | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/db/1.ts diff --git a/src/database.ts b/src/database.ts index 2cbc440..1ee8a17 100644 --- a/src/database.ts +++ b/src/database.ts @@ -22,6 +22,7 @@ async function addUser(name: string, password: string){ await query('INSERT INTO users (username, password_hash, stream_key, record_flag) VALUES ('+raw.escape(name)+', '+raw.escape(hash)+', '+raw.escape(key)+', 0)'); await query('INSERT INTO user_meta (username, title, about, live) VALUES ('+raw.escape(name)+',\'\',\'\',false)'); await query('INSERT INTO chat_integration (username, irc, xmpp, twitch, discord) VALUES ('+raw.escape(name)+',\'\',\'\',\'\',\'\')'); + await query('INSERT INTO twitch_mirror (username) VALUES ('+raw.escape(name)+')'); return true; } @@ -30,6 +31,8 @@ async function rmUser(name: string){ if(!exist[0]) return false; await query('delete from users where username='+raw.escape(name)+' limit 1'); await query('delete from user_meta where username='+raw.escape(name)+' limit 1'); + await query('delete from chat_integration where username='+raw.escape(name)+' limit 1'); + await query('delete from twitch_mirror where username='+raw.escape(name)+' limit 1'); return true; } diff --git a/src/db/1.ts b/src/db/1.ts new file mode 100644 index 0000000..55b8d89 --- /dev/null +++ b/src/db/1.ts @@ -0,0 +1,9 @@ +import * as db from "../database"; + +async function run () { + await db.query('CREATE TABLE IF NOT EXISTS twitch_mirror(username VARCHAR(25), enabled TINYINT DEFAULT 0, twitch_key VARCHAR(50) DEFAULT \"\")'); + await db.query('INSERT INTO twitch_mirror(username) SELECT username FROM users'); + await db.query('INSERT INTO db_meta (version) VALUES (1)'); +} + +export { run } \ No newline at end of file From 4ff4a6329dfc924a4b9974957233d5e5139b8345 Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 12 Oct 2020 10:54:55 -0500 Subject: [PATCH 2/7] Add configuration options for twitch mirror --- install/config.example.yml | 6 +++++- src/config.ts | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/install/config.example.yml b/install/config.example.yml index da27427..fe66753 100644 --- a/install/config.example.yml +++ b/install/config.example.yml @@ -56,4 +56,8 @@ chat: enabled: false username: #https://twitchapps.com/tmi/ - password: \ No newline at end of file + password: + +twitch_mirror: +# enable to allow users to mirror video streams to twitch + enabled: false \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index 3f74000..d118a25 100644 --- a/src/config.ts +++ b/src/config.ts @@ -81,6 +81,9 @@ const config: Object = { username: null, token: null }, localconfig['chat']['twitch']) - } + }, + twitch_mirror: Object.assign({ + enabled: false + }, localconfig['twitch_mirror']) }; export { config }; \ No newline at end of file From 44cc3213ca780bf274818b69c4b1999edbe8d165 Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 12 Oct 2020 11:14:59 -0500 Subject: [PATCH 3/7] Tweak config changes, add functionality in server.ts Still needs an API and a UI, then good to go. --- install/config.example.yml | 6 +++++- src/config.ts | 3 ++- src/server.ts | 9 +++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/install/config.example.yml b/install/config.example.yml index fe66753..c67d16b 100644 --- a/install/config.example.yml +++ b/install/config.example.yml @@ -60,4 +60,8 @@ chat: twitch_mirror: # enable to allow users to mirror video streams to twitch - enabled: false \ No newline at end of file +# for those with truly no bandwidth limits + enabled: false + # https://stream.twitch.tv/ingests/ + # do not include {stream_key} + ingest: 'rtmp://live-ord02.twitch.tv/app/ \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index d118a25..4bd6ec0 100644 --- a/src/config.ts +++ b/src/config.ts @@ -83,7 +83,8 @@ const config: Object = { }, localconfig['chat']['twitch']) }, twitch_mirror: Object.assign({ - enabled: false + enabled: false, + ingest: null }, localconfig['twitch_mirror']) }; export { config }; \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index dd58ee5..fcd409d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -68,6 +68,15 @@ function init () { console.log('[NodeMediaServer] Skipping recording for stream:',id); } db.query('update user_meta set live=true where username=\''+results[0].username+'\' limit 1'); + db.query('SELECT twitch_key,enabled from twitch_mirror where username='+db.raw.escape(results[0].username)+' limit 1').then(async (tm) => { + if(!tm[0]['enabled'] || !config['twitch_mirror']['enabled'] || !config['twitch_mirror']['ingest']) return; + else + execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'libx264', '-acodec', 'libaac', '-f', 'flv', config['twitch_mirror']['ingest']+tm[0]['twitch_key']], { + detached: true, + stdio : 'inherit', + maxBuffer: Infinity + }).unref(); + }); console.log('[NodeMediaServer] Stream key ok for stream:',id); } else{ From 98927bd7b84ddba895f341670e09a661f28bbfb2 Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 12 Oct 2020 12:11:04 -0500 Subject: [PATCH 4/7] Add API functionality for twitch mirror. --- src/api.ts | 15 +++++++++++++-- src/http.ts | 6 +++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/api.ts b/src/api.ts index bcc79ae..a803d63 100644 --- a/src/api.ts +++ b/src/api.ts @@ -18,7 +18,7 @@ async function register(name: string, password: string, confirm: string): Promis } async function update(fields: object): Promise{ - if(!fields['title'] && !fields['bio'] && (fields['rec'] !== 'true' && fields['rec'] !== 'false')) return {"error":"no valid fields specified"}; + if(!fields['title'] && !fields['bio'] && (fields['rec'] !== 'true' && fields['rec'] !== 'false') && (fields['twitch'] !== 'true' && fields['twitch'] !== 'false') && !fields['twitch_key']) return {"error":"no valid fields specified"}; let qs: string = ""; let f: boolean = false; if(fields['title']) {qs += ' user_meta.title='+db.raw.escape(fields['title']);f = true;} @@ -30,8 +30,19 @@ async function update(fields: object): Promise{ if(typeof(fields['rec']) === 'boolean' || typeof(fields['rec']) === 'number') { if(f) qs+=','; qs += ' users.record_flag='+db.raw.escape(fields['rec']); + f=true; + } + if(typeof(fields['twitch']) === 'boolean' || typeof(fields['twitch']) === 'number') { + if(f) qs+=','; + qs += ' twitch_mirror.enabled='+db.raw.escape(fields['twitch']); + f=true; + } + if(fields['twitch_key']){ + if(f) qs+=','; + qs += ' twitch_mirror.twitch_key='+db.raw.escape(fields['twitch_key']); + f = true; } - await db.query('UPDATE users,user_meta SET'+qs+' WHERE users.username='+db.raw.escape(fields['name'])+' AND user_meta.username='+db.raw.escape(fields['name'])); + await db.query('UPDATE users,user_meta,twitch_mirror SET'+qs+' WHERE users.username='+db.raw.escape(fields['name'])+' AND user_meta.username='+db.raw.escape(fields['name'])+' AND twitch_mirror.username='+db.raw.escape(fields['name'])); return {success:""}; } diff --git a/src/http.ts b/src/http.ts index 7474e5d..a179b2d 100644 --- a/src/http.ts +++ b/src/http.ts @@ -238,10 +238,14 @@ async function initAPI() { if(t) { if(req.body.record === "true") req.body.record = true; else if(req.body.record === "false") req.body.record = false; + if(req.body.twitch === "true") req.body.twitch = true; + else if(req.body.twitch === "false") req.body.twitch = false; return api.update({name: t['username'], title: "title" in req.body ? req.body.title : false, bio: "bio" in req.body ? req.body.bio : false, - rec: "record" in req.body ? req.body.record : "NA" + rec: "record" in req.body ? req.body.record : "NA", + twitch: "twitch" in req.body ? req.body.twitch: "NA", + twitch_key: "twitch_key" in req.body ? req.body.twitch_key : false }).then((r) => { res.json(r); return; From d4bb2ceebec8bd39c7ee3da14a40c4d98b4427c3 Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 12 Oct 2020 12:12:27 -0500 Subject: [PATCH 5/7] Update documentation for API. All that's left for twitch mirroring is a UI and then testing. --- docs/REST.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/REST.md b/docs/REST.md index 74786c9..3160cf7 100644 --- a/docs/REST.md +++ b/docs/REST.md @@ -124,9 +124,9 @@ Update the current user's information **Authentication**: yes -**Parameters**: title, bio, rec +**Parameters**: title, bio, rec, twitch, twitch_key -Rec is a boolean (whether to record VODs), others are strings. Parameters that are not included in the request will not be updated. +Rec is a boolean (whether to record VODs), twitch is a boolean (whether to mirror video streams to twitch) others are strings. Twitch_key is the stream key to use for twitch. Parameters that are not included in the request will not be updated. **Response**: Returns `{error: "error code"}` or `{success: ""}` From 93738d27bc15a3daa32cc88fd217eeb3eb383d5c Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 12 Oct 2020 13:34:24 -0500 Subject: [PATCH 6/7] Add sections in profile.njk for adjusting settings. Everything tested and working apart from the actual streaming functionality. --- src/http.ts | 4 +++- templates/profile.njk | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/http.ts b/src/http.ts index a179b2d..b5f9c53 100644 --- a/src/http.ts +++ b/src/http.ts @@ -496,7 +496,9 @@ async function initSite(openReg) { if(tryDecode(req.cookies.Authorization)) { db.query('select * from user_meta where username='+db.raw.escape(JWT.decode(req.cookies.Authorization)['username'])).then((result) => { db.query('select record_flag from users where username='+db.raw.escape(JWT.decode(req.cookies.Authorization)['username'])).then((r2) => { - res.render('profile.njk', Object.assign({rflag: r2[0]}, {meta: result[0]}, {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf)); + db.query('select enabled from twitch_mirror where username='+db.raw.escape(JWT.decode(req.cookies.Authorization)['username'])).then((r3) => { + res.render('profile.njk', Object.assign({twitch: r3[0]}, {rflag: r2[0]}, {meta: result[0]}, {auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf)); + }); }); }); //res.render('profile.njk', Object.assign({auth: {is: true, name: JWT.decode(req.cookies.Authorization)['username']}}, njkconf)); diff --git a/templates/profile.njk b/templates/profile.njk index 465a8da..33ed1dd 100644 --- a/templates/profile.njk +++ b/templates/profile.njk @@ -5,7 +5,9 @@
Stream Title:

Bio:

- Record VODs: Yes No

+ ReStream to Twitch: Yes No
+ Record VODs: Yes No
+ Twitch Key:


From 7b84253fc145ae9743cf0a782bf4bcbdac921cd5 Mon Sep 17 00:00:00 2001 From: knotteye Date: Mon, 12 Oct 2020 20:53:22 -0500 Subject: [PATCH 7/7] Add some logging for twitch mirror --- src/server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server.ts b/src/server.ts index fcd409d..dd60538 100644 --- a/src/server.ts +++ b/src/server.ts @@ -70,8 +70,8 @@ function init () { db.query('update user_meta set live=true where username=\''+results[0].username+'\' limit 1'); db.query('SELECT twitch_key,enabled from twitch_mirror where username='+db.raw.escape(results[0].username)+' limit 1').then(async (tm) => { if(!tm[0]['enabled'] || !config['twitch_mirror']['enabled'] || !config['twitch_mirror']['ingest']) return; - else - execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'libx264', '-acodec', 'libaac', '-f', 'flv', config['twitch_mirror']['ingest']+tm[0]['twitch_key']], { + console.log('[NodeMediaServer] Mirroring to twitch for stream:',id) + execFile(config['media']['ffmpeg'], ['-loglevel', 'fatal', '-i', 'rtmp://127.0.0.1:'+config['rtmp']['port']+'/'+config['media']['privateEndpoint']+'/'+key, '-vcodec', 'copy', '-acodec', 'copy', '-f', 'flv', config['twitch_mirror']['ingest']+tm[0]['twitch_key']], { detached: true, stdio : 'inherit', maxBuffer: Infinity