Skip to content

Commit 09a8eba

Browse files
committed
i think isshin is git gud lvl tough
0 parents  commit 09a8eba

6 files changed

Lines changed: 351 additions & 0 deletions

File tree

.gitignore

Whitespace-only changes.

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
[![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
2+
3+
Git - fast, scalable, distributed revision control system
4+
=========================================================
5+
6+
Git is a fast, scalable, distributed revision control system with an
7+
unusually rich command set that provides both high-level operations
8+
and full access to internals.
9+
10+
Git is an Open Source project covered by the GNU General Public
11+
License version 2 (some parts of it are under different licenses,
12+
compatible with the GPLv2). It was originally written by Linus
13+
Torvalds with help of a group of hackers around the net.
14+
15+
Please read the file [INSTALL][] for installation instructions.
16+
17+
Many Git online resources are accessible from <https://git-scm.com/>
18+
including full documentation and Git related tools.
19+
20+
See [Documentation/gittutorial.adoc][] to get started, then see
21+
[Documentation/giteveryday.adoc][] for a useful minimum set of commands, and
22+
`Documentation/git-<commandname>.adoc` for documentation of each command.
23+
If git has been correctly installed, then the tutorial can also be
24+
read with `man gittutorial` or `git help tutorial`, and the
25+
documentation of each command with `man git-<commandname>` or `git help
26+
<commandname>`.
27+
28+
CVS users may also want to read [Documentation/gitcvs-migration.adoc][]
29+
(`man gitcvs-migration` or `git help cvs-migration` if git is
30+
installed).
31+
32+
The user discussion and development of Git take place on the Git
33+
mailing list -- everyone is welcome to post bug reports, feature
34+
requests, comments and patches to git@vger.kernel.org (read
35+
[Documentation/SubmittingPatches][] for instructions on patch submission
36+
and [Documentation/CodingGuidelines][]).
37+
38+
Those wishing to help with error message, usage and informational message
39+
string translations (localization l10) should see [po/README.md][]
40+
(a `po` file is a Portable Object file that holds the translations).
41+
42+
To subscribe to the list, send an email to <git+subscribe@vger.kernel.org>
43+
(see https://subspace.kernel.org/subscribing.html for details). The mailing
44+
list archives are available at <https://lore.kernel.org/git/>,
45+
<https://marc.info/?l=git> and other archival sites.
46+
47+
Issues which are security relevant should be disclosed privately to
48+
the Git Security mailing list <git-security@googlegroups.com>.
49+
50+
The maintainer frequently sends the "What's cooking" reports that
51+
list the current status of various development topics to the mailing
52+
list. The discussion following them give a good reference for
53+
project status, development direction and remaining tasks.
54+
55+
The name "git" was given by Linus Torvalds when he wrote the very
56+
first version. He described the tool as "the stupid content tracker"
57+
and the name as (depending on your mood):
58+
59+
- random three-letter combination that is pronounceable, and not
60+
actually used by any common UNIX command. The fact that it is a
61+
mispronunciation of "get" may or may not be relevant.
62+
- stupid. contemptible and despicable. simple. Take your pick from the
63+
dictionary of slang.
64+
- "global information tracker": you're in a good mood, and it actually
65+
works for you. Angels sing, and a light suddenly fills the room.
66+
- "goddamn idiotic truckload of sh*t": when it breaks
67+
68+
[INSTALL]: INSTALL
69+
[Documentation/gittutorial.adoc]: Documentation/gittutorial.adoc
70+
[Documentation/giteveryday.adoc]: Documentation/giteveryday.adoc
71+
[Documentation/gitcvs-migration.adoc]: Documentation/gitcvs-migration.adoc
72+
[Documentation/SubmittingPatches]: Documentation/SubmittingPatches
73+
[Documentation/CodingGuidelines]: Documentation/CodingGuidelines
74+
[po/README.md]: po/README.md

cmd/warpcentral/main.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"os/signal"
8+
"strings"
9+
"syscall"
10+
11+
"github.com/LaZyMugen/warpcentral/internal/downloader"
12+
)
13+
14+
func formatBytes(n int64) string {
15+
const unit = 1024
16+
if n < unit {
17+
return fmt.Sprintf("%d B", n)
18+
}
19+
div, exp := int64(unit), 0
20+
for v := n / unit; v >= unit; v /= unit {
21+
div *= unit
22+
exp++
23+
}
24+
return fmt.Sprintf("%.2f %cB", float64(n)/float64(div), "KMGTPE"[exp])
25+
}
26+
27+
func formatSpeed(bps float64) string {
28+
if bps < 1024 {
29+
return fmt.Sprintf("%.0f B/s", bps)
30+
}
31+
if bps < 1024*1024 {
32+
return fmt.Sprintf("%.2f KB/s", bps/1024)
33+
}
34+
return fmt.Sprintf("%.2f MB/s", bps/(1024*1024))
35+
}
36+
37+
func main() {
38+
if len(os.Args) < 3 {
39+
fmt.Println("Usage:")
40+
fmt.Println(" warpcentral download <url> [output_file]")
41+
return
42+
}
43+
44+
cmd := strings.ToLower(os.Args[1])
45+
if cmd != "download" {
46+
fmt.Println("Unknown command:", cmd)
47+
return
48+
}
49+
50+
rawURL := os.Args[2]
51+
out := ""
52+
if len(os.Args) >= 4 {
53+
out = os.Args[3]
54+
}
55+
56+
ctx, cancel := context.WithCancel(context.Background())
57+
defer cancel()
58+
59+
// Ctrl+C cancel
60+
sigCh := make(chan os.Signal, 1)
61+
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)
62+
go func() {
63+
<-sigCh
64+
fmt.Println("\nStopping...")
65+
cancel()
66+
}()
67+
68+
dl := downloader.New()
69+
70+
fmt.Println("Starting download...")
71+
72+
err := dl.Download(ctx, rawURL, out, func(p downloader.Progress) {
73+
if p.Total > 0 {
74+
percent := float64(p.Downloaded) * 100 / float64(p.Total)
75+
fmt.Printf("\r%s / %s (%.2f%%) | %s",
76+
formatBytes(p.Downloaded),
77+
formatBytes(p.Total),
78+
percent,
79+
formatSpeed(p.SpeedBps),
80+
)
81+
} else {
82+
fmt.Printf("\r%s / ? | %s",
83+
formatBytes(p.Downloaded),
84+
formatSpeed(p.SpeedBps),
85+
)
86+
}
87+
})
88+
89+
if err != nil {
90+
fmt.Println("\nFailed:", err)
91+
return
92+
}
93+
94+
fmt.Println("\nDone.")
95+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/LaZyMugen/warpcentral
2+
3+
go 1.25.0

internal/downloader/downloader.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package downloader
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"net/url"
10+
"os"
11+
"path/filepath"
12+
"strings"
13+
"time"
14+
)
15+
16+
type Progress struct {
17+
Downloaded int64
18+
Total int64 // -1 if unknown
19+
SpeedBps float64
20+
}
21+
22+
type Downloader struct {
23+
Client *http.Client
24+
}
25+
26+
func New() *Downloader {
27+
return &Downloader{
28+
Client: &http.Client{
29+
Timeout: 0,
30+
},
31+
}
32+
}
33+
34+
func (d *Downloader) Download(ctx context.Context, rawURL, outPath string, onProgress func(Progress)) error {
35+
if strings.TrimSpace(rawURL) == "" {
36+
return errors.New("empty url")
37+
}
38+
_, err := url.ParseRequestURI(rawURL)
39+
if err != nil {
40+
return fmt.Errorf("invalid url: %w", err)
41+
}
42+
43+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
44+
if err != nil {
45+
return err
46+
}
47+
req.Header.Set("User-Agent", "WarpCentral/0.1")
48+
49+
resp, err := d.Client.Do(req)
50+
if err != nil {
51+
return err
52+
}
53+
defer resp.Body.Close()
54+
55+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
56+
return fmt.Errorf("http error: %s", resp.Status)
57+
}
58+
59+
if outPath == "" {
60+
outPath = guessFileName(resp, rawURL)
61+
}
62+
outPath = filepath.Clean(outPath)
63+
64+
f, err := os.Create(outPath)
65+
if err != nil {
66+
return err
67+
}
68+
defer f.Close()
69+
70+
total := resp.ContentLength // -1 if unknown
71+
var downloaded int64
72+
73+
start := time.Now()
74+
75+
// For speed calculation: bytes over last interval
76+
lastTime := time.Now()
77+
lastBytes := int64(0)
78+
79+
ticker := time.NewTicker(500 * time.Millisecond)
80+
defer ticker.Stop()
81+
82+
emitProgress := func() {
83+
now := time.Now()
84+
dt := now.Sub(lastTime).Seconds()
85+
if dt <= 0 {
86+
return
87+
}
88+
delta := downloaded - lastBytes
89+
speed := float64(delta) / dt
90+
91+
lastTime = now
92+
lastBytes = downloaded
93+
94+
if onProgress != nil {
95+
onProgress(Progress{
96+
Downloaded: downloaded,
97+
Total: total,
98+
SpeedBps: speed,
99+
})
100+
}
101+
}
102+
103+
// progress loop (NO goroutine needed)
104+
buf := make([]byte, 1024*256)
105+
106+
for {
107+
select {
108+
case <-ctx.Done():
109+
// If we already fully downloaded the file, consider it a success.
110+
if total > 0 && downloaded >= total {
111+
emitProgress()
112+
return nil
113+
}
114+
emitProgress()
115+
return ctx.Err()
116+
117+
case <-ticker.C:
118+
emitProgress()
119+
default:
120+
n, readErr := resp.Body.Read(buf)
121+
if n > 0 {
122+
_, wErr := f.Write(buf[:n])
123+
if wErr != nil {
124+
return wErr
125+
}
126+
downloaded += int64(n)
127+
}
128+
129+
if readErr == io.EOF {
130+
break
131+
}
132+
if readErr != nil {
133+
return readErr
134+
}
135+
}
136+
}
137+
138+
// final progress (avg-ish)
139+
elapsed := time.Since(start).Seconds()
140+
avgSpeed := float64(downloaded)
141+
if elapsed > 0 {
142+
avgSpeed = avgSpeed / elapsed
143+
}
144+
if onProgress != nil {
145+
onProgress(Progress{
146+
Downloaded: downloaded,
147+
Total: total,
148+
SpeedBps: avgSpeed,
149+
})
150+
}
151+
152+
return nil
153+
}
154+
155+
func guessFileName(resp *http.Response, rawURL string) string {
156+
cd := resp.Header.Get("Content-Disposition")
157+
if cd != "" {
158+
lower := strings.ToLower(cd)
159+
idx := strings.Index(lower, "filename=")
160+
if idx != -1 {
161+
part := cd[idx+len("filename="):]
162+
part = strings.TrimSpace(part)
163+
part = strings.Trim(part, `"'`)
164+
if part != "" {
165+
return part
166+
}
167+
}
168+
}
169+
170+
u, err := url.Parse(rawURL)
171+
if err == nil {
172+
base := filepath.Base(u.Path)
173+
if base != "" && base != "/" && base != "." {
174+
return base
175+
}
176+
}
177+
178+
return "download.bin"
179+
}

sample-30s.mp4

20.7 MB
Binary file not shown.

0 commit comments

Comments
 (0)