花了不少時間,總算把 Netflix 雙字幕作出來了~~~(撒花)
使用方式與之前 3waNetflix V1.8 以前都相同,啟動後
我把原本官方的字幕畫面,複製一份放到分頁 tab 裡的「字幕選擇」空間
在字幕選擇可以切換音訊、主要字幕、次要字幕
主要字幕就與之前的設定方式一樣
第二行的字幕通常大家會用英文吧,所以預設的字體大小跟字距稍微調小調宅些
V1.9 算是滿大的更新,作起來相當累人...
(2022-09-24) V1.9 版:
1、加回 jQuery 3.6.0
2、雙字幕功能
3、第二字幕 UI 設定介面
4、加入字型選擇
5、使用者正在「準備切換下一集」或「選集數」 或 「調影片速度」,停用設定 UI
6、UI 控制區,只有滑鼠進入的高度 70% 切入才有效,不然螢幕太小時,調時間軸也會一直檔到
7、原 3wanetflix 的字幕要插在 video 裡,這樣全螢幕才有作用,現在發現要再 video 外層的 div 才行
8、修正字幕不需要強制大寫:font-variant: small-caps
9、主要字幕、次要字幕可各自設定樣式
10、如果有下一集、工作人員名單、返回瀏覽、略過簡介,要可以點
11、已知問題:
(1). 啟動 3waNetflix V1.9 版後,下方進度條會失蹤,需要用滑鼠滑過下排音量控制才會出現
(2). 當滑鼠進入下排控制區時,字幕功能就會暫停,請往上移開
(3). 已知有些改 1080p 或是部分字幕是「圖片型字幕」出字會異常,之後再研究
然後來寫點開發過程的心痠:
禮拜一主管 Jeffrey 聽說我有在作 Netflix Plugin 可以放大、改字幕顏色的功能,問說能不能加個
雙字幕,這樣方便學英文還是其他語言。自己也是很期待這個功能開發能不能實現~~
初期開發在看 Netflix 一直按 F12 ,從 console 裡面找看看有哪些資料能用,但一直都找不到
與 track、語言有關的資訊,localstorage 也沒有
嘗試在 window 找了許久,沒找到有用的資訊可以幫助雙字幕
然後羽山想嘗試找出網頁裡會呼叫字幕連結的位置,比如點了「英語字幕」會抓到一個 xml 的位置
然後利用 source 攔截,一步一步看,就一直進入 window.a000 的虛空
看不太出來他加密後的邏輯怎麼解算
羽山之前有找到一篇網友提供 Request 加載的 xhr ,是直接改寫 ajax 呼叫方式:
From : https://stackoverflow.com/questions/25335648/how-to-intercept-all-ajax-requests-made-by-different-js-libraries
(function(open) {
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
console.log(url)
this.addEventListener("readystatechange", function() {
console.log(this.readyState); // this one I changed
}, false);
open.call(this, method, url, async, user, pass);
};
})(XMLHttpRequest.prototype.open);
如此一來,任何 ajax 呼叫後,都可以從中提取曾呼叫的 url 網址~~~
或是重新註冊 JSON.parse ,把 global JSON.parse 改寫成一樣會作 json decode 但可以把值寫到自己想要的變數,測錄 json 包。
這二個作法可能在 chrome extension V2 比較有機會達成,自從升 V3 以後,直接變成 background service_worker,初期光 access html dom 就有點中混亂,得讓使用者自己 chrome.action.onClicked
這個作法是參考另一個 NflxMultiSubs 開源專案裡的一段 code
// Hook JSON.parse() and attempt to intercept the manifest
// For cadmium-playercore-6.0022.710.042.js and later
const hookJsonParseAndAddCallback = function(_window) {
const _parse = JSON.parse;
_window.JSON.parse = (...args) => {
const result = _parse.call(JSON, ...args);
if (result && result.result && result.result.movieId) {
const movieId = result.result.movieId
console.log(`Intercepted manifest ${movieId}`);
window.__NflxMultiSubs.updateManifest(result.result);
}
return result;
};
};
hookJsonParseAndAddCallback(window);
寫的真的很猛~~學習到這個技術真是痛快啊XD,以前怎麼都沒想這麼作...♥
如果 V3 可以用就好了 :( 權限不知道要怎麼加才辦的到...
熬夜試了一、二個晚上沒成功,就繼續照自己原來的土炮繼續作 XD
一個大膽的想法,就是如果一直按「繁體中文、英文」,反覆一直按呢...
沒錯,按完後可以拿到「繁中、英文」而且只有第一次他會xhr載字幕,稍微頓一下
之後切換都相當順暢
從這個角度切入後,要克服的就是不能讓「選字幕的位置不見」,Netflix 有個奇怪的設計
他不需要的東西,直接從畫面 remove 掉,不是隱藏呦,直接 remove...
比如你在選「某一集」的選單開啟,這時他的「時間軸」看似隱藏,實際也是 remove 掉
整個 dom 元件不給你操作,笑死...
所以土炮仔羽山的作法,就是讓一個屁孩,一直狂點右下角的字幕,只要一直點就不會消失...
然後再一直反覆點你要的字幕(繁中、英文),點完再收集到記憶體位置,然後再用自己的字體樣式
重新拚裝後,插到網頁裡的畫面
測試的範例程式:
這段程式是我最初測雙字幕寫的
前面那堆 js 就 jQuery-3.6.0.min.js
流程:
1、載入 jQuery
2、隱藏原本的字幕(因為要用雙字幕合併後的字幕)
3、隱藏右下角字幕選單(註解掉,沒執行,這樣大家比較明白)
4、跑一個 Interval ,裡面再跑三組 timeout
(1) 一直按右下角那個「字幕」的按鈕,讓選單出現
(2) 按「繁體中文」,等100ms
(3) 取得繁體中文字的內容,再按「英文」,再等100ms
(4) 將「取得的中、英文」合併成一組文字,如果這組跟之前一樣,就不顯示了
(5) 在 body 插一個浮動 div[reqc='myword'] ,然後把字的樣式設一設就顯示
(話說 Netflix 的工程師好像很愛用 屬性 data-uia,而羽山自己很愛用 reqc)
這個測試看來是有成功了~那麼可以土炮達成就成功了一半~~~(大心~撒花)
接下來就偷工簡料,重新引入 jquery ,之前自己寫 jquery-light 還是沒辦法把事件寫的那麼完美
重新把肥肥的 jquery-3.6.0.min.js 塞入程式的最開始,因為不知道怎麼用 js 檔引入,所以就放
在程式碼的最前面...(笑)
塞入整包的 jquery 真的事半工備...
可惜短命的 jquery-light (By 3wa FeatherMountain...),換成正牌 jquery 幾乎沒改啥 Code 就直上,當初努力想實作還是很有價值~
短命的 jquery-light 也是從 v0.5~v1.8 很努力工作了
接下來講到哪了~
對了~講到拷貝字幕 UI
拷貝後的字幕 UI 可以省下很多自己刻的時間
但拷貝完後的字幕選單,要確保這是單向使用,原來的官方選單就隱藏放到 z-index:-2 藏起來
然後在點擊時,先把「勾勾」的SVG 樣式、點到的亮字、暗字 class 都記下來,當使用者在點擊時
就註冊新的勾勾,註冊亮、暗字,然後把使用者點擊「主要字幕、次要字幕」寫到 localstorage、global window 裡。
這裡羽山用 my_netflix_sub1、my_netflix_sub2
神奇的點雙字幕小人就會一直幫你點這二個字幕,點個不停。
有些情況是不能點的,剛才提到如果使用者想使用時間軸、換下一集、選集、全螢幕、調整影片速度…
當滑鼠觸及這些東西,就不能讓小人再點擊字幕按鈕、字幕選擇
如程式碼 1295 行附近,就是在指這個情境
當一切都很美好時,發現全螢幕一點下去,又變成地獄了…
所以改寫 netflix 的全螢幕按鈕
新的全螢幕寫法參考:https://usefulangle.com/post/12/javascript-going-fullscreen-is-rare
這寫的還真是符合我需要的功能,直接把這段拿來用,然後全螢幕整個 body 就接近90%成就達成^_^~
目前美中不足就是因為小人一直在點字幕,所以 timeline 時間軸會很不好按
於是把整個下排控制 bar 作了一個 body onmousemove 的區塊,滑鼠一進去,小人就不點了
此時滑鼠只要經過原本下排任意一個功能,時間軸就會顯示出來
也因為小人不幫你點,所以滑鼠在下排控制區,就多了一行文字提示...
滑鼠在下方控制區時,雙字幕將無法正常使用
因為小人沒在點了齁
總之還有一個大魔王要克服,就是像「2077 電馭叛客:邊緣行者」裡的「日文字幕」
他是使用「圖片型」的字幕~在舊的 V1.8 我有實作可以調整大小、高度,但樣式都無法調整
在這個版本我目前爆肝有點嚴重,最近都二、三點才睡,所以完全沒修他...
選了圖片型的日文字幕,目前就會變成這樣
或是使用者安裝了嘻花的 1080p extension
老實說我不知道那個 1080p 到底畫質有沒有比較好,但載入後都是抓圖片型的字幕,相當奇妙
以上大概就是我目前 V1.9 Beta 的一些心得
2022-09-26 補充:
昨晚在奮戰圖片型的字幕,$("svg image") 的 href,值如:
blob:http://www.netflix.com/..........................................
這個用法經過一連串查找相關的資料,最接近的使用方法是:
URL.createObjectURL(blob);
但 blob:URL 這種 data urls 是上面那行執行完的成果
且他在畫入 image 元件後,應該馬上就又執行 URL.RevokeObjectURL 把顯示後的結果回收
土炮的字幕來回切換的過程,就無法延續使用 blob 的內容,就算用 jquery clone obj 也
無法正確的把 svg image 複製出來在另建的空間裡呈現
思來想去到凌晨四點還睡不著:( 早上八點多忽然驚醒,眼看要遲到,不如就請特休一小時休息好好準備上班,就在請完假後,腦中忽然出現一個想法~~
「background service 到底能不能 inject 啊…就算他有嚴格的 meta Content-Security-Policy」
但是否就作到滴水不漏!?
這個想法迫使我再一次挑戰 inject script
想法很簡單,利用 <img onerror=""> 注入 script 看看呢!?
然後要注到他的 window 也能覆蓋齁~如果注的下去,那 extension V2 的寫法,就可以整個搬來 V3 了
所以先假設...
<img onerror="alert('test');" src="//59-126-75-42.hinet-ip.hinet.net/xxxx">
隨便加載一張失敗的圖,載入後,居然有跳出 alert 呢~~~
那麼...
<img onerror="URL.createObjectURL = null;"> 看看會不會整個日文字幕都壞掉?
咦,失敗了
那麼
<img onerror="window.URL.createObjectURL = null;"> 呢!?
靠B,成功了~~~笑死
原來還可以這樣硬幹一張假圖,onerror 去把整個網站的 js 注成我插的啊....囧>
這樣就有趣了,我只要再註冊一次他的 URL.createObjectURL、JSON.parse 就什麼事都能作了啊
之前這麼土炮還真辛苦 XD
所以你各位裝來路不明的 extension 最好看仔細他們寫了什麼鬼東西啊...
為了讓「流過的圖片blob」可以被攔劫,所以我作了一點點擴充,我把 blob 的內容用
FileReader 開啟,然後在 body 放一個可以藏 base64 的 div 作為資料交換空間
var reader = new FileReader(); // 加載 FileReader
reader.onload = function() {
var dom = document.querySelectorAll("div[reqc='my_netflix_imageSubsB64']");
if(dom.length==0) return;
var dataUrl = reader.result;
var b64 = dataUrl;
var m = dom[0].innerHTML.split('|||3WA_BR|||');
//字幕|||3WA|||時間
m.push(b64+"|||3WA|||"+new Date().getTime());
m = m.slice(-10); /* keep last 10 */
dom[0].innerHTML = m.join('|||3WA_BR|||');
};
這個 div html 的內容會保留最後抓到的十張字幕影像
內容為
base64|||3WA|||當時的時間
|||3WA_BR||| --> 這是斷行
base64|||3WA|||當時的時間
|||3WA_BR||| --> 這是斷行
大概是這樣,出字幕的時候,大概只需要針對還持續更新的字幕(大概0.5秒內吧,最多二組不同內容的字幕)
這樣應該就可以把圖片的字幕作出來,擺在我想放的空間裡
日文的圖片字幕常常滿多特別效果,之後再考慮如果屬特殊位置的字,再作調整
總算作出 V2.0 版了,這次 UI 調整後較為緊緻一些
然後原來滑鼠移入中間就會出現,改成要點選右下原來的字幕按鈕
由於重新定義了 URL.createObjectURL 昨晚測下來,發現如果回 Netflix 首頁,再重新點回影片播放頁
都會出現 Netflix 錯誤異常,需要重整瀏覽器,把新加的定義註解掉後,就不會當機
所以在 appClass.method 加入 fixOrinURL:
狀況一:
如果畫面不是播放頁時,且發現 URL.createObjectURL 有改寫,就還原原版
狀況二:
如果使用者按「下一集、切換其他集」這裡畫面仍是播放頁,但仍會當機,解決方法就是把這二個按鈕,再 bind click,按下去就還原原版
經過這一波操作就不會 netflix crash
V2.0 作了以下修正調整:
(2022-09-27) V2.0 版:
1、使用注意事項獨立一個 tab 分頁
2、圖片型字幕閃耀問題修正
3、當畫面靜置一段時間,會發生無法回上頁、右上的問題反應也無法點選
4、調整 UI 時,有時會失效
5、無人說話時,字幕退場的時間不精準
6、如果使用者在調整時間軸,延長消失時間(6秒)
7、全螢幕時,滑鼠沒移動一段時間後要自動隱藏(6秒)
8、縮小設定畫面可以觸發顯示的範圍
9、調整畫面很容易滑鼠移動就消失,將 mouseout 改成 mouseleave 後較為正常
10、UI 控制區,只有滑鼠進入的高度 36% 切入才有效,不然螢幕太小時,調時間軸也會一直檔到
11、UI 控制區,改成點右下角語言,並加入嘻花效果
12、英文字幕,二行字會黏在一起
13、UI 控制區畫面改緊緻,滑塊加大
14、播放結束後,按下一集異常
15、直接按下一集的三角按鈕
(V2.0 畫面) 字幕語言選單
(V2.0 畫面) 主要字幕
(V2.0 畫面) 次要字幕
(V2.0 畫面) 注意事項獨立收在一起
有興趣一起研究的同好也歡迎加入
真是美好的一天,能作出來真的很開心~~~
2022-09-27 09:41 羽山筆
(2022-09-29) V2.1 版:
1、直接按下一集的三角按鈕,會發生異常
2、全螢幕時,立刻隱藏下方控制區
3、影片暫停時,暫停取新字幕
4、按「空白鍵」可以控制「播放、停止」
5、按「o 或 O」(Open) 可以「喚出字幕選單」
6、3WA Icon 、x_close.png 改成 base64 png
7、滑鼠移出設定 UI 視窗,等 1 秒再隱藏,重新滑入就停止計時,不然太容易不見
(2022-10-05) V2.2 版:
1、全螢幕回上一頁,要停止全螢幕
2、滑鼠進入下方 Control 區,時間軸需要顯示,不用透過滑過聲音鈕(總算找到解法了~撒花)
3、如果第二字幕有字幕檔,直接讀出字幕檔顯示
4、安裝後 3waNetflix 後,自動啟動,不用手點了
5、(圖片型字幕) 當滑鼠進入下方控制區,包含進度條,雙字幕將無法正常使用,請往上移開
6、主字幕可正常依雙行字顯示
7、音訊切換,造成主字幕勾勾跑版
(2022-10-08) V2.3 版:
1、停用自動顯示進度條(太容易造成影片停住)
2、影片標題下移、加一點透明度
3、當切換音訊,再切回原來的音訊,沒有正常執行
(2022-10-19) V2.4 版:
1、76、寬螢幕如果遇到超長字幕,偶爾會透在底下(如:迷霧:第6集 剩 1:30 左右)
2、77、當影片播放進入最後 1%,停止翻譯字幕
3、78、當影片暫停時,停止翻譯字幕
4、79、全螢幕按鈕應避免 Focus ,不然空白鍵會觸發
5、80、熱鍵 → 下10秒
6、81、熱鍵 ← 上10秒
7、82、熱鍵 M 消音、有聲
8、83、熱鍵 F 全螢幕
9、83、熱鍵 S 略過片頭
10、84、熱鍵 N 下一集
V2.4 版主要是把一些常用的功能作成鍵盤熱鍵
同時也把說明的部分加強
(2022-11-05) V2.5 版:
1、85、熱鍵 S 發現 bug,有時按下後,會回到片頭
2、86、使用者可自定自動跳過片頭
3、87、使用者可自定自動跳至下一集
4、88、電影,在片尾時「返回瀏覽」,如果是全螢幕,離開全螢幕
V2.5 版,加入自動功能,可以自動跳過片頭、自動跳下一集
(2022-11-10) V2.6 版:
1、89、清字幕 localStorage 不小心清到字幕設定
(2022-11-10) V2.7 版:
1、90、六人行「英文 (CC)」 字幕,有全大寫的問題
V2.7 有網友許願,想看六人行時,英文字幕不要全大寫
這部分只針對「英文 (CC)」字幕處理,所有字轉成小寫,用 . 裁切英文句子,依每一句字首首字符合[a-z]
就轉成大寫,另外若有遇到 “.....“ 這種六人行對話很常見的強調區,裡面全改大寫。
詳見:appClass.method.fix_sentence_first_letter_upper_case
(2022-11-10) V2.8 版:
1、91、修正自動下一集會 crash 的問題
2、92、自動跳過片頭,才不會發生出現跳過片頭,使用者點了進度條或切頁,數量變 0 的問題
羽山為了取圖片型字幕,重新包裝後的「URL.createObjectURL」,經過包裝後的函式,當回到 Netflix 主頁就會發生抱歉,服務中斷,之前一直沒明白失敗的原因~~
後來的作法就是在每一個有機會回到首頁的按鈕,再把原本的「URL.createObjectURL」換回原生的
但有網友表示,自動播放下一集,仍會發生服務中斷,因為 Netflix 內鍵的自動下一集是靠程式觸發,不屬
於任何按鈕
羽山的個人設定,很早之前就取消這二個預設勾選的設定,所以一直沒發現這個 crash 的原因
於是再次認真研究如何安全的包裝「URL.createObjectURL」,反覆的在播放頁註冊 URL.createObjectURL,某次在回到首頁的時候,看到出現 meta policy 的錯誤提示 (可惜沒有截圖)
大概就是 blob content not allow
netflix 的畫面設計的很有意思,所有的換頁似乎都是靠 ajax 在重讀資料,沒有作到所謂的更換連結
所以改過的 URL.createObjectURL 回到首頁,如果函式無法正常使用就會出現錯誤視窗
經過一連串測試,我在 361 行包了 try catch ,原本要讀出「圖片型字幕」的部分,在首頁如果發生問題,這段就 pass 掉,另外原本 URL.createObjectURL 會 return 值,在原來的 my_netflix_URLCREATEOBJECTURL 就 return 值,再次嘗試會不會 crash...
不過私心覺得還是不要勾 netflix 內鍵的自動播下一集,自製的比較好用 XD
看來應該是修好了,V2.8 版的自動下一集總算不會再 crash 了 (大心,撒花)
希望就此可以完全根除 netflix 服務中斷的問題
下一版該認真面對下方進度條了嗎...