/
index.ts
142 lines (116 loc) · 3.83 KB
/
index.ts
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
import _debug from "debug";
import { ChromecastDevice } from "stratocaster";
import { BaseApp, MEDIA_NS } from "../base";
import { ILoadRequest, IMedia } from "../../cast";
import { DisneyApi } from "./api";
import { DisneyPlayerChannel } from "./channel";
import { DisneyConfigurable, IDisneyOpts } from "./config";
import { IPlayableOptions } from "../../app";
const debug = _debug("babbling:DisneyApp");
export { IDisneyOpts } from "./config";
const APP_ID = "C3DE6BC2";
export class DisneyApp extends BaseApp {
// declare Player support
public static tokenConfigKeys = ["token", "refreshToken"];
public static configurable = new DisneyConfigurable();
public static createPlayerChannel(options: IDisneyOpts) {
return new DisneyPlayerChannel(options);
}
constructor(
device: ChromecastDevice,
private readonly options: IDisneyOpts,
) {
super(device, {
appId: APP_ID,
sessionNs: MEDIA_NS,
});
}
public async playById(
entityId: string,
opts: {
language?: string;
startTime?: number;
} = {},
) {
const language = opts.language || "en";
const [s, credentials] = await Promise.all([
this.ensureCastSession(),
this.loadCredentials(),
]);
const media: IMedia = {
contentId: entityId,
contentType: "application/x-mpegurl",
streamType: "BUFFERED",
// ?
customData: {
audioLanguage: language,
dataSaver: "auto",
subtitlesLanguage: language,
},
};
const request: ILoadRequest = {
autoplay: true,
customData: {
credentials,
uiLanguage: language,
},
media,
sessionId: s.destination ?? "",
type: "LOAD",
};
if (opts.startTime !== undefined) {
request.currentTime = opts.startTime;
}
// send LOAD request!
const ms = await s.send(request as any);
if (ms.type !== "MEDIA_STATUS") {
throw new Error(`Load failed: ${ms}`);
}
debug("LOAD complete", (ms as any).status[0].media);
}
public async playSeriesById(seriesId: string, opts: IPlayableOptions = {}) {
debug("find resume for series", seriesId);
const api = new DisneyApi(this.options);
const resume = await api.pickEpisodeForSeries(seriesId);
if (!resume) {
debug("no episode to resume; start from the beginning?");
return;
}
const { episode, startTime } = resume;
debug("... resume:", episode, " at ", startTime);
if (opts.resume === false) {
return this.playById(episode.contentId);
}
await this.playById(episode.contentId, {
startTime,
});
}
public async playByFamilyId(familyId: string, opts: IPlayableOptions = {}) {
const api = new DisneyApi(this.options);
const { contentId, startTime } = await api.getResumeForFamilyId(
familyId,
);
if (opts.resume === false) {
return this.playById(contentId);
}
return this.playById(contentId, {
startTime,
});
}
private async loadCredentials() {
const api = new DisneyApi(this.options);
const tokens = await api.ensureTokensValid();
return {
accessState: JSON.stringify({
data: {
contextState: {
modes: ["bamIdentity"],
},
refreshToken: tokens.refreshToken,
token: tokens.token,
},
version: "4.9",
}),
};
}
}