diff --git a/README.md b/README.md index 1e390c7..42c9aac 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/dist/manifest.json b/dist/manifest.json index 5f71370..39e5ef6 100644 --- a/dist/manifest.json +++ b/dist/manifest.json @@ -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", diff --git a/dist/page_script.js b/dist/page_script.js index 30b0ab9..cb5eeab 100644 --- a/dist/page_script.js +++ b/dist/page_script.js @@ -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', @@ -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 @@ -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; @@ -306,6 +385,9 @@ updateToggleDisplay(); disableDownloadButton(); + // Populate the shortcut dropdowns with available languages + populateLanguageDropdowns(tracks); + handleSubsListSetOrChange(selectElem); } @@ -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'); @@ -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);