New feature to search for individual videos by name#393
New feature to search for individual videos by name#393Mixgyt wants to merge 9 commits intoaandrew-me:mainfrom
Conversation
WalkthroughAdds a YouTube search feature: UI elements and styles, renderer logic to run yt-dlp searches and display results, translations for search strings across locales, a small compressor path-change, and bumps devDependency Electron from ^30.0.0 to ^40.2.1. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as Renderer / UI
participant YTDLP as yt-dlp Process
participant App as App Logic (getInfo)
User->>UI: Enter query + Click Search / Press Enter
activate UI
UI->>UI: validate query
alt empty
UI->>User: show "enterSearchTerm" warning
else valid
UI->>User: show loading, hide input/list
UI->>YTDLP: run yt-dlp --print-json --flat-playlist "ytsearchN:query"
activate YTDLP
YTDLP-->>UI: stream newline-delimited JSON
deactivate YTDLP
UI->>UI: parse JSON lines → results array
UI->>UI: render results list with thumbnails & metadata
User->>UI: click result
UI->>App: call getInfo(videoUrl)
App-->>UI: video details (populate download UI)
UI->>User: restore/hide search views as needed
end
deactivate UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
translations/vi-VN.json (1)
155-174:⚠️ Potential issue | 🟡 MinorSeveral strings remain untranslated (English) in the Vietnamese locale.
Lines 155–164 contain English text that was never localized (e.g.,
fileDoesNotExist,updatingYtdlp,updateError,downloadingUpdate, etc.). While most of these are pre-existing,downloadingUpdate(Line 164) was touched in this PR and is still English. Consider translating these while you're updating this file.The new search keys (Lines 165–174) are properly translated — nice work there.
translations/hi-IN.json (1)
155-174:⚠️ Potential issue | 🟡 MinorSame untranslated English strings as other locales.
Lines 155–163 plus
downloadingUpdate(Line 164) remain in English. The new search keys (Lines 165–174) are properly translated to Hindi. Same issue as flagged invi-VN.json.translations/hu-HU.json (1)
155-174:⚠️ Potential issue | 🟡 MinorA few pre-existing strings remain in English.
Lines 155–160 and 163 (
fileDoesNotExist,updatingYtdlp,updatedYtdlp,ytDlpUpdateRequired,failedToDeleteHistoryItem,customArgsTxt,unableToAccessDir) are still in English. The new search keys (Lines 165–174) are properly translated to Hungarian.
🤖 Fix all issues with AI agents
In `@html/index.html`:
- Line 80: The span with id "pasteText" includes a hardcoded tooltip via
title="Download for link url" that is not localizable; either remove the title
attribute if redundant with the visible text or add a localization key by
replacing title with a translatable attribute such as
data-translate-title="pasteTextTitle" (and ensure the translation system maps
that key) so the tooltip is translated along with the element's data-translate
text.
- Around line 86-89: The fallback text for the search UI is grammatically off
and inconsistent with translations; update the title attribute on the element
with id "inputSearchContainer" to "Search for videos by name", change the
placeholder on the input with id "searchName" if needed to match the translation
key "searchPlaceholder", and change the button text of the element with id
"searchBtn" to the same fallback text used by the translation key "searchInfo"
(e.g., "Search") so the visible defaults match the i18n strings and avoid a
flash of inconsistent text before translations load.
In `@package.json`:
- Line 34: You bumped Electron to ^40.2.1 in package.json; verify build/tooling
compatibility by ensuring electron-builder@^26.0.12 is used and CI/dev machines
run Node ≥22, then audit code for breaking API changes: search main.js, preload
scripts and renderer code for deprecated APIs (replace setPreloads/getPreloads
usages with registerPreloadScript/getPreloadScripts, replace File.path usages
with webUtils.getPathForFile, remove any clearStorageData quota option, remove
handling of webContents 'plugin-crashed', and move renderer Clipboard usage into
a preload with contextBridge), update native modules if needed for Electron 40
ABI, and run full cross-platform tests (Windows/macOS/Linux/Wayland) to confirm
behavior.
In `@src/renderer.js`:
- Around line 914-935: The current code clears listContainer.innerHTML
(CONSTANTS.DOM_IDS.DOWNLOAD_LIST) which destroys UI created by
_createDownloadUI/_queueDownload; instead create or use a separate results
container (e.g., an element with id "searchResults") and render the search
header and results into that container rather than into listContainer, or at
minimum stop calling listContainer.innerHTML = "" and just hide `#list`; update
the "backToSearch" click handler to clear and hide `#searchResults`, show
$(CONSTANTS.DOM_IDS.INPUT_SEARCH_CONTAINER) and
$(CONSTANTS.DOM_IDS.DOWNLOAD_LIST) again and reset
$(CONSTANTS.DOM_IDS.SEARCH_NAME_INPUT).value, and ensure any code that currently
writes search output targets the new "searchResults" container so active
download UIs from _createDownloadUI/_queueDownload are preserved.
- Around line 938-995: The search-results block builds resultItem.innerHTML with
user-controlled values (video.title, video.uploader, thumbnail) causing an
XSS→RCE risk when nodeIntegration is enabled; replace the innerHTML construction
in the results.forEach loop with DOM API calls: create elements via
document.createElement for the wrapper, img, title div, uploader div, duration
div and chevron SVG container, set text nodes using element.textContent (not
innerHTML) for video.title, video.uploader and the formatted duration
(this._formatDuration), and set img.src only after validating/sanitizing the
thumbnail URL (or omit it if invalid); keep the existing event listeners on
resultItem and preserve logic that clears listContainer and calls
this.getInfo(videoUrl) and toggles CONSTANTS.DOM_IDS.INPUT_SEARCH_CONTAINER.
In `@translations/ne-NP.json`:
- Line 167: The JSON key "searchNameToolTip" has an extra leading space before
the quote causing a formatting inconsistency; remove the stray space so the line
starts exactly with the key (e.g., `"searchNameToolTip": "नामद्वारा
खोज्नुहोस्",`) matching the formatting of other entries and ensuring consistent
indentation for the translations file.
🧹 Nitpick comments (3)
assets/css/index.css (1)
686-698: Duplicatetext-aligndeclaration — remove the dead one.
text-alignis set tocenteron line 692 and then overridden tolefton line 697. The first declaration is dead code.Also, consider adding
border: noneexplicitly (consistent with the.titleinput at line 341) for cross-browser consistency.♻️ Suggested cleanup
`#searchName` { padding: 10px; padding-right: 4rem; border-radius: 8px; + border: none; outline: none; width: 250px; - text-align: center; background-color: var(--box-separation); color: var(--text); font-size: large; font-family: "Ubuntu"; text-align: left; }src/renderer.js (2)
1000-1016:_formatDurationduplicates existing_formatTime(line 1953).Both methods convert seconds to
H:MM:SS/MM:SS. The only difference is_formatTimezero-pads minutes (05:03) while_formatDurationdoesn't (5:03), and_formatTimehandlesnull. Consider reusing_formatTimehere to avoid maintaining two nearly identical methods.
838-901: Theprocessvariable (line 859) shadows the global Node.jsprocessobject.While it doesn't cause a bug in this method's current scope, it would prevent access to
process.env,process.platform, etc. if needed later. The existing_fetchVideoMetadatamethod (line 1077) has the same issue, but it's still worth using a more descriptive name for new code.Rename suggestion
- const process = this.state.ytDlp.exec(args, {shell: true}); + const ytDlpProcess = this.state.ytDlp.exec(args, {shell: true}); console.log( "Spawned yt-dlp search with args:", - process.ytDlpProcess.spawnargs.join(" ") + ytDlpProcess.ytDlpProcess.spawnargs.join(" ") ); let stdout = ""; let stderr = ""; - process.ytDlpProcess.stdout.on("data", (data) => { + ytDlpProcess.ytDlpProcess.stdout.on("data", (data) => { stdout += data; }); - process.ytDlpProcess.stderr.on("data", (data) => (stderr += data)); + ytDlpProcess.ytDlpProcess.stderr.on("data", (data) => (stderr += data)); - process.on("close", () => { + ytDlpProcess.on("close", () => {
| "description": "Download videos and audios from YouTube and many other sites", | ||
| "devDependencies": { | ||
| "electron": "^30.0.0", | ||
| "electron": "^40.2.1", |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Electron 40.2.1 release date version history
💡 Result:
- Electron 40.2.1 release date (published on GitHub Releases): February 6, 2026 (shown as “06 Feb 02:22”). The release notes list one change: an MSIX updater refactor. [1]
- Tag/commit date shown on GitHub Tags: tag v40.2.1 is dated February 5, 2026. [2]
v40.x version history (around 40.2.1)
- v40.2.1 — Feb 6, 2026 (GitHub Release) / Feb 5, 2026 (tag) [1][2]
- v40.2.0 — Feb 4, 2026 (tag) [2]
- v40.1.0 — Jan 29, 2026 (GitHub Release) [3]
Sources: GitHub Releases and Tags for electron/electron. [1][2][3]
🌐 Web query:
electron-builder v26 compatibility Electron 40 support
💡 Result:
electron-builder v26 is not tied to a specific Electron major—it packages whatever electron version you declare in your app (and will download that version during packaging), so Electron 40 can be used with electron-builder v26 in principle.
What to watch for in practice:
- Electron 40 ships with Node 24.11.1 (inside Electron), but that doesn’t determine the Node.js version you use to run
electron-builder. Electron 40’s stack: Chromium 144 / Node 24.11.1 / V8 14.4. [1] - electron-builder v26’s published engine field is permissive (example v26.x package.json shows
engines.node >= 14.0.0). [2] - However, the electron-builder project tracked that Electron’s
@electron/*ecosystem packages were bumping minimum Node.js to 22, and electron-builder uses some@electron/*packages—so if you hit install/runtime issues, use Node.js ≥ 22 for your build environment (even if electron-builder’senginessays otherwise). [3]
If you want the “safest” setup for Electron 40: use the latest v26.x electron-builder release available to you and run builds on Node 22+.
Sources: [1] [2] [3]
🌐 Web query:
Electron 30 to 40 breaking changes migration guide
💡 Result:
Below is a 30 → 40 migration-oriented list of breaking changes you’re likely to hit, by major version (scan in order; many removals were preceded by deprecations in earlier majors).
Electron 30
- Cross-origin iframes must use
allow="..."(Permission Policy) to access features. [1] - Removed
--disable-color-correct-rendering. [1] - BrowserView macOS auto-resize behavior changed due to BrowserView becoming a wrapper around WebContentsView. [1]
- Removed
params.inputFormTypefromcontext-menuonWebContents. [2] - Removed
process.getIOCounters(). [2]
Electron 31
- Removed WebSQL support (Chromium upstream removal). [3]
- Behavior changes:
nativeImage.toDataURL()now preserves PNG colorspace. [3]win.flashFrame(true)on macOS now flashes continuously untilfalse. [3]
Electron 32
- Removed nonstandard
File.path→ usewebUtils.getPathForFile(file). [4] - First run may delete
<userData>/databases(legacy WebSQL cleanup). [4]
Electron 33
- Removed macOS 10.15 support. [5]
- Native modules now require C++20. [5]
- Behavior changes:
- Custom protocol URL handling on Windows changed. [5]
apploginevent’swebContentsbehavior changed. [5]
- Deprecations to prepare for future:
texturedoption inBrowserWindowConstructorOption.type. [5]systemPreferences.accessibilityDisplayShouldReduceTransparency. [5]
Electron 34
- Windows: menu bar is now hidden in fullscreen (parity with Linux). [6]
Electron 35
- Removed
PrinterInfo.isDefaultandPrinterInfo.status. [7] - Deprecations:
session.serviceWorkers.fromVersionID()→getInfoFromVersionID(). [7]session.setPreloads/getPreloads→registerPreloadScript/unregisterPreloadScript/getPreloadScripts. [7]- Other deprecations/behavior tweaks called out in the v35 blog post (e.g.,
console-messageargs,WebRequestFilter.urls). [7]
Electron 36
- Deprecations:
NativeImage.getBitmap()(alias oftoBitmap()). [8]- Extension methods/events moved from
session.*tosession.extensions.*. [8] session.clearStorageData({ quota })quotaoption deprecated. [8]
- Removed
session.clearStorageData({ quota: 'syncable' })support. [8] - Linux/GTK + CLI behavior changes:
- GTK 4 default on GNOME (possible GTK symbol conflicts; workaround via
--gtk-version). [8] app.commandLinenow lowercases switches/args; useprocess.argvfor app-specific parsing. [8]
- GTK 4 default on GNOME (possible GTK symbol conflicts; workaround via
Electron 37
- UtilityProcess behavior changes:
- Unhandled rejections warn instead of crashing. [9]
process.exit()in utility process is now synchronous. [9]
- WebUSB/WebSerial now enforce Chromium blocklists (override via flags). [9]
- Removed
ProtocolResponse.session = nullbehavior (no more “random independent session”; emulate withsession.fromPartition(...)). [9] - Linux:
BrowserWindow.IsVisibleOnAllWorkspaces()now returns false if the window isn’t visible. [9]
Electron 38
- Removed macOS 11 support (macOS 12+ required). [10]
- Linux/Wayland: Removed
ELECTRON_OZONE_PLATFORM_HINT; default--ozone-platformis nowauto(Wayland-native by default on Wayland sessions; use--ozone-platform=x11to force Xwayland). [10] - Removed
webContentsplugin-crashedevent. [10] - Deprecated (prepare for future removal):
webFrame.routingId(andwebFrame.findFrameByRoutingId(routingId)). [10]
Electron 39
- Deprecated
--host-rules→ use--host-resolver-rules. [11] window.openpopups are now always resizable (WHATWG-aligned; usesetWindowOpenHandlerto restore prior behavior). [11]- Offscreen shared texture OSR
paintevent: data structure changed to a unifiedhandle(relevant if you use shared-texture OSR). [11]
Electron 40
- Deprecated using the clipboard API directly in renderer processes; call from preload and expose via
contextBridge. [12] - macOS debug symbols: dSYM artifacts switched from
.zipto.tar.xz(update tooling/CI that expects zip). [12]
Primary references (official): Electron “Breaking Changes” index + the v30–v40 release blog posts. [4][2]
Verify Electron v40 compatibility with electron-builder and review breaking changes.
This is a jump of 10 major versions (^30 → ^40), introducing significant breaking changes across the stack. Electron 40.2.1 (released Feb 6, 2026) is stable. electron-builder@^26.0.12 supports it, but ensure your build environment runs Node.js ≥ 22 (Electron 40 ships with Node 24.11.1 and @electron/* packages require Node 22+).
Key breaking changes between Electron 30–40 to audit in your codebase:
- Electron 31: WebSQL removed
- Electron 32: Removed nonstandard
File.path(usewebUtils.getPathForFile()) - Electron 35: Preload script API changed (
setPreloads/getPreloads→registerPreloadScript/getPreloadScripts) - Electron 36: Session clearStorageData
quotaoption removed; GTK 4 default on GNOME (may affect Linux/GTK symbol conflicts) - Electron 38: macOS 11 support removed; Wayland/Ozone platform handling changed;
webContentsplugin-crashedevent removed - Electron 40: Clipboard API deprecated in renderer (move to preload +
contextBridge)
Review main.js, renderer processes, and any native modules for usage of removed/deprecated APIs. Test on target platforms (Windows menu bar fullscreen behavior changed in v34, Wayland default in v38+).
🤖 Prompt for AI Agents
In `@package.json` at line 34, You bumped Electron to ^40.2.1 in package.json;
verify build/tooling compatibility by ensuring electron-builder@^26.0.12 is used
and CI/dev machines run Node ≥22, then audit code for breaking API changes:
search main.js, preload scripts and renderer code for deprecated APIs (replace
setPreloads/getPreloads usages with registerPreloadScript/getPreloadScripts,
replace File.path usages with webUtils.getPathForFile, remove any
clearStorageData quota option, remove handling of webContents 'plugin-crashed',
and move renderer Clipboard usage into a preload with contextBridge), update
native modules if needed for Electron 40 ABI, and run full cross-platform tests
(Windows/macOS/Linux/Wayland) to confirm behavior.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@src/renderer.js`:
- Around line 839-902: In _fetchSearchResults (and mirror the same change in
_fetchVideoMetadata) remove the unsafe use of shell: true and stop interpolating
the quoted search string into a shell command — pass the yt-dlp arguments as a
plain array (include the raw searchQuery string like ytsearch10:yourQuery
without extra shell quotes) to this.state.ytDlp.exec so the underlying API
spawns the process without shell interpretation; also harden the JSON parsing by
trimming and filtering out empty/blank lines before JSON.parse (e.g.
.trim().split("\n").filter(Boolean).map(line => JSON.parse(line))) to avoid
crashing on blank output lines.
In `@translations/es-ES.json`:
- Around line 164-174: The Spanish locale is missing the search key
searchYouTubeOnlyWarning; add a new entry "searchYouTubeOnlyWarning" to the
translations (matching the structure used for other keys like
"searchPlaceholder" and "noResultsFound") and provide the Spanish translation
equivalent to the English message (or copy the English text if unsure) so the
YouTube-only search warning displays for es-ES users.
In `@translations/ne-NP.json`:
- Around line 164-174: Add the missing translation key
"searchYouTubeOnlyWarning" to the Nepali translations JSON (alongside keys like
"searchPlaceholder" and "noTitle"); copy the English value from the en locale
and provide an appropriate Nepali translation for the warning so users don’t
fall back to the default locale.
🧹 Nitpick comments (4)
src/compressor.js (1)
208-209: Consider computing the file path once and passing it through.
webUtils.getPathForFile(file)is called three times for the sameFileobject acrossgenerateOutputPath,compressVideo, andbuildFFmpegCommand. SincecompressVideoalready callsbuildFFmpegCommandwith the samefile, the path is resolved twice within a single compression run (plus once more ingenerateOutputPath).Resolving the path once in
startCompressionand threading it as a parameter would reduce duplication and make the data flow clearer.Also applies to: 253-255, 302-302
assets/css/index.css (1)
689-701: Duplicatetext-aligndeclaration — first one is dead code.Line 695 sets
text-align: centerbut line 700 overrides it withtext-align: left. Remove line 695 to avoid confusion.🔧 Proposed fix
`#searchName` { padding: 10px; padding-right: 4rem; border-radius: 8px; outline: none; width: 250px; - text-align: center; background-color: var(--box-separation); color: var(--text); font-size: large; font-family: "Ubuntu"; text-align: left; }src/renderer.js (2)
810-832: No way to cancel an in-progress search.If yt-dlp hangs or the network is slow, the user has no way to abort the search — the loading spinner shows indefinitely. Consider storing the search process handle and providing a cancel mechanism, or at minimum adding a timeout.
909-946: Search results container and back-button wiring looks correct.The flow properly hides the input/download list, renders results into the dedicated container, and the back button restores the original view. One minor note: the
backToSearchbutton's event listener (line 929) is attached viadocument.getElementByIdon a dynamically-created element — this works but could be slightly cleaner by referencing the element directly from_createSearchResultsHeader's return value.
| _fetchSearchResults(query) { | ||
| return new Promise((resolve, reject) => { | ||
| const {proxy, browserForCookies, configPath} = this.state.preferences; | ||
| const searchQuery = `ytsearch10:${query}`; | ||
|
|
||
| const args = [ | ||
| "-j", | ||
| "--flat-playlist", | ||
| "--no-warnings", | ||
| proxy ? "--proxy" : "", | ||
| proxy, | ||
| browserForCookies ? "--cookies-from-browser" : "", | ||
| browserForCookies, | ||
| this.state.jsRuntimePath | ||
| ? `--no-js-runtimes --js-runtime ${this.state.jsRuntimePath}` | ||
| : "", | ||
| configPath ? "--config-location" : "", | ||
| configPath ? `"${configPath}"` : "", | ||
| `"${searchQuery}"`, | ||
| ].filter(Boolean); | ||
|
|
||
| const process = this.state.ytDlp.exec(args, {shell: true}); | ||
|
|
||
| console.log( | ||
| "Spawned yt-dlp search with args:", | ||
| process.ytDlpProcess.spawnargs.join(" ") | ||
| ); | ||
|
|
||
| let stdout = ""; | ||
| let stderr = ""; | ||
|
|
||
| process.ytDlpProcess.stdout.on("data", (data) => { | ||
| stdout += data; | ||
| }); | ||
| process.ytDlpProcess.stderr.on("data", (data) => (stderr += data)); | ||
|
|
||
| process.on("close", () => { | ||
| if (stdout) { | ||
| try { | ||
| // Parse multiple JSON objects separated by newlines | ||
| const results = stdout | ||
| .trim() | ||
| .split("\n") | ||
| .map((line) => JSON.parse(line)); | ||
| resolve(results); | ||
| } catch (e) { | ||
| reject( | ||
| new Error( | ||
| "Failed to parse search results: " + | ||
| (stderr || e.message) | ||
| ) | ||
| ); | ||
| } | ||
| } else { | ||
| reject( | ||
| new Error( | ||
| stderr || "yt-dlp search exited with no results." | ||
| ) | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| process.on("error", (err) => reject(err)); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how shell: true is used throughout the codebase
rg -n "shell:\s*true" --type=js -C 3Repository: aandrew-me/ytDownloader
Length of output: 1373
🏁 Script executed:
#!/bin/bash
# Find _fetchVideoMetadata and related ytDlp.exec() calls
rg -n "_fetchVideoMetadata|ytDlp\.exec" --type=js -B 2 -A 10Repository: aandrew-me/ytDownloader
Length of output: 3021
🏁 Script executed:
#!/bin/bash
# Look for ytDlp definition and its exec method
rg -n "ytDlp\s*=" --type=js -A 5 | head -100Repository: aandrew-me/ytDownloader
Length of output: 847
🏁 Script executed:
#!/bin/bash
# Check if there are any utility functions for sanitizing/escaping arguments
rg -n "sanitize|escape|quote|shell" --type=js | grep -i "function\|const.*=.*=>" | head -20Repository: aandrew-me/ytDownloader
Length of output: 49
🏁 Script executed:
#!/bin/bash
# Find YTDlpWrap definition to understand exec() API
find . -type f -name "*.js" -o -name "*.json" | xargs grep -l "YTDlpWrap" | head -10Repository: aandrew-me/ytDownloader
Length of output: 124
🏁 Script executed:
#!/bin/bash
# Find YTDlpWrap class definition
rg -n "class YTDlpWrap|YTDlpWrap\s*=.*function|module\.exports.*YTDlpWrap" --type=js -A 20Repository: aandrew-me/ytDownloader
Length of output: 49
🏁 Script executed:
#!/bin/bash
# Read full _fetchVideoMetadata method
sed -n '1164,1240p' src/renderer.jsRepository: aandrew-me/ytDownloader
Length of output: 1960
🏁 Script executed:
#!/bin/bash
# Check if there's any input validation for URL or search query
rg -n "searchQuery|query.*sanitize|query.*escape|validateInput|validateUrl" --type=js -B 2 -A 2Repository: aandrew-me/ytDownloader
Length of output: 586
🏁 Script executed:
#!/bin/bash
# Check package.json for YTDlpWrap dependency
cat package.json | jq '.dependencies, .devDependencies' 2>/dev/null || cat package.jsonRepository: aandrew-me/ytDownloader
Length of output: 256
🏁 Script executed:
#!/bin/bash
# Find where YTDlpWrap is imported
rg -n "import.*YTDlpWrap|require.*YTDlpWrap" --type=js -B 1 -A 1Repository: aandrew-me/ytDownloader
Length of output: 49
🏁 Script executed:
#!/bin/bash
# Check if there's a node_modules reference or see what libraries are available
ls -la | grep -E "package|node_modules"Repository: aandrew-me/ytDownloader
Length of output: 201
🏁 Script executed:
#!/bin/bash
# Look at the full _fetchSearchResults method
sed -n '839,902p' src/renderer.jsRepository: aandrew-me/ytDownloader
Length of output: 1639
🌐 Web query:
yt-dlp-wrap-plus npm exec API shell option
💡 Result:
yt-dlp-wrap-plus doesn’t provide an npm exec CLI with its own flags. The “shell option” you’re seeing is a Node.js child_process.spawn option you pass as the 2nd argument to the wrapper’s execution methods (e.g., .exec(...)), not something you pass to npm exec. [1]
Example (API usage):
import YTDlpWrap from "yt-dlp-wrap-plus";
const ytDlpWrap = new YTDlpWrap("path/to/yt-dlp"); // or just "yt-dlp" if on PATH
const controller = new AbortController();
ytDlpWrap.exec(
["https://www.youtube.com/watch?v=aqz-KE-bpKQ", "-f", "best", "-o", "output.mp4"],
{ shell: true, detached: true }, // <-- spawn options
controller.signal // <-- AbortSignal (optional)
);So: use { shell: true } in the library call, not as an npm exec argument. [1]
Source
[1] yt-dlp-wrap-plus README (“Options and Cancellation” example showing .exec(args, { shell: true, detached: true }, signal)).
🌐 Web query:
yt-dlp-wrap-plus github documentation exec method
💡 Result:
In yt-dlp-wrap-plus, the .exec(...) method is documented as the EventEmitter interface for running yt-dlp. It:
- Executes
yt-dlpand returns anEventEmitteryou can subscribe to. [1] - Emits (at least) these events:
progress→ parsed download progress (percent, totalSize, currentSpeed, eta). [1]ytDlpEvent→ exposes raw yt-dlp log events as(eventType, eventData)(e.g. a[download] ...line becomes("download", "...")). [1]errorandclose. [1]
- The returned emitter exposes the spawned process as
ytDlpEventEmitter.ytDlpProcess(e.g., you can read.pid). [1]
Call shape (as shown in docs):
ytDlpWrap.exec(argsArray)
ytDlpWrap.exec(argsArray, spawnOptions, abortSignal)where:
argsArrayis an array ofyt-dlpCLI args (including the URL). [1]spawnOptionsare Node spawn options like{ shell: true, detached: true }. [1]abortSignalis anAbortController().signalused to cancel the process. [1]
Example from the GitHub README:
let ytDlpEventEmitter = ytDlpWrap.exec(
['https://www.youtube.com/watch?v=aqz-KE-bpKQ', '-f', 'best', '-o', 'output.mp4'],
{ shell: true, detached: true },
controller.signal
);[1]
Sources: yt-dlp-wrap-plus GitHub README/docs page. [1]
Command injection and JSON parsing vulnerabilities need immediate attention.
The search query (and similarly the metadata fetch URL) is interpolated directly into a shell command with shell: true, making it vulnerable to injection via shell metacharacters like backticks and $() even within quotes. The shell: true option is not required by the yt-dlp-wrap-plus API and should be removed—pass arguments as an array without shell interpretation to let the OS handle boundary safety.
Additionally, .split("\n").map((line) => JSON.parse(line)) will crash on any empty or blank line in yt-dlp's output. Filter empty lines before parsing:
Suggested fix
const results = stdout
.trim()
.split("\n")
+ .filter((line) => line.trim())
.map((line) => JSON.parse(line));The same command injection pattern exists in _fetchVideoMetadata() (line 1164) and should also be fixed by removing shell: true.
🤖 Prompt for AI Agents
In `@src/renderer.js` around lines 839 - 902, In _fetchSearchResults (and mirror
the same change in _fetchVideoMetadata) remove the unsafe use of shell: true and
stop interpolating the quoted search string into a shell command — pass the
yt-dlp arguments as a plain array (include the raw searchQuery string like
ytsearch10:yourQuery without extra shell quotes) to this.state.ytDlp.exec so the
underlying API spawns the process without shell interpretation; also harden the
JSON parsing by trimming and filtering out empty/blank lines before JSON.parse
(e.g. .trim().split("\n").filter(Boolean).map(line => JSON.parse(line))) to
avoid crashing on blank output lines.
| "downloadingUpdate": "Descargando actualización", | ||
| "searchInfo": "Buscar", | ||
| "searchPlaceholder": "Buscar videos...", | ||
| "searchNameToolTip": "Buscar por nombre", | ||
| "searchResults": "Resultados de búsqueda", | ||
| "backToSearch": "Volver a buscar", | ||
| "noResultsFound": "No se encontraron resultados", | ||
| "resultsFound": "resultados encontrados", | ||
| "enterSearchTerm": "Por favor ingresa un término de búsqueda", | ||
| "live": "En vivo", | ||
| "noTitle": "Sin título" |
There was a problem hiding this comment.
Missing searchYouTubeOnlyWarning translation key.
Same as ne-NP.json — the English locale defines searchYouTubeOnlyWarning (line 175 of en.json) but it's absent here. Users on this locale won't see the YouTube-only search warning.
🤖 Prompt for AI Agents
In `@translations/es-ES.json` around lines 164 - 174, The Spanish locale is
missing the search key searchYouTubeOnlyWarning; add a new entry
"searchYouTubeOnlyWarning" to the translations (matching the structure used for
other keys like "searchPlaceholder" and "noResultsFound") and provide the
Spanish translation equivalent to the English message (or copy the English text
if unsure) so the YouTube-only search warning displays for es-ES users.
| "downloadingUpdate": "Downloading update", | ||
| "searchInfo": "खोजी गर्नुहोस्", | ||
| "searchPlaceholder": "भिडियोहरू खोज्नुहोस्...", | ||
| "searchNameToolTip": "नामद्वारा खोज्नुहोस्", | ||
| "searchResults": "खोजी परिणामहरू", | ||
| "backToSearch": "खोजीमा फर्कनुहोस्", | ||
| "noResultsFound": "कुनै परिणाम फेला परेन", | ||
| "resultsFound": "परिणामहरू फेला पर्यो", | ||
| "enterSearchTerm": "कृपया खोजी शब्द प्रविष्ट गर्नुहोस्", | ||
| "live": "लाइभ", | ||
| "noTitle": "कुनै शीर्षक छैन" |
There was a problem hiding this comment.
Missing searchYouTubeOnlyWarning translation key.
The English locale (translations/en.json, line 175) includes a searchYouTubeOnlyWarning key, but it's absent here. This will cause a missing-translation fallback for Nepali users seeing the YouTube-only warning.
🤖 Prompt for AI Agents
In `@translations/ne-NP.json` around lines 164 - 174, Add the missing translation
key "searchYouTubeOnlyWarning" to the Nepali translations JSON (alongside keys
like "searchPlaceholder" and "noTitle"); copy the English value from the en
locale and provide an appropriate Nepali translation for the warning so users
don’t fall back to the default locale.
Three commits with new additions
Function to search for videos individually by name (has a limit of 10 videos).
I also added new commit with translations for the interface parts I added, but most of the translations were done using AI.
And a final commit with an Electron update.
Summary by CodeRabbit
New Features
UI Improvements
Dependencies
Localization