-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from 2jun0/dev
Dev: 1.3.2
- Loading branch information
Showing
23 changed files
with
391 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,38 @@ | ||
<p align="center"> | ||
<img src="https://github.com/2jun0/yt-subtitle-filter/blob/main/chrome/asset/logo/logo-big-128.png" width="75" height="75"/> | ||
<img src="chrome/asset/logo/logo-big-128.png" width="75" height="75"/> | ||
</p> | ||
|
||
<h1 align="center">YouTube Subtitle Filter</h1> | ||
|
||
We support Korean version! | ||
follow the below page to read it in multiple languages. | ||
|
||
[한국어 보기](README_KO.md) | ||
[한국어](README_KO.md) | ||
|
||
Add a subtitle tag language that you want on the video thumbnail in the YouTube. | ||
|
||
## Download | ||
|
||
- [Chrome Web Store](https://chrome.google.com/webstore/detail/Youtube-subtitle-filter/onmelgncdnoihoaopmkcacadlmjmcehd) | ||
|
||
- [Firefox Add On](https://addons.mozilla.org/ko/firefox/addon/youtube-subtitle-filter) | ||
|
||
--- | ||
|
||
## Showcase | ||
|
||
 | ||
|
||
 | ||
|
||
--- | ||
|
||
## Customize | ||
|
||
- You can customize tag color in popup menu (background and text color) | ||
- You can resize the subtitle tags | ||
- You can search for subtitles by grouping regions. (ex en-US + en-GB) | ||
<img src="chrome/asset/showcase/showcase_popup.jpg" align="right" width="200"> | ||
|
||
  | ||
- You can customize tag color in popup menu (background and text color) | ||
|
||
## Download | ||
- You can resize the subtitle tags | ||
|
||
- [Chrome Web Store](https://chrome.google.com/webstore/detail/Youtube-subtitle-filter/onmelgncdnoihoaopmkcacadlmjmcehd) | ||
- [Firefox Add On](https://addons.mozilla.org/ko/firefox/addon/youtube-subtitle-filter) | ||
- You can search for subtitles by grouping regions. (ex en-US + en-GB) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,37 @@ | ||
<p align="center"> | ||
<img src="https://github.com/2jun0/yt-subtitle-filter/blob/main/chrome/asset/logo/logo-big-128.png" width="75" height="75"/> | ||
<img src="chrome/asset/logo/logo-big-128.png" width="75" height="75"/> | ||
</p> | ||
|
||
<h1 align="center">유튜브 자막 필터</h1> | ||
|
||
README 페이지를 영문로 보시려면 아래를 참고하세요. | ||
|
||
[영문 보기](README.md) | ||
[영문](README.md) | ||
|
||
유튜브 내 동영상 썸네일에 원하는 언어의 자막 태그를 추가해줍니다. | ||
|
||
## 다운로드 | ||
|
||
- [Chrome 웹 스토어](https://chrome.google.com/webstore/detail/Youtube-subtitle-filter/onmelgncdnoihoaopmkcacadlmjmcehd) | ||
|
||
- [Firefox Add On](https://addons.mozilla.org/ko/firefox/addon/youtube-subtitle-filter) | ||
|
||
--- | ||
|
||
## 예시화면 | ||
|
||
 | ||
|
||
 | ||
|
||
--- | ||
|
||
## 사용자 설정 | ||
|
||
- 팝업 메뉴에서 태그의 색상을 선택할 수 있습니다. (배경색상, 글자색상) | ||
- 태그의 크기를 조절 할 수 있습니다. | ||
- 지역언어 통합 기능으로 여러지역으로 나눠진 자막을 하나로 검색할 수 도 있습니다. (예: 영어(영국) + 영어(미국)) | ||
<img src="chrome/asset/showcase/showcase_popup.jpg" align="right" width="200"> | ||
|
||
  | ||
- 팝업 메뉴에서 태그의 색상을 선택할 수 있습니다. (배경색상, 글자색상) | ||
|
||
## 다운로드 | ||
- 태그의 크기를 조절 할 수 있습니다. | ||
|
||
- [Chrome 웹 스토어](https://chrome.google.com/webstore/detail/Youtube-subtitle-filter/onmelgncdnoihoaopmkcacadlmjmcehd) | ||
- [Firefox Add On](https://addons.mozilla.org/ko/firefox/addon/youtube-subtitle-filter) | ||
- 지역언어 통합 기능으로 여러지역으로 나눠진 자막을 하나로 검색할 수 도 있습니다. (예: 영어(영국) + 영어(미국)) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,129 @@ | ||
import { FIELD_VIDEO_LANGS, loadData, saveData } from '../storage.js'; | ||
import { | ||
getLangListUrl, | ||
getTabId, | ||
getYTVideoId, | ||
requestAysnc, | ||
} from '../common.js'; | ||
import { | ||
FIELD_VIDEO_LANG_LIST_URL, | ||
loadDataAsync, | ||
saveData, | ||
} from '../storage.js'; | ||
|
||
let TabId; | ||
getTabId(tabId => (TabId = tabId)); | ||
|
||
let waittingIntervals = {}; | ||
|
||
// Load YouTube Video Iframe Url | ||
function loadYtPlayer(videoId, callback) { | ||
// Already exists | ||
if (document.getElementById(`player-${videoId}`)) return; | ||
|
||
let playerElm = document.createElement('div'); | ||
playerElm.id = `player-${videoId}`; | ||
document.body.appendChild(playerElm); | ||
|
||
let ytPlayer = new YT.Player(`player-${videoId}`, { | ||
videoId: videoId, | ||
playerVars: { | ||
autoplay: 1, | ||
cc_load_policy: 1, | ||
suggestedQuality: 'tiny', | ||
}, | ||
events: { | ||
onReady: ({ target, data }) => { | ||
ytPlayer.pauseVideo(); | ||
|
||
// Wait until the option is loaded. | ||
let count = 0; | ||
let intervalId = setInterval(() => { | ||
let ccList = ytPlayer.getOption('captions', 'tracklist'); | ||
// over 60 sec => video doesn't have any captions | ||
if (ccList || count > 600) { | ||
clearInterval(intervalId); | ||
callback(ytPlayer); | ||
return; | ||
} | ||
count++; | ||
}, 100); | ||
async function loadYtPlayerAsync(videoId) { | ||
return new Promise(resolve => { | ||
// Already exists | ||
if (document.getElementById(`player-${videoId}`)) return; | ||
|
||
let playerElm = document.createElement('div'); | ||
playerElm.id = `player-${videoId}`; | ||
document.body.appendChild(playerElm); | ||
|
||
let ytPlayer = new YT.Player(`player-${videoId}`, { | ||
videoId: videoId, | ||
playerVars: { | ||
autoplay: 1, | ||
cc_load_policy: 1, | ||
suggestedQuality: 'tiny', | ||
}, | ||
events: { | ||
onReady: ({ target, data }) => { | ||
ytPlayer.pauseVideo(); | ||
resolve(); | ||
}, | ||
}, | ||
}, | ||
}); | ||
}); | ||
} | ||
|
||
function checkLangCodes(videoId, langs, callback) { | ||
const langCodeCheck = RegExp(`(${langs.join('|')})`); | ||
const vLangField = `${FIELD_VIDEO_LANGS}_${videoId}`; | ||
async function createWattingIntervalAsync(videoId) { | ||
const vLangListUrlField = `${FIELD_VIDEO_LANG_LIST_URL}_${videoId}`; | ||
|
||
loadData(vLangField, items => { | ||
if (items[vLangField]) { | ||
const langCodes = items[vLangField].langCodes; | ||
const searchTime = items[vLangField].searchTime; | ||
return new Promise(resolve => { | ||
let count = 0; | ||
waittingIntervals[videoId] = { | ||
id: setInterval(() => { | ||
// during 1 sec, wait web request | ||
if (count >= 60) { | ||
resolve(); | ||
return; | ||
} | ||
|
||
// After one day, the search starts again. | ||
if (Date.now() - searchTime < 86400000) { | ||
callback(langCodeCheck.test(langCodes)); | ||
return; | ||
} | ||
} | ||
// when find timedtext url, save url and return | ||
if (waittingIntervals[videoId].langListUrl) { | ||
saveData(vLangListUrlField, waittingIntervals[videoId].langListUrl); | ||
resolve(waittingIntervals[videoId].langListUrl); | ||
return; | ||
} | ||
count++; | ||
}, 1000), | ||
}; | ||
}).then(langListUrl => { | ||
// remove yt player and interval | ||
document.getElementById(`player-${videoId}`).remove(); | ||
clearInterval(waittingIntervals[videoId].id); | ||
return langListUrl; | ||
}); | ||
} | ||
|
||
// The subtitle search start | ||
loadYtPlayer(videoId, ytPlayer => { | ||
let langCodeList = ( | ||
ytPlayer.getOption('captions', 'tracklist') || [] | ||
).map(cc => cc.languageCode); | ||
async function getLangListUrlAsync(videoId) { | ||
const vLangListUrlField = `${FIELD_VIDEO_LANG_LIST_URL}_${videoId}`; | ||
|
||
let hasSubtitles = false; | ||
langCodeList.forEach(langCode => { | ||
hasSubtitles ||= langCodeCheck.test(langCode); | ||
}); | ||
return loadDataAsync(vLangListUrlField).then(items => { | ||
let langListUrl = items[vLangListUrlField]; | ||
|
||
saveData(vLangField, { | ||
langCodes: langCodeList.join(','), | ||
searchTime: Date.now(), | ||
if (langListUrl) { | ||
// already saved | ||
return langListUrl; | ||
} else { | ||
// load Youtube Player (wait until the "onReady" event occurs) | ||
return loadYtPlayerAsync(videoId).then(() => { | ||
return createWattingIntervalAsync(videoId); | ||
}); | ||
// remove yt player | ||
document.getElementById(`player-${videoId}`).remove(); | ||
} | ||
}); | ||
} | ||
|
||
callback(hasSubtitles); | ||
}); | ||
async function hasSubtitlesAsync(videoId, langs) { | ||
const langCodeCheck = RegExp(`lang_code="(${langs.join('|')})"`); | ||
|
||
return getLangListUrlAsync(videoId).then(langListUrl => { | ||
if (!langListUrl) { | ||
return false; | ||
} else { | ||
return requestAysnc('GET', langListUrl).then(res => { | ||
return langCodeCheck.test(res); | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
// Get web Request | ||
chrome.webRequest.onBeforeRequest.addListener( | ||
details => { | ||
let videoId = getYTVideoId(details.url); | ||
let langListUrl = getLangListUrl(details.url); | ||
|
||
if (waittingIntervals[videoId]) | ||
waittingIntervals[videoId].langListUrl = langListUrl; | ||
}, | ||
{ tabId: TabId, urls: ['*://*.youtube.com/api/timedtext*'] }, | ||
['requestBody'], | ||
); | ||
|
||
// Get content script message | ||
chrome.runtime.onMessage.addListener(({ type, value }, sender, sendRes) => { | ||
if (type === 'has-subtitles') { | ||
let langs = value.langs; | ||
let videoId = value.videoId; | ||
|
||
checkLangCodes(videoId, langs, sendRes); | ||
hasSubtitlesAsync(videoId, langs).then(sendRes); | ||
} | ||
return true; // Inform Chrome that we will make a delayed sendResponse | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
export function getYTVideoId(url) { | ||
return url.match(/\?v=([\w-]+)/)[1]; | ||
} | ||
|
||
export function getLangListUrl(timedtextUrl) { | ||
// remove '&fmt=json3&xorb=2&xobt=3&xovt=3' and add '&type=list' | ||
return timedtextUrl.substr(0, timedtextUrl.length - 31) + '&type=list'; | ||
} | ||
|
||
// Get current tab id | ||
export function getTabId(callback) { | ||
const intervalId = setInterval(() => { | ||
chrome.tabs.query({ currentWindow: true }, tabs => { | ||
if (tabs) { | ||
callback(tabs[0].id); | ||
clearInterval(intervalId); | ||
} | ||
}); | ||
}, 1); | ||
} | ||
|
||
export function requestAysnc(method, url, msg = null) { | ||
return new Promise(resolve => { | ||
let xhr = new XMLHttpRequest(); | ||
xhr.onreadystatechange = function () { | ||
if (this.readyState == this.DONE) { | ||
if (this.status == 200) { | ||
resolve(this.responseText); | ||
} | ||
} | ||
}; | ||
|
||
xhr.open(method, url); | ||
console.log(method, url, msg); | ||
xhr.send(msg); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.