mirror of
https://github.com/neosubhamoy/neodlp.git
synced 2026-02-05 01:52:22 +05:30
Compare commits
10 Commits
16
.github/banner.svg
vendored
Normal file
16
.github/banner.svg
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<svg width="600" height="130" viewBox="0 0 600 130" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_3_2)">
|
||||
<path d="M86.9062 11H21.0938C9.44399 11 0 20.444 0 32.0938V97.9062C0 109.556 9.44399 119 21.0938 119H86.9062C98.556 119 108 109.556 108 97.9062V32.0938C108 20.444 98.556 11 86.9062 11Z" fill="url(#paint0_linear_3_2)"/>
|
||||
<path d="M55.8196 96.5455C54.7881 97.5856 53.1065 97.5856 52.075 96.5455L27.028 71.2863C25.3778 69.6221 26.5566 66.793 28.9002 66.793H78.9943C81.3379 66.793 82.5168 69.6221 80.8666 71.2863L55.8196 96.5455Z" fill="#FAFAFA"/>
|
||||
<path d="M67.8164 34.4141H40.0781C38.6219 34.4141 37.4414 35.5946 37.4414 37.0508V68.2695C37.4414 69.7257 38.6219 70.9062 40.0781 70.9062H67.8164C69.2726 70.9062 70.4531 69.7257 70.4531 68.2695V37.0508C70.4531 35.5946 69.2726 34.4141 67.8164 34.4141Z" fill="#FAFAFA"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3_2" x1="13.6582" y1="26.6621" x2="97.1367" y2="102.02" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#4444FF"/>
|
||||
<stop offset="1" stop-color="#FF43D0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_3_2">
|
||||
<rect width="108" height="108" fill="white" transform="translate(0 11)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
138
.github/mockup.svg
vendored
Normal file
138
.github/mockup.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 541 KiB |
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,23 +1,12 @@
|
||||
### ✨ Changelog
|
||||
|
||||
- DOWNLOADER: Introduced 'Combine' download mode (Now, You can combine a video and audio stream of your choice)
|
||||
- SETTINGS: Added global video/audio file format selection option (Available Formats: MP4, WEBM, MKV, M4A, OPUS, MP3)
|
||||
- SETTINGS: Added video/audio file metadata embeding option
|
||||
- SETTINGS: Added thumbnail embeding option in audio files (as cover art)
|
||||
- SETTINGS: Added re-encode video over remuxing option (when file format convertion is needed)
|
||||
- SETTINGS: Added strict downloadablity check option
|
||||
- SETTINGS: Added download speed rate limit option
|
||||
- SETTINGS: Added download max retries option
|
||||
- SETTINGS: Added temporary download folder cleanup option
|
||||
- UI: Improved 'Settings' ui/layout with categories (tabs)
|
||||
- UI: Merged 'Extension' sidebar tab within 'Settings' (Settings > Extension > Install)
|
||||
- UI: Improved 'Library' ui/layout with tabs
|
||||
- UI: Added 'Stop' all ongoing downloads button in 'Library'
|
||||
- UI: Renamed settings 'General' tab to 'Application' ('General' is now a sub-category of 'Application' tab)
|
||||
- UI: Improved all alert dialog messages (for better undestanding/UX)
|
||||
- FIXED: Unexpected crashing of yt-dlp causing downloads to stuck on a unrevocable state (Now, coresponding download will be 'paused' on detection of unexpected yt-dlp crash)
|
||||
- FIXED: Broken app updater progress bar/percentage (also improved the update notification card)
|
||||
- Lots of other minor fixes and improvements
|
||||
- DOWNLOADER: Added 'Cancel Search' button
|
||||
- FIXED: Downloaded files are not moving from temp to download folder in some linux distros
|
||||
- FIXED: 'Stop' all ongoing downloads button not working on linux
|
||||
- Improved search and download error handling
|
||||
- Removed subtitle (CC) embeding restriction from M4A files
|
||||
- Migrated to Zod v4 and improved form validations
|
||||
- Other minor fixes and improvements
|
||||
|
||||
### 📝 Notes
|
||||
|
||||
|
||||
56
README.md
56
README.md
@@ -1,30 +1,58 @@
|
||||

