Skip to content

Commit c63689e

Browse files
update 08/01/2026
Co-Authored-By: M Ramzan Ch <191318204+MegaMind-Solution@users.noreply.github.com>
1 parent e918d0b commit c63689e

7 files changed

Lines changed: 452 additions & 260 deletions

File tree

README.md

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# DownGD 2.O 🚀
1+
# DownGD 2.0 🚀
22

33
A powerful, pixel-perfect, single-page web application to download files or folders from GitHub repositories. It supports public URLs, private repositories (via Token), and requires **no server**.
44

@@ -8,10 +8,11 @@ A powerful, pixel-perfect, single-page web application to download files or fold
88
- **Client-Side Only**: Works 100% in the browser (GitHub Pages / Vercel compatible).
99
- **Smart Routing**: Share links like `mysite.com/#/github.com/user/repo` to auto-start downloads.
1010
- **Private Repo Support**: Enter a Personal Access Token (stored locally) to access private files.
11-
- **Beautiful UI**: Smooth animations, file previews, and 2.Ogress bars.
11+
- **Partial Downloads**: Download large repositories in steps (e.g., files 0-100) to avoid browser limits.
12+
- **Beautiful UI**: Smooth animations, file previews, and progress bars.
1213
- **Theming**: 8 Themes including Dracula, Cyberpunk, AMOLED, and more.
1314

14-
## 🔗 How to Use Samrt Links
15+
## 🔗 How to Use Smart Links
1516

1617
To share a direct download link with a friend, simply add the GitHub URL after the hash (`?=`):
1718

