Skip to content

New feature to search for individual videos by name#393

Open
Mixgyt wants to merge 9 commits intoaandrew-me:mainfrom
Mixgyt:main
Open

New feature to search for individual videos by name#393
Mixgyt wants to merge 9 commits intoaandrew-me:mainfrom
Mixgyt:main

Conversation

@Mixgyt
Copy link

@Mixgyt Mixgyt commented Feb 7, 2026

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

    • Added video search UI with input, search button, results view and back navigation; selecting a result opens video details.
  • UI Improvements

    • Centered and elevated modal with size constraints, scrollable content, and improved visual styling.
    • Added a YouTube-only search warning near the search control.
  • Dependencies

    • Bumped Electron dev dependency from v30 to v40.
  • Localization

    • Added search-related translations for 23 languages.

@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

Walkthrough

Adds 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

Cohort / File(s) Summary
Search UI
assets/css/index.css, html/index.html
Added styling and markup for a search input container, text input, search button, a YouTube-only warning, and a hidden search results container (plus a duplicate placeholder in downloads).
Search Behavior / Renderer
src/renderer.js
Added DOM IDs, event handlers (click/Enter), and new methods: searchVideos(), _fetchSearchResults(query) (invokes yt-dlp, parses newline-delimited JSON), _displaySearchResults(results), _createSearchResultsHeader(), _createSearchResultItem(), _validateImageUrl(), _formatDuration(); toggles views, shows loading, and calls existing getInfo when a result is selected.
Localization
translations/*.json
Added search-related keys (searchInfo, searchPlaceholder, searchNameToolTip, searchResults, backToSearch, noResultsFound, resultsFound, enterSearchTerm, live, noTitle, and one warning key in en.json) across ~24 locale files and adjusted trailing commas for valid JSON.
Dependency bump
package.json
Updated devDependency electron from ^30.0.0 to ^40.2.1.
Compressor path handling
src/compressor.js
Replaced use of shell with webUtils, and switched file.path usage to webUtils.getPathForFile(file) for input/output path resolution and progress labeling.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main feature addition: a video search functionality by name, which aligns with the substantial code changes across UI, logic, and translations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Several 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 | 🟡 Minor

Same 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 in vi-VN.json.

translations/hu-HU.json (1)

155-174: ⚠️ Potential issue | 🟡 Minor

A 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: Duplicate text-align declaration — remove the dead one.

text-align is set to center on line 692 and then overridden to left on line 697. The first declaration is dead code.

Also, consider adding border: none explicitly (consistent with the .title input 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: _formatDuration duplicates existing _formatTime (line 1953).

Both methods convert seconds to H:MM:SS / MM:SS. The only difference is _formatTime zero-pads minutes (05:03) while _formatDuration doesn't (5:03), and _formatTime handles null. Consider reusing _formatTime here to avoid maintaining two nearly identical methods.


838-901: The process variable (line 859) shadows the global Node.js process object.

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 _fetchVideoMetadata method (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",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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’s engines says 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.inputFormType from context-menu on WebContents. [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 until false. [3]

Electron 32

  • Removed nonstandard File.path → use webUtils.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]
    • app login event’s webContents behavior changed. [5]
  • Deprecations to prepare for future:
    • textured option in BrowserWindowConstructorOption.type. [5]
    • systemPreferences.accessibilityDisplayShouldReduceTransparency. [5]

Electron 34

  • Windows: menu bar is now hidden in fullscreen (parity with Linux). [6]

Electron 35

  • Removed PrinterInfo.isDefault and PrinterInfo.status. [7]
  • Deprecations:
    • session.serviceWorkers.fromVersionID()getInfoFromVersionID(). [7]
    • session.setPreloads/getPreloadsregisterPreloadScript / unregisterPreloadScript / getPreloadScripts. [7]
    • Other deprecations/behavior tweaks called out in the v35 blog post (e.g., console-message args, WebRequestFilter.urls). [7]

Electron 36

  • Deprecations:
    • NativeImage.getBitmap() (alias of toBitmap()). [8]
    • Extension methods/events moved from session.* to session.extensions.*. [8]
    • session.clearStorageData({ quota }) quota option 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.commandLine now lowercases switches/args; use process.argv for app-specific parsing. [8]

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 = null behavior (no more “random independent session”; emulate with session.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-platform is now auto (Wayland-native by default on Wayland sessions; use --ozone-platform=x11 to force Xwayland). [10]
  • Removed webContents plugin-crashed event. [10]
  • Deprecated (prepare for future removal):
    • webFrame.routingId (and webFrame.findFrameByRoutingId(routingId)). [10]

Electron 39

  • Deprecated --host-rules → use --host-resolver-rules. [11]
  • window.open popups are now always resizable (WHATWG-aligned; use setWindowOpenHandler to restore prior behavior). [11]
  • Offscreen shared texture OSR paint event: data structure changed to a unified handle (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 .zip to .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 (use webUtils.getPathForFile())
  • Electron 35: Preload script API changed (setPreloads/getPreloadsregisterPreloadScript/getPreloadScripts)
  • Electron 36: Session clearStorageData quota option removed; GTK 4 default on GNOME (may affect Linux/GTK symbol conflicts)
  • Electron 38: macOS 11 support removed; Wayland/Ozone platform handling changed; webContents plugin-crashed event 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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 same File object across generateOutputPath, compressVideo, and buildFFmpegCommand. Since compressVideo already calls buildFFmpegCommand with the same file, the path is resolved twice within a single compression run (plus once more in generateOutputPath).

Resolving the path once in startCompression and 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: Duplicate text-align declaration — first one is dead code.

Line 695 sets text-align: center but line 700 overrides it with text-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 backToSearch button's event listener (line 929) is attached via document.getElementById on a dynamically-created element — this works but could be slightly cleaner by referencing the element directly from _createSearchResultsHeader's return value.

Comment on lines +839 to +902
_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));
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how shell: true is used throughout the codebase
rg -n "shell:\s*true" --type=js -C 3

Repository: 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 10

Repository: 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 -100

Repository: 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 -20

Repository: 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 -10

Repository: 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 20

Repository: aandrew-me/ytDownloader

Length of output: 49


🏁 Script executed:

#!/bin/bash
# Read full _fetchVideoMetadata method
sed -n '1164,1240p' src/renderer.js

Repository: 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 2

Repository: 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.json

Repository: 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 1

Repository: 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.js

Repository: 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-dlp and returns an EventEmitter you 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]
    • error and close. [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:

  • argsArray is an array of yt-dlp CLI args (including the URL). [1]
  • spawnOptions are Node spawn options like { shell: true, detached: true }. [1]
  • abortSignal is an AbortController().signal used 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.

Comment on lines +164 to +174
"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"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment on lines +164 to +174
"downloadingUpdate": "Downloading update",
"searchInfo": "खोजी गर्नुहोस्",
"searchPlaceholder": "भिडियोहरू खोज्नुहोस्...",
"searchNameToolTip": "नामद्वारा खोज्नुहोस्",
"searchResults": "खोजी परिणामहरू",
"backToSearch": "खोजीमा फर्कनुहोस्",
"noResultsFound": "कुनै परिणाम फेला परेन",
"resultsFound": "परिणामहरू फेला पर्यो",
"enterSearchTerm": "कृपया खोजी शब्द प्रविष्ट गर्नुहोस्",
"live": "लाइभ",
"noTitle": "कुनै शीर्षक छैन"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant