1
1
import BaseVideo from "./baseVideo" ;
2
2
import $ from "jquery" ;
3
3
4
-
5
-
6
4
// https://github.com/mikesteele/dual-captions/blob/b0ab92e4670100a27b76b2796995ad1be89f1672/site_integrations/netflix/index.js
7
5
// https://stackoverflow.com/questions/42105028/netflix-video-player-in-chrome-how-to-seek
8
6
@@ -16,83 +14,76 @@ export default class Netflix extends BaseVideo {
16
14
static captionContainerSelector = "" ;
17
15
static captionWindowSelector = "" ;
18
16
static captionBoxSelector = "" ;
19
- static sub = { } ;
17
+ static sub = { } ;
20
18
21
19
static #injectScriptConstructor = ( ( ) => {
22
- console . log ( "Netflix inject script constructor" ) ;
23
-
24
20
this . listenMessageFrameFromInject ( ) ;
25
21
} ) ( ) ;
26
- static async listenPlayer ( ) {
27
-
28
- }
22
+ static async listenPlayer ( ) { }
29
23
static async handleUrlChange ( url = window . location . href ) {
30
- console . log ( "Netflix handle url change" ) ;
31
24
this . pausedByExtension = false ;
32
25
this . callMethodFromInject ( "activateCaption" , url ) ;
33
26
}
34
27
static async activateCaption ( url ) {
35
- console . log ( "activateCaption------------------00" )
36
- console . log ( "toggle" , this . setting [ "subtitleButtonToggle" ] )
37
- console . log ( "url" , url )
38
- console . log ( "isVideoUrl" , this . isVideoUrl ( url ) )
39
28
// skip if user caption off, is shorts skip
40
- if (
41
- // this.setting["subtitleButtonToggle"] == "false" ||
42
- ! this . isVideoUrl ( url )
43
- ) {
29
+ if ( ! this . isVideoUrl ( url ) ) {
44
30
return ;
45
31
}
46
- console . log ( "activateCaption------------------0" )
47
32
await this . waitPlayerReady ( ) ; //wait player load
48
33
// get video lang
49
- var lang = await this . guessVideoLang ( ) ;
50
- console . log ( "activateCaption------------------1" )
34
+ var lang = await this . guessVideoLang ( ) ;
51
35
this . killInterceptDebounce ( ) ; // end caption intercept
52
- console . log ( "activateCaption------------------2" )
53
36
await this . interceptCaption ( ) ; // start caption intercept
54
- console . log ( "activateCaption------------------3" )
55
37
await this . waitUntilForever ( ( ) => this . getVideoId ( ) ) ;
56
- console . log ( "activateCaption------------------4" )
57
- var videoId = this . getVideoId ( ) ;
58
- this . requestTrack ( lang , videoId ) ; //turn on caption on specified lang
59
-
38
+
39
+ if ( this . checkPlayerCaptionOff ( ) ) {
40
+ console . log ( "caption is off" ) ;
41
+ } else {
42
+ console . log ( this . getPlayer ( ) . getTextTrack ( ) )
43
+ var videoId = this . getVideoId ( ) ;
44
+ this . requestTrack ( lang , videoId ) ; //turn on caption on specified lang
45
+ }
60
46
}
61
- static async isVideoUrl ( url ) {
47
+ static async isVideoUrl ( url ) {
62
48
return url . includes ( `${ this . baseUrl } /watch` ) ;
63
49
}
64
50
static async waitPlayerReady ( ) {
65
51
await this . waitUntilForever ( ( ) => this . getPlayer ( ) ) ;
66
52
var player = this . getPlayer ( ) ;
67
53
await this . waitUntilForever ( ( ) => player . getAudioTrack ( ) ) ;
68
54
}
69
- static setPlayerCaption ( lang ) {
55
+ static setPlayerCaption ( lang ) {
70
56
this . getPlayer ( ) . setTimedTextTrack ( lang ) ;
71
57
}
72
- static setPlayerCaptionOff ( ) {
73
- var offTrack = this . getPlayer ( ) . getTimedTextTrackList ( ) [ 0 ] ;
58
+ static setPlayerCaptionOff ( ) {
59
+ var offTrack = this . getPlayer ( )
60
+ . getTimedTextTrackList ( )
61
+ . find ( ( track ) => track . trackId . includes ( "NONE" ) ) ;
74
62
this . getPlayer ( ) . setTimedTextTrack ( offTrack ) ;
75
63
}
64
+ static checkPlayerCaptionOff ( ) {
65
+ const textTrackList = this . getPlayer ( ) . getTimedTextTrackList ( ) ;
66
+ var currentTrack = this . getPlayer ( ) . getTextTrack ( ) ;
67
+ return ! currentTrack || currentTrack . trackId === textTrackList [ 0 ] ?. trackId ;
68
+ }
76
69
77
70
static getPlayer ( ) {
78
- var videoPlayer = window ?. netflix ?. appContext
79
- ?. state
80
- ?. playerApp
81
- ?. getAPI ( )
82
- . videoPlayer
83
- var playerSessionId = videoPlayer ?. getAllPlayerSessionIds ( )
84
- . find ( s => s . includes ( "watch" ) ) ;
85
- var player = videoPlayer ?. getVideoPlayerBySessionId ( playerSessionId )
86
- return player
71
+ var videoPlayer =
72
+ window ?. netflix ?. appContext ?. state ?. playerApp ?. getAPI ( ) . videoPlayer ;
73
+ var playerSessionId = videoPlayer
74
+ ?. getAllPlayerSessionIds ( )
75
+ . find ( ( s ) => s . includes ( "watch" ) ) ;
76
+ var player = videoPlayer ?. getVideoPlayerBySessionId ( playerSessionId ) ;
77
+ return player ;
87
78
}
88
79
static getVideoId ( ) {
89
80
return String ( this . getPlayer ( ) . getMovieId ( ) ) ;
90
81
}
91
82
static async guessVideoLang ( ) {
92
- return this . getPlayer ( ) ?. getAudioTrack ( ) ?. bcp47
83
+ return this . getPlayer ( ) ?. getAudioTrack ( ) ?. bcp47 ;
93
84
}
94
85
static async guessSubtitleLang ( url , subtitle ) {
95
- return this . getPlayer ( ) ?. getTimedTextTrack ( ) ?. bcp47
86
+ return this . getPlayer ( ) ?. getTimedTextTrack ( ) ?. bcp47 ;
96
87
}
97
88
98
89
static async interceptCaption ( ) {
@@ -108,134 +99,127 @@ export default class Netflix extends BaseVideo {
108
99
var response = await this . requestSubtitle ( request . url ) ;
109
100
var targetLang = this . setting [ "translateTarget" ] ;
110
101
var videoId = this . getVideoId ( ) ;
111
- console . log ( "videoId" , videoId ) ;
112
- console . log ( "request.url" , request . url ) ;
113
- var xml = await response . text ( )
114
- var sub1 = this . parseSubtitle ( xml , videoId ) ;
102
+ var xml = await response . text ( ) ;
103
+ var sub1 = this . parseSubtitle ( xml , videoId ) ;
115
104
var responseSub = sub1 ;
116
-
117
-
118
105
119
- if ( sub1 . lang != targetLang ) {
106
+ if ( sub1 . lang != targetLang ) {
120
107
var sub2 = await this . requestSubtitleWithReset ( targetLang ) ;
121
108
var mergedSub = this . mergeSubtitles ( sub1 , sub2 ) ;
122
109
responseSub = mergedSub ;
123
- console . log ( "merged@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" )
124
- console . log ( mergedSub )
125
- }
126
- var xmlRes = this . encodeMergedSubtitles ( responseSub ) ;
127
-
128
-
129
- request . respondWith (
130
- new Response ( xmlRes )
131
- ) ;
110
+ }
111
+ var xmlRes = this . encodeMergedSubtitles ( responseSub ) ;
112
+ request . respondWith ( new Response ( xmlRes ) ) ;
132
113
}
133
114
} catch ( error ) {
134
115
console . log ( error ) ;
135
116
}
136
117
} ) ;
137
118
}
138
-
139
119
140
120
static async requestSubtitle ( subUrl , lang , tlang , videoId ) {
141
- var res = await fetch ( subUrl ) ;
142
- return res ;
121
+ var res = await fetch ( subUrl ) ;
122
+ return res ;
143
123
}
144
- static async requestSubtitleWithReset ( lang , videoId ) {
145
- var player = this . getPlayer ( )
146
- var prevSub = player . getTimedTextTrack ( )
147
- var sub = await this . requestTrack ( lang , videoId )
148
- player . setTextTrack ( prevSub )
149
- return sub
124
+ static async requestSubtitleWithReset ( lang , videoId ) {
125
+ var player = this . getPlayer ( ) ;
126
+ var prevSub = player . getTimedTextTrack ( ) ;
127
+ var sub = await this . requestTrack ( lang , videoId ) ;
128
+ player . setTextTrack ( prevSub ) ;
129
+ return sub ;
150
130
}
151
- static async requestTrack ( lang , videoId ) {
131
+ static async requestTrack ( lang , videoId ) {
152
132
var videoId = videoId || this . getVideoId ( ) ;
153
- console . log ( "video43" , videoId )
154
-
155
133
if ( this . sub ?. [ videoId ] ?. [ lang ] ) {
156
- console . log ( "requestTrackExist" , lang ) ;
157
134
return this . sub ?. [ videoId ] ?. [ lang ] ;
158
135
}
159
-
160
- console . log ( "requestTracksssssssssssssssssssss" , lang ) ;
161
- console . log ( player ) ;
162
- var player = this . getPlayer ( )
163
- var subList = player . getTimedTextTrackList ( )
136
+ var player = this . getPlayer ( ) ;
137
+ var subList = player . getTimedTextTrackList ( ) ;
164
138
const selectedTimedTextTrack = subList
165
- . sort ( ( a , b ) => ( a . trackType === 'ASSISTIVE' ? - 1 : 1 ) )
139
+ . sort ( ( a , b ) => ( a . trackType === "ASSISTIVE" ? - 1 : 1 ) )
140
+ . filter ( ( textTrack ) => textTrack . isImageBased === false )
166
141
. find ( ( textTrack ) => textTrack . bcp47 === lang ) ;
167
142
168
143
if ( ! selectedTimedTextTrack ) {
169
144
return null ;
170
145
}
171
146
172
- player . setTextTrack ( selectedTimedTextTrack )
173
- console . log ( "reqq" )
147
+ player . setTextTrack ( selectedTimedTextTrack ) ;
174
148
await this . waitUntilForever ( ( ) => {
175
- var a = this . sub ?. [ videoId ] ?. [ lang ]
176
- console . log ( "requestTrackEnd2" , a ) ;
177
- console . log ( videoId )
178
- return a
149
+ return this . sub ?. [ videoId ] ?. [ lang ] ;
179
150
} ) ;
180
- console . log ( "requestTrackEnd" , this . sub ?. [ videoId ] ?. [ lang ] ) ;
181
151
return this . sub ?. [ videoId ] ?. [ lang ] ;
182
-
183
152
}
184
153
static extractVideoId ( url ) {
185
154
const match = url . match ( / \/ w a t c h \/ ( \d + ) / ) ;
186
155
return match ? match [ 1 ] : null ;
187
- }
156
+ }
188
157
189
- static parseSubtitle ( sub , videoId ) {
158
+ static parseSubtitle ( sub , videoId ) {
190
159
const concatSubtitles = [ ] ;
191
160
const parser = new DOMParser ( ) ;
192
161
const xmlDoc = parser . parseFromString ( sub , "text/xml" ) ;
193
- const subtitles = Array . from ( xmlDoc . getElementsByTagName ( "p" ) ) . map ( p => p . cloneNode ( true ) ) ;
162
+ const subtitles = Array . from ( xmlDoc . getElementsByTagName ( "p" ) ) . map ( ( p ) =>
163
+ p . cloneNode ( true )
164
+ ) ;
194
165
var lang = xmlDoc . documentElement . getAttribute ( "xml:lang" ) ;
195
166
// remove div tag
196
167
const div = xmlDoc . querySelector ( "div" ) ;
197
168
div ?. parentNode ?. removeChild ( div ) ;
198
169
170
+ // Extract all regions and update their IDs to start with the language code
171
+ const layout = xmlDoc . getElementsByTagName ( "layout" ) [ 0 ] ;
172
+ if ( layout ) {
173
+ var regions = Array . from ( layout . getElementsByTagName ( "region" ) ) ;
174
+ regions . forEach ( ( region , index ) => {
175
+ const newId = `region${ index } _${ lang } ` ;
176
+ region . setAttribute ( "xml:id" , newId ) ;
177
+ } ) ;
178
+ }
179
+
199
180
// parse subtitles
200
181
for ( let i = 0 ; i < subtitles . length ; i ++ ) {
201
182
const subtitle = subtitles [ i ] ;
202
183
const start = parseInt ( subtitle . getAttribute ( "begin" ) . replace ( "t" , "" ) ) ;
203
184
const end = parseInt ( subtitle . getAttribute ( "end" ) . replace ( "t" , "" ) ) ;
204
185
const text = subtitle . textContent ;
205
-
186
+ const region = subtitle . getAttribute ( "region" ) + "_" + lang ;
206
187
var prev = concatSubtitles ?. [ concatSubtitles . length - 1 ] ;
207
188
if ( prev && prev . start === start && prev . end === end ) {
208
189
prev . text += " " + text ;
209
190
} else {
210
- concatSubtitles . push ( { start, end, text } ) ;
191
+ concatSubtitles . push ( { start, end, text, region } ) ;
211
192
}
212
193
}
213
-
214
194
215
-
216
-
217
- var parsedSubMeta = {
195
+ var parsedSubMeta = {
218
196
xmlDoc,
219
197
lang,
220
198
subtitles : concatSubtitles ,
221
- }
199
+ regions,
200
+ } ;
222
201
if ( ! this . sub [ videoId ] ) {
223
202
this . sub [ videoId ] = { } ;
224
203
}
225
- this . sub [ videoId ] [ lang ] = parsedSubMeta
226
- console . log ( this . sub )
227
- return parsedSubMeta
204
+ this . sub [ videoId ] [ lang ] = parsedSubMeta ;
205
+ return parsedSubMeta ;
228
206
}
229
207
230
208
static mergeSubtitles ( sub1Meta , sub2Meta ) {
231
- var mergedSub = [ ]
232
- var sub1 = sub1Meta . subtitles ;
233
- var sub2 = sub2Meta . subtitles ;
234
- var mergedSubMeta = { ...sub1Meta }
209
+ var mergedSub = [ ] ;
210
+ var sub1 = sub1Meta . subtitles ;
211
+ var sub2 = sub2Meta . subtitles ;
212
+ var mergedSubMeta = { ...sub1Meta } ;
213
+
214
+ const layout = mergedSubMeta ?. xmlDoc ?. getElementsByTagName ( "layout" ) ?. [ 0 ] ;
215
+
216
+ sub2Meta ?. regions ?. forEach ( ( region ) => {
217
+ layout ?. appendChild ( region ) ;
218
+ } ) ;
235
219
236
220
// fix mismatch length between sub1 sub2
237
221
for ( let [ i , sub1Line ] of sub1 . entries ( ) ) {
238
- var line1 = sub1Line
222
+ var line1 = sub1Line ;
239
223
var line2 = "" ;
240
224
// get most overlapped sub
241
225
sub2 . forEach ( ( line ) => {
@@ -247,40 +231,38 @@ export default class Netflix extends BaseVideo {
247
231
sub2 . sort ( ( a , b ) => a . overlap - b . overlap ) ;
248
232
249
233
// if sub2 has no overlap, use sub1
250
- mergedSub . push ( line1 )
234
+ mergedSub . push ( line1 ) ;
251
235
if ( sub2 . length && 0 < sub2 [ 0 ] . overlap ) {
252
236
line2 = sub2 [ 0 ] ;
253
- let line1Copy = { ...line1 }
254
- line1Copy . text = line2 . text ;
255
- mergedSub . push ( line1Copy )
237
+ let line1Copy = { ...line1 } ;
238
+ line1Copy . text = line2 . text ;
239
+ mergedSub . push ( line1Copy ) ;
256
240
}
257
241
}
258
-
242
+
259
243
mergedSubMeta . subtitles = mergedSub ;
260
244
return mergedSubMeta ;
261
245
}
262
246
263
-
264
247
static encodeMergedSubtitles ( subMeta ) {
265
- var xmlDoc = subMeta . xmlDoc
266
- var subtitles = subMeta . subtitles
248
+ var xmlDoc = subMeta . xmlDoc ;
249
+ var subtitles = subMeta . subtitles ;
267
250
const body = xmlDoc . getElementsByTagName ( "body" ) [ 0 ] ;
268
251
let div = body . getElementsByTagName ( "div" ) [ 0 ] ;
269
252
if ( ! div ) {
270
253
div = xmlDoc . createElement ( "div" ) ;
271
254
body . appendChild ( div ) ;
272
255
}
273
256
274
-
275
- subtitles . forEach ( sub => {
257
+ subtitles . forEach ( ( sub ) => {
276
258
const p = xmlDoc . createElement ( "p" ) ;
277
259
p . setAttribute ( "xml:id" , `subtitle${ subtitles . indexOf ( sub ) + 1 } ` ) ;
278
260
p . setAttribute ( "begin" , `${ sub . start } t` ) ;
279
261
p . setAttribute ( "end" , `${ sub . end } t` ) ;
280
- p . setAttribute ( "region" , "region0" ) ;
262
+ p . setAttribute ( "region" , sub . region ) ;
281
263
282
264
const span = xmlDoc . createElement ( "span" ) ;
283
- span . setAttribute ( "style" , "style1 " ) ;
265
+ span . setAttribute ( "style" , "style0 " ) ;
284
266
span . textContent = sub . text ;
285
267
286
268
p . appendChild ( span ) ;
@@ -293,18 +275,5 @@ export default class Netflix extends BaseVideo {
293
275
294
276
static encodeSubtitle ( subtitles ) {
295
277
return subtitles ;
296
- }
297
- }
298
- // request track
299
- // parse
300
- // lang video id current
301
- // video audio and get lang
302
- // video lang to request audio
303
- //
304
- //
305
- // no tittle or only one title
306
- // current lang and off
307
- // target lang and off
308
- // current lang and source lang
309
- // current lang and target lang
310
-
278
+ }
279
+ }
0 commit comments