@@ -21,38 +22,55 @@ To share a direct download link with a friend, simply add the GitHub URL after t
2122
### With Name
2223
> [https://downgd.github.io/?=https://github.com/facebook/react/tree/main/packages&name=react-core](https://downgd.github.io/?=https://github.com/facebook/react/tree/main/packages&name=react-core)
2324
25+
### Partial / Range Download
26+
If a repository is too large, you can limit the number of files to download using `&st` (Start) and `&mx` (Max).
27+
> [https://downgd.github.io/?=https://github.com/facebook/react/tree/main/packages&st=50&mx=150](https://downgd.github.io/?=https://github.com/facebook/react/tree/main/packages&st=50&mx=150)
28+
29+
* `&st=50`: Start downloading from the 50th file.
30+
* `&mx=150`: Stop downloading at the 150th file.
31+
2432
When the user visits this link, the app will:
2533
1. Parse the URL.
26-
2. Auto-fill the input.
27-
3. Immediately start fetching and zipping the folder.
34+
2. Auto-fill the input and limit fields.
35+
3. Immediately start fetching and zipping files **50 to 150**.
2836

2937
## 🔑 Private Repositories
3038

3139
1. Generate a GitHub Token (Settings > Developer Settings > Personal Access Tokens).
3240
2. Scopes needed: `repo` (for private) or just public access.
33-
3. Open DownGD 2.O > Click Gear Icon (⚙️).
41+
3. Open DownGD 2.0 > Click Gear Icon (⚙️).
3442
4. Paste token. It is saved in your browser's LocalStorage and never sent to any 3rd party server.
35-
Got it 👍
36-
Below are **ONLY the two new sections** you asked for — **Script details** and **Extension details** — written to drop directly into your existing README **without changing anything else**.
3743

3844
---
39-
## 🔗 Api
45+
## 🔗 API
4046

41-
To embed a direct download link with in your website/page, simply use this pattern
47+
To embed a direct download link within your website/page, simply use this pattern. The API also supports the `&st` and `&mx` parameters.
4248

43-
### embed as a link
49+
### Embed as a link
4450
```html
45-
<a href="https://downgd.github.io/api/?=https://github.com/facebook/react/tree/main/packages&name=react-cor"></a>
51+
<!-- Download all files -->
52+
<a href="https://downgd.github.io/api/?=https://github.com/facebook/react/tree/main/packages&name=react-core">Download Core</a>
53+
54+
<!-- Download files 0 to 100 only -->
55+
<a href="https://downgd.github.io/api/?=https://github.com/facebook/react/tree/main/packages&name=react-core&st=0&mx=100">Download Part 1</a>
4656
```
47-
### or use like this js
57+
58+
### Use via JavaScript
4859
```javascript
4960
function triggerDownload() {
5061
const repo = "https://github.com/facebook/react/tree/main/packages";
51-
const apiUrl = `https://git-zip-pro.vercel.app/api/?url=${repo}`;
62+
63+
// Optional: Add limits for large folders
64+
const params = "&st=0&mx=200";
65+
66+
const apiUrl = `https://git-zip-pro.vercel.app/api/?url=${repo}${params}`;
5267

5368
window.open(apiUrl, '_blank');
5469
}
5570
```
71+
72+
---
73+
5674
## 📜 Userscript
5775

5876
DownGD also provides a **GitHub-integrated userscript** that adds a native-looking **Download button directly inside GitHub menus**.
@@ -61,7 +79,6 @@ DownGD also provides a **GitHub-integrated userscript** that adds a native-looki
6179

6280
* Injects a **“Download Repo / Folder / File”** action into GitHub’s context menus
6381
* Automatically detects:
64-
6582
* **Repository menu** → shows **Download Repo**
6683
* **Folder menu** → shows **Download Folder**
6784
* **File menu** → shows **Download**
@@ -95,6 +112,7 @@ DownGD also provides a **GitHub-integrated userscript** that adds a native-looki
95112
The DownGD userscript can also be packaged as a **lightweight browser extension** using ScriptRunner-style extensions.
96113

97114
## [Download Now](https://downgd.github.io/api/?=https://github.com/DownGD/downgd.github.io/tree/main/extension&name=Extension)
115+
98116
### 🚀 Extension Capabilities
99117

100118
* Zero background services
@@ -118,7 +136,6 @@ The DownGD userscript can also be packaged as a **lightweight browser extension*
118136

119137
---
120138

121-
122139
## 🤝 Contributing
123140

124141
Feel free to open issues or submit PRs. The code is written in JS/CSS for maximum simplicity and performance.

api/api.js

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
2-
31
const API_BASE = "https://api.github.com/repos";
42
const RAW_BASE = "https://raw.githubusercontent.com";
53

6-
74
const config = {
85
token: localStorage.getItem("gh_token") || "",
96
controller: new AbortController()
107
};
118

12-
139
const ui = {
1410
card: document.getElementById("card"),
1511
title: document.getElementById("status-title"),
@@ -18,19 +14,27 @@ const ui = {
1814
bar: document.getElementById("bar")
1915
};
2016

21-
2217
(function init() {
23-
2418
const params = new URLSearchParams(window.location.search);
2519

2620
let url = params.get("url") || params.get("");
2721
const customName = params.get("name");
2822

23+
// --- NEW: Parse Limit Parameters ---
24+
// We use undefined if not present, so we can handle defaults later
25+
const startLimit = params.has("st") ? parseInt(params.get("st")) : null;
26+
const maxLimit = params.has("mx") ? parseInt(params.get("mx")) : null;
2927

3028
if (!url) {
31-
3229
const search = window.location.search.substring(1);
30+
// Logic to extract URL if it's the first param without a key
31+
// Note: This might need adjustment if params like &st= are first,
32+
// but assuming url is usually first or named "url="
3333
if(search.startsWith("=")) url = search.substring(1);
34+
else if(!search.includes("url=") && search.includes("&")) {
35+
// Handle case: ?github.com/user/repo&st=50
36+
url = search.split("&")[0];
37+
}
3438
}
3539

3640
if (!url) return setError("Missing URL", "Please provide a GitHub link.");
@@ -41,10 +45,11 @@ const ui = {
4145
else url = "https://github.com/" + url;
4246
}
4347

44-
startDownload(url, customName);
48+
// Pass limits to startDownload
49+
startDownload(url, customName, startLimit, maxLimit);
4550
})();
4651

47-
async function startDownload(url, saveName) {
52+
async function startDownload(url, saveName, startLimit, maxLimit) {
4853
try {
4954
const parsed = parseGitHubUrl(url);
5055
if (!parsed) throw new Error("Invalid GitHub URL");
@@ -60,7 +65,8 @@ async function startDownload(url, saveName) {
6065
if (parsed.type === "blob") {
6166
await processSingleFile(parsed, saveName);
6267
} else {
63-
await processFolder(parsed, saveName);
68+
// Pass limits to processFolder
69+
await processFolder(parsed, saveName, startLimit, maxLimit);
6470
}
6571

6672
handleSuccess();
@@ -70,7 +76,6 @@ async function startDownload(url, saveName) {
7076
}
7177
}
7278

73-
7479
function handleSuccess() {
7580
ui.card.classList.add("state-success");
7681
ui.title.innerText = "Downloaded";
@@ -81,27 +86,21 @@ function handleSuccess() {
8186

8287
setTimeout(() => {
8388
if (returnUrl && returnUrl.startsWith("http")) {
84-
8589
ui.text.innerText = "Returning...";
8690
window.location.href = returnUrl;
8791
}
8892
else if (window.history.length > 1 && document.referrer) {
89-
9093
ui.text.innerText = "Returning to page...";
9194
window.history.back();
9295
}
9396
else {
94-
9597
ui.text.innerText = "Closing tab...";
96-
9798
window.close();
98-
9999
try { window.opener = null; window.open("", "_self"); window.close(); } catch (e) {}
100100

101101
setTimeout(() => {
102102
if (!window.closed) {
103103
ui.text.innerText = "Download complete. You can close this tab.";
104-
105104
if (document.referrer) {
106105
ui.text.innerText = "Returning...";
107106
window.location.href = document.referrer;
@@ -112,29 +111,51 @@ function handleSuccess() {
112111
}, 1500);
113112
}
114113

115-
116-
117-
async function processFolder(parsed, customName) {
114+
async function processFolder(parsed, customName, startOpt, maxOpt) {
118115
ui.title.innerText = "Fetching files...";
119116
const data = await fetchAPI(`/${parsed.user}/${parsed.repo}/git/trees/${parsed.branch}?recursive=1`);
120117
const targetPath = parsed.path ? parsed.path + "/" : "";
121-
const files = data.tree.filter(f => f.type === "blob" && (parsed.path === "" || f.path.startsWith(targetPath)));
118+
119+
// Get ALL valid blobs first
120+
const allFiles = data.tree.filter(f => f.type === "blob" && (parsed.path === "" || f.path.startsWith(targetPath)));
121+
122+
if (!allFiles.length) throw new Error("Folder is empty");
123+
124+
// --- NEW: Slicing Logic ---
125+
let start = startOpt !== null ? startOpt : 0;
126+
let end = maxOpt !== null ? maxOpt : allFiles.length;
127+
128+
// Validation to prevent crashes
129+
if (start < 0) start = 0;
130+
if (end > allFiles.length) end = allFiles.length;
131+
if (start >= end) {
132+
// Fallback: if user sets Start > End, just download everything or reset Start
133+
start = 0;
134+
end = allFiles.length;
135+
}
136+
137+
// Slice the files array
138+
const filesToDownload = allFiles.slice(start, end);
139+
const totalToDownload = filesToDownload.length;
122140

123-
if (!files.length) throw new Error("Folder is empty");
141+
if (totalToDownload === 0) throw new Error("No files in selected range");
142+
143+
ui.title.innerText = `Downloading (${start}-${end})...`;
144+
ui.text.innerText = `Preparing ${totalToDownload} files...`;
145+
// --------------------------
124146

125-
ui.title.innerText = "Downloading...";
126147
const zip = new JSZip();
127148
let count = 0;
128149
const CHUNK = 5;
129150

130-
for (let i = 0; i < files.length; i += CHUNK) {
131-
await Promise.all(files.slice(i, i + CHUNK).map(async f => {
151+
for (let i = 0; i < totalToDownload; i += CHUNK) {
152+
await Promise.all(filesToDownload.slice(i, i + CHUNK).map(async f => {
132153
try {
133154
const content = await fetchContent(parsed, f);
134155
const zipPath = parsed.path ? f.path.substring(parsed.path.length + 1) : f.path;
135156
zip.file(zipPath, content);
136157
count++;
137-
updateProgress(count, files.length);
158+
updateProgress(count, totalToDownload);
138159
} catch (e) {}
139160
}));
140161
}
@@ -160,7 +181,6 @@ async function processSingleFile(parsed, customName) {
160181
saveAs(new Blob([content]), fileName);
161182
}
162183

163-
164184
async function fetchAPI(endpoint) {
165185
const headers = config.token ? { "Authorization": `token ${config.token}` } : {};
166186
const res = await fetch(`${API_BASE}${endpoint}`, { headers });

api/readme.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ You can also use the hash or `?=` syntax:
4646
```
4747
https://DownGD.github.io/api/?=https://github.com/facebook/react/tree/main/packages
4848
```
49+
### Partial / Range Download
50+
If a repository is too large, you can limit the number of files to download using `&st` (Start) and `&mx` (Max).
51+
```html
52+
https://downgd.github.io/?=https://github.com/facebook/react/tree/main/packages&st=50&mx=150
53+
```
54+
55+
* `&st=50`: Start downloading from the 50th file.
56+
* `&mx=150`: Stop downloading at the 150th file.
57+
58+
When the user visits this link, the app will:
59+
1. Parse the URL.
60+
2. Auto-fill the input and limit fields.
61+
3. Immediately start fetching and zipping files **50 to 150**.
4962

5063
### 4. Integration Example (JavaScript)
5164
You can use an `iframe` or `window.open` to trigger downloads from your own website:

0 commit comments

Comments
 (0)