|
||||
|
||||
# NeoDLP - (Neo Downloader Plus)
|
||||
|
||||
Cross-platform Video/Audio Downloader Desktop App with Modern UI and Browser Integration
|
||||
|
||||
[](https://github.com/neosubhamoy/neodlp)
|
||||
[](https://github.com/neosubhamoy/neodlp)
|
||||
[](https://github.com/neosubhamoy/neodlp/releases)
|
||||
[](https://github.com/neosubhamoy/neodlp)
|
||||
|
||||
> **🥰 Liked this project? Please consider giving it a Star (🌟) on github to show us your appreciation and help the algorythm recommend this project to even more awesome people like you!**
|
||||
|
||||
[](https://repology.org/project/neodlp/versions)
|
||||
|
||||
### ✨ Highlighted Features
|
||||
|
||||
- Download Video/Audio from popular sites (YT, FB, IG, X and other 2.5k+ [supported sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md))
|
||||
- Download Video/Audio in your preffered format (MP4, WEBM, MKV, MP3 etc.)
|
||||
- Supports both Video and Playlist download
|
||||
- Supports Combining Video, Audio streams of your choice
|
||||
- Supports Multi-Language Subtitle/Caption (CC) embeding
|
||||
- Different Video/Audio metadata embeding options (info, chapters, thumbnail etc.)
|
||||
- Highly customizable and many more...😉
|
||||
|
||||
### 🧩 Browser Integration
|
||||
|
||||
You can integrate NeoDLP with your favourite browser (any Chrome/Chromium/Firefox based browser) Just, install [NeoDLP Extension](https://github.com/neosubhamoy/neodlp-extension) to get started!
|
||||
|
||||
After installing the extension you can do the following directly from the browser:
|
||||
|
||||
- Quick Search (search current browser address with NeoDLP) (via pressing keyboard shortcut `ALT`+`SHIFT`+`Q`, You can also change this shortcut key combo from browser settings)
|
||||
|
||||
- Right Click Context Menu Action (Download with Neo Downloader Plus - Link, Selection, Media Source)
|
||||
|
||||
### 👀 Sneak Peek
|
||||
|
||||

|
||||
|
||||
### 💻 Supported Platforms
|
||||
|
||||
- Windows (10 / 11)
|
||||
- Linux (Debian / Fedora / Arch Linux base)
|
||||
- MacOS (>10.3)
|
||||
|
||||
### 🌐 Supported Sites
|
||||
> ⚠️ **NOTE:** Though most linux (debian/fedora/arch base) distros are supported but not all packages are tested on all these platforms, to save time (and some brain cells) and ship the software as fast as possible! (Currently only the debian package is tested on Ubuntu 24.04 - So, other linux packages may have issues, test it yourself and feel free to report issues if you found one)
|
||||
|
||||
- All [Supported Sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) by [yt-dlp](https://github.com/yt-dlp/yt-dlp) **(2.5K+)**
|
||||
### 🤝 External Dependencies
|
||||
|
||||
### 🧩 External Dependencies
|
||||
|
||||
- [yt-dlp](https://github.com/yt-dlp/yt-dlp) - The core CLI Tool used to download Video/Audio from the Web
|
||||
- [FFmpeg](https://www.ffmpeg.org) - Used for Video/Audio Post-processing
|
||||
- [YT-DLP](https://github.com/yt-dlp/yt-dlp) - The core CLI tool used to download Video/Audio from the Web (Hero of the show 😎)
|
||||
- [FFmpeg](https://www.ffmpeg.org) - Used for Video/Audio post-processing
|
||||
|
||||
### ⬇️ Download and Installation
|
||||
|
||||
1. Download the latest [NeoDLP](https://github.com/neosubhamoy/neodlp/releases/latest) release based on your OS and CPU Architecture then install it or install it directly from an available distribution channel
|
||||
1. Download the latest [NeoDLP](https://github.com/neosubhamoy/neodlp/releases/latest) release based on your OS and CPU Architecture, then install it! -OR- Install it directly from an available distribution channel (listed below)
|
||||
|
||||
| Arch\OS | Windows | Linux | MacOS |
|
||||
| :---- | :---- | :---- | :---- |
|
||||
@@ -37,6 +65,18 @@ Cross-platform Video/Audio Downloader Desktop App with Modern UI and Browser Int
|
||||
| MacOS Universal | Curl-Bash Installer | `curl -sSL https://neodlp.neosubhamoy.com/neodlp_macos_installer.sh \| bash` |
|
||||
| Linux x86_64 (Arch Linux) | AUR | `yay -S neodlp` |
|
||||
|
||||
### 💝 Support the Development
|
||||
|
||||
NeoDLP is and will be always FREE to Use for Everyone and Open-Sourced. On the other hand the developent process of NeoDLP takes lots of time, effort and even sometimes money! So, if you appriciate my work and have the ability to donate, then please consider supporting the development by donating (even a very small donation matters and helps NeoDLP to be a better product!) Your support is the key to my motivation...🤗
|
||||
|
||||
<a href="https://buymeacoffee.com/neosubhamoy" target="_blank" title="buymeacoffee">
|
||||
<img src="https://iili.io/JoQ0zN9.md.png" alt="buymeacoffee-orange-badge" style="width: 150px;">
|
||||
</a>
|
||||
|
||||
<br></br>
|
||||
|
||||
> 📌 **NOTE:** You can also donate via UPI by sending donations to this UPI ID directly: **[subhamoybiswas636-2@oksbi](upi://pay?pa=subhamoybiswas636-2@oksbi&pn=Subhamoy%20Biswas)**
|
||||
|
||||
### ⚡ Technologies Used
|
||||
|
||||

|
||||
@@ -55,7 +95,7 @@ Want to be part of this? Feel free to contribute...!! Pull Requests are always w
|
||||
2. Git clone the forked repo in your local machine.
|
||||
3. Install Node.js dependencies: `npm install`
|
||||
4. Run development / build process
|
||||
> ⚠️ Make sure to run the build command once before running the dev command for the first time to avoid build time errors
|
||||
> ⚠️ **IMPORTANT:** Make sure to run the build command once before running the dev command for the first time to avoid compile time errors
|
||||
```code
|
||||
# for windows and linux users
|
||||
npm run tauri dev # for development
|
||||
|
||||
80
package-lock.json
generated
80
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "neodlp",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "neodlp",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.1.1",
|
||||
"@radix-ui/react-accordion": "^1.2.11",
|
||||
@@ -39,8 +39,8 @@
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"@tanstack/react-query-devtools": "^5.83.0",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-dialog": "^2.3.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.3.1",
|
||||
"@tauri-apps/plugin-fs": "^2.4.1",
|
||||
"@tauri-apps/plugin-opener": "^2.4.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.0",
|
||||
"@tauri-apps/plugin-process": "^2.3.0",
|
||||
@@ -60,27 +60,27 @@
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"react-resizable-panels": "^3.0.3",
|
||||
"react-router-dom": "^7.6.3",
|
||||
"react-router-dom": "^7.7.0",
|
||||
"recharts": "^2.15.4",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.25.76",
|
||||
"zod": "^4.0.5",
|
||||
"zustand": "^5.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"@types/node": "^24.0.13",
|
||||
"@types/node": "^24.0.15",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.3.5",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.0.4"
|
||||
"vite": "^7.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@@ -2306,9 +2306,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-beta.19",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.19.tgz",
|
||||
"integrity": "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==",
|
||||
"version": "1.0.0-beta.27",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
|
||||
"integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -3170,18 +3170,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-dialog": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.3.0.tgz",
|
||||
"integrity": "sha512-ylSBvYYShpGlKKh732ZuaHyJ5Ie1JR71QCXewCtsRLqGdc8Is4xWdz6t43rzXyvkItM9syNPMvFVcvjgEy+/GA==",
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.3.1.tgz",
|
||||
"integrity": "sha512-B7jvyhycV8SI/WHzPjciwtYfdFM6/9EXuMjRgYWZwn8GPDmHxpT80aJdb/eDVN+NgoAFDh9bu4QPonYahoYnZQ==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-fs": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.4.0.tgz",
|
||||
"integrity": "sha512-Sp8AdDcbyXyk6LD6Pmdx44SH3LPeNAvxR2TFfq/8CwqzfO1yOyV+RzT8fov0NNN7d9nvW7O7MtMAptJ42YXA5g==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.4.1.tgz",
|
||||
"integrity": "sha512-vJlKZVGF3UAFGoIEVT6Oq5L4HGDCD78WmA4uhzitToqYiBKWAvZR61M6zAyQzHqLs0ADemkE4RSy/5sCmZm6ZQ==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.6.0"
|
||||
@@ -3357,9 +3357,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz",
|
||||
"integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==",
|
||||
"version": "24.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz",
|
||||
"integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3387,16 +3387,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-react": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz",
|
||||
"integrity": "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==",
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
|
||||
"integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.27.4",
|
||||
"@babel/core": "^7.28.0",
|
||||
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
|
||||
"@rolldown/pluginutils": "1.0.0-beta.19",
|
||||
"@rolldown/pluginutils": "1.0.0-beta.27",
|
||||
"@types/babel__core": "^7.20.5",
|
||||
"react-refresh": "^0.17.0"
|
||||
},
|
||||
@@ -3404,7 +3404,7 @@
|
||||
"node": "^14.18.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
|
||||
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aria-hidden": {
|
||||
@@ -4549,9 +4549,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.3.tgz",
|
||||
"integrity": "sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA==",
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz",
|
||||
"integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1",
|
||||
@@ -4571,12 +4571,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.3.tgz",
|
||||
"integrity": "sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw==",
|
||||
"version": "7.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.0.tgz",
|
||||
"integrity": "sha512-wwGS19VkNBkneVh9/YD0pK3IsjWxQUVMDD6drlG7eJpo1rXBtctBqDyBm/k+oKHRAm1x9XWT3JFC82QI9YOXXA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-router": "7.6.3"
|
||||
"react-router": "7.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
@@ -4987,9 +4987,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.4.tgz",
|
||||
"integrity": "sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==",
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
|
||||
"integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5069,9 +5069,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz",
|
||||
"integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
|
||||
16
package.json
16
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "neodlp",
|
||||
"private": true,
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -41,8 +41,8 @@
|
||||
"@tanstack/react-query": "^5.83.0",
|
||||
"@tanstack/react-query-devtools": "^5.83.0",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-dialog": "^2.3.0",
|
||||
"@tauri-apps/plugin-fs": "^2.4.0",
|
||||
"@tauri-apps/plugin-dialog": "^2.3.1",
|
||||
"@tauri-apps/plugin-fs": "^2.4.1",
|
||||
"@tauri-apps/plugin-opener": "^2.4.0",
|
||||
"@tauri-apps/plugin-os": "^2.3.0",
|
||||
"@tauri-apps/plugin-process": "^2.3.0",
|
||||
@@ -62,26 +62,26 @@
|
||||
"react-dom": "^19.1.0",
|
||||
"react-hook-form": "^7.60.0",
|
||||
"react-resizable-panels": "^3.0.3",
|
||||
"react-router-dom": "^7.6.3",
|
||||
"react-router-dom": "^7.7.0",
|
||||
"recharts": "^2.15.4",
|
||||
"sonner": "^2.0.6",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"vaul": "^1.1.2",
|
||||
"zod": "^3.25.76",
|
||||
"zod": "^4.0.5",
|
||||
"zustand": "^5.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/vite": "^4.1.11",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"@types/node": "^24.0.13",
|
||||
"@types/node": "^24.0.15",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tw-animate-css": "^1.3.5",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^7.0.4"
|
||||
"vite": "^7.0.5"
|
||||
}
|
||||
}
|
||||
|
||||
129
src-tauri/Cargo.lock
generated
129
src-tauri/Cargo.lock
generated
@@ -86,7 +86,7 @@ dependencies = [
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
@@ -138,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3"
|
||||
checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"cfg-if",
|
||||
@@ -149,10 +149,9 @@ dependencies = [
|
||||
"futures-lite",
|
||||
"parking",
|
||||
"polling",
|
||||
"rustix 1.0.7",
|
||||
"rustix 1.0.8",
|
||||
"slab",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -168,9 +167,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-process"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc"
|
||||
checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-io",
|
||||
@@ -181,8 +180,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"event-listener",
|
||||
"futures-lite",
|
||||
"rustix 1.0.7",
|
||||
"tracing",
|
||||
"rustix 1.0.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -198,9 +196,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-signal"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d"
|
||||
checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1"
|
||||
dependencies = [
|
||||
"async-io",
|
||||
"async-lock",
|
||||
@@ -208,10 +206,10 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"rustix 1.0.7",
|
||||
"rustix 1.0.8",
|
||||
"signal-hook-registry",
|
||||
"slab",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -480,9 +478,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.29"
|
||||
version = "1.2.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
|
||||
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -1458,7 +1456,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55"
|
||||
dependencies = [
|
||||
"rustix 1.0.7",
|
||||
"rustix 1.0.8",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
@@ -2278,9 +2276,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.4"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
|
||||
checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"libc",
|
||||
@@ -2503,7 +2501,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "neodlp"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"directories",
|
||||
@@ -3275,17 +3273,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.8.0"
|
||||
version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50"
|
||||
checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"concurrent-queue",
|
||||
"hermit-abi",
|
||||
"pin-project-lite",
|
||||
"rustix 1.0.7",
|
||||
"tracing",
|
||||
"windows-sys 0.59.0",
|
||||
"rustix 1.0.8",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3432,7 +3429,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.3",
|
||||
"lru-slab",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
@@ -3500,9 +3497,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
@@ -3591,9 +3588,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
version = "0.5.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
]
|
||||
@@ -3811,15 +3808,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4044,9 +4041,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
version = "1.0.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -4766,9 +4763,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.6.2"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d"
|
||||
checksum = "352a4bc7bf6c25f5624227e3641adf475a6535707451b09bb83271df8b7a6ac7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -4816,9 +4813,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83"
|
||||
checksum = "182d688496c06bf08ea896459bf483eb29cdff35c1c4c115fb14053514303064"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -4838,9 +4835,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406"
|
||||
checksum = "b54a99a6cd8e01abcfa61508177e6096a4fe2681efecee9214e962f2f073ae4a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@@ -4865,9 +4862,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.3.1"
|
||||
version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc"
|
||||
checksum = "7945b14dc45e23532f2ded6e120170bbdd4af5ceaa45784a6b33d250fbce3f9e"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -4879,9 +4876,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3"
|
||||
checksum = "5bd5c1e56990c70a906ef67a9851bbdba9136d26075ee9a2b19c8b46986b3e02"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@@ -4896,9 +4893,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aefb14219b492afb30b12647b5b1247cadd2c0603467310c36e0f7ae1698c28"
|
||||
checksum = "05bedd4c3cf6f7aa97918a8814a736bd3695c9ddf3ede2d50eda6069c3290edc"
|
||||
dependencies = [
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
@@ -4914,9 +4911,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f"
|
||||
checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
@@ -5007,9 +5004,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-single-instance"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b441b6d5d1a194e9fee0b358fe0d602ded845d0f580e1f8c8ef78ebc3c8b225d"
|
||||
checksum = "e2e5df105284154e5ec277e1b42d022cb2ccd0da08a6bc10eea70eb2a60c9475"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -5073,9 +5070,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.7.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4"
|
||||
checksum = "2b1cc885be806ea15ff7b0eb47098a7b16323d9228876afda329e34e2d6c4676"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
@@ -5095,9 +5092,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.7.1"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad"
|
||||
checksum = "fe653a2fbbef19fe898efc774bc52c8742576342a33d3d028c189b57eb1d2439"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@@ -5122,9 +5119,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.5.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e"
|
||||
checksum = "9330c15cabfe1d9f213478c9e8ec2b0c76dab26bb6f314b8ad1c8a568c1d186e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli",
|
||||
@@ -5178,7 +5175,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix 1.0.7",
|
||||
"rustix 1.0.8",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -5595,7 +5592,7 @@ dependencies = [
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"rand 0.9.1",
|
||||
"rand 0.9.2",
|
||||
"sha1",
|
||||
"thiserror 2.0.12",
|
||||
"utf-8",
|
||||
@@ -6046,9 +6043,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
||||
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
@@ -6678,7 +6675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix 1.0.7",
|
||||
"rustix 1.0.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6707,9 +6704,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus"
|
||||
version = "5.8.0"
|
||||
version = "5.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597f45e98bc7e6f0988276012797855613cd8269e23b5be62cc4e5d28b7e515d"
|
||||
checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad"
|
||||
dependencies = [
|
||||
"async-broadcast",
|
||||
"async-executor",
|
||||
@@ -6741,9 +6738,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zbus_macros"
|
||||
version = "5.8.0"
|
||||
version = "5.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5c8e4e14dcdd9d97a98b189cd1220f30e8394ad271e8c987da84f73693862c2"
|
||||
checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659"
|
||||
dependencies = [
|
||||
"proc-macro-crate 3.3.0",
|
||||
"proc-macro2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "neodlp"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
description = "NeoDLP"
|
||||
authors = ["neosubhamoy <hey@neosubhamoy.com>"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f061f5be11e96c3764442b5339f91102316ac9a7eb8270f017e7912ed0384eba
|
||||
size 18112457
|
||||
oid sha256:5fa4862eb50050941370fa78a8c56faa31516d5abaf1f7cf31bc56de166347d9
|
||||
size 18123562
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "NeoDLP",
|
||||
"mainBinaryName": "neodlp",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"identifier": "com.neosubhamoy.neodlp",
|
||||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
|
||||
62
src/App.tsx
62
src/App.tsx
@@ -43,6 +43,8 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
const tempDownloadDirPath = useBasePathsStore((state) => state.tempDownloadDirPath);
|
||||
const downloadDirPath = useBasePathsStore((state) => state.downloadDirPath);
|
||||
|
||||
const setSearchPid = useCurrentVideoMetadataStore((state) => state.setSearchPid);
|
||||
|
||||
// const isUsingDefaultSettings = useSettingsPageStatesStore((state) => state.isUsingDefaultSettings);
|
||||
const setIsUsingDefaultSettings = useSettingsPageStatesStore((state) => state.setIsUsingDefaultSettings);
|
||||
const setSettingsKey = useSettingsPageStatesStore((state) => state.setSettingsKey);
|
||||
@@ -121,14 +123,19 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
jsonOutput += line;
|
||||
});
|
||||
|
||||
command.on('close', async () => {
|
||||
try {
|
||||
const data: RawVideoInfo = JSON.parse(jsonOutput);
|
||||
resolve(data);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Failed to parse JSON: ${e}`);
|
||||
command.on('close', async (data) => {
|
||||
if (data.code !== 0) {
|
||||
console.error(`yt-dlp failed to fetch metadata with code ${data.code}`);
|
||||
resolve(null);
|
||||
} else {
|
||||
try {
|
||||
const parsedData: RawVideoInfo = JSON.parse(jsonOutput);
|
||||
resolve(parsedData);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(`Failed to parse JSON: ${e}`);
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -137,7 +144,9 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
resolve(null);
|
||||
});
|
||||
|
||||
command.spawn().catch(e => {
|
||||
command.spawn().then(child => {
|
||||
setSearchPid(child.pid);
|
||||
}).catch(e => {
|
||||
console.error(`Failed to spawn command: ${e}`);
|
||||
resolve(null);
|
||||
});
|
||||
@@ -265,7 +274,7 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
console.log('Starting download with args:', args);
|
||||
const command = Command.sidecar('binaries/yt-dlp', args);
|
||||
|
||||
command.on('close', async data => {
|
||||
command.on('close', async (data) => {
|
||||
if (data.code !== 0) {
|
||||
console.error(`Download failed with code ${data.code}`);
|
||||
if (!isErrorExpected) {
|
||||
@@ -273,21 +282,12 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
setErroredDownloadId(downloadId);
|
||||
}
|
||||
} else {
|
||||
downloadStatusUpdater.mutate({ download_id: downloadId, download_status: 'completed' }, {
|
||||
onSuccess: (data) => {
|
||||
console.log("Download status updated successfully:", data);
|
||||
queryClient.invalidateQueries({ queryKey: ['download-states'] });
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Failed to update download status:", error);
|
||||
}
|
||||
})
|
||||
|
||||
if (await fs.exists(tempDownloadPath)) {
|
||||
downloadFilePath = await generateSafeFilePath(downloadFilePath);
|
||||
await fs.rename(tempDownloadPath, downloadFilePath);
|
||||
await fs.copyFile(tempDownloadPath, downloadFilePath);
|
||||
await fs.remove(tempDownloadPath);
|
||||
}
|
||||
|
||||
|
||||
downloadFilePathUpdater.mutate({ download_id: downloadId, filepath: downloadFilePath }, {
|
||||
onSuccess: (data) => {
|
||||
console.log("Download filepath updated successfully:", data);
|
||||
@@ -297,11 +297,23 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
console.error("Failed to update download filepath:", error);
|
||||
}
|
||||
})
|
||||
|
||||
downloadStatusUpdater.mutate({ download_id: downloadId, download_status: 'completed' }, {
|
||||
onSuccess: (data) => {
|
||||
console.log("Download status updated successfully:", data);
|
||||
queryClient.invalidateQueries({ queryKey: ['download-states'] });
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Failed to update download status:", error);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
command.on('error', error => {
|
||||
console.error(`Error: ${error}`);
|
||||
setIsErrored(true);
|
||||
setErroredDownloadId(downloadId);
|
||||
});
|
||||
|
||||
command.stdout.on('data', line => {
|
||||
@@ -463,9 +475,11 @@ export default function App({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const pauseDownload = async (downloadState: DownloadState) => {
|
||||
try {
|
||||
setIsErrorExpected(true); // Set error expected to true to handle UI state
|
||||
console.log("Killing process with PID:", downloadState.process_id);
|
||||
await invoke('kill_all_process', { pid: downloadState.process_id });
|
||||
if ((downloadState.download_status === 'downloading' && downloadState.process_id) || (downloadState.download_status === 'starting' && downloadState.process_id)) {
|
||||
setIsErrorExpected(true); // Set error expected to true to handle UI state
|
||||
console.log("Killing process with PID:", downloadState.process_id);
|
||||
await invoke('kill_all_process', { pid: downloadState.process_id });
|
||||
}
|
||||
downloadStatusUpdater.mutate({ download_id: downloadState.download_id, download_status: 'paused' }, {
|
||||
onSuccess: (data) => {
|
||||
console.log("Download status updated successfully:", data);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useToast } from "@/hooks/use-toast";
|
||||
import { useAppContext } from "@/providers/appContextProvider";
|
||||
import { useCurrentVideoMetadataStore, useDownloaderPageStatesStore, useSettingsPageStatesStore } from "@/services/store";
|
||||
import { determineFileType, fileFormatFilter, formatBitrate, formatDurationString, formatFileSize, formatReleaseDate, formatYtStyleCount, isObjEmpty, sortByBitrate } from "@/utils";
|
||||
import { Calendar, Clock, DownloadCloud, Eye, Info, Loader2, Music, ThumbsUp, Video, File, ListVideo, PackageSearch, AlertCircleIcon } from "lucide-react";
|
||||
import { Calendar, Clock, DownloadCloud, Eye, Info, Loader2, Music, ThumbsUp, Video, File, ListVideo, PackageSearch, AlertCircleIcon, X } from "lucide-react";
|
||||
import { FormatSelectionGroup, FormatSelectionGroupItem } from "@/components/custom/formatSelectionGroup";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/custom/legacyToggleGroup";
|
||||
@@ -23,10 +23,14 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from "@/component
|
||||
import { config } from "@/config";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
const searchFormSchema = z.object({
|
||||
url: z.string().min(1, { message: "URL is required" })
|
||||
.url({message: "Invalid URL format." }),
|
||||
url: z.url({
|
||||
error: (issue) => issue.input === undefined || issue.input === null || issue.input === ""
|
||||
? "URL is required"
|
||||
: "Invalid URL format"
|
||||
}),
|
||||
});
|
||||
|
||||
export default function DownloaderPage() {
|
||||
@@ -38,11 +42,14 @@ export default function DownloaderPage() {
|
||||
const isMetadataLoading = useCurrentVideoMetadataStore((state) => state.isMetadataLoading);
|
||||
const requestedUrl = useCurrentVideoMetadataStore((state) => state.requestedUrl);
|
||||
const autoSubmitSearch = useCurrentVideoMetadataStore((state) => state.autoSubmitSearch);
|
||||
const searchPid = useCurrentVideoMetadataStore((state) => state.searchPid);
|
||||
const setVideoUrl = useCurrentVideoMetadataStore((state) => state.setVideoUrl);
|
||||
const setVideoMetadata = useCurrentVideoMetadataStore((state) => state.setVideoMetadata);
|
||||
const setIsMetadataLoading = useCurrentVideoMetadataStore((state) => state.setIsMetadataLoading);
|
||||
const setRequestedUrl = useCurrentVideoMetadataStore((state) => state.setRequestedUrl);
|
||||
const setAutoSubmitSearch = useCurrentVideoMetadataStore((state) => state.setAutoSubmitSearch);
|
||||
const setSearchPid = useCurrentVideoMetadataStore((state) => state.setSearchPid);
|
||||
const setShowSearchError = useCurrentVideoMetadataStore((state) => state.setShowSearchError);
|
||||
|
||||
const activeDownloadModeTab = useDownloaderPageStatesStore((state) => state.activeDownloadModeTab);
|
||||
const isStartingDownload = useDownloaderPageStatesStore((state) => state.isStartingDownload);
|
||||
@@ -202,22 +209,29 @@ export default function DownloaderPage() {
|
||||
mode: "onChange",
|
||||
})
|
||||
const watchedUrl = searchForm.watch("url");
|
||||
const { errors: searchFormErrors } = searchForm.formState;
|
||||
|
||||
function handleSearchSubmit(values: z.infer<typeof searchFormSchema>) {
|
||||
setVideoMetadata(null);
|
||||
setSearchPid(null);
|
||||
setShowSearchError(true);
|
||||
setIsMetadataLoading(true);
|
||||
setSelectedDownloadFormat('best');
|
||||
setSelectedCombinableVideoFormat('');
|
||||
setSelectedCombinableAudioFormat('');
|
||||
setSelectedSubtitles([]);
|
||||
setSelectedPlaylistVideoIndex('1');
|
||||
|
||||
fetchVideoMetadata(values.url).then((metadata) => {
|
||||
if (!metadata || (metadata._type !== 'video' && metadata._type !== 'playlist') || (metadata && metadata._type === 'video' && metadata.formats.length <= 0) || (metadata && metadata._type === 'playlist' && metadata.entries.length <= 0)) {
|
||||
toast({
|
||||
title: 'Opps! No results found',
|
||||
description: 'The provided URL does not contain any downloadable content. Please check the URL and try again.',
|
||||
variant: "destructive"
|
||||
});
|
||||
const showSearchError = useCurrentVideoMetadataStore.getState().showSearchError;
|
||||
if (showSearchError) {
|
||||
toast({
|
||||
title: 'Oops! No results found',
|
||||
description: 'The provided URL does not contain any downloadable content or you are not connected to the internet. Please check the URL, your network connection and try again.',
|
||||
variant: "destructive"
|
||||
});
|
||||
}
|
||||
}
|
||||
if (metadata && (metadata._type === 'video' || metadata._type === 'playlist') && ((metadata._type === 'video' && metadata.formats.length > 0) || (metadata._type === 'playlist' && metadata.entries.length > 0))) setVideoMetadata(metadata);
|
||||
if (metadata) console.log(metadata);
|
||||
@@ -225,6 +239,16 @@ export default function DownloaderPage() {
|
||||
});
|
||||
}
|
||||
|
||||
const cancelSearch = async (pid: number | null) => {
|
||||
setShowSearchError(false);
|
||||
if (pid) {
|
||||
console.log("Killing process with PID:", pid);
|
||||
await invoke('kill_all_process', { pid: pid });
|
||||
}
|
||||
setVideoMetadata(null);
|
||||
setIsMetadataLoading(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const updateBottomBarWidth = (): void => {
|
||||
if (containerRef.current && bottomBarRef.current) {
|
||||
@@ -338,9 +362,20 @@ export default function DownloaderPage() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{isMetadataLoading && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
size="icon"
|
||||
disabled={!isMetadataLoading}
|
||||
onClick={() => cancelSearch(searchPid)}
|
||||
>
|
||||
<X className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!videoUrl || isMetadataLoading}
|
||||
disabled={!videoUrl || Object.keys(searchFormErrors).length > 0 || isMetadataLoading}
|
||||
>
|
||||
{isMetadataLoading ? (
|
||||
<>
|
||||
@@ -428,7 +463,7 @@ export default function DownloaderPage() {
|
||||
className="flex flex-col items-start gap-2 mb-2"
|
||||
value={selectedSubtitles}
|
||||
onValueChange={(value) => setSelectedSubtitles(value)}
|
||||
disabled={selectedFormat?.ext !== 'mp4' && selectedFormat?.ext !== 'mkv' && selectedFormat?.ext !== 'webm'}
|
||||
// disabled={selectedFormat?.ext !== 'mp4' && selectedFormat?.ext !== 'mkv' && selectedFormat?.ext !== 'webm'}
|
||||
>
|
||||
<p className="text-xs">Subtitle Languages</p>
|
||||
<div className="flex gap-2 flex-wrap items-center">
|
||||
@@ -449,10 +484,10 @@ export default function DownloaderPage() {
|
||||
value={selectedDownloadFormat}
|
||||
onValueChange={(value) => {
|
||||
setSelectedDownloadFormat(value);
|
||||
const currentlySelectedFormat = value === 'best' ? videoMetadata?.requested_downloads[0] : allFilteredFormats.find((format) => format.format_id === value);
|
||||
if (currentlySelectedFormat?.ext !== 'mp4' && currentlySelectedFormat?.ext !== 'mkv' && currentlySelectedFormat?.ext !== 'webm') {
|
||||
setSelectedSubtitles([]);
|
||||
}
|
||||
// const currentlySelectedFormat = value === 'best' ? videoMetadata?.requested_downloads[0] : allFilteredFormats.find((format) => format.format_id === value);
|
||||
// if (currentlySelectedFormat?.ext !== 'mp4' && currentlySelectedFormat?.ext !== 'mkv' && currentlySelectedFormat?.ext !== 'webm') {
|
||||
// setSelectedSubtitles([]);
|
||||
// }
|
||||
}}
|
||||
>
|
||||
<p className="text-xs">Suggested</p>
|
||||
@@ -532,7 +567,6 @@ export default function DownloaderPage() {
|
||||
className="flex flex-col items-start gap-2 mb-2"
|
||||
value={selectedSubtitles}
|
||||
onValueChange={(value) => setSelectedSubtitles(value)}
|
||||
disabled={selectedFormat?.ext !== 'mp4' && selectedFormat?.ext !== 'mkv' && selectedFormat?.ext !== 'webm'}
|
||||
>
|
||||
<p className="text-xs">Subtitle Languages</p>
|
||||
<div className="flex gap-2 flex-wrap items-center">
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Progress } from "@/components/ui/progress";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useAppContext } from "@/providers/appContextProvider";
|
||||
import { useDownloadActionStatesStore, useDownloaderPageStatesStore, useDownloadStatesStore, useLibraryPageStatesStore } from "@/services/store";
|
||||
import { useDownloadActionStatesStore, useDownloadStatesStore, useLibraryPageStatesStore } from "@/services/store";
|
||||
import { formatBitrate, formatCodec, formatDurationString, formatFileSize, formatSecToTimeString, formatSpeed } from "@/utils";
|
||||
import { AudioLines, Clock, File, FileAudio2, FileQuestion, FileVideo2, FolderInput, ListVideo, Loader2, Music, Pause, Play, Square, Trash2, Video, X } from "lucide-react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
@@ -33,8 +33,6 @@ export default function LibraryPage() {
|
||||
const setIsCancelingDownload = useDownloadActionStatesStore(state => state.setIsCancelingDownload);
|
||||
const setIsDeleteFileChecked = useDownloadActionStatesStore(state => state.setIsDeleteFileChecked);
|
||||
|
||||
const setIsErrorExpected = useDownloaderPageStatesStore((state) => state.setIsErrorExpected);
|
||||
|
||||
const { pauseDownload, resumeDownload, cancelDownload } = useAppContext()
|
||||
const { toast } = useToast();
|
||||
|
||||
@@ -108,21 +106,25 @@ export default function LibraryPage() {
|
||||
|
||||
const stopOngoingDownloads = async () => {
|
||||
if (ongoingDownloads.length > 0) {
|
||||
setIsErrorExpected(true); // Set error expected to true to handle UI state
|
||||
try {
|
||||
await invoke('pause_ongoing_downloads').then(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['download-states'] });
|
||||
for (const state of ongoingDownloads) {
|
||||
setIsPausingDownload(state.download_id, true);
|
||||
try {
|
||||
await pauseDownload(state);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast({
|
||||
title: 'Stopped downloads',
|
||||
description: 'All ongoing downloads are being stopped.',
|
||||
title: 'Failed to stop download',
|
||||
description: `An error occurred while trying to stop the download for ${state.title}.`,
|
||||
variant: "destructive"
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
setIsPausingDownload(state.download_id, false);
|
||||
}
|
||||
}
|
||||
if (ongoingDownloads.length === 0) {
|
||||
toast({
|
||||
title: 'Failed to stop downloads',
|
||||
description: 'An error occurred while trying to stop the downloads.',
|
||||
variant: "destructive"
|
||||
title: 'Stopped ongoing downloads',
|
||||
description: 'All ongoing downloads have been stopped successfully.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -29,9 +29,12 @@ import { join } from "@tauri-apps/api/path";
|
||||
import { formatSpeed } from "@/utils";
|
||||
|
||||
const websocketPortSchema = z.object({
|
||||
port: z.coerce.number({
|
||||
required_error: "Websocket Port is required",
|
||||
invalid_type_error: "Websocket Port must be a valid number",
|
||||
port: z.coerce.number<number>({
|
||||
error: (issue) => issue.input === undefined || issue.input === null || issue.input === ""
|
||||
? "Websocket Port is required"
|
||||
: "Websocket Port must be a valid number"
|
||||
}).int({
|
||||
message: "Websocket Port must be an integer"
|
||||
}).min(50000, {
|
||||
message: "Websocket Port must be at least 50000"
|
||||
}).max(60000, {
|
||||
@@ -40,13 +43,20 @@ const websocketPortSchema = z.object({
|
||||
})
|
||||
|
||||
const proxyUrlSchema = z.object({
|
||||
url: z.string().min(1, { message: "Proxy URL is required" }).url({ message: "Invalid URL format" })
|
||||
url: z.url({
|
||||
error: (issue) => issue.input === undefined || issue.input === null || issue.input === ""
|
||||
? "Proxy URL is required"
|
||||
: "Invalid URL format"
|
||||
})
|
||||
});
|
||||
|
||||
const rateLimitSchema = z.object({
|
||||
rate_limit: z.coerce.number({
|
||||
required_error: "Rate Limit is required",
|
||||
invalid_type_error: "Rate Limit must be a valid number",
|
||||
rate_limit: z.coerce.number<number>({
|
||||
error: (issue) => issue.input === undefined || issue.input === null || issue.input === ""
|
||||
? "Rate Limit is required"
|
||||
: "Rate Limit must be a valid number"
|
||||
}).int({
|
||||
message: "Rate Limit must be an integer"
|
||||
}).min(1024, {
|
||||
message: "Rate Limit must be at least 1024 bytes/s (1 KB/s)"
|
||||
}).max(104857600, {
|
||||
|
||||
@@ -34,11 +34,15 @@ export const useCurrentVideoMetadataStore = create<CurrentVideoMetadataStore>((s
|
||||
isMetadataLoading: false,
|
||||
requestedUrl: '',
|
||||
autoSubmitSearch: false,
|
||||
searchPid: null,
|
||||
showSearchError: true,
|
||||
setVideoUrl: (url) => set(() => ({ videoUrl: url })),
|
||||
setVideoMetadata: (metadata) => set(() => ({ videoMetadata: metadata })),
|
||||
setIsMetadataLoading: (isLoading) => set(() => ({ isMetadataLoading: isLoading })),
|
||||
setRequestedUrl: (url) => set(() => ({ requestedUrl: url })),
|
||||
setAutoSubmitSearch: (autoSubmit) => set(() => ({ autoSubmitSearch: autoSubmit })),
|
||||
setSearchPid: (pid) => set(() => ({ searchPid: pid })),
|
||||
setShowSearchError: (showError) => set(() => ({ showSearchError: showError }))
|
||||
}));
|
||||
|
||||
export const useDownloaderPageStatesStore = create<DownloaderPageStatesStore>((set) => ({
|
||||
|
||||
@@ -23,11 +23,15 @@ export interface CurrentVideoMetadataStore {
|
||||
isMetadataLoading: boolean;
|
||||
requestedUrl: string;
|
||||
autoSubmitSearch: boolean;
|
||||
searchPid: number | null;
|
||||
showSearchError: boolean;
|
||||
setVideoUrl: (url: string) => void;
|
||||
setVideoMetadata: (metadata: RawVideoInfo | null) => void;
|
||||
setIsMetadataLoading: (isLoading: boolean) => void;
|
||||
setRequestedUrl: (url: string) => void;
|
||||
setAutoSubmitSearch: (autoSubmit: boolean) => void;
|
||||
setSearchPid: (pid: number | null) => void;
|
||||
setShowSearchError: (showError: boolean) => void;
|
||||
}
|
||||
|
||||
export interface DownloaderPageStatesStore {
|
||||
|
||||
@@ -9,7 +9,9 @@ const host = process.env.TAURI_DEV_HOST;
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [react(), tailwindcss()],
|
||||
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1024, // 1MB
|
||||
},
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
|
||||
Reference in New Issue
Block a user