@gibme/tablo.tv 20.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +0 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/lighthouse.d.ts +155 -0
- package/dist/lighthouse.js +308 -0
- package/dist/lighthouse.js.map +1 -0
- package/dist/live_transcoder.d.ts +92 -0
- package/dist/live_transcoder.js +289 -0
- package/dist/live_transcoder.js.map +1 -0
- package/dist/tablo.d.ts +362 -0
- package/dist/tablo.js +465 -0
- package/dist/tablo.js.map +1 -0
- package/dist/tablo_api.d.ts +101 -0
- package/dist/tablo_api.js +228 -0
- package/dist/tablo_api.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2025, Brandon Lehmann <brandonlehmann@gmail.com>
|
|
3
|
+
//
|
|
4
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
// of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
// in the Software without restriction, including without limitation the rights
|
|
7
|
+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
// copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
// furnished to do so, subject to the following conditions:
|
|
10
|
+
//
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
// copies or substantial portions of the Software.
|
|
13
|
+
//
|
|
14
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
// SOFTWARE.
|
|
21
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
22
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
24
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
25
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
26
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
27
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
|
+
};
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.LiveTranscoder = void 0;
|
|
35
|
+
const events_1 = require("events");
|
|
36
|
+
const child_process_1 = require("child_process");
|
|
37
|
+
const fs_1 = require("fs");
|
|
38
|
+
const path_1 = require("path");
|
|
39
|
+
const ffmpeg_static_1 = __importDefault(require("ffmpeg-static"));
|
|
40
|
+
const which_1 = __importDefault(require("which"));
|
|
41
|
+
const crypto_1 = require("crypto");
|
|
42
|
+
const timer_1 = __importDefault(require("@gibme/timer"));
|
|
43
|
+
class LiveTranscoder extends events_1.EventEmitter {
|
|
44
|
+
/**
|
|
45
|
+
* Creates a new live transcoder instance.
|
|
46
|
+
*
|
|
47
|
+
* The `output_path` must be a valid path to a directory on the local filesystem.
|
|
48
|
+
*
|
|
49
|
+
* The `channel_id` must be a valid channel ID for the specified `device`.
|
|
50
|
+
*
|
|
51
|
+
* The `device` must be a valid `Device` instance.
|
|
52
|
+
* @param id
|
|
53
|
+
* @param device
|
|
54
|
+
* @param channel_id
|
|
55
|
+
* @param output_path
|
|
56
|
+
* @param filename
|
|
57
|
+
* @param auto_restart
|
|
58
|
+
*/
|
|
59
|
+
constructor(id, device, channel_id, output_path, filename = 'stream.m3u8', auto_restart = true) {
|
|
60
|
+
super();
|
|
61
|
+
this.id = id;
|
|
62
|
+
this.device = device;
|
|
63
|
+
this.channel_id = channel_id;
|
|
64
|
+
this.output_path = output_path;
|
|
65
|
+
this.filename = filename;
|
|
66
|
+
this.auto_restart = auto_restart;
|
|
67
|
+
this.ffmpeg_path = ffmpeg_static_1.default !== null && ffmpeg_static_1.default !== void 0 ? ffmpeg_static_1.default : which_1.default.sync('ffmpeg');
|
|
68
|
+
this.emitter = new events_1.EventEmitter();
|
|
69
|
+
this._use_count = 0;
|
|
70
|
+
this._active = false;
|
|
71
|
+
this._full_path = '';
|
|
72
|
+
this.emitter.on('abort', () => __awaiter(this, void 0, void 0, function* () {
|
|
73
|
+
var _a, _b;
|
|
74
|
+
if (this.process) {
|
|
75
|
+
(_a = this.process.stdin) === null || _a === void 0 ? void 0 : _a.write('q', error => {
|
|
76
|
+
if (error) {
|
|
77
|
+
this.emit('error', error);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
if (!this.process.killed) {
|
|
82
|
+
this.process.kill();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (_c) {
|
|
86
|
+
}
|
|
87
|
+
delete this.process;
|
|
88
|
+
(_b = this.timer) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
89
|
+
delete this.timer;
|
|
90
|
+
if (this.session) {
|
|
91
|
+
yield this.device.deleteSession(this.session);
|
|
92
|
+
}
|
|
93
|
+
this.active = false;
|
|
94
|
+
this.emitter.emit('stopped');
|
|
95
|
+
}
|
|
96
|
+
}));
|
|
97
|
+
this.emitter.on('stopped', () => {
|
|
98
|
+
try {
|
|
99
|
+
const current_path = (0, path_1.resolve)(output_path, `./${this.id}`);
|
|
100
|
+
if ((0, fs_1.existsSync)(current_path)) {
|
|
101
|
+
(0, fs_1.rmSync)(current_path, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (_a) {
|
|
105
|
+
}
|
|
106
|
+
this.emit('stopped');
|
|
107
|
+
});
|
|
108
|
+
this.full_path = (0, path_1.resolve)(output_path, `./${this.id}`);
|
|
109
|
+
if ((0, fs_1.existsSync)(this.full_path)) {
|
|
110
|
+
(0, fs_1.rmSync)(this.full_path, { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
(0, fs_1.mkdirSync)(this.full_path, { recursive: true });
|
|
113
|
+
this.full_path = (0, path_1.resolve)(this.full_path, `./${this.filename}`);
|
|
114
|
+
}
|
|
115
|
+
get use_count() {
|
|
116
|
+
return this._use_count;
|
|
117
|
+
}
|
|
118
|
+
get active() {
|
|
119
|
+
return this._active;
|
|
120
|
+
}
|
|
121
|
+
set active(active) {
|
|
122
|
+
this._active = active;
|
|
123
|
+
}
|
|
124
|
+
get full_path() {
|
|
125
|
+
return this._full_path;
|
|
126
|
+
}
|
|
127
|
+
set full_path(path) {
|
|
128
|
+
this._full_path = path;
|
|
129
|
+
}
|
|
130
|
+
get session() {
|
|
131
|
+
return this._session;
|
|
132
|
+
}
|
|
133
|
+
set session(session) {
|
|
134
|
+
this._session = session;
|
|
135
|
+
}
|
|
136
|
+
get channel() {
|
|
137
|
+
var _a;
|
|
138
|
+
return (_a = this.session) === null || _a === void 0 ? void 0 : _a.channel;
|
|
139
|
+
}
|
|
140
|
+
get relative_path() {
|
|
141
|
+
if (this.id) {
|
|
142
|
+
return `${this.output_path}/${this.id}/${this.filename}`
|
|
143
|
+
.replace('//', '/');
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
return '';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Retrieves an existing instance of a live transcoder or creates a new instance if one does not exist.
|
|
151
|
+
*
|
|
152
|
+
* The `device` must be a valid `Device` instance.
|
|
153
|
+
*
|
|
154
|
+
* The `channel_id` must be a valid channel ID for the specified `device`.
|
|
155
|
+
*
|
|
156
|
+
* The `output_path` must be a valid path to a directory on the local filesystem.
|
|
157
|
+
*
|
|
158
|
+
* The `filename` is the name of the output file.
|
|
159
|
+
*
|
|
160
|
+
* @param device
|
|
161
|
+
* @param channel_id
|
|
162
|
+
* @param output_path
|
|
163
|
+
* @param filename
|
|
164
|
+
* @param auto_restart
|
|
165
|
+
*/
|
|
166
|
+
static instance(device_1, channel_id_1, output_path_1) {
|
|
167
|
+
return __awaiter(this, arguments, void 0, function* (device, channel_id, output_path, filename = 'stream.m3u8', auto_restart = true) {
|
|
168
|
+
const info = yield device.info();
|
|
169
|
+
if (!info) {
|
|
170
|
+
throw new Error('Failed to retrieve device information');
|
|
171
|
+
}
|
|
172
|
+
const id = (0, crypto_1.createHash)('sha256')
|
|
173
|
+
.update(JSON.stringify({ server_id: info.server_id, channel_id }))
|
|
174
|
+
.digest('hex');
|
|
175
|
+
let instance = this.instances.get(id);
|
|
176
|
+
if (!instance) {
|
|
177
|
+
instance = new LiveTranscoder(id, device, channel_id, output_path, filename, auto_restart);
|
|
178
|
+
this.instances.set(id, instance);
|
|
179
|
+
}
|
|
180
|
+
return instance;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
on(event, listener) {
|
|
184
|
+
return super.on(event, listener);
|
|
185
|
+
}
|
|
186
|
+
once(event, listener) {
|
|
187
|
+
return super.once(event, listener);
|
|
188
|
+
}
|
|
189
|
+
off(event, listener) {
|
|
190
|
+
return super.off(event, listener);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Starts the live transcoder.
|
|
194
|
+
*
|
|
195
|
+
* The live transcoder will attempt to start a new session for the specified `channel_id` on the specified `device`.
|
|
196
|
+
*
|
|
197
|
+
* If a session is successfully started, the live transcoder will attempt to start a new transcoding process.
|
|
198
|
+
*/
|
|
199
|
+
start() {
|
|
200
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
201
|
+
if (this.active) {
|
|
202
|
+
this._use_count++;
|
|
203
|
+
this.emit('ready');
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
this.session = yield this.device.watchChannel(this.channel_id);
|
|
207
|
+
if (!this.session) {
|
|
208
|
+
this.emit('error', new Error('Failed to start session'));
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
this.timer = new timer_1.default((this.session.keepalive - 30) * 1000);
|
|
212
|
+
this.timer.on('tick', () => __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
if (this.session) {
|
|
214
|
+
yield this.device.keepaliveSession(this.session);
|
|
215
|
+
}
|
|
216
|
+
}));
|
|
217
|
+
this._use_count++;
|
|
218
|
+
const started = this.start_ffmpeg();
|
|
219
|
+
if (started) {
|
|
220
|
+
const check = () => setTimeout(() => {
|
|
221
|
+
if ((0, fs_1.existsSync)(this.full_path)) {
|
|
222
|
+
this.active = true;
|
|
223
|
+
this.emit('ready');
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
check();
|
|
227
|
+
}
|
|
228
|
+
}, 100);
|
|
229
|
+
check();
|
|
230
|
+
}
|
|
231
|
+
return started;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Stops the live transcoder.
|
|
236
|
+
*/
|
|
237
|
+
stop() {
|
|
238
|
+
this._use_count--;
|
|
239
|
+
if (this.active && this.use_count <= 0) {
|
|
240
|
+
this.emitter.emit('abort');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Attempts to start the FFMpeg process
|
|
245
|
+
* @private
|
|
246
|
+
*/
|
|
247
|
+
start_ffmpeg() {
|
|
248
|
+
if (!this.session) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const args = ['-re', '-i', `${this.session.playlist_url}`,
|
|
252
|
+
'-c:v', 'libx264', '-preset', 'veryfast', '-tune',
|
|
253
|
+
'zerolatency', '-crf', '23', '-g', '48',
|
|
254
|
+
'-keyint_min', '48', '-sc_threshold', '0', '-ac',
|
|
255
|
+
'2', '-c:a', 'aac', '-b:a', '128k',
|
|
256
|
+
'-f', 'hls', '-hls_time', '4', '-hls_list_size',
|
|
257
|
+
'6', '-hls_flags', 'delete_segments+program_date_time+append_list',
|
|
258
|
+
this.full_path];
|
|
259
|
+
this.process = (0, child_process_1.spawn)(this.ffmpeg_path, args, {
|
|
260
|
+
detached: false,
|
|
261
|
+
shell: false,
|
|
262
|
+
windowsHide: false,
|
|
263
|
+
stdio: ['pipe', 'ignore', 'ignore']
|
|
264
|
+
});
|
|
265
|
+
this.process.on('error', error => {
|
|
266
|
+
this.emit('error', error);
|
|
267
|
+
if (!this.auto_restart) {
|
|
268
|
+
this.emitter.emit('abort');
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
this.start_ffmpeg();
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
this.process.on('exit', code => {
|
|
275
|
+
this.emit('exit', code);
|
|
276
|
+
if (!this.auto_restart) {
|
|
277
|
+
this.emitter.emit('abort');
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
this.start_ffmpeg();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
exports.LiveTranscoder = LiveTranscoder;
|
|
287
|
+
LiveTranscoder.instances = new Map();
|
|
288
|
+
exports.default = LiveTranscoder;
|
|
289
|
+
//# sourceMappingURL=live_transcoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live_transcoder.js","sourceRoot":"","sources":["../src/live_transcoder.ts"],"names":[],"mappings":"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"}
|
package/dist/tablo.d.ts
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import TabloAPI from './tablo_api';
|
|
2
|
+
import Lighthouse from './lighthouse';
|
|
3
|
+
import type { Logo } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* See https://jessedp.github.io/tablo-api-docs/#tablo-api-introduction
|
|
6
|
+
* for an extensive list of device endpoints
|
|
7
|
+
*
|
|
8
|
+
* Note: this implementation is currently incomplete and is unlikely to have all endpoints implemented.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Tablo extends TabloAPI {
|
|
11
|
+
private readonly cache;
|
|
12
|
+
private readonly session_channels;
|
|
13
|
+
/**
|
|
14
|
+
* Attempts to discover the Tablo devices on the network from which this API is made.
|
|
15
|
+
* @param timeout
|
|
16
|
+
*/
|
|
17
|
+
static discover(timeout?: number): Promise<Lighthouse.Device[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Returns the currently available airings
|
|
20
|
+
*
|
|
21
|
+
* Note: This method contains a loop that results in the method taking a bit of time to complete,
|
|
22
|
+
* you may specify a progress callback to help report the progress to the caller.
|
|
23
|
+
*
|
|
24
|
+
* Repeated calls to this method are cached for approximately 10 minutes.
|
|
25
|
+
*
|
|
26
|
+
* @param all if true, will return all airings, otherwise will only return airings that are currently playing.
|
|
27
|
+
* @param timeout
|
|
28
|
+
* @param force_refresh if set to true, will force a refresh of the cache.
|
|
29
|
+
* @param progress_callback
|
|
30
|
+
*/
|
|
31
|
+
airings(all?: boolean, timeout?: number, force_refresh?: boolean, progress_callback?: (total: number, received: number) => void): Promise<Tablo.Airing[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Retrieves account subscription information from the device
|
|
34
|
+
* @param timeout
|
|
35
|
+
*/
|
|
36
|
+
accountSubscription(timeout?: number): Promise<Tablo.AccountSubscription | undefined>;
|
|
37
|
+
/**
|
|
38
|
+
* Retrieves the capabilities of the device.
|
|
39
|
+
* @param timeout
|
|
40
|
+
*/
|
|
41
|
+
capabilities(timeout?: number): Promise<string[]>;
|
|
42
|
+
channel(channel_id: string, timeout?: number): Promise<Tablo.Channel | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns a list of the available channels on the device.
|
|
45
|
+
* @param timeout
|
|
46
|
+
*/
|
|
47
|
+
channels(timeout?: number): Promise<Tablo.Channel[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Retrieves information regarding the latest (or a specified) channel scan.
|
|
50
|
+
*
|
|
51
|
+
* @param scan_idx if not specified, will pull the latest channel scan information
|
|
52
|
+
* @param timeout
|
|
53
|
+
*/
|
|
54
|
+
channelScanInfo(scan_idx?: number | string, timeout?: number): Promise<Tablo.ChannelScan | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Deletes/stops an existing watch (streaming) session
|
|
57
|
+
* @param tokenOrPlayerSession
|
|
58
|
+
* @param timeout
|
|
59
|
+
*/
|
|
60
|
+
deleteSession(tokenOrPlayerSession: string | Tablo.PlayerSession, timeout?: number): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Retrieves device subscription information.
|
|
63
|
+
* @param timeout
|
|
64
|
+
*/
|
|
65
|
+
deviceSubscription(timeout?: number): Promise<Tablo.DeviceSubscription | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Retrieves the guide status from the device
|
|
68
|
+
* @param timeout
|
|
69
|
+
*/
|
|
70
|
+
guideStatus(timeout?: number): Promise<Tablo.GuideStatus | undefined>;
|
|
71
|
+
/**
|
|
72
|
+
* Retrieves a list of the hard drives connected to the device.
|
|
73
|
+
* @param timeout
|
|
74
|
+
*/
|
|
75
|
+
hardDrives(timeout?: number): Promise<Tablo.HardDrive[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Retrieves device information
|
|
78
|
+
* @param timeout
|
|
79
|
+
*/
|
|
80
|
+
info(timeout?: number): Promise<Tablo.Info | undefined>;
|
|
81
|
+
/**
|
|
82
|
+
* Sends a watch (streaming) session keepalive request so that the session does not time out and stop
|
|
83
|
+
* @param tokenOrPlayerSession
|
|
84
|
+
* @param timeout
|
|
85
|
+
*/
|
|
86
|
+
keepaliveSession(tokenOrPlayerSession: string | Tablo.PlayerSession, timeout?: number): Promise<Tablo.PlayerSession | undefined>;
|
|
87
|
+
/**
|
|
88
|
+
* Retrieves device location information
|
|
89
|
+
* @param timeout
|
|
90
|
+
*/
|
|
91
|
+
location(timeout?: number): Promise<Tablo.Location | undefined>;
|
|
92
|
+
/**
|
|
93
|
+
* Attempts to retrieve an existing watch (streaming) session
|
|
94
|
+
* @param tokenOrPlayerSession
|
|
95
|
+
* @param timeout
|
|
96
|
+
*/
|
|
97
|
+
session(tokenOrPlayerSession: string | Tablo.PlayerSession, timeout?: number): Promise<Tablo.PlayerSession | undefined>;
|
|
98
|
+
/**
|
|
99
|
+
* Retrieves the settings of the device.
|
|
100
|
+
* @param timeout
|
|
101
|
+
*/
|
|
102
|
+
settings(timeout?: number): Promise<Tablo.Settings | undefined>;
|
|
103
|
+
/**
|
|
104
|
+
* Retrieves the list of supported storage types.
|
|
105
|
+
* @param timeout
|
|
106
|
+
*/
|
|
107
|
+
storage(timeout?: number): Promise<string[]>;
|
|
108
|
+
/**
|
|
109
|
+
* Retrieves tuner information of the device.
|
|
110
|
+
* @param timeout
|
|
111
|
+
*/
|
|
112
|
+
tuners(timeout?: number): Promise<Tablo.Tuner[]>;
|
|
113
|
+
/**
|
|
114
|
+
* Retrieves device update information.
|
|
115
|
+
* @param timeout
|
|
116
|
+
*/
|
|
117
|
+
updateInfo(timeout?: number): Promise<Tablo.UpdateInfo | undefined>;
|
|
118
|
+
/**
|
|
119
|
+
* Retrieves device update progress information.
|
|
120
|
+
* @param timeout
|
|
121
|
+
*/
|
|
122
|
+
updateProgress(timeout?: number): Promise<unknown | undefined>;
|
|
123
|
+
/**
|
|
124
|
+
* Initiates a channel watch (streaming) session on the device which must be managed via
|
|
125
|
+
* `keepaliveSession` and `deleteSession`
|
|
126
|
+
* @param channel_id
|
|
127
|
+
* @param device_info
|
|
128
|
+
* @param timeout
|
|
129
|
+
*/
|
|
130
|
+
watchChannel(channel_id: string, device_info?: Partial<Tablo.Client.Device>, timeout?: number): Promise<Tablo.PlayerSession | undefined>;
|
|
131
|
+
}
|
|
132
|
+
export declare namespace Tablo {
|
|
133
|
+
export namespace Client {
|
|
134
|
+
type DeviceExtra = {
|
|
135
|
+
deviceOS: string;
|
|
136
|
+
deviceId: string;
|
|
137
|
+
width: number;
|
|
138
|
+
deviceModel: string;
|
|
139
|
+
lang: string;
|
|
140
|
+
height: number;
|
|
141
|
+
deviceOSVersion: string;
|
|
142
|
+
limitedAdTracking: number;
|
|
143
|
+
deviceMake: string;
|
|
144
|
+
};
|
|
145
|
+
export type Device = {
|
|
146
|
+
device_id: string;
|
|
147
|
+
extra: Partial<DeviceExtra>;
|
|
148
|
+
platform: string;
|
|
149
|
+
bandwidth: unknown;
|
|
150
|
+
};
|
|
151
|
+
export {};
|
|
152
|
+
}
|
|
153
|
+
export type Info = {
|
|
154
|
+
server_id: string;
|
|
155
|
+
name: string;
|
|
156
|
+
timezone: string;
|
|
157
|
+
deprecated: string;
|
|
158
|
+
version: string;
|
|
159
|
+
local_address: string;
|
|
160
|
+
setup_completed: boolean;
|
|
161
|
+
build_number: number;
|
|
162
|
+
model: {
|
|
163
|
+
wifi: boolean;
|
|
164
|
+
tuners: number;
|
|
165
|
+
type: string;
|
|
166
|
+
name: string;
|
|
167
|
+
};
|
|
168
|
+
availability: string;
|
|
169
|
+
cache_key: string;
|
|
170
|
+
product: string;
|
|
171
|
+
};
|
|
172
|
+
export type Location = {
|
|
173
|
+
state: string;
|
|
174
|
+
location: {
|
|
175
|
+
postal_code: string;
|
|
176
|
+
name: string;
|
|
177
|
+
locality: string;
|
|
178
|
+
area: string;
|
|
179
|
+
state: string;
|
|
180
|
+
latitude: number;
|
|
181
|
+
longitude: number;
|
|
182
|
+
};
|
|
183
|
+
timezone: {
|
|
184
|
+
name: string;
|
|
185
|
+
raw_offset: number;
|
|
186
|
+
dst_offset: number;
|
|
187
|
+
does_dst: boolean;
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
export type Tuner = {
|
|
191
|
+
in_use: boolean;
|
|
192
|
+
channel: string | null;
|
|
193
|
+
recording: string | null;
|
|
194
|
+
channel_identifier: string | null;
|
|
195
|
+
};
|
|
196
|
+
export type Settings = {
|
|
197
|
+
led: string;
|
|
198
|
+
extended_live_recordings: boolean;
|
|
199
|
+
auto_delete_recordings: boolean;
|
|
200
|
+
exclude_duplicates: boolean;
|
|
201
|
+
preferred_audio_track: string;
|
|
202
|
+
data_collection: boolean;
|
|
203
|
+
enable_amplifier: boolean;
|
|
204
|
+
};
|
|
205
|
+
export type Channel = {
|
|
206
|
+
call_sign: string;
|
|
207
|
+
name: string;
|
|
208
|
+
call_sign_src: string;
|
|
209
|
+
major: number;
|
|
210
|
+
minor: number;
|
|
211
|
+
network: string;
|
|
212
|
+
flags: string[];
|
|
213
|
+
resolution: string;
|
|
214
|
+
favourite: boolean;
|
|
215
|
+
tms_station_id: string;
|
|
216
|
+
tms_affiliate_id: string;
|
|
217
|
+
channel_identifier: string;
|
|
218
|
+
source: string;
|
|
219
|
+
logos: Logo[];
|
|
220
|
+
};
|
|
221
|
+
export type PlayerSession<DateType = Date> = {
|
|
222
|
+
token: string;
|
|
223
|
+
expires: DateType;
|
|
224
|
+
keepalive: number;
|
|
225
|
+
playlist_url: string;
|
|
226
|
+
video_details: {
|
|
227
|
+
container_format: string;
|
|
228
|
+
flags: string[];
|
|
229
|
+
};
|
|
230
|
+
channel: Channel;
|
|
231
|
+
};
|
|
232
|
+
export type HardDrive = {
|
|
233
|
+
error: unknown | null;
|
|
234
|
+
connected: boolean;
|
|
235
|
+
format_state: string;
|
|
236
|
+
name: string;
|
|
237
|
+
busy_state: string;
|
|
238
|
+
kind: string;
|
|
239
|
+
size: number;
|
|
240
|
+
size_mib: number;
|
|
241
|
+
usage: number;
|
|
242
|
+
usage_mib: number;
|
|
243
|
+
free: number;
|
|
244
|
+
free_mib: number;
|
|
245
|
+
limit: number;
|
|
246
|
+
limit_mib: number;
|
|
247
|
+
};
|
|
248
|
+
export type GuideStatus<DateType = Date> = {
|
|
249
|
+
guide_seeded: boolean;
|
|
250
|
+
last_update: DateType;
|
|
251
|
+
limit: DateType;
|
|
252
|
+
download_progress: number | null;
|
|
253
|
+
};
|
|
254
|
+
export type DeviceSubscription<DateType = Date> = {
|
|
255
|
+
state: string;
|
|
256
|
+
expires: DateType | null;
|
|
257
|
+
url: string;
|
|
258
|
+
identifier: string;
|
|
259
|
+
};
|
|
260
|
+
type Subscription<DateType = Date> = {
|
|
261
|
+
kind: string;
|
|
262
|
+
state: string;
|
|
263
|
+
name: string;
|
|
264
|
+
title: string;
|
|
265
|
+
deprecated: string;
|
|
266
|
+
expires: DateType | null;
|
|
267
|
+
registration_url: string;
|
|
268
|
+
registration_identifier: string;
|
|
269
|
+
subtitle: string;
|
|
270
|
+
description: string;
|
|
271
|
+
actions: any[];
|
|
272
|
+
warnings: any[];
|
|
273
|
+
};
|
|
274
|
+
export type AccountSubscription<DateType = Date> = {
|
|
275
|
+
services: {
|
|
276
|
+
guide_data: {
|
|
277
|
+
selected: boolean;
|
|
278
|
+
active: boolean;
|
|
279
|
+
};
|
|
280
|
+
cloud_dvr: unknown | null;
|
|
281
|
+
deprecated: string;
|
|
282
|
+
};
|
|
283
|
+
state: string;
|
|
284
|
+
trial: unknown | null;
|
|
285
|
+
offered_option: unknown | null;
|
|
286
|
+
registration: {
|
|
287
|
+
url: string;
|
|
288
|
+
identifier: string;
|
|
289
|
+
};
|
|
290
|
+
subscriptions: Subscription<DateType>[];
|
|
291
|
+
};
|
|
292
|
+
export type UpdateInfo<DateType = Date> = {
|
|
293
|
+
details: unknown | null;
|
|
294
|
+
available_update: unknown | null;
|
|
295
|
+
last_checked: DateType;
|
|
296
|
+
last_update: DateType | null;
|
|
297
|
+
sequence: string[];
|
|
298
|
+
current_step: unknown | null;
|
|
299
|
+
state: string;
|
|
300
|
+
error: unknown | null;
|
|
301
|
+
};
|
|
302
|
+
export type ChannelScan<DateType = Date> = {
|
|
303
|
+
object_id: string;
|
|
304
|
+
path: string;
|
|
305
|
+
postal_code: string;
|
|
306
|
+
datetime: DateType;
|
|
307
|
+
completed: boolean;
|
|
308
|
+
progress: number;
|
|
309
|
+
preferred_audio_track: string;
|
|
310
|
+
};
|
|
311
|
+
export type Episode<DateType = Date> = {
|
|
312
|
+
title: string;
|
|
313
|
+
description: string;
|
|
314
|
+
number: number;
|
|
315
|
+
season_number: number;
|
|
316
|
+
orig_air_date: DateType;
|
|
317
|
+
tms_id: string;
|
|
318
|
+
};
|
|
319
|
+
export type Airing<DateType = Date> = {
|
|
320
|
+
show_title: string;
|
|
321
|
+
start_time: DateType;
|
|
322
|
+
end_time: DateType;
|
|
323
|
+
duration: number;
|
|
324
|
+
episode: Episode<DateType>;
|
|
325
|
+
channel: Channel;
|
|
326
|
+
};
|
|
327
|
+
export namespace Batched {
|
|
328
|
+
type Root = {
|
|
329
|
+
object_id: number;
|
|
330
|
+
path: string;
|
|
331
|
+
};
|
|
332
|
+
type Channel = {
|
|
333
|
+
channel: Tablo.Channel;
|
|
334
|
+
};
|
|
335
|
+
type Airing = {
|
|
336
|
+
series_path: string;
|
|
337
|
+
season_path: string;
|
|
338
|
+
episode: Episode<string>;
|
|
339
|
+
airing_details: {
|
|
340
|
+
show_title: string;
|
|
341
|
+
datetime: string;
|
|
342
|
+
duration: number;
|
|
343
|
+
channel_path: string;
|
|
344
|
+
channel: Root & Channel;
|
|
345
|
+
};
|
|
346
|
+
qualifiers: string[];
|
|
347
|
+
schedule: {
|
|
348
|
+
state: string;
|
|
349
|
+
qualifier: string;
|
|
350
|
+
skip_reason: string;
|
|
351
|
+
skip_detail: unknown | null;
|
|
352
|
+
offsets: {
|
|
353
|
+
start: number;
|
|
354
|
+
end: number;
|
|
355
|
+
source: string;
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
export {};
|
|
361
|
+
}
|
|
362
|
+
export default Tablo;
|