@warren-bank/fx_cast_bridge 0.3.1

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,107 @@
1
+ "use strict";
2
+ var __importDefault =
3
+ (this && this.__importDefault) ||
4
+ function (mod) {
5
+ return mod && mod.__esModule ? mod : { default: mod };
6
+ };
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.handleCastMessage = handleCastMessage;
9
+ const Session_1 = __importDefault(require("./Session"));
10
+ const client_1 = __importDefault(require("./client"));
11
+ const sessions = new Map();
12
+ function handleCastMessage(messaging, message) {
13
+ switch (message.subject) {
14
+ case "bridge:createCastSession": {
15
+ const { appId, receiverDevice } = message.data;
16
+ const session = new Session_1.default(
17
+ appId,
18
+ receiverDevice,
19
+ messaging,
20
+ sessionId => {
21
+ sessions.set(sessionId, session);
22
+ }
23
+ );
24
+ break;
25
+ }
26
+ case "bridge:sendCastReceiverMessage": {
27
+ const { sessionId, messageData, messageId } = message.data;
28
+ const session = sessions.get(sessionId);
29
+ if (!session) {
30
+ messaging.sendMessage({
31
+ subject: "cast:impl_sendMessage",
32
+ data: {
33
+ error: "Session does not exist",
34
+ sessionId,
35
+ messageId
36
+ }
37
+ });
38
+ break;
39
+ }
40
+ try {
41
+ session.sendReceiverMessage(messageData);
42
+ } catch (err) {
43
+ messaging.sendMessage({
44
+ subject: "cast:impl_sendMessage",
45
+ data: {
46
+ error: `Failed to send message (${err})`,
47
+ sessionId,
48
+ messageId
49
+ }
50
+ });
51
+ break;
52
+ }
53
+ messaging.sendMessage({
54
+ subject: "cast:impl_sendMessage",
55
+ data: { sessionId, messageId }
56
+ });
57
+ break;
58
+ }
59
+ case "bridge:sendCastSessionMessage": {
60
+ const { namespace, sessionId, messageId } = message.data;
61
+ const session = sessions.get(sessionId);
62
+ if (!session) {
63
+ messaging.sendMessage({
64
+ subject: "cast:impl_sendMessage",
65
+ data: {
66
+ error: "Session does not exist",
67
+ sessionId,
68
+ messageId
69
+ }
70
+ });
71
+ break;
72
+ }
73
+ try {
74
+ let { messageData } = message.data;
75
+ if (typeof messageData === "string") {
76
+ messageData = JSON.parse(messageData);
77
+ }
78
+ session.sendMessage(namespace, messageData);
79
+ } catch (err) {
80
+ messaging.sendMessage({
81
+ subject: "cast:impl_sendMessage",
82
+ data: {
83
+ error: `Failed to send message (${err})`,
84
+ sessionId,
85
+ messageId
86
+ }
87
+ });
88
+ break;
89
+ }
90
+ messaging.sendMessage({
91
+ subject: "cast:impl_sendMessage",
92
+ data: { sessionId, messageId }
93
+ });
94
+ break;
95
+ }
96
+ case "bridge:stopCastSession": {
97
+ const { receiverDevice } = message.data;
98
+ const client = new client_1.default();
99
+ client
100
+ .connect(receiverDevice.host, { port: receiverDevice.port })
101
+ .then(() => {
102
+ client.sendReceiverMessage({ type: "STOP" });
103
+ });
104
+ break;
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __importDefault =
3
+ (this && this.__importDefault) ||
4
+ function (mod) {
5
+ return mod && mod.__esModule ? mod : { default: mod };
6
+ };
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const client_1 = __importDefault(require("./client"));
9
+ const NS_MEDIA = "urn:x-cast:com.google.cast.media";
10
+ class Remote extends client_1.default {
11
+ constructor(host, options) {
12
+ super();
13
+ this.host = host;
14
+ this.options = options;
15
+ super
16
+ .connect(host, {
17
+ port:
18
+ options === null || options === void 0
19
+ ? void 0
20
+ : options.port,
21
+ onReceiverMessage: message => {
22
+ this.onReceiverMessage(message);
23
+ }
24
+ })
25
+ .then(() => {
26
+ this.sendReceiverMessage({ type: "GET_STATUS" });
27
+ });
28
+ }
29
+ disconnect() {
30
+ var _a;
31
+ super.disconnect();
32
+ (_a = this.transportClient) === null || _a === void 0
33
+ ? void 0
34
+ : _a.disconnect();
35
+ }
36
+ sendMediaMessage(message) {
37
+ var _a;
38
+ (_a = this.transportClient) === null || _a === void 0
39
+ ? void 0
40
+ : _a.sendMediaMessage(message);
41
+ }
42
+ onReceiverMessage(message) {
43
+ var _a, _b, _c, _d, _e, _f, _g, _h;
44
+ if (message.type !== "RECEIVER_STATUS") {
45
+ return;
46
+ }
47
+ const application =
48
+ (_a = message.status.applications) === null || _a === void 0
49
+ ? void 0
50
+ : _a[0];
51
+ if (!application || application.isIdleScreen) {
52
+ if (this.transportClient) {
53
+ this.transportClient = undefined;
54
+ (_c =
55
+ (_b = this.options) === null || _b === void 0
56
+ ? void 0
57
+ : _b.onApplicationClose) === null || _c === void 0
58
+ ? void 0
59
+ : _c.call(_b);
60
+ }
61
+ }
62
+ (_e =
63
+ (_d = this.options) === null || _d === void 0
64
+ ? void 0
65
+ : _d.onReceiverStatusUpdate) === null || _e === void 0
66
+ ? void 0
67
+ : _e.call(_d, message.status);
68
+ if (application && !this.transportClient) {
69
+ this.transportClient = new RemoteTransport(
70
+ application.transportId,
71
+ message => this.onMediaMessage(message)
72
+ );
73
+ this.transportClient
74
+ .connect(this.host, {
75
+ port:
76
+ (_f = this.options) === null || _f === void 0
77
+ ? void 0
78
+ : _f.port
79
+ })
80
+ .then(() => {
81
+ var _a;
82
+ (_a = this.transportClient) === null || _a === void 0
83
+ ? void 0
84
+ : _a.sendMediaMessage({
85
+ type: "GET_STATUS",
86
+ requestId: 0
87
+ });
88
+ });
89
+ (_h =
90
+ (_g = this.options) === null || _g === void 0
91
+ ? void 0
92
+ : _g.onApplicationFound) === null || _h === void 0
93
+ ? void 0
94
+ : _h.call(_g);
95
+ }
96
+ }
97
+ onMediaMessage(message) {
98
+ var _a, _b;
99
+ if (message.type !== "MEDIA_STATUS") {
100
+ return;
101
+ }
102
+ (_b =
103
+ (_a = this.options) === null || _a === void 0
104
+ ? void 0
105
+ : _a.onMediaStatusUpdate) === null || _b === void 0
106
+ ? void 0
107
+ : _b.call(_a, message.status[0]);
108
+ }
109
+ }
110
+ exports.default = Remote;
111
+ class RemoteTransport extends client_1.default {
112
+ constructor(transportId, onMediaMessage) {
113
+ super(undefined, transportId);
114
+ this.mediaChannel = this.createChannel(NS_MEDIA);
115
+ this.mediaChannel.on("message", message => onMediaMessage(message));
116
+ }
117
+ sendMediaMessage(message) {
118
+ this.mediaChannel.send(message);
119
+ }
120
+ }
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UserAction = exports.HlsVideoSegmentFormat = void 0;
4
+ var Capability;
5
+ (function (Capability) {
6
+ Capability["VIDEO_OUT"] = "video_out";
7
+ Capability["AUDIO_OUT"] = "audio_out";
8
+ Capability["VIDEO_IN"] = "video_in";
9
+ Capability["AUDIO_IN"] = "audio_in";
10
+ Capability["MULTIZONE_GROUP"] = "multizone_group";
11
+ })(Capability || (Capability = {}));
12
+ var ReceiverType;
13
+ (function (ReceiverType) {
14
+ ReceiverType["CAST"] = "cast";
15
+ ReceiverType["DIAL"] = "dial";
16
+ ReceiverType["HANGOUT"] = "hangout";
17
+ ReceiverType["CUSTOM"] = "custom";
18
+ })(ReceiverType || (ReceiverType = {}));
19
+ var VolumeControlType;
20
+ (function (VolumeControlType) {
21
+ VolumeControlType["ATTENUATION"] = "attenuation";
22
+ VolumeControlType["FIXED"] = "fixed";
23
+ VolumeControlType["MASTER"] = "master";
24
+ })(VolumeControlType || (VolumeControlType = {}));
25
+ var IdleReason;
26
+ (function (IdleReason) {
27
+ IdleReason["CANCELLED"] = "CANCELLED";
28
+ IdleReason["INTERRUPTED"] = "INTERRUPTED";
29
+ IdleReason["FINISHED"] = "FINISHED";
30
+ IdleReason["ERROR"] = "ERROR";
31
+ })(IdleReason || (IdleReason = {}));
32
+ var HlsSegmentFormat;
33
+ (function (HlsSegmentFormat) {
34
+ HlsSegmentFormat["AAC"] = "aac";
35
+ HlsSegmentFormat["AC3"] = "ac3";
36
+ HlsSegmentFormat["MP3"] = "mp3";
37
+ HlsSegmentFormat["TS"] = "ts";
38
+ HlsSegmentFormat["TS_AAC"] = "ts_aac";
39
+ HlsSegmentFormat["E_AC3"] = "e_ac3";
40
+ HlsSegmentFormat["FMP4"] = "fmp4";
41
+ })(HlsSegmentFormat || (HlsSegmentFormat = {}));
42
+ var HlsVideoSegmentFormat;
43
+ (function (HlsVideoSegmentFormat) {
44
+ HlsVideoSegmentFormat["MPEG2_TS"] = "mpeg2_ts";
45
+ HlsVideoSegmentFormat["FMP4"] = "fmp4";
46
+ })(
47
+ HlsVideoSegmentFormat ||
48
+ (exports.HlsVideoSegmentFormat = HlsVideoSegmentFormat = {})
49
+ );
50
+ var MetadataType;
51
+ (function (MetadataType) {
52
+ MetadataType[(MetadataType["GENERIC"] = 0)] = "GENERIC";
53
+ MetadataType[(MetadataType["MOVIE"] = 1)] = "MOVIE";
54
+ MetadataType[(MetadataType["TV_SHOW"] = 2)] = "TV_SHOW";
55
+ MetadataType[(MetadataType["MUSIC_TRACK"] = 3)] = "MUSIC_TRACK";
56
+ MetadataType[(MetadataType["PHOTO"] = 4)] = "PHOTO";
57
+ MetadataType[(MetadataType["AUDIOBOOK_CHAPTER"] = 5)] = "AUDIOBOOK_CHAPTER";
58
+ })(MetadataType || (MetadataType = {}));
59
+ var PlayerState;
60
+ (function (PlayerState) {
61
+ PlayerState["IDLE"] = "IDLE";
62
+ PlayerState["PLAYING"] = "PLAYING";
63
+ PlayerState["PAUSED"] = "PAUSED";
64
+ PlayerState["BUFFERING"] = "BUFFERING";
65
+ })(PlayerState || (PlayerState = {}));
66
+ var RepeatMode;
67
+ (function (RepeatMode) {
68
+ RepeatMode["OFF"] = "REPEAT_OFF";
69
+ RepeatMode["ALL"] = "REPEAT_ALL";
70
+ RepeatMode["SINGLE"] = "REPEAT_SINGLE";
71
+ RepeatMode["ALL_AND_SHUFFLE"] = "REPEAT_ALL_AND_SHUFFLE";
72
+ })(RepeatMode || (RepeatMode = {}));
73
+ var ResumeState;
74
+ (function (ResumeState) {
75
+ ResumeState["PLAYBACK_START"] = "PLAYBACK_START";
76
+ ResumeState["PLAYBACK_PAUSE"] = "PLAYBACK_PAUSE";
77
+ })(ResumeState || (ResumeState = {}));
78
+ var StreamType;
79
+ (function (StreamType) {
80
+ StreamType["BUFFERED"] = "BUFFERED";
81
+ StreamType["LIVE"] = "LIVE";
82
+ StreamType["OTHER"] = "OTHER";
83
+ })(StreamType || (StreamType = {}));
84
+ var TrackType;
85
+ (function (TrackType) {
86
+ TrackType["TEXT"] = "TEXT";
87
+ TrackType["AUDIO"] = "AUDIO";
88
+ TrackType["VIDEO"] = "VIDEO";
89
+ })(TrackType || (TrackType = {}));
90
+ var UserAction;
91
+ (function (UserAction) {
92
+ UserAction["LIKE"] = "LIKE";
93
+ UserAction["DISLIKE"] = "DISLIKE";
94
+ UserAction["FOLLOW"] = "FOLLOW";
95
+ UserAction["UNFOLLOW"] = "UNFOLLOW";
96
+ })(UserAction || (exports.UserAction = UserAction = {}));
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ var __awaiter =
3
+ (this && this.__awaiter) ||
4
+ function (thisArg, _arguments, P, generator) {
5
+ function adopt(value) {
6
+ return value instanceof P
7
+ ? value
8
+ : new P(function (resolve) {
9
+ resolve(value);
10
+ });
11
+ }
12
+ return new (P || (P = Promise))(function (resolve, reject) {
13
+ function fulfilled(value) {
14
+ try {
15
+ step(generator.next(value));
16
+ } catch (e) {
17
+ reject(e);
18
+ }
19
+ }
20
+ function rejected(value) {
21
+ try {
22
+ step(generator["throw"](value));
23
+ } catch (e) {
24
+ reject(e);
25
+ }
26
+ }
27
+ function step(result) {
28
+ result.done
29
+ ? resolve(result.value)
30
+ : adopt(result.value).then(fulfilled, rejected);
31
+ }
32
+ step(
33
+ (generator = generator.apply(thisArg, _arguments || [])).next()
34
+ );
35
+ });
36
+ };
37
+ var __importDefault =
38
+ (this && this.__importDefault) ||
39
+ function (mod) {
40
+ return mod && mod.__esModule ? mod : { default: mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.mediaServer = void 0;
44
+ exports.startMediaServer = startMediaServer;
45
+ exports.stopMediaServer = stopMediaServer;
46
+ const fs_1 = __importDefault(require("fs"));
47
+ const http_1 = __importDefault(require("http"));
48
+ const os_1 = __importDefault(require("os"));
49
+ const path_1 = __importDefault(require("path"));
50
+ const stream_1 = __importDefault(require("stream"));
51
+ const mime_types_1 = __importDefault(require("mime-types"));
52
+ const subtitles_1 = require("../lib/subtitles");
53
+ function startMediaServer(messaging, filePath, port) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ if (
56
+ exports.mediaServer === null || exports.mediaServer === void 0
57
+ ? void 0
58
+ : exports.mediaServer.listening
59
+ ) {
60
+ yield stopMediaServer();
61
+ }
62
+ let fileDir;
63
+ let fileName;
64
+ let fileSize;
65
+ try {
66
+ const stat = yield fs_1.default.promises.lstat(filePath);
67
+ if (stat.isFile()) {
68
+ fileDir = path_1.default.dirname(filePath);
69
+ fileName = path_1.default.basename(filePath);
70
+ fileSize = stat.size;
71
+ } else {
72
+ messaging.sendMessage({
73
+ subject: "mediaCast:mediaServerError",
74
+ data: "Media path is not a file."
75
+ });
76
+ return;
77
+ }
78
+ } catch (err) {
79
+ messaging.sendMessage({
80
+ subject: "mediaCast:mediaServerError",
81
+ data: "Failed to find media path."
82
+ });
83
+ return;
84
+ }
85
+ const contentType = mime_types_1.default.lookup(filePath);
86
+ if (!contentType) {
87
+ messaging.sendMessage({
88
+ subject: "mediaCast:mediaServerError",
89
+ data: "Failed to find media type."
90
+ });
91
+ return;
92
+ }
93
+ const subtitles = new Map();
94
+ try {
95
+ const dirEntries = yield fs_1.default.promises.readdir(fileDir, {
96
+ withFileTypes: true
97
+ });
98
+ for (const dirEntry of dirEntries) {
99
+ if (
100
+ dirEntry.isFile() &&
101
+ mime_types_1.default.lookup(dirEntry.name) ===
102
+ "application/x-subrip"
103
+ ) {
104
+ subtitles.set(
105
+ dirEntry.name,
106
+ yield (0, subtitles_1.convertSrtToVtt)(
107
+ path_1.default.join(fileDir, dirEntry.name)
108
+ )
109
+ );
110
+ }
111
+ }
112
+ } catch (err) {
113
+ console.error(
114
+ `Error: Failed to find/convert subtitles (${filePath}).`
115
+ );
116
+ }
117
+ exports.mediaServer = http_1.default.createServer((req, res) =>
118
+ __awaiter(this, void 0, void 0, function* () {
119
+ if (!req.url) {
120
+ return;
121
+ }
122
+ let decodedUrl = decodeURIComponent(req.url);
123
+ if (decodedUrl.startsWith("/")) {
124
+ decodedUrl = decodedUrl.slice(1);
125
+ }
126
+ switch (decodedUrl) {
127
+ case fileName: {
128
+ const { range } = req.headers;
129
+ if (range) {
130
+ const bounds = range.substring(6).split("-");
131
+ const start = parseInt(bounds[0]);
132
+ const end = bounds[1]
133
+ ? parseInt(bounds[1])
134
+ : fileSize - 1;
135
+ res.writeHead(206, {
136
+ "Accept-Ranges": "bytes",
137
+ "Content-Range": `bytes ${start}-${end}/${fileSize}`,
138
+ "Content-Length": end - start + 1,
139
+ "Content-Type": contentType
140
+ });
141
+ fs_1.default
142
+ .createReadStream(filePath, { start, end })
143
+ .pipe(res);
144
+ } else {
145
+ res.writeHead(200, {
146
+ "Content-Length": fileSize,
147
+ "Content-Type": contentType
148
+ });
149
+ fs_1.default.createReadStream(filePath).pipe(res);
150
+ }
151
+ break;
152
+ }
153
+ default: {
154
+ if (subtitles.has(req.url)) {
155
+ const vttSource = subtitles.get(req.url);
156
+ const vttStream =
157
+ stream_1.default.Readable.from(vttSource);
158
+ res.setHeader("Access-Control-Allow-Origin", "*");
159
+ vttStream.pipe(res);
160
+ }
161
+ break;
162
+ }
163
+ }
164
+ })
165
+ );
166
+ exports.mediaServer.on("close", () => {
167
+ messaging.sendMessage({
168
+ subject: "mediaCast:mediaServerStopped"
169
+ });
170
+ });
171
+ exports.mediaServer.on("error", err => {
172
+ messaging.sendMessage({
173
+ subject: "mediaCast:mediaServerError",
174
+ data: err.message
175
+ });
176
+ });
177
+ exports.mediaServer.listen(port, () => {
178
+ const localAddresses = [];
179
+ for (const iface of Object.values(
180
+ os_1.default.networkInterfaces()
181
+ )) {
182
+ const matchingIface =
183
+ iface === null || iface === void 0
184
+ ? void 0
185
+ : iface.find(
186
+ details =>
187
+ details.family === "IPv4" && !details.internal
188
+ );
189
+ if (matchingIface) {
190
+ localAddresses.push(matchingIface.address);
191
+ }
192
+ }
193
+ if (!localAddresses.length) {
194
+ messaging.sendMessage({
195
+ subject: "mediaCast:mediaServerError",
196
+ data: "Failed to get local address."
197
+ });
198
+ stopMediaServer();
199
+ return;
200
+ }
201
+ messaging.sendMessage({
202
+ subject: "mediaCast:mediaServerStarted",
203
+ data: {
204
+ mediaPath: fileName,
205
+ subtitlePaths: Array.from(subtitles.keys()),
206
+ localAddress: localAddresses[0]
207
+ }
208
+ });
209
+ });
210
+ });
211
+ }
212
+ function stopMediaServer() {
213
+ return new Promise((resolve, reject) => {
214
+ if (
215
+ !(exports.mediaServer === null || exports.mediaServer === void 0
216
+ ? void 0
217
+ : exports.mediaServer.listening)
218
+ ) {
219
+ resolve();
220
+ return;
221
+ }
222
+ exports.mediaServer.close(err => {
223
+ if (err) {
224
+ reject();
225
+ } else {
226
+ resolve();
227
+ }
228
+ });
229
+ exports.mediaServer = undefined;
230
+ });
231
+ }