Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Subadub is a browser extension for Chrome and Firefox that enhances Netflix subt

- Subtitles are displayed as selectable text, so you can copy+paste them to make flash cards and look up words in a dictionary (e.g. using the Yomichan or Rikaikun extensions for Japanese)
- Full subtitles for a video can be downloaded in SRT format for personal study/review
- Add keyboard shortcut for quickly changing between subtitle languages

## Installation

Expand Down
2 changes: 1 addition & 1 deletion dist/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Subadub",
"description" : "Enhanced Netflix subtitles for foreign language study",
"version": "0.1.11",
"version": "0.1.12",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
Expand Down
104 changes: 104 additions & 0 deletions dist/page_script.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
const TRACK_ELEM_ID = 'subadub-track';
const DOWNLOAD_BUTTON_ID = 'subadub-download';
const CUSTOM_SUBS_ELEM_ID = 'subadub-custom-subs';
const SHORTCUT_A_DROPDOWN_ID = 'subadub-shortcut-a';
const SHORTCUT_Z_DROPDOWN_ID = 'subadub-shortcut-z';

const NETFLIX_PROFILES = [
'heaac-2-dash',
Expand All @@ -25,6 +27,64 @@
'BIF320'
]

// Add language shortcut mapping
let LANGUAGE_SHORTCUTS = {
'a': 'en', // English
'z': 'ko' // Korean
};

// Store available languages for shortcuts
let availableLanguages = new Set();

function updateLanguageShortcuts() {
const shortcutA = document.getElementById(SHORTCUT_A_DROPDOWN_ID);
const shortcutZ = document.getElementById(SHORTCUT_Z_DROPDOWN_ID);

if (shortcutA && shortcutZ) {
LANGUAGE_SHORTCUTS['a'] = shortcutA.value;
LANGUAGE_SHORTCUTS['z'] = shortcutZ.value;
}
}

function populateLanguageDropdowns(tracks) {
// Update available languages set
availableLanguages = new Set(tracks.map(track => track.language));

// Get or create dropdowns
let shortcutADropdown = document.getElementById(SHORTCUT_A_DROPDOWN_ID);
let shortcutZDropdown = document.getElementById(SHORTCUT_Z_DROPDOWN_ID);

if (!shortcutADropdown || !shortcutZDropdown) {
return; // Dropdowns not created yet
}

// Clear existing options
shortcutADropdown.innerHTML = '';
shortcutZDropdown.innerHTML = '';

// Add options for each available language
availableLanguages.forEach(lang => {
const track = tracks.find(t => t.language === lang);
const optionText = track.languageDescription + (track.isClosedCaptions ? ' [CC]' : '');

// Add to A shortcut dropdown
const optionA = document.createElement('option');
optionA.value = lang;
optionA.textContent = optionText;
shortcutADropdown.appendChild(optionA);

// Add to Z shortcut dropdown
const optionZ = document.createElement('option');
optionZ.value = lang;
optionZ.textContent = optionText;
shortcutZDropdown.appendChild(optionZ);
});

// Set current values
shortcutADropdown.value = LANGUAGE_SHORTCUTS['a'];
shortcutZDropdown.value = LANGUAGE_SHORTCUTS['z'];
}

const trackListCache = new Map(); // from movie ID to list of available tracks
const webvttCache = new Map(); // from 'movieID/trackID' to blob
let urlMovieId; // this is mis-named now, it's pulled from the HTML
Expand Down Expand Up @@ -290,11 +350,30 @@
downloadSRT();
}, false);

// Create shortcut dropdowns
const shortcutADropdown = document.createElement('select');
shortcutADropdown.id = SHORTCUT_A_DROPDOWN_ID;
shortcutADropdown.style.cssText = 'color: black; margin: 5px';
shortcutADropdown.title = 'Language for A key';
shortcutADropdown.addEventListener('change', updateLanguageShortcuts);

const shortcutZDropdown = document.createElement('select');
shortcutZDropdown.id = SHORTCUT_Z_DROPDOWN_ID;
shortcutZDropdown.style.cssText = 'color: black; margin: 5px';
shortcutZDropdown.title = 'Language for Z key';
shortcutZDropdown.addEventListener('change', updateLanguageShortcuts);

const panelElem = document.createElement('div');
panelElem.style.cssText = 'position: absolute; z-index: 1000; top: 0; right: 0; font-size: 16px; color: white; pointer-events: auto';
panelElem.appendChild(toggleDisplayButtonElem);
panelElem.appendChild(selectElem);
panelElem.appendChild(downloadButtonElem);
panelElem.appendChild(document.createElement('br'));
panelElem.appendChild(document.createTextNode('Keyboard shortcut "a": '));
panelElem.appendChild(shortcutADropdown);
panelElem.appendChild(document.createElement('br'));
panelElem.appendChild(document.createTextNode('Keyboard shortcut "z": '));
panelElem.appendChild(shortcutZDropdown);

const containerElem = document.createElement('div');
containerElem.id = SUBS_LIST_ELEM_ID;
Expand All @@ -306,6 +385,9 @@
updateToggleDisplay();
disableDownloadButton();

// Populate the shortcut dropdowns with available languages
populateLanguageDropdowns(tracks);

handleSubsListSetOrChange(selectElem);
}

Expand Down Expand Up @@ -483,6 +565,23 @@
renderAndReconcile();
}, POLL_INTERVAL_MS);

function selectTrackByLanguage(languageCode) {
if (!urlMovieId || !trackListCache.has(urlMovieId)) {
return;
}

const tracks = trackListCache.get(urlMovieId);
const matchingTrack = tracks.find(track => track.language === languageCode);

if (matchingTrack) {
const selectElem = document.querySelector(`#${SUBS_LIST_ELEM_ID} select`);
if (selectElem) {
selectElem.value = matchingTrack.id;
handleSubsListSetOrChange(selectElem);
}
}
}

document.body.addEventListener('keydown', function(e) {
if ((e.keyCode === 67) && !e.altKey && !e.ctrlKey && !e.metaKey) { // unmodified C key
// console.log('copying subs text to clipboard');
Expand All @@ -500,6 +599,11 @@
if (el) {
el.click();
}
} else if (!e.altKey && !e.ctrlKey && !e.metaKey) { // Check for language shortcuts
const key = e.key.toLowerCase();
if (LANGUAGE_SHORTCUTS[key]) {
selectTrackByLanguage(LANGUAGE_SHORTCUTS[key]);
}
}
}, false);

Expand Down