reorganizing
This commit is contained in:
698
conf/vods.json
Executable file
698
conf/vods.json
Executable file
@@ -0,0 +1,698 @@
|
||||
{
|
||||
"alttp": [
|
||||
{
|
||||
"id": "ot-seg-escape",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Escape (OTTAS Seg)",
|
||||
"chatName": "Escape (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\01-escape.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 352,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-eastern",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Eastern (OTTAS Seg)",
|
||||
"chatName": "Eastern (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\02-eastern.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 277,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-desert",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Desert (OTTAS Seg)",
|
||||
"chatName": "Desert (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\03-desert.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 345,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-hera",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Hera (OTTAS Seg)",
|
||||
"chatName": "Hera (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\04-hera.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 299,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-atower",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "ATower (OTTAS Seg)",
|
||||
"chatName": "ATower (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\05-atower.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 352,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-pod",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "PoD (OTTAS Seg)",
|
||||
"chatName": "PoD (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\06-pod.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 309,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-thieves",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Thieves (OTTAS Seg)",
|
||||
"chatName": "Thieves (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\07-thieves.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 377,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-skull",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Skull (OTTAS Seg)",
|
||||
"chatName": "Skull (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\08-skull.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 267,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-ice",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Ice (OTTAS Seg)",
|
||||
"chatName": "Ice (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\09-ice.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 318,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-swamp",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Swamp (OTTAS Seg)",
|
||||
"chatName": "Swamp (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\10-swamp.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 345,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-mire",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Mire (OTTAS Seg)",
|
||||
"chatName": "Mire (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\11-mire.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 365,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-trock",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "TRock (OTTAS Seg)",
|
||||
"chatName": "TRock (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\12-trock.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 361,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-gtower",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "GTower (OTTAS Seg)",
|
||||
"chatName": "GTower (OTTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\13-gtower.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 396,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "ot-seg-ganon",
|
||||
"category": "Optimal TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Ganon (TTAS Seg)",
|
||||
"chatName": "Ganon (TTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\Risky\\final-versions-timed\\14-ganon.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 108,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-escape",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Escape (STTAS Seg)",
|
||||
"chatName": "Escape (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\01-escape.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 354,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-eastern",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Eastern (STTAS Seg)",
|
||||
"chatName": "Eastern (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\02-eastern.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 281,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-desert",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Desert (STTAS Seg)",
|
||||
"chatName": "Desert (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\03-desert.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 347,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-hera",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Hera (STTAS Seg)",
|
||||
"chatName": "Hera (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\04-hera.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 303,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-atower",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Agah Tower (STTAS Seg)",
|
||||
"chatName": "Agah Tower (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\05-atower.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 354,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-pod",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "PoD (STTAS Seg)",
|
||||
"chatName": "PoD (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\06-pod.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 310,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-thieves",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Thieves (STTAS Seg)",
|
||||
"chatName": "Thieves (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\07-thieves.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 379,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-skull",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Skull (STTAS Seg)",
|
||||
"chatName": "Skull (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\08-skull.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 270,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-ice",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Ice (STTAS Seg)",
|
||||
"chatName": "Ice (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\09-ice.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 321,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-swamp",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Swamp (STTAS Seg)",
|
||||
"chatName": "Swamp (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\10-swamp.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 351,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-mire",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "Mire (STTAS Seg)",
|
||||
"chatName": "Mire (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\11-mire.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 370,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-trock",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "TRock (STTAS Seg)",
|
||||
"chatName": "TRock (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\12-trock.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 364,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "st-seg-gtower",
|
||||
"category": "Safe TTAS Segment",
|
||||
"label": false,
|
||||
"name": "GTower (STTAS Seg)",
|
||||
"chatName": "GTower (STTAS Seg)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\ttas\\RTA\\final-versions-timed\\13-gtower.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 408,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-escape",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Escape (5:52.43) [2018-09-19]",
|
||||
"name": "Escape (NMG Gold)",
|
||||
"chatName": "Escape (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\01-[552.43]-2018-09-19-escape.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 360,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-eastern",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Eastern (5:00.12) [2018-06-25]",
|
||||
"name": "Eastern (NMG Gold)",
|
||||
"chatName": "Eastern (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\02-[500.12]-2018-06-25-eastern.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 302,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-desert",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Desert (6:10.32) [2018-06-02]",
|
||||
"name": "Desert (NMG Gold)",
|
||||
"chatName": "Desert (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\03-[610.32]-2018-06-02-desert.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 374,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-hera",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Hera (5:28.83) [2018-06-01]",
|
||||
"name": "Hera (NMG Gold)",
|
||||
"chatName": "Hera (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\04-[528.83]-2018-06-01-hera.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 332,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-atower",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Agah Tower (5:13.45) [2018-09-07]",
|
||||
"name": "Agah Tower (NMG Gold)",
|
||||
"chatName": "Agah Tower (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\05a-[513.45]-2018-09-07-atower.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 314,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-pod",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Palace of Darkness (6:11.15) [2018-05-27]",
|
||||
"name": "PoD (NMG Gold)",
|
||||
"chatName": "PoD (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\06-[611.15]-2018-05-27-pod.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 376,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-thieves",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Thieves Town (7:08.37) [2018-07-01]",
|
||||
"name": "Thieves (NMG Gold)",
|
||||
"chatName": "Thieves (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\07-[708.37]-2018-07-01-thieves.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 432,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-skull",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Skull Woods (5:24.28) [2018-06-02]",
|
||||
"name": "Skull (NMG Gold)",
|
||||
"chatName": "Skull (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\08-[524.28]-2018-06-02-skull.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 328,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-ice",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Ice Palace (6:18.13) [2018-09-08]",
|
||||
"name": "Ice (NMG Gold)",
|
||||
"chatName": "Ice (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\09-[618.13]-2018-09-08-ice.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 379,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-swamp",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Swamp Palace (6:51.87) [2018-06-10]",
|
||||
"name": "Swamp (NMG Gold)",
|
||||
"chatName": "Swamp (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\10-[651.87]-2018-06-10-swamp.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 418,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-mire",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Misery Mire (7:07.17) [2018-06-10]",
|
||||
"name": "Mire (NMG Gold)",
|
||||
"chatName": "Mire (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\11-[707.17]-2018-06-10-mire.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 432,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-trock",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Turtle Rock (7:07.34) [2018-06-25]",
|
||||
"name": "TRock (NMG Gold)",
|
||||
"chatName": "TRock (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\12-[707.34]-2018-06-25-trock.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 434,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-gtower",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Ganon's Tower (7:09.85) [2018-08-19]",
|
||||
"name": "GTower (NMG Gold)",
|
||||
"chatName": "GTower (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\13-[709.85]-2018-08-19-gtower.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 433,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nmg-gold-ganon",
|
||||
"category": "Any% NMG Gold Segment",
|
||||
"label": "Any% NMG Gold Segment: Ganon (1:43.06) [2018-05-30]",
|
||||
"name": "Ganon (NMG Gold)",
|
||||
"chatName": "Ganon (NMG Gold)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\gold-segments\\any%-nmg-nsq\\720p\\timed\\14-[143.06]-2018-05-30-ganon.mp4",
|
||||
"sceneItem": "4x3ph",
|
||||
"length": 117,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-100-ahp",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: 100% All Heart Pieces (1:19:12) [2017-12-22]",
|
||||
"name": "100% MG AHP (PB)",
|
||||
"chatName": "100% MG AHP (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\100%-mg-ahp\\2017-12-22-100mg-11912.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 4786,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-ab",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: All Bosses No EG (1:09:23) [2017-11-20]",
|
||||
"name": "All Bosses (PB)",
|
||||
"chatName": "All Bosses (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\all-bosses\\2017-11-20-ab-10923.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 4200,
|
||||
"includeInShuffle": false
|
||||
},
|
||||
{
|
||||
"id": "pb-ad",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: All Dungeons No EG/DW/WW (1:14:59) [2017-11-19]",
|
||||
"name": "All Dungeons (PB)",
|
||||
"chatName": "All Dungeons (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\all-dungeons\\2017-11-19-ad-11459.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 4555,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-any-nmg",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Any% NMG No S+Q (1:26:24) [2018-05-27]",
|
||||
"name": "Any% NMG (PB)",
|
||||
"chatName": "Any% NMG (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\any%-nmg-nsq\\2018-05-27-nmg-12624.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 5190,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-any-no-eg",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Any% No EG (30:28) [2018-09-09]",
|
||||
"name": "Any% No EG (PB)",
|
||||
"chatName": "Any% No EG (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\any%-no-eg\\2018-09-09-no-eg-3028.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 1833,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-master-sword",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Master Sword NMG (22:23) [2018-08-23]",
|
||||
"name": "Master Sword (PB)",
|
||||
"chatName": "Master Sword (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\master-sword\\2018-08-23-master-sword-2223.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 1409,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-ms",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Mirror Shield NMG (50:32) [2017-06-20]",
|
||||
"name": "Mirror Shield (PB)",
|
||||
"chatName": "Mirror Shield (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\mirror-shield\\2017-06-20-mirror-shield-5032.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 3068,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-ms-no-eg",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Mirror Shield No EG (11:38) [2017-07-02]",
|
||||
"name": "Mirror Shield No EG (PB)",
|
||||
"chatName": "Mirror Shield No EG (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\mirror-shield-no-eg\\2017-07-02-mirror-shield-no-eg-1138.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 738,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-rbo",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Reverse Boss Order (1:18:13) [2017-12-01]",
|
||||
"name": "Reverse Boss Order (PB)",
|
||||
"chatName": "Reverse Boss Order (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\rbo\\2017-12-01-rbo-11813.mp4",
|
||||
"sceneItem": "16x9ph",
|
||||
"length": 4725,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "pb-100-nmg",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: 100% NMG (1:49:30) [2017-01-31]",
|
||||
"name": "100% NMG (PB)",
|
||||
"chatName": "100% NMG (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\100%\\2017-01-31-hundo-14930.mp4",
|
||||
"sceneItem": "legacyph",
|
||||
"length": 6598,
|
||||
"includeInShuffle": false
|
||||
},
|
||||
{
|
||||
"id": "pb-dg",
|
||||
"category": "Personal Best",
|
||||
"label": "Personal Best: Defeat Ganon (13:41) [2017-02-20]",
|
||||
"name": "Defeat Ganon (PB)",
|
||||
"chatName": "Defeat Ganon (PB)",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\my-vids\\personal-bests\\defeat-ganon\\2017-02-20_12-55-40-defeat-ganon-1341.mp4",
|
||||
"sceneItem": "legacyph",
|
||||
"length": 868,
|
||||
"includeInShuffle": true
|
||||
}
|
||||
],
|
||||
"memes": [
|
||||
{
|
||||
"id": "auw",
|
||||
"name": "auw",
|
||||
"filePath": "Y:\\media\\videos\\black-box-everybody-wow.mp4",
|
||||
"sceneItem": "everybody-wow",
|
||||
"length": 247,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "archery",
|
||||
"name": "archery",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\archery-contest.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 27,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "69blazeit",
|
||||
"name": "69blazeit",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\69BlazeIt.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 92,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "rpgfarm",
|
||||
"name": "rpgfarm",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\2016-07-24-1424-06-rpg-race-sniped.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 144,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "emmapeg",
|
||||
"name": "emmapeg",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\emma-pegging.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 8,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "handy",
|
||||
"name": "handy",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\handy-in-the-mothhole.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 39,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "bodyguard",
|
||||
"name": "bodyguard",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\heroic-popo.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 14,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "whowillitbe",
|
||||
"name": "whowillitbe",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\its-gonna-be-may.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 30,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "mindblown",
|
||||
"name": "mindblown",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\mindblowing-and-lifechanging.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 55,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "nerd-nookie",
|
||||
"name": "nerd-nookie",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\memes\\nerd-bizkit.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 27,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "curling",
|
||||
"name": "curling",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\curling-bored-janitors.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 16,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "airplane",
|
||||
"name": "airplane",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\emetaPlane.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 5,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "hard-things",
|
||||
"name": "hard-things",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\questions-about-hard-things.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 39,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "18arrows",
|
||||
"name": "18arrows",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\screevo-18-arrows-fine.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 41,
|
||||
"includeInShuffle": true
|
||||
},
|
||||
{
|
||||
"id": "quake",
|
||||
"name": "quake",
|
||||
"filePath": "Y:\\media\\videos\\ALttP\\trock-indoor-quake.mp4",
|
||||
"sceneItem": "meme1",
|
||||
"length": 17,
|
||||
"includeInShuffle": true
|
||||
}
|
||||
]
|
||||
}
|
||||
987
config.json
987
config.json
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
TODO:
|
||||
☐ Modularize OBS and Twitch code
|
||||
☐ Room vid requests / import
|
||||
✔ Add random chance for room grind playlist to show for certain amount of time @done (18-09-21 12:28)
|
||||
☐ modularize OBS and Twitch code
|
||||
☐ Rotating background images (leftside)
|
||||
☐ Stream alerts for chat
|
||||
☐ support for $pause
|
||||
@@ -17,11 +16,12 @@ TODO:
|
||||
☐ Ability to include/exclude vods from shuffle in config
|
||||
|
||||
Ideas:
|
||||
☐ Web interface for viewers to issue commands -- twitch extension?!?!?!
|
||||
☐ Web interface for viewers to issue commands -- twitch extension?!
|
||||
☐ Support songrequests -- play through discord?
|
||||
|
||||
___________________
|
||||
Archive:
|
||||
✔ Add random chance for room grind playlist to show for certain amount of time @done (18-09-21 12:28) @project(TODO)
|
||||
✔ show commercials after a video length cap is hit -- show at conclusion of video @done (18-09-19 11:11) @project(TODO)
|
||||
✔ add memes to commercial scene @done (18-09-19 11:11) @project(TODO)
|
||||
✔ add $setcurrent support (to update text label through obs websocket instead of chat) @done (18-09-17 18:06) @project(TODO)
|
||||
|
||||
385
fgfm.js
385
fgfm.js
@@ -4,64 +4,55 @@
|
||||
|
||||
// Import modules
|
||||
const irc = require('irc');
|
||||
const OBSWebSocket = require('obs-websocket-js');
|
||||
const schedule = require('node-schedule');
|
||||
const util = require('./lib/util');
|
||||
const GHOBS = require('./lib/ghobs');
|
||||
|
||||
// Read internal configuration
|
||||
let config = require('./config.json');
|
||||
const snesGames = require('./conf/snesgames.json');
|
||||
const twitchChannel = config.twitch.channels[0].toLowerCase();
|
||||
config.vods = require(config.vodConfigFile);
|
||||
let snesGames = require('./conf/snesgames.json');
|
||||
|
||||
let videoQueue = recentlyPlayed = [];
|
||||
let currentVideo;
|
||||
let videoTimer;
|
||||
let lastCommercialShownAt;
|
||||
let commercialPlaying = false;
|
||||
// Set up initial state
|
||||
let state = {
|
||||
"videoQueue": [],
|
||||
"recentlyPlayed": [],
|
||||
"currentVideo": null,
|
||||
"videoTimer": null,
|
||||
"lastCommercialShownAt": Date.now(),
|
||||
"commercialPlaying": false
|
||||
};
|
||||
|
||||
// Connect to OBS Websocket
|
||||
const obs = new OBSWebSocket();
|
||||
console.log(`Connecting to OBS...`);
|
||||
obs.connect({ address: config.obs.websocket.address, password: config.obs.websocket.password })
|
||||
.then(() => {
|
||||
console.log(`Success! We're connected to OBS!`);
|
||||
return twitchInit(config, obs);
|
||||
})
|
||||
.then(twitch => {
|
||||
return streamInit(config, obs, twitch);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
// Listen for errors from OBS
|
||||
obs.on('error', err => {
|
||||
console.error(`OBS socket error: ${JSON.stringify(err)}`);
|
||||
});
|
||||
const obs = new GHOBS(config);
|
||||
obs.init()
|
||||
.then(() => {return twitchInit(config.twitch)})
|
||||
.then(twitch => {return streamInit(config, twitch)})
|
||||
.catch(console.error);
|
||||
|
||||
// Connect to twitch, set up basic event listeners
|
||||
const twitchInit = (config, obs) => {
|
||||
const twitchInit = (config) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('Connecting to Twitch...');
|
||||
console.log(`Connecting to Twitch / ${config.channel}...`);
|
||||
|
||||
let defaultTwitchConfig = {
|
||||
autoRejoin: true,
|
||||
retryCount: 10,
|
||||
channels: config.twitch.channels,
|
||||
channels: [config.channel],
|
||||
debug: config.debug
|
||||
};
|
||||
|
||||
// Connect to Twitch with the bot account
|
||||
let botChat = new irc.Client(
|
||||
config.twitch.ircServer,
|
||||
config.twitch.username,
|
||||
Object.assign({password: config.twitch.oauth}, defaultTwitchConfig)
|
||||
config.ircServer,
|
||||
config.botLogin.username,
|
||||
Object.assign({password: config.botLogin.oauth}, defaultTwitchConfig)
|
||||
);
|
||||
|
||||
// Connect to Twitch with an editor account
|
||||
let editorChat = new irc.Client(
|
||||
config.twitch.ircServer,
|
||||
config.twitch.editorLogin.username,
|
||||
Object.assign({password: config.twitch.editorLogin.oauth}, defaultTwitchConfig)
|
||||
config.ircServer,
|
||||
config.editorLogin.username,
|
||||
Object.assign({password: config.editorLogin.oauth}, defaultTwitchConfig)
|
||||
);
|
||||
|
||||
let twitchErrorHandler = message => {
|
||||
@@ -76,127 +67,106 @@ const twitchInit = (config, obs) => {
|
||||
|
||||
resolve({"botChat": botChat, "editorChat": editorChat});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize Stream automation
|
||||
const streamInit = (config, obs, twitch) => {
|
||||
// @TODO: Move anything that calls websocket here to GHOBS lib
|
||||
const streamInit = (config, twitch) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
videoQueue = config.vods.sort(util.randSort).slice(0, config.initialQueueSize);
|
||||
console.log(`Initial video queue: ${videoQueue.map((c, i) => `[${i+1}] ${c.chatName}`).join(' | ')}`);
|
||||
|
||||
// Shows a video in the given scene and triggers a callback when it's finished
|
||||
const playVideoInScene = (video, scene, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// set the file path
|
||||
obs.setSourceSettings({"sourceName": video.sceneItem, "sourceSettings": {"local_file": video.filePath}})
|
||||
// show the video
|
||||
.then(data => obs.setSceneItemProperties({"item": video.sceneItem, "scene-name": scene, "visible": true}))
|
||||
// trigger callback when the video is over, but resolve promise immediately with the timer
|
||||
.then(data => {
|
||||
resolve(setTimeout(() => {callback(data)}, video.length*1000));
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
// Set up the initial queue by randomly choosing the configured amount of vods included in shuffling
|
||||
state.videoQueue = config.vods.alttp.filter(e => e.includeInShuffle === true).sort(util.randSort).slice(0, config.initialQueueSize);
|
||||
console.log(`Initial video queue: ${state.videoQueue.map((c, i) => `[${i+1}] ${c.chatName}`).join(' | ')}`);
|
||||
|
||||
// Show a gameplay vod
|
||||
const showVideo = video => {
|
||||
console.log(`Showing video: ${video.chatName}`);
|
||||
|
||||
let handleVideoFinish = () => {
|
||||
obs.setSceneItemProperties({"item": video.sceneItem, "scene-name": config.defaultSceneName, "visible": false})
|
||||
.then(data => {nextVideo()})
|
||||
.catch(console.error);
|
||||
};
|
||||
// play the next video when the previous finishes
|
||||
let handleVideoEnd = () => {nextVideo()};
|
||||
|
||||
obs.setCurrentScene({"scene-name": config.defaultSceneName})
|
||||
.then(res => {
|
||||
playVideoInScene(video, config.defaultSceneName, handleVideoFinish)
|
||||
.then(timer => {
|
||||
// track timer so we can cancel callback later on if necessary
|
||||
videoTimer = timer;
|
||||
obs.playVideoInScene(video, config.defaultSceneName, handleVideoEnd)
|
||||
.then(timer => {
|
||||
// track timer so we can cancel callback later on if necessary
|
||||
state.videoTimer = timer;
|
||||
|
||||
// update activity label and show/hide appropriately
|
||||
if (video.hasOwnProperty('label') && video.label !== false) {
|
||||
obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": video.label});
|
||||
} else {
|
||||
obs.setSceneItemProperties({"item": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "visible": false});
|
||||
}
|
||||
});
|
||||
// update activity label and show/hide appropriately
|
||||
if (video.hasOwnProperty('label') && video.label !== false) {
|
||||
obs.showActivity(video.label);
|
||||
} else {
|
||||
obs.hideActivity();
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
|
||||
// Picks the next video in the queue (shuffles if empty)
|
||||
// Also handles "commercial breaks"
|
||||
// Also handles "commercial breaks" if enabled
|
||||
const nextVideo = () => {
|
||||
// Show a "commercial break" if it's been long enough since the last one
|
||||
let secondsSinceLastCommercial = (Date.now() - lastCommercialShownAt) / 1000;
|
||||
/* let secondsSinceLastCommercial = (Date.now() - state.lastCommercialShownAt) / 1000;
|
||||
if (config.commercialsEnabled === true && secondsSinceLastCommercial >= config.commercialInterval) {
|
||||
commercialPlaying = true;
|
||||
state.commercialPlaying = true;
|
||||
|
||||
console.log(`It has been ${secondsSinceLastCommercial} seconds since the last commercial break!`);
|
||||
// Random chance for it to be "everybody wow"
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= config.auwChance) {
|
||||
console.log(`Showing AUW!`);
|
||||
auw(() => {
|
||||
// show next video in queue
|
||||
lastCommercialShownAt = Date.now();
|
||||
commercialPlaying = false;
|
||||
// show next video in queue once the commercial is done
|
||||
state.lastCommercialShownAt = Date.now();
|
||||
state.commercialPlaying = false;
|
||||
nextVideo();
|
||||
});
|
||||
} else {
|
||||
let commercial = config.memes.sort(util.randSort)[0];
|
||||
let commercial = config.vods.memes.sort(util.randSort)[0];
|
||||
console.log(`Showing random meme: ${commercial.name}`);
|
||||
|
||||
obs.setCurrentScene({"scene-name": config.commercialSceneName})
|
||||
.then(res => {
|
||||
return playVideoInScene(commercial, config.commercialSceneName, () => {
|
||||
// hide video
|
||||
obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false})
|
||||
// unmute songrequest audio
|
||||
twitch.editorChat.say(twitchChannel, `!volume ${config.defaultSRVolume}`);
|
||||
// show next video in queue
|
||||
lastCommercialShownAt = Date.now();
|
||||
commercialPlaying = false;
|
||||
nextVideo();
|
||||
})
|
||||
})
|
||||
let handleCommercialFinish = () => {
|
||||
// unmute songrequest audio
|
||||
twitch.editorChat.say(config.twitch.channel, `!volume ${config.defaultSRVolume}`);
|
||||
|
||||
// update commercial state and show next video in queue
|
||||
state.lastCommercialShownAt = Date.now();
|
||||
state.commercialPlaying = false;
|
||||
nextVideo();
|
||||
};
|
||||
|
||||
obs.playVideoInScene(commercial, config.commercialSceneName, handleCommercialFinish)
|
||||
.then(res => {
|
||||
// mute songrequest audio
|
||||
twitch.editorChat.say(twitchChannel, '!volume 0');
|
||||
})
|
||||
.catch(console.error);
|
||||
twitch.editorChat.say(config.twitch.channel, `!volume 0`);
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
// Keep track of recently played videos
|
||||
if (recentlyPlayed.length === config.recentlyPlayedMemory) {
|
||||
recentlyPlayed.shift();
|
||||
if (state.recentlyPlayed.length === config.recentlyPlayedMemory) {
|
||||
state.recentlyPlayed.shift();
|
||||
}
|
||||
recentlyPlayed.push(currentVideo.id);
|
||||
state.recentlyPlayed.push(state.currentVideo.id);
|
||||
|
||||
// if a commercial/meme is playing (manually triggered), wait until it's done and calls this function again
|
||||
if (commercialPlaying === true) {
|
||||
return;
|
||||
// If a commercial is playing, wait until it's done
|
||||
while (state.commercialPlaying === true) {
|
||||
//
|
||||
}
|
||||
|
||||
// play the next video in the queue, or pick one at random if the queue is empty
|
||||
if (videoQueue.length > 0) {
|
||||
currentVideo = videoQueue.shift();
|
||||
if (state.videoQueue.length > 0) {
|
||||
state.currentVideo = state.videoQueue.shift();
|
||||
} else {
|
||||
// Random chance for room grind to be played for an amount of time instead of another video be shuffled to
|
||||
if ((Math.floor(Math.random() * 100) + 1) <= config.roomGrindChance) {
|
||||
console.log(`Room grind selected!`);
|
||||
// show room-grind source
|
||||
obs.setSceneItemProperties({"item": "room-grind", "scene-name": config.defaultSceneName, "visible": true})
|
||||
// obs.showRoomGrind(config.roomGrindPlaytime);
|
||||
obs.websocket.setSceneItemProperties({"item": "room-grind", "scene-name": config.defaultSceneName, "visible": true})
|
||||
.then(res => {
|
||||
obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": "NOW SHOWING: TTAS Room Grind !ttas"});
|
||||
videoTimer = setTimeout(() => {
|
||||
obs.showActivity("NOW SHOWING: TTAS Room Grind !ttas");
|
||||
state.videoTimer = setTimeout(() => {
|
||||
// after timeout, hide room-grind and call nextVideo()
|
||||
obs.setSceneItemProperties({"item": "room-grind", "scene-name": config.defaultSceneName, "visible": false});
|
||||
obs.websocket.setSceneItemProperties({"item": "room-grind", "scene-name": config.defaultSceneName, "visible": false});
|
||||
nextVideo();
|
||||
}, config.roomGrindPlaytime*1000)
|
||||
});
|
||||
@@ -205,53 +175,54 @@ const streamInit = (config, obs, twitch) => {
|
||||
}
|
||||
|
||||
// filter recently played from shuffle
|
||||
let freshVods = config.vods.filter(e => {
|
||||
return !recentlyPlayed.includes(e.id);
|
||||
let freshVods = config.vods.alttp.filter(e => {
|
||||
return e.includeInShuffle === true && !state.recentlyPlayed.includes(e.id);
|
||||
});
|
||||
currentVideo = freshVods.sort(util.randSort).slice(0, 1).shift();
|
||||
state.currentVideo = freshVods.sort(util.randSort).slice(0, 1).shift();
|
||||
}
|
||||
|
||||
showVideo(currentVideo);
|
||||
showVideo(state.currentVideo);
|
||||
};
|
||||
|
||||
lastCommercialShownAt = Date.now();
|
||||
// Start queue playback
|
||||
state.currentVideo = state.videoQueue.shift();
|
||||
showVideo(state.currentVideo);
|
||||
|
||||
// grab the first video in the queue and show it
|
||||
currentVideo = videoQueue.shift();
|
||||
showVideo(currentVideo);
|
||||
const showCommercial = (video, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let handleFinish = () => {
|
||||
// unmute songrequest audio
|
||||
//twitch.editorChat.say(config.twitch.channel, `!volume ${config.defaultSRVolume}`);
|
||||
if (typeof callback !== 'undefined') callback();
|
||||
};
|
||||
|
||||
obs.playVideoInScene(video, config.commercialSceneName, handleFinish)
|
||||
.then(timer => {
|
||||
// mute songrequest audio
|
||||
//twitch.editorChat.say(config.twitch.channel, `!volume 0`);
|
||||
resolve(timer);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
// Everybody OwenWow
|
||||
const auw = (callback) => {
|
||||
let currentScene;
|
||||
obs.getCurrentScene()
|
||||
.then(res => {
|
||||
currentScene = res.name;
|
||||
// switch to commercial scene
|
||||
return obs.setCurrentScene({"scene-name": config.commercialSceneName});
|
||||
})
|
||||
.then(res => {
|
||||
// show the video
|
||||
return obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": config.commercialSceneName, "visible": true});
|
||||
})
|
||||
.then(res => {
|
||||
// mute songrequest audio
|
||||
twitch.editorChat.say(twitchChannel, '!volume 0');
|
||||
// find the vod in memes
|
||||
let video = config.vods.memes.find(e => e.id === 'auw');
|
||||
let handleFinish = () => {
|
||||
// hide owen
|
||||
obs.websocket.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": false});
|
||||
// trigger user callback
|
||||
if (typeof callback !== 'undefined') callback();
|
||||
};
|
||||
|
||||
showCommercial(video, handleFinish)
|
||||
.then(videoHasStarted => {
|
||||
// show owen
|
||||
obs.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": true});
|
||||
obs.websocket.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": true});
|
||||
// tell chat what's up
|
||||
twitch.botChat.say(twitchChannel, 'Everybody OwenWow');
|
||||
// swap back to the original scene after the video ends
|
||||
setTimeout(() => {
|
||||
// hide video
|
||||
obs.setSceneItemProperties({"item": "everybody-wow", "scene-name": config.commercialSceneName, "visible": false})
|
||||
// hide owen
|
||||
obs.setSceneItemProperties({"item": "owen", "scene-name": config.commercialSceneName, "visible": false});
|
||||
// unmute songrequest audio
|
||||
twitch.editorChat.say(twitchChannel, `!volume ${config.defaultSRVolume}`);
|
||||
// swap back to fgfm
|
||||
obs.setCurrentScene({"scene-name": currentScene});
|
||||
// trigger user callback
|
||||
if (callback) callback();
|
||||
}, 246500);
|
||||
twitch.botChat.say(config.twitch.channel, 'Everybody OwenWow');
|
||||
})
|
||||
.catch(console.error);
|
||||
};
|
||||
@@ -289,13 +260,13 @@ const streamInit = (config, obs, twitch) => {
|
||||
sceneItem["scene-name"] = sceneOrGroup;
|
||||
}
|
||||
|
||||
obs.getSceneItemProperties(sceneItem)
|
||||
obs.websocket.getSceneItemProperties(sceneItem)
|
||||
.then(data => {
|
||||
if (data.visible === newVisibility) {
|
||||
twitch.botChat.say(to, `This scene item is already ${visibleTerm}. DerpHam`);
|
||||
} else {
|
||||
sceneItem.visible = newVisibility;
|
||||
obs.setSceneItemProperties(sceneItem)
|
||||
obs.websocket.setSceneItemProperties(sceneItem)
|
||||
.then(res => {
|
||||
twitch.botChat.say(to, `${target} is now ${visibleTerm}.`);
|
||||
})
|
||||
@@ -305,9 +276,7 @@ const streamInit = (config, obs, twitch) => {
|
||||
.catch(err => {
|
||||
twitch.botChat.say(to, JSON.stringify(err));
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// TOGGLE SOURCE VISIBILITY
|
||||
} else if (commandNoPrefix === 't') {
|
||||
let target = commandParts[1] || false;
|
||||
@@ -318,13 +287,13 @@ const streamInit = (config, obs, twitch) => {
|
||||
|
||||
let sceneItem = {"item": target};
|
||||
|
||||
obs.getSceneItemProperties(sceneItem)
|
||||
obs.websocket.getSceneItemProperties(sceneItem)
|
||||
.then(data => {
|
||||
let newVisibility = !data.visible;
|
||||
let visibleTerm = (newVisibility ? 'visible' : 'hidden');
|
||||
|
||||
sceneItem.visible = newVisibility;
|
||||
obs.setSceneItemProperties(sceneItem)
|
||||
obs.websocket.setSceneItemProperties(sceneItem)
|
||||
.then(res => {
|
||||
twitch.botChat.say(to, `${target} is now ${visibleTerm}.`);
|
||||
})
|
||||
@@ -334,7 +303,6 @@ const streamInit = (config, obs, twitch) => {
|
||||
twitch.botChat.say(to, JSON.stringify(err));
|
||||
});
|
||||
|
||||
|
||||
// SWAP -- Hide one source, show another
|
||||
} else if (commandNoPrefix === 'swap') {
|
||||
// hide first argument, show second argument
|
||||
@@ -345,42 +313,28 @@ const streamInit = (config, obs, twitch) => {
|
||||
return
|
||||
}
|
||||
|
||||
obs.setSceneItemProperties({"item": targetToHide, "visible": false})
|
||||
obs.websocket.setSceneItemProperties({"item": targetToHide, "visible": false})
|
||||
.then(res => {
|
||||
obs.setSceneItemProperties({"item": targetToShow, "visible": true});
|
||||
obs.websocket.setSceneItemProperties({"item": targetToShow, "visible": true});
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
|
||||
|
||||
// Black Box "Everybody Wow"
|
||||
} else if (commandNoPrefix === 'auw') {
|
||||
commercialPlaying = true;
|
||||
state.commercialPlaying = true;
|
||||
auw(() => {
|
||||
commercialPlaying = false;
|
||||
state.commercialPlaying = false;
|
||||
});
|
||||
|
||||
// memes on-demand
|
||||
} else if (commandNoPrefix === 'meme') {
|
||||
commercialPlaying = true;
|
||||
let commercial = config.memes.sort(util.randSort)[0];
|
||||
obs.setCurrentScene({"scene-name": config.commercialSceneName})
|
||||
.then(res => {
|
||||
return playVideoInScene(commercial, config.commercialSceneName, () => {
|
||||
// video is done playing, hide it
|
||||
obs.setSceneItemProperties({"item": commercial.sceneItem, "scene-name": config.commercialSceneName, "visible": false})
|
||||
// unmute songrequest audio
|
||||
twitch.editorChat.say(to, `!volume ${config.defaultSRVolume}`);
|
||||
// swap back to fgfm
|
||||
obs.setCurrentScene({"scene-name": config.defaultSceneName});
|
||||
commercialPlaying = false;
|
||||
});
|
||||
})
|
||||
.then(res => {
|
||||
// mute songrequest audio once video starts playing
|
||||
twitch.editorChat.say(to, '!volume 0');
|
||||
})
|
||||
.catch(console.error);
|
||||
// @TODO: support request by ID
|
||||
state.commercialPlaying = true;
|
||||
let commercial = config.vods.memes.sort(util.randSort)[0];
|
||||
showCommercial(commercial, () => {
|
||||
state.commercialPlaying = false;
|
||||
});
|
||||
|
||||
|
||||
// SWITCH SCENES
|
||||
} else if (commandNoPrefix === 'switch') {
|
||||
|
||||
@@ -390,19 +344,18 @@ const streamInit = (config, obs, twitch) => {
|
||||
return;
|
||||
}
|
||||
|
||||
obs.getCurrentScene()
|
||||
obs.websocket.getCurrentScene()
|
||||
.then(data => {
|
||||
if (data.name === target) {
|
||||
twitch.botChat.say(to, `That scene is already active! DerpHam`);
|
||||
} else {
|
||||
obs.setCurrentScene({"scene-name": target})
|
||||
obs.websocket.setCurrentScene({"scene-name": target})
|
||||
.then(() => {twitch.botChat.say(to, `${target} is now active`)})
|
||||
.catch(console.error);
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
|
||||
// SET ON-SCREEN ACTIVITY
|
||||
} else if (commandNoPrefix === 'setactivity') {
|
||||
let target = commandParts.slice(1).join(' ');
|
||||
@@ -411,30 +364,27 @@ const streamInit = (config, obs, twitch) => {
|
||||
return;
|
||||
}
|
||||
|
||||
obs.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": target})
|
||||
obs.websocket.setTextGDIPlusProperties({"source": config.currentActivitySceneItemName, "scene-name": config.defaultSceneName, "render": true, "text": target})
|
||||
.then(res => {
|
||||
twitch.botChat.say(to, `Activity updated!`);
|
||||
return;
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
|
||||
|
||||
// REBOOT
|
||||
} else if (commandNoPrefix === 'reboot') {
|
||||
console.log('Received request from admin to reboot...');
|
||||
twitch.botChat.say(to, 'Rebooting...');
|
||||
process.exit(0);
|
||||
|
||||
|
||||
// SKIP
|
||||
} else if (commandNoPrefix === 'skip') {
|
||||
clearTimeout(videoTimer);
|
||||
obs.setSceneItemProperties({"item": currentVideo.sceneItem, "scene-name": config.defaultSceneName, "visible": false})
|
||||
clearTimeout(state.videoTimer);
|
||||
obs.websocket.setSceneItemProperties({"item": state.currentVideo.sceneItem, "scene-name": config.defaultSceneName, "visible": false})
|
||||
.then(res => {
|
||||
nextVideo();
|
||||
});
|
||||
|
||||
|
||||
// ADD
|
||||
} else if (commandNoPrefix === 'add') {
|
||||
let requestedVideoId = commandParts[1] || false;
|
||||
@@ -444,30 +394,28 @@ const streamInit = (config, obs, twitch) => {
|
||||
}
|
||||
|
||||
// make sure request vid isn't in the queue already
|
||||
if (videoQueue.findIndex(e => e.id == requestedVideoId) !== -1) {
|
||||
if (state.videoQueue.findIndex(e => e.id == requestedVideoId) !== -1) {
|
||||
twitch.botChat.say(to, `That video is in the queue already!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// search for req'd vid by id in config.vods
|
||||
let vodIndex = config.vods.findIndex(e => e.id == requestedVideoId);
|
||||
// search for req'd vid by id in config.vods.alttp
|
||||
let vodIndex = config.vods.alttp.findIndex(e => e.id == requestedVideoId);
|
||||
if (vodIndex === -1) {
|
||||
twitch.botChat.say(to, `A video with that ID does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to queue if it exists
|
||||
videoQueue.push(config.vods[vodIndex]);
|
||||
twitch.botChat.say(to, `${config.vods[vodIndex].chatName} has been added to the queue [${videoQueue.length}]`);
|
||||
state.videoQueue.push(config.vods.alttp[vodIndex]);
|
||||
twitch.botChat.say(to, `${config.vods.alttp[vodIndex].chatName} has been added to the queue [${state.videoQueue.length}]`);
|
||||
return;
|
||||
|
||||
|
||||
// START VOTE
|
||||
} else if (commandNoPrefix === 'startvote') {
|
||||
videoVoteJob.reschedule("*/15 * * * *");
|
||||
twitch.botChat.say(to, `Video Queue Voting will start in 15 minutes!`);
|
||||
|
||||
|
||||
// PAUSE VOTE
|
||||
} else if (commandNoPrefix === 'pausevote') {
|
||||
clearInterval(rtvInterval);
|
||||
@@ -514,8 +462,8 @@ const streamInit = (config, obs, twitch) => {
|
||||
|
||||
// QUEUE STATUS
|
||||
} else if (commandNoPrefix === 'queue') {
|
||||
if (videoQueue.length > 0) {
|
||||
let chatQueue = videoQueue.map((c, i) => {
|
||||
if (state.videoQueue.length > 0) {
|
||||
let chatQueue = state.videoQueue.map((c, i) => {
|
||||
return `[${i+1}] ${c.chatName}`;
|
||||
});
|
||||
twitch.botChat.say(to, chatQueue.join(' | '));
|
||||
@@ -526,13 +474,13 @@ const streamInit = (config, obs, twitch) => {
|
||||
|
||||
// CURRENT VIDEO
|
||||
} else if (commandNoPrefix === 'current') {
|
||||
twitch.botChat.say(to, `Now Playing: ${currentVideo.chatName}`);
|
||||
twitch.botChat.say(to, `Now Playing: ${state.currentVideo.chatName}`);
|
||||
|
||||
|
||||
// NEXT VIDEO
|
||||
} else if (commandNoPrefix === 'next') {
|
||||
if (videoQueue.length > 0) {
|
||||
twitch.botChat.say(to, `Next Video: ${videoQueue[0].chatName}`);
|
||||
if (state.videoQueue.length > 0) {
|
||||
twitch.botChat.say(to, `Next Video: ${state.videoQueue[0].chatName}`);
|
||||
} else {
|
||||
twitch.botChat.say(to, `No videos currently in queue!`);
|
||||
}
|
||||
@@ -547,21 +495,21 @@ const streamInit = (config, obs, twitch) => {
|
||||
}
|
||||
|
||||
// make sure request vid isn't in the queue already
|
||||
if (videoQueue.findIndex(e => e.id === requestedVideoId) !== -1) {
|
||||
if (state.videoQueue.findIndex(e => e.id === requestedVideoId) !== -1) {
|
||||
twitch.botChat.say(to, `That video is in the queue already!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// search for req'd vid by id in config.vods
|
||||
let vodIndex = config.vods.findIndex(e => e.id === requestedVideoId);
|
||||
// search for req'd vid by id in config.vods.alttp
|
||||
let vodIndex = config.vods.alttp.findIndex(e => e.id === requestedVideoId);
|
||||
if (vodIndex === -1) {
|
||||
twitch.botChat.say(to, `A video with that ID does not exist!`);
|
||||
return;
|
||||
}
|
||||
|
||||
// add to queue if it exists
|
||||
videoQueue.push(config.vods[vodIndex]);
|
||||
twitch.botChat.say(to, `${config.vods[vodIndex].chatName} has been added to the queue [${videoQueue.length}]`);
|
||||
state.videoQueue.push(config.vods.alttp[vodIndex]);
|
||||
twitch.botChat.say(to, `${config.vods.alttp[vodIndex].chatName} has been added to the queue [${state.videoQueue.length}]`);
|
||||
return;
|
||||
|
||||
// RNGAMES
|
||||
@@ -571,13 +519,12 @@ const streamInit = (config, obs, twitch) => {
|
||||
////////////////
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Initializing stream timers...`);
|
||||
|
||||
// @TODO: Modularize timed events
|
||||
console.log(`Initializing stream timers...`);
|
||||
let userVotes = currentChoices = [];
|
||||
let rockTheVote = () => {};
|
||||
let rtvInterval = setInterval(() => {rockTheVote()}, 300000);
|
||||
|
||||
let videoVoteJob = new schedule.Job(async () => {
|
||||
// Tally votes from previous election (if there was one), add the winner to the queue
|
||||
let winner;
|
||||
@@ -586,7 +533,7 @@ const streamInit = (config, obs, twitch) => {
|
||||
// choose a random element from currentChoices
|
||||
winner = util.randElement(currentChoices);
|
||||
console.log(`VIDEO CHOSEN RANDOMLY: ${winner.chatName}`);
|
||||
twitch.botChat.say(twitchChannel, `No Votes Logged -- Next Video Chosen at Random: ${winner.chatName}`);
|
||||
twitch.botChat.say(config.twitch.channel, `No Votes Logged -- Next Video Chosen at Random: ${winner.chatName}`);
|
||||
} else {
|
||||
// tally and sort votes
|
||||
let voteTallies = [];
|
||||
@@ -612,18 +559,18 @@ const streamInit = (config, obs, twitch) => {
|
||||
console.log(`Voting Results: ${JSON.stringify(voteTallies)}`);
|
||||
winner = currentChoices[voteTallies[0].id-1];
|
||||
console.log(`WINNER OF THE VOTE: ${winner.chatName}`);
|
||||
twitch.botChat.say(twitchChannel, `Winner of the Video Vote: ${winner.chatName}`);
|
||||
twitch.botChat.say(config.twitch.channel, `Winner of the Video Vote: ${winner.chatName}`);
|
||||
|
||||
// clear user votes
|
||||
userVotes = [];
|
||||
}
|
||||
|
||||
videoQueue.push(winner);
|
||||
state.videoQueue.push(winner);
|
||||
}
|
||||
|
||||
// choose more random videos from config.vods (that aren't already in the queue)
|
||||
let vodsNotInQueue = config.vods.filter(e => {
|
||||
let inQueue = videoQueue.findIndex(q => q.id === e.id) !== -1;
|
||||
// choose more random videos from config.vods.alttp (that aren't already in the queue)
|
||||
let vodsNotInQueue = config.vods.alttp.filter(e => {
|
||||
let inQueue = state.videoQueue.findIndex(q => q.id === e.id) !== -1;
|
||||
return !inQueue;
|
||||
});
|
||||
currentChoices = vodsNotInQueue.sort(util.randSort).slice(0, config.videoPollSize);
|
||||
@@ -634,16 +581,16 @@ const streamInit = (config, obs, twitch) => {
|
||||
});
|
||||
|
||||
rockTheVote = () => {
|
||||
twitch.botChat.say(twitchChannel, `Vote for which video you'd like to add to the queue using ${config.twitch.cmdPrefix}vote #: ${chatChoices.join(' | ')}`)
|
||||
twitch.botChat.say(config.twitch.channel, `Vote for which video you'd like to add to the queue using ${config.twitch.cmdPrefix}vote #: ${chatChoices.join(' | ')}`)
|
||||
};
|
||||
clearInterval(rtvInterval);
|
||||
rockTheVote();
|
||||
rtvInterval = setInterval(() => {rockTheVote()}, 300000);
|
||||
});
|
||||
|
||||
resolve();
|
||||
resolve(obs);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// catches Promise errors
|
||||
process.on('unhandledRejection', console.error);
|
||||
|
||||
70
lib/ghobs.js
Executable file
70
lib/ghobs.js
Executable file
@@ -0,0 +1,70 @@
|
||||
const OBSWebSocket = require('obs-websocket-js');
|
||||
|
||||
function GHOBS(config) {
|
||||
this.config = config;
|
||||
this.websocket = new OBSWebSocket();
|
||||
|
||||
this.init = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(`Connecting to OBS Websocket...`);
|
||||
this.websocket.connect({ address: this.config.obs.websocket.address, password: this.config.obs.websocket.password })
|
||||
.then(() => {
|
||||
console.log(`Success! We're connected to OBS!`);
|
||||
this.websocket.getCurrentScene().then(res => this.currentScene = res.name);
|
||||
this.websocket.onSwitchScenes(data => this.currentScene = data['scene-name']);
|
||||
resolve();
|
||||
})
|
||||
.catch(reject);
|
||||
|
||||
// Listen for errors from OBS
|
||||
this.websocket.on('error', err => {
|
||||
console.error(`OBS websocket error: ${JSON.stringify(err)}`);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// @TODO: pass any unrecognized commands to the websocket
|
||||
|
||||
// Shows a video in the given scene/item and then hides it and switches back to the original scene when finished
|
||||
this.playVideoInScene = (video, scene, callback) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let originalScene = this.currentScene;
|
||||
this.websocket.setCurrentScene({"scene-name": scene})
|
||||
.then(res => {
|
||||
// set the file path on the source
|
||||
this.websocket.setSourceSettings({"sourceName": video.sceneItem, "sourceSettings": {"local_file": video.filePath}})
|
||||
// show the video scene item
|
||||
.then(data => this.websocket.setSceneItemProperties({"item": video.sceneItem, "scene-name": scene, "visible": true}))
|
||||
// when the video is over, hide it and trigger the user callback, but resolve promise immediately with the timer
|
||||
.then(data => {
|
||||
resolve(setTimeout(() => {
|
||||
this.websocket.setSceneItemProperties({"item": video.sceneItem, "scene-name": scene, "visible": false});
|
||||
this.websocket.setCurrentScene({"scene-name": originalScene});
|
||||
callback(data);
|
||||
}, video.length*1000))
|
||||
});
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
this.showActivity = (newActivity) => {
|
||||
let update = {
|
||||
"source": this.config.currentActivitySceneItemName,
|
||||
"scene-name": this.config.defaultSceneName,
|
||||
"render": true
|
||||
};
|
||||
|
||||
if (typeof newActivity !== 'undefined' && newActivity.length > 0) {
|
||||
update.text = newActivity;
|
||||
}
|
||||
|
||||
return this.websocket.setTextGDIPlusProperties(update);
|
||||
};
|
||||
|
||||
this.hideActivity = () => {
|
||||
return this.websocket.setSceneItemProperties({"item": this.config.currentActivitySceneItemName, "scene-name": this.config.defaultSceneName, "visible": false});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = GHOBS;
|
||||
Reference in New Issue
Block a user