-
Notifications
You must be signed in to change notification settings - Fork 163
/
server.js
312 lines (228 loc) · 10.7 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
'use strict'; // eslint-disable-line strict
const express = require('express');
const app = express();
const Helper = require('./helpers.js');
const Broker = require('./broker.js');
const bodyParser = require('body-parser');
const LoadConfig = require('./config.js');
const SettingsApp = require('./apps/settings.js');
const TagesschauApp = require('./apps/tagesschau.js');
const ResponseException = require('./exceptions.js').ResponseException;
const config = new LoadConfig();
const handleError = (error, request, response, next) => { // eslint-disable-line no-unused-vars
console.log('request failed');
console.log('route: ', request.route ? request.route.path : '');
console.log('query: ', request.query);
console.log('error: ', error);
console.log('body: ', request.body);
console.log('versions: ', process.versions);
let publicError = error;
if (error instanceof Error) {
// native js-errors are not stringifyable
publicError = error.message;
}
try {
let userRequest = request.query ? request.query.q : '';
Helper.kodiShowError(`${request} ${response} ${userRequest} : ${publicError}`);
} catch (err) {
// swallow early error handling error
}
response
.status(error.status || 500)
.send(JSON.stringify(publicError, null, 2));
};
const exec = (action) => {
return (request, response, next) => {
let route = request.route ? request.route.path : '';
console.log('==== BEGIN === route: ', route);
action(request, response, next)
.then(() => {
if (!response.headersSent) {
response.send('OK');
}
})
.catch((error) => handleError(error, request, response, next))
.then(() => console.log('==== END === route: ', route));
};
};
const authenticate = function(request, response, next) {
if (request === null || request.query === request) {
console.log('401 - Unauthorized request');
throw new ResponseException('401 - Unauthorized request', 403);
}
if (!request.body) {
console.log('401 - Missing request body');
throw new ResponseException('401 - Missing request body', 401);
}
let requestToken = request.body.token;
if (!requestToken) {
console.log('401 - Missing request body');
throw new ResponseException('401 - Missing access token', 401);
}
if (requestToken !== config.globalConf.authToken) {
console.log(`wrong secret token = ${requestToken}`);
throw new ResponseException('403 - Wrong access token', 403);
}
console.log('Authentication succeeded');
next();
};
const selectKodiInstance = function(request, response, next) {
config.routeKodiInstance(request);
next();
};
const allRoutesExceptRoot = /\/.+/;
app.use(bodyParser.json());
app.use(express.static(`${__dirname}/views`));
app.use('/listRoutes', Helper.listRoutes);
app.use(allRoutesExceptRoot, authenticate);
app.use(allRoutesExceptRoot, selectKodiInstance);
// Pause or Resume video player
app.all('/playpause', exec(Helper.kodiPlayPause));
// Stop video player
app.all('/stop', exec(Helper.kodiStop));
// mute or unmute kodi
app.all('/mute', exec(Helper.kodiMuteToggle));
// set kodi volume
app.all('/volume', exec(Helper.kodiSetVolume));
// Increase kodi volume
app.all('/volumeup', exec(Helper.kodiIncreaseVolume));
// Decrease kodi volume
app.all('/volumedown', exec(Helper.kodiDecreaseVolume));
// Turn on TV and Switch to Kodi's HDMI input
app.all('/activatetv', exec(Helper.kodiActivateTv));
// Send HDMI-CEC 'StandBy' command to power off TV
app.all('/standbytv', exec(Helper.kodiStandbyTv));
app.all('/playfile', exec(Helper.kodiPlayFile));
// Parse request to watch a movie
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playmovie?q=[MOVIE_NAME]
app.all('/playmovie', exec(Helper.kodiPlayMovie));
app.all('/resumemovie', exec(Helper.kodiResumeMovie));
// Supports optional genre and year query parameters
app.all('/playrandommovie', exec(Helper.kodiPlayRandomMovie));
app.all('/openmovie', exec(Helper.kodiOpenMovie));
// Parse request to open a specific tv show
// Request format: http://[THIS_SERVER_IP_ADDRESS]/opentvshow?q=[TV_SHOW_NAME]
app.all('/opentvshow', exec(Helper.kodiOpenTvshow));
// Start a new library scan
app.all('/scanlibrary', exec(Helper.kodiScanLibrary));
app.all('/cleanlibrary', exec(Helper.kodiCleanLibrary));
// Parse request to watch your next unwatched episode for a given tv show
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playtvshow?q=[TV_SHOW_NAME]
app.all('/playtvshow', exec(Helper.kodiPlayTvshow));
app.all('/resumetvshow', exec(Helper.kodiResumeTvshow));
app.all('/bingewatchtvshow', exec(Helper.kodiBingeWatchTvshow));
// Parse request to watch a specific episode for a given tv show
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playepisode?q=[TV_SHOW_NAME]season=[SEASON_NUMBER]episode&e=[EPISODE_NUMBER]
// For example, if IP was 1.1.1.1 a request to watch season 2 episode 3 in tv show named 'bla' looks like:
// http://1.1.1.1/playepisode?q=bla+season+2&e=3
app.all('/playepisode', exec(Helper.kodiPlayEpisodeHandler));
// Parse request to watch the most recently added tv show episode
app.all('/playrecentepisode', exec(Helper.kodiPlayRecentEpisodeHandler));
// Parse request to Shutdown the kodi system
// Request format: http://[THIS_SERVER_IP_ADDRESS]/shutdown
app.all('/shutdown', exec(Helper.kodiShutdown));
app.all('/hibernate', exec(Helper.kodiHibernate));
app.all('/reboot', exec(Helper.kodiReboot));
app.all('/suspend', exec(Helper.kodiSuspend));
// Parse request to watch a random episode for a given tv show
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playepisode?q[TV_SHOW_NAME]season[SEASON_NUMBER]episode&e[EPISODE_NUMBER]
// For example, if IP was 1.1.1.1 a request to watch season 2 episode 3 in tv show named 'bla' looks like:
// http://1.1.1.1/playepisode?q=bla+season+2+episode&e=3
app.all('/shuffleepisode', exec(Helper.kodiShuffleEpisodeHandler));
// Parse request to watch all episodes of a show on shuffle
app.all('/shuffleshow', exec(Helper.kodiShuffleShowHandler));
// Deprecated! Use explicit TV or Radio Versions
app.all('/playpvrchannelbyname', exec(Helper.kodiPlayChannelByName));
// Deprecated! Use explicit TV or Radio Versions
app.all('/playpvrchannelbynumber', exec(Helper.kodiPlayChannelByNumber));
// TV
app.all('/playtvchannelbyname', exec(Helper.kodiPlayTvChannelByName));
app.all('/playtvchannelbynumber', exec(Helper.kodiPlayTvChannelByNumber));
// Radio
app.all('/playradiochannelbyname', exec(Helper.kodiPlayRadioChannelByName));
app.all('/playradiochannelbynumber', exec(Helper.kodiPlayRadioChannelByNumber));
// Parse request to search for a youtube video. The video will be played using the youtube addon.
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playyoutube?q=[TV_SHOW_NAME]
// For example, if IP was 1.1.1.1 a request to watch season 2 episode 3 in tv show named 'bla' looks like:
// http://1.1.1.1/playyoutube?q=bla
app.all('/playyoutube', exec(Helper.kodiPlayYoutube));
app.all('/searchyoutube', exec(Helper.kodiSearchYoutube));
app.all('/searchserenforshows', exec(Helper.kodiSearchSerenForShows));
app.all('/searchserenmovies', exec(Helper.kodiSearchSerenForMovies));
app.all('/playItemOfDirectory', exec(Helper.playItemOfDirectory));
// Parse request to test the end2end kodi connectivity.
// Request format: http://[THIS_SERVER_IP_ADDRESS]/koditestconnection
app.all('/koditestconnection', exec(Helper.kodiTestConnection));
// *********************************Navigation
// Navigation Down
app.all('/navdown', exec(Helper.kodiNavDown));
// Navigation Up
app.all('/navup', exec(Helper.kodiNavUp));
// Navigation Right
app.all('/navright', exec(Helper.kodiNavRight));
// Navigation Left
app.all('/navleft', exec(Helper.kodiNavLeft));
// Navigation Back
app.all('/navback', exec(Helper.kodiNavBack));
// Navigation Select
app.all('/navselect', exec(Helper.kodiNavSelect));
// Navigation ContectMenu
app.all('/navcontextmenu', exec(Helper.kodiNavContextMenu));
// Show Info
app.all('/displayinfo', exec(Helper.kodiDisplayInfo));
// Navigation Home
app.all('/navhome', exec(Helper.kodiNavHome));
// Show window
app.all('/showWindow', exec(Helper.kodiShowWindow));
app.all('/executeAddon', exec(Helper.kodiExecuteAddon));
// **************************End of navigation controls
// Set subtitles
app.all('/setsubtitles', exec(Helper.kodiSetSubs));
// Set subtitles direct track selection
app.all('/setsubtitlesdirect', exec(Helper.kodiSetSubsDirect));
// Set audio stream
app.all('/setaudio', exec(Helper.kodiSetAudio));
// Set audio stream direct track selection
app.all('/setaudiodirect', exec(Helper.kodiSetAudioDirect));
// Go to x minutes
app.all('/seektominutes', exec(Helper.kodiSeektominutes));
/*
//Bug when seeking forward less than 60 seconds in kodi json https://forum.kodi.tv/showthread.php?tid=237408 so I'm disabling this until a work around is working.
//Seek forward x seconds
app.all('/seekforwardseconds', exec(Helper.kodiSeekForwardSeconds));
//Seek backward x seconds
app.all('/seekbackwardseconds', exec(Helper.kodiSeekBackwardSeconds));
*/
// Seek forward x minutes
app.all('/seekforwardminutes', exec(Helper.kodiSeekForwardMinutes));
// Seek backward x minutes
app.all('/seekbackwardminutes', exec(Helper.kodiSeekBackwardMinutes));
// Parse request to play a song
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playsong?q=[SONG_NAME]
app.all('/playsong', exec(Helper.kodiPlaySong));
// Parse request to play an album
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playalbum?q=[ALBUM_NAME]
app.all('/playalbum', exec(Helper.kodiPlayAlbum));
app.all('/shuffleAlbum', exec(Helper.kodiShuffleAlbum));
// Parse request to play an artist
// Request format: http://[THIS_SERVER_IP_ADDRESS]/playartist?q=[artist_NAME]
app.all('/playartist', exec(Helper.kodiPlayArtist));
app.all('/playgenre', exec(Helper.kodiPlayMusicByGenre));
app.all('/loadProfile', exec(Helper.kodiLoadProfile));
app.all('/showMovieGenre', exec(Helper.kodiShowMovieGenre));
app.all('/togglePartymode', exec(Helper.kodiTogglePartymode));
app.all('/toggleFullscreen', exec(Helper.kodiToggleFullscreen));
// Playlist Control
app.all('/playplaylist', exec(Helper.kodiPlayPlaylist));
app.all('/playercontrol', exec(Helper.playercontrol));
app.all('/playfavourite', exec(Helper.kodiOpenFavourite));
// broker for parsing all phrases
app.all('/broker', exec(Broker.processRequest));
app.use('/settings', SettingsApp.build(exec));
app.use('/tagesschau', TagesschauApp.build(exec));
// error handlers need to be last
app.use(handleError);
// listen for requests :)
const listener = app.listen(config.globalConf.listenerPort, () => {
console.log(`Your app is listening on port ${listener.address().port}`);
});