@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.
@@ -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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"}
@@ -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;