From 8b580a4ce5052637ab66f6cc0412a4c5adb4918d Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Thu, 5 Mar 2026 18:36:04 +0530
Subject: [PATCH 1/9] fix the new issue for the codec of webm and mp4 and m4a
---
app.go | 157 +++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 119 insertions(+), 38 deletions(-)
diff --git a/app.go b/app.go
index acf0d9b..0ed011a 100644
--- a/app.go
+++ b/app.go
@@ -412,78 +412,159 @@ func extractResolution(resolution string) string {
/* -------------------- Format Builders -------------------- */
+// buildVideoFormatString builds format string for mp4 container with proper codec handling
+// Uses separate video+audio selection to ensure reliable merging
func buildVideoFormatString(cleanRes string, preferH264 bool) string {
- // Build format string that handles all codecs (h264, vp9, av1)
- // CRITICAL: Always use m4a audio for mp4 container compatibility
- // webm/opus audio causes ffmpeg merge failures with mp4 video
+ // Build format string that prioritizes h264+aac for mp4 container compatibility
+ // Use separate selection (bv*+ba*) to ensure proper download and merging
if cleanRes == "best" {
- // Try h264 first, then vp9, then av1
- // Audio: STRICTLY m4a/aac only to ensure mp4 compatibility
- return "bestvideo[vcodec^=avc1]+bestaudio[ext=m4a]/" +
- "bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/" +
- "bestvideo[vcodec^=vp9]+bestaudio[ext=m4a]/" +
- "bestvideo[vcodec^=vp9]+bestaudio[acodec^=mp4a]/" +
- "bestvideo[vcodec^=av01]+bestaudio[ext=m4a]/" +
- "bestvideo[vcodec^=av01]+bestaudio[acodec^=mp4a]/" +
- "bestvideo+bestaudio[ext=m4a]/" +
- "bestvideo+bestaudio[acodec^=mp4a]/" +
- "bestvideo+bestaudio/best"
+ // Best quality - prioritize h264 with aac/m4a for reliable mp4 merging
+ return "bestvideo[vcodec^=avc1]+bestaudio[ext=m4a]/bestvideo[vcodec^=avc1]+bestaudio[acodec^=mp4a]/" +
+ "bestvideo[vcodec^=avc1]+bestaudio/" +
+ "bestvideo[vcodec^=vp9]+bestaudio[ext=m4a]/bestvideo[vcodec^=vp9]+bestaudio[acodec^=mp4a]/" +
+ "bestvideo[vcodec^=vp9]+bestaudio/" +
+ "bestvideo[vcodec^=av01]+bestaudio[ext=m4a]/bestvideo[vcodec^=av01]+bestaudio[acodec^=mp4a]/" +
+ "bestvideo[vcodec^=av01]+bestaudio/" +
+ "bestvideo+bestaudio[ext=m4a]/bestvideo+bestaudio[acodec^=mp4a]/bestvideo+bestaudio/best"
}
resNum, _ := strconv.Atoi(cleanRes)
if resNum >= 1440 {
- // High resolutions - try exact height first, then fall back to <=
- // STRICTLY m4a audio only to prevent ffmpeg merge failures
+ // High resolutions (1440p+) - prioritize h264 with exact or close height match
return fmt.Sprintf(
- // Try exact height match first with m4a audio only
+ // h264 with m4a audio - best for mp4 merging
"bestvideo[vcodec^=avc1][height=%s]+bestaudio[ext=m4a]/"+
"bestvideo[vcodec^=avc1][height=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[vcodec^=avc1][height=%s]+bestaudio/"+
+ // h264 with <= height fallback
+ "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[ext=m4a]/"+
+ "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[vcodec^=avc1][height<=%s]+bestaudio/"+
+ // vp9 fallback
"bestvideo[vcodec^=vp9][height=%s]+bestaudio[ext=m4a]/"+
"bestvideo[vcodec^=vp9][height=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio/"+
+ // av1 fallback
"bestvideo[vcodec^=av01][height=%s]+bestaudio[ext=m4a]/"+
"bestvideo[vcodec^=av01][height=%s]+bestaudio[acodec^=mp4a]/"+
- // Fall back to <= if exact match not available - still m4a only
- "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[ext=m4a]/"+
- "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[acodec^=mp4a]/"+
- "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[ext=m4a]/"+
- "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[acodec^=mp4a]/"+
- "bestvideo[vcodec^=av01][height<=%s]+bestaudio[ext=m4a]/"+
- "bestvideo[vcodec^=av01][height<=%s]+bestaudio[acodec^=mp4a]/"+
- // Final fallbacks - still prefer m4a
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio/"+
+ // Any video codec fallback
"bestvideo[height<=%s]+bestaudio[ext=m4a]/"+
"bestvideo[height<=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[height<=%s]+bestaudio/"+
"best[height<=%s]",
cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
- cleanRes, cleanRes, cleanRes,
+ cleanRes, cleanRes, cleanRes, cleanRes,
)
}
- // Standard resolutions - try exact height first, then fall back
- // STRICTLY m4a audio only to prevent ffmpeg merge failures
+ // Standard resolutions - prioritize h264 with m4a audio
return fmt.Sprintf(
- // Try exact height match first with m4a audio only
+ // h264 with m4a audio - best for mp4 merging
"bestvideo[vcodec^=avc1][height=%s]+bestaudio[ext=m4a]/"+
"bestvideo[vcodec^=avc1][height=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[vcodec^=avc1][height=%s]+bestaudio/"+
+ // h264 with <= height fallback
+ "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[ext=m4a]/"+
+ "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[vcodec^=avc1][height<=%s]+bestaudio/"+
+ // vp9 fallback
"bestvideo[vcodec^=vp9][height=%s]+bestaudio[ext=m4a]/"+
"bestvideo[vcodec^=vp9][height=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio/"+
+ // av1 fallback
"bestvideo[vcodec^=av01][height=%s]+bestaudio[ext=m4a]/"+
"bestvideo[vcodec^=av01][height=%s]+bestaudio[acodec^=mp4a]/"+
- // Fall back to <= if exact match not available - still m4a only
- "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[ext=m4a]/"+
- "bestvideo[vcodec^=avc1][height<=%s]+bestaudio[acodec^=mp4a]/"+
- "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[ext=m4a]/"+
- "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[acodec^=mp4a]/"+
- "bestvideo[vcodec^=av01][height<=%s]+bestaudio[ext=m4a]/"+
- "bestvideo[vcodec^=av01][height<=%s]+bestaudio[acodec^=mp4a]/"+
- // Final fallbacks - still prefer m4a
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio/"+
+ // Any video codec fallback
"bestvideo[height<=%s]+bestaudio[ext=m4a]/"+
"bestvideo[height<=%s]+bestaudio[acodec^=mp4a]/"+
+ "bestvideo[height<=%s]+bestaudio/"+
+ "best[height<=%s]",
+ cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
+ cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
+ cleanRes, cleanRes, cleanRes, cleanRes,
+ )
+}
+
+// buildWebmFormatString builds format string for webm container with VP9/AV1 + opus
+// This ensures best quality webm output with modern codecs
+func buildWebmFormatString(cleanRes string) string {
+ // For webm, we want VP9 or AV1 video with opus audio
+ // This provides better quality than older vp8/opus combinations
+
+ if cleanRes == "best" {
+ // Best quality webm - prioritize av01 (AV1) then vp9
+ return "bestvideo[vcodec^=av01]+bestaudio[acodec^=opus]/" +
+ "bestvideo[vcodec^=av01]+bestaudio[ext=webm]/" +
+ "bestvideo[vcodec^=av01]+bestaudio/" +
+ "bestvideo[vcodec^=vp9]+bestaudio[acodec^=opus]/" +
+ "bestvideo[vcodec^=vp9]+bestaudio[ext=webm]/" +
+ "bestvideo[vcodec^=vp9]+bestaudio/" +
+ "bestvideo[vcodec^=vp8]+bestaudio[acodec^=opus]/" +
+ "bestvideo[vcodec^=vp8]+bestaudio/" +
+ "bestvideo+bestaudio[acodec^=opus]/bestvideo+bestaudio/best"
+ }
+
+ resNum, _ := strconv.Atoi(cleanRes)
+ if resNum >= 1440 {
+ // High resolutions - prioritize av01 then vp9
+ return fmt.Sprintf(
+ // AV1 with opus - best quality
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio/"+
+ // AV1 <= height fallback
+ "bestvideo[vcodec^=av01][height<=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=av01][height<=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=av01][height<=%s]+bestaudio/"+
+ // VP9 with opus
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio/"+
+ // VP9 <= height fallback
+ "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=vp9][height<=%s]+bestaudio/"+
+ // Any codec fallback
+ "bestvideo[height<=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[height<=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[height<=%s]+bestaudio/"+
+ "best[height<=%s]",
+ cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
+ cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
+ cleanRes, cleanRes, cleanRes, cleanRes,
+ )
+ }
+
+ // Standard resolutions - prioritize av01 then vp9 with opus
+ return fmt.Sprintf(
+ // AV1 with opus
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=av01][height=%s]+bestaudio/"+
+ // AV1 <= height fallback
+ "bestvideo[vcodec^=av01][height<=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=av01][height<=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=av01][height<=%s]+bestaudio/"+
+ // VP9 with opus
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=vp9][height=%s]+bestaudio/"+
+ // VP9 <= height fallback
+ "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[vcodec^=vp9][height<=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[vcodec^=vp9][height<=%s]+bestaudio/"+
+ // Any codec fallback
+ "bestvideo[height<=%s]+bestaudio[acodec^=opus]/"+
+ "bestvideo[height<=%s]+bestaudio[ext=webm]/"+
+ "bestvideo[height<=%s]+bestaudio/"+
"best[height<=%s]",
cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
cleanRes, cleanRes, cleanRes, cleanRes, cleanRes, cleanRes,
- cleanRes, cleanRes, cleanRes,
+ cleanRes, cleanRes, cleanRes, cleanRes,
)
}
From dcc58e848e46b366a7f6ff4fdebf60856da70ef3 Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Thu, 5 Mar 2026 19:37:39 +0530
Subject: [PATCH 2/9] improve readme.md
---
README.md | 155 ++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 120 insertions(+), 35 deletions(-)
diff --git a/README.md b/README.md
index 743e906..acf8878 100644
--- a/README.md
+++ b/README.md
@@ -1,51 +1,88 @@
# Predator
-
+
-A simple desktop app for grabbing videos and audio from YouTube and Instagram. Built with Go and Wails (web tech frontend), using yt-dlp under the hood to do the heavy lifting.
-
-## What it does
+
+
+
+
+
+
-- **Download videos** from YouTube in any resolution from 144p up to 4K
-- **Grab audio only** in MP3, M4A, Opus, or WAV format
-- **Works with Instagram too** - reels, posts, stories, TV videos
-- **Playlist support** - Download entire YouTube playlists or pick specific videos
-- **Shows file sizes** before you download so you know what you're getting into
-- **Live progress tracking** - Watch speed, ETA, and percentage in real-time
-- **Cancel anytime** - Change your mind? Hit cancel and it stops cleanly
-- **Picks up where it left off** - Failed downloads auto-retry up to 3 times
-- **Duplicate detection** - Warns if you already downloaded something
-- **Choose where files go** - Set your download folder once, it remembers
-- **Keeps history** - See what you've downloaded, filter by video/audio, open files directly
-- **Dark and light themes** - Toggle with Ctrl+T
+A simple desktop app for grabbing videos and audio from YouTube and Instagram. Built with Go and Wails (web tech frontend), using yt-dlp under the hood to do the heavy lifting.
-## Getting started
+## Table of Contents
+
+- [Features](#features)
+- [Getting Started](#getting-started)
+- [How to Use](#how-to-use)
+- [Keyboard Shortcuts](#keyboard-shortcuts)
+- [Technical Details](#technical-details)
+- [Requirements](#requirements)
+- [Troubleshooting](#troubleshooting)
+- [Contributing](#contributing)
+- [License](#license)
+
+## Features
+
+### Video Downloads
+- **Multiple resolutions** - From 144p up to 4K (2160p)
+- **Best quality option** - Automatically picks the highest available
+- **Smart codec handling** - Uses H.264 video + AAC audio for maximum compatibility
+- **WebM support** - Available with VP9/AV1 + Opus for better quality
+
+### Audio Downloads
+- **Multiple formats** - MP3, M4A, Opus, or WAV
+- **High quality output** - Uses best available source and converts to your chosen format
+- **MP3**: V0 quality (~245kbps)
+- **M4A**: AAC 192kbps
+- **WAV**: 16-bit PCM 44.1kHz stereo
+
+### Platform Support
+- **YouTube** - Videos, shorts, live streams
+- **Instagram** - Reels, posts, stories, TV videos
+
+### User Experience
+- **Playlist support** - Download entire playlists or select specific videos
+- **File size preview** - See file sizes before downloading
+- **Live progress** - Real-time speed, ETA, and percentage tracking
+- **Cancel anytime** - Clean cancellation with automatic temp file cleanup
+- **Auto-retry** - Failed downloads retry up to 3 times automatically
+- **Duplicate detection** - Warns if content was already downloaded
+- **Custom download folder** - Set once, remembers forever
+- **Download history** - View past downloads, filter by type, open file location
+- **Theme toggle** - Dark and light themes (Ctrl+T)
+
+## Getting Started
You'll need **Go 1.24 or newer** installed.
```bash
+# Clone the repository
git clone https://github.com/Aswanidev-vs/Predator.git
cd Predator
+
+# Install dependencies
go mod download
```
-To run in development mode:
+### Running the App
+**Development mode:**
```bash
wails dev
```
-To build the actual app:
-
+**Build for production:**
```bash
wails build
```
**First time setup:** The app will ask to download yt-dlp and ffmpeg automatically. Just say yes - it handles everything and caches them locally. No need to install anything else manually.
-## How to use
+## How to Use
1. **Open the app** - It'll show the main download tab
2. **Set your download folder** - Click "Change Download Location" (or it'll use the current folder)
@@ -55,31 +92,78 @@ wails build
- For audio: Pick format (MP3, M4A, Opus, WAV)
5. **Hit download** - Watch the progress bar do its thing
+## Keyboard Shortcuts
+
+| Shortcut | Action |
+|----------|--------|
+| `Ctrl+L` | Focus the URL input |
+| `Ctrl+D` | Start download (when button is active) |
+| `Ctrl+T` | Toggle dark/light theme |
+| `Esc` | Close any open modal |
+
**Pro tips:**
-- **Ctrl+L** - Focus the URL input
-- **Ctrl+D** - Start download (when button is active)
-- **Ctrl+T** - Toggle dark/light theme
-- **Esc** - Close any open modal
- Playlists show a modal where you can select specific videos to download
- Click the folder icon in history to open where a file is saved
+- The app auto-updates yt-dlp to the latest version on each download
+
+## Technical Details
+
+### Video Format
+- **Container:** MP4 (with WebM option)
+- **Video codec:** H.264 (avc1) for maximum device compatibility
+- **Audio codec:** AAC (m4a) for best audio quality
+- **Fallback handling:** If preferred codec isn't available, automatically falls back to next best option
-## A few things to know
+### Audio Format
+- Uses best available source (webm/opus or m4a/aac) and converts to your chosen format using ffmpeg
-- **Resolutions:** The app tries to get exactly what you picked. If that's not available, it falls back to the next best quality automatically.
-- **Video format:** Everything gets merged into a single MP4 file with H.264 codec for maximum compatibility.
-- **Audio quality:** Uses the best available source and converts to your chosen format.
-- **Downloads folder:** Set via the button or environment variable `PREDATOR_OUTPUT_DIR`.
-- **History:** Stored locally in `~/.predator/history.json` (keeps last 100 items).
-- **Concurrent downloads:** Max 3 at a time to not overwhelm your connection.
-- **Partial files:** If you cancel, the app cleans up `.part` and temp files automatically.
+### File Locations
+- **Downloads:** User-selected folder or `PREDATOR_OUTPUT_DIR` environment variable
+- **History:** `~/.predator/history.json` (keeps last 100 items)
+- **Settings:** `~/.predator/settings.json`
+- **Dependencies:** `~/.cache/yt-dlp/` (yt-dlp, ffmpeg, ffprobe)
+
+### Performance
+- **Concurrent downloads:** Max 3 at a time to not overwhelm your connection
+- **Progress updates:** Every 200ms for smooth progress bars
+- **Speed smoothing:** Uses exponential moving average for stable speed display
+
+### Cleanup
+- Cancelled downloads automatically clean up `.part` and temp files
+- Failed downloads after max retries also trigger cleanup
## Requirements
- Go 1.24+
-- Internet connection (obviously)
+- Internet connection
- yt-dlp and ffmpeg (auto-installed on first run)
-## Legal stuff
+## Troubleshooting
+
+### "ffmpeg not found" error
+The app will prompt you to download ffmpeg on first run. If you skip it, you can:
+1. Install ffmpeg manually from [ffmpeg.org](https://ffmpeg.org)
+2. Or delete `~/.cache/yt-dlp/` and restart the app - it'll ask again
+
+### Slow downloads
+- Check your internet connection
+- YouTube might be throttling (try a different resolution)
+- Max 3 concurrent downloads - close other apps using bandwidth
+
+### Download fails with "format not available"
+- Try a lower resolution
+- The video might be region-locked or unavailable
+- Try using "best" option for auto-selection of best available
+
+### "Permission denied" errors
+- Check if your download folder is writable
+- Try a different download location
+
+## Contributing
+
+Contributions are welcome! Please read the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines.
+
+## Legal Stuff
**This tool is for educational and personal use only.**
@@ -97,3 +181,4 @@ MIT License - see [LICENSE](LICENSE) file.
---
Built with [Wails](https://wails.io/) + Go + vanilla JS. No React, no bloat.
+
From 7cc915de5d2fafee6debd9bfc4f529c51cbca6f1 Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Thu, 5 Mar 2026 19:39:13 +0530
Subject: [PATCH 3/9] improve readme.md
---
README.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/README.md b/README.md
index acf8878..f999f78 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,8 @@
-
+
-
-
A simple desktop app for grabbing videos and audio from YouTube and Instagram. Built with Go and Wails (web tech frontend), using yt-dlp under the hood to do the heavy lifting.
From d9e21326d38cab0121a5043e45d800f0520d4116 Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Thu, 5 Mar 2026 19:44:49 +0530
Subject: [PATCH 4/9] backup
---
.github/workflows/submodule-check.yml | 62 +++++++++++++++++++++++++++
CONTRIBUTING.md | 24 +++++++++++
2 files changed, 86 insertions(+)
create mode 100644 .github/workflows/submodule-check.yml
create mode 100644 CONTRIBUTING.md
diff --git a/.github/workflows/submodule-check.yml b/.github/workflows/submodule-check.yml
new file mode 100644
index 0000000..4acbe7d
--- /dev/null
+++ b/.github/workflows/submodule-check.yml
@@ -0,0 +1,62 @@
+name: Submodule Cleanliness Check
+
+on:
+ pull_request:
+ branches: [ main, master ]
+ push:
+ branches: [ main, master ]
+
+jobs:
+ verify-submodules:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository with submodules
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+
+ - name: Initialize/Update submodules
+ run: |
+ git submodule sync --recursive
+ git submodule update --init --recursive --jobs 4
+
+ - name: Ensure submodule status has no anomalies
+ run: |
+ set -euo pipefail
+ anomalies=0
+
+ echo "Checking git submodule status output"
+ status_output=$(git submodule status --recursive || true)
+ echo "$status_output"
+
+ # If any line starts with '-', submodule is not initialized
+ if echo "$status_output" | grep -E '^-' >/dev/null; then
+ echo "ERROR: Found uninitialized submodules (lines starting with '-')" >&2
+ anomalies=1
+ fi
+
+ # If any line starts with '+', submodule commit is different than recorded
+ if echo "$status_output" | grep -E '^\+' >/dev/null; then
+ echo "ERROR: Found submodules not at recorded commit (lines starting with '+')" >&2
+ anomalies=1
+ fi
+
+ # If any line contains 'dirty', submodule has local changes
+ if echo "$status_output" | grep -E 'dirty' >/dev/null; then
+ echo "ERROR: Found dirty submodule working trees" >&2
+ anomalies=1
+ fi
+
+ # Also ensure working tree under submodules is clean
+ if ! git diff --submodule=diff --quiet; then
+ echo "ERROR: Detected differences in submodules (working tree not clean)" >&2
+ anomalies=1
+ fi
+
+ if [ "$anomalies" -ne 0 ]; then
+ echo "Submodule anomalies detected; failing the job." >&2
+ exit 1
+ fi
+
+ echo "Submodule status clean."
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..bd6cab8
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,24 @@
+# Contributing
+
+## Submodules policy
+
+This repository includes a `frontend/` submodule. To maintain reproducible builds and CI integrity:
+
+- Never commit a dirty submodule state. Ensure `git submodule status` shows no `dirty` markers.
+- Update submodules deterministically using:
+ - `git submodule sync --recursive`
+ - `git submodule update --init --recursive --checkout`
+- When you change the `frontend/` code, commit and push inside the submodule first, then update the pointer in this repo:
+ 1. `cd frontend`
+ 2. Work and commit: `git add -A && git commit -m "feat: ..." && git push`
+ 3. `cd .. && git add frontend && git commit -m "chore(frontend): bump submodule" && git push`
+
+CI will block merges that contain uninitialized, mismatched, or dirty submodule pointers (see `.github/workflows/submodule-check.yml`).
+
+## Development quickstart
+
+- Clone with submodules: `git clone --recurse-submodules https://github.com/Aswanidev-vs/Predator.git`
+- Or initialize after clone: `git submodule update --init --recursive`
+- Go toolchain: see `go.mod` (`go 1.25`). Build/dev commands:
+ - Dev: `wails dev`
+ - Build: `wails build`
From f7e2b029f567c784400c02a28b3a00d6db6937ab Mon Sep 17 00:00:00 2001
From: Aswanidev vs
Date: Thu, 5 Mar 2026 20:17:59 +0530
Subject: [PATCH 5/9] Update README.md
---
README.md | 86 +++++++++++++------------------------------------------
1 file changed, 20 insertions(+), 66 deletions(-)
diff --git a/README.md b/README.md
index fd9fb8d..f999f78 100644
--- a/README.md
+++ b/README.md
@@ -70,27 +70,25 @@ go mod download
**Development mode:**
```bash
-go run main.go
+wails dev
```
**Build for production:**
```bash
-# For Windows
-go install fyne.io/tools/cmd/fyne-cross@latest
-fyne-cross windows
-
-# For macOS
-fyne-cross darwin
-
-# For Linux
-fyne-cross linux
+wails build
```
-## Usage
+**First time setup:** The app will ask to download yt-dlp and ffmpeg automatically. Just say yes - it handles everything and caches them locally. No need to install anything else manually.
## How to Use
-## Architecture
+1. **Open the app** - It'll show the main download tab
+2. **Set your download folder** - Click "Change Download Location" (or it'll use the current folder)
+3. **Paste a URL** - YouTube or Instagram links both work
+4. **Pick your format:**
+ - For video: Choose resolution (144p to 4K, or "best")
+ - For audio: Pick format (MP3, M4A, Opus, WAV)
+5. **Hit download** - Watch the progress bar do its thing
## Keyboard Shortcuts
@@ -132,41 +130,7 @@ fyne-cross linux
- Cancelled downloads automatically clean up `.part` and temp files
- Failed downloads after max retries also trigger cleanup
-- `main()`: Initializes the UI and event handlers
-- `fetchVideoInfo()`: Dynamically fetches video metadata and available resolutions
-- `formatBytes()`: Converts byte sizes to human-readable format
-- `formatETA()`: Converts duration to HH:MM:SS format
-
-## Project Structure
-
-```
-Predator/
-├── main.go # Main application code
-├── go.mod # Go module definition
-├── go.sum # Go module checksums
-├── logov4.png # Application icon
-├── asset/ # Icon and logo assets
-├── fyne-cross/ # Cross-platform build configuration
-├── sample.txt # Code sample reference
-└── samplev*.txt # Additional code samples
-```
-
-## Dependencies
-
-- **fyne.io/fyne/v2**: Cross-platform GUI framework
-- **github.com/lrstanley/go-ytdlp**: Go wrapper for yt-dlp
-
-
-## Performance
-
-- **Lazy Metadata Loading**: Video info is only fetched when a valid URL is entered
-- **Debounced Input**: 600ms debounce on URL input to prevent excessive API calls
-- **Atomic Operations**: Thread-safe state management for concurrent operations
-- **Streaming Download**: Supports live progress updates during download
-
-## Disclaimer
-
-**IMPORTANT LEGAL NOTICE**
+## Requirements
- Go 1.24+
- Internet connection
@@ -199,30 +163,20 @@ Contributions are welcome! Please read the [CONTRIBUTING.md](CONTRIBUTING.md) fi
## Legal Stuff
-- **Copyright Compliance**: Users must respect copyright laws and YouTube's Terms of Service. Downloading copyrighted content without proper authorization may be illegal in your jurisdiction.
-- **Terms of Service**: By using this application, you acknowledge that you are bound by YouTube's Terms of Service and agree not to violate them.
-- **Personal Use Only**: This tool is intended for downloading content you own or have permission to download.
-- **Liability**: The authors and contributors of Predator are not responsible for:
- - Any copyright infringement or violations of third-party rights
- - Misuse of downloaded content
- - Any legal consequences arising from use of this application
- - Data loss or corruption
- - Any other damages resulting from use of this software
+**This tool is for educational and personal use only.**
-### Responsible Use
+- Respect copyright laws and YouTube's Terms of Service
+- Only download content you own or have permission to download
+- Don't use this to redistribute copyrighted material
+- The authors aren't responsible for how you use this tool
-Users should:
-- Only download content they have permission to download
-- Respect content creators' rights and intellectual property
-- Use downloaded content in compliance with local laws
-- Not distribute copyrighted content without proper licensing
-- Be aware that many YouTube videos are protected by copyright
+Basically: be cool, don't pirate stuff you shouldn't.
-### No Warranty
+## License
-This application is provided "as-is" without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, or non-infringement.
+MIT License - see [LICENSE](LICENSE) file.
-The authors assume no responsibility for any illegal activities or misuse of this tool. By using Predator, you accept all risks and responsibilities associated with your downloads.
+---
Built with [Wails](https://wails.io/) + Go + vanilla JS. No React, no bloat.
From 30cbc5449dff6d22fbf6faa3f2883ae00018c0ec Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Thu, 5 Mar 2026 23:50:56 +0530
Subject: [PATCH 6/9] fix the playlist issue and related issues
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index f38c484..61caa98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
build/bin
TODO.md
+node_modules/
\ No newline at end of file
From a3e8e708316fee97aca5fafcb1b6fc42b0821c93 Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Fri, 6 Mar 2026 17:59:41 +0530
Subject: [PATCH 7/9] backup code
---
.gitignore | 3 ++-
README.md | 10 +---------
2 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/.gitignore b/.gitignore
index 61caa98..b00c9e3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
build/bin
TODO.md
-node_modules/
\ No newline at end of file
+node_modules/
+
diff --git a/README.md b/README.md
index f999f78..9ab3fd7 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ A simple desktop app for grabbing videos and audio from YouTube and Instagram. B
- **Duplicate detection** - Warns if content was already downloaded
- **Custom download folder** - Set once, remembers forever
- **Download history** - View past downloads, filter by type, open file location
-- **Theme toggle** - Dark and light themes (Ctrl+T)
+
## Getting Started
@@ -90,14 +90,6 @@ wails build
- For audio: Pick format (MP3, M4A, Opus, WAV)
5. **Hit download** - Watch the progress bar do its thing
-## Keyboard Shortcuts
-
-| Shortcut | Action |
-|----------|--------|
-| `Ctrl+L` | Focus the URL input |
-| `Ctrl+D` | Start download (when button is active) |
-| `Ctrl+T` | Toggle dark/light theme |
-| `Esc` | Close any open modal |
**Pro tips:**
- Playlists show a modal where you can select specific videos to download
From f7f06e1a227122b02d49d9f5abbeb18f089836e2 Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Fri, 6 Mar 2026 18:05:36 +0530
Subject: [PATCH 8/9] ignore
---
.gitignore | 1 -
1 file changed, 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index b00c9e3..214c86a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
build/bin
-
TODO.md
node_modules/
From 07ec0f84905be4059fd8896808df90db95349bfd Mon Sep 17 00:00:00 2001
From: Aswanidev
Date: Sat, 14 Mar 2026 19:24:17 +0530
Subject: [PATCH 9/9] backup
---
.gitignore | 1 -
1 file changed, 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index b00c9e3..214c86a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
build/bin
-
TODO.md
node_modules/