mirror of
https://github.com/Xpl0itU/WiiUDownloader.git
synced 2025-05-15 15:44:56 -04:00
Revert aria2go changes
This commit is contained in:
parent
60ecc29424
commit
abdec05445
21 changed files with 92 additions and 1333 deletions
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
||||||
msystem: UCRT64
|
msystem: UCRT64
|
||||||
release: true
|
release: true
|
||||||
update: true
|
update: true
|
||||||
install: zip git mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-gtk3 mingw-w64-ucrt-x86_64-pkg-config mingw-w64-ucrt-x86_64-go mingw-w64-ucrt-x86_64-ntldd-git make
|
install: zip git mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-gtk3 mingw-w64-ucrt-x86_64-pkg-config mingw-w64-ucrt-x86_64-go mingw-w64-ucrt-x86_64-ntldd-git
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
python3 grabTitles.py
|
python3 grabTitles.py
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -9,5 +9,3 @@ main
|
||||||
*.a
|
*.a
|
||||||
out/
|
out/
|
||||||
log.txt
|
log.txt
|
||||||
aria2-lib/
|
|
||||||
_obj/
|
|
|
@ -1,8 +1,7 @@
|
||||||
FROM ubuntu:22.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive \
|
ENV DEBIAN_FRONTEND=noninteractive \
|
||||||
PATH="$HOME/go/bin:/usr/local/go/bin:$PATH" \
|
PATH="$HOME/go/bin:/usr/local/go/bin:$PATH"
|
||||||
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:pkg/aria2go/aria2-lib/lib"
|
|
||||||
|
|
||||||
RUN mkdir -p /usr/share/man/man1 /usr/share/man/man2 && \
|
RUN mkdir -p /usr/share/man/man1 /usr/share/man/man2 && \
|
||||||
apt -y --no-install-recommends update && \
|
apt -y --no-install-recommends update && \
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
|
@ -27,12 +28,12 @@ func getCert(tmdData []byte, id int, numContents uint16) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultCert(cancelCtx context.Context, progressReporter ProgressReporter, buffer []byte, ariaSessionPath string) ([]byte, error) {
|
func getDefaultCert(cancelCtx context.Context, progressReporter ProgressReporter, client *http.Client, buffer []byte) ([]byte, error) {
|
||||||
if len(cetkData) >= 0x350+0x300 {
|
if len(cetkData) >= 0x350+0x300 {
|
||||||
return cetkData[0x350 : 0x350+0x300], nil
|
return cetkData[0x350 : 0x350+0x300], nil
|
||||||
}
|
}
|
||||||
cetkDir := path.Join(os.TempDir(), "cetk")
|
cetkDir := path.Join(os.TempDir(), "cetk")
|
||||||
if err := downloadFile(cancelCtx, progressReporter, "http://ccs.cdn.c.shop.nintendowifi.net/ccs/download/000500101000400a/cetk", cetkDir, true, buffer, ariaSessionPath); err != nil {
|
if err := downloadFile(cancelCtx, progressReporter, client, "http://ccs.cdn.c.shop.nintendowifi.net/ccs/download/000500101000400a/cetk", cetkDir, true, buffer); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cetkData, err := os.ReadFile(cetkDir)
|
cetkData, err := os.ReadFile(cetkDir)
|
||||||
|
@ -50,7 +51,7 @@ func getDefaultCert(cancelCtx context.Context, progressReporter ProgressReporter
|
||||||
return nil, fmt.Errorf("failed to download OSv10 cetk, length: %d", len(cetkData))
|
return nil, fmt.Errorf("failed to download OSv10 cetk, length: %d", len(cetkData))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateCert(tmdData []byte, contentCount uint16, progressReporter ProgressReporter, cancelCtx context.Context, buffer []byte, ariaSessionPath string) (bytes.Buffer, error) {
|
func GenerateCert(tmdData []byte, contentCount uint16, progressReporter ProgressReporter, client *http.Client, cancelCtx context.Context, buffer []byte) (bytes.Buffer, error) {
|
||||||
cert := bytes.Buffer{}
|
cert := bytes.Buffer{}
|
||||||
|
|
||||||
cert0, err := getCert(tmdData, 0, contentCount)
|
cert0, err := getCert(tmdData, 0, contentCount)
|
||||||
|
@ -65,7 +66,7 @@ func GenerateCert(tmdData []byte, contentCount uint16, progressReporter Progress
|
||||||
}
|
}
|
||||||
cert.Write(cert1)
|
cert.Write(cert1)
|
||||||
|
|
||||||
defaultCert, err := getDefaultCert(cancelCtx, progressReporter, buffer, ariaSessionPath)
|
defaultCert, err := getDefaultCert(cancelCtx, progressReporter, client, buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bytes.Buffer{}, err
|
return bytes.Buffer{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
wiiudownloader "github.com/Xpl0itU/WiiUDownloader"
|
wiiudownloader "github.com/Xpl0itU/WiiUDownloader"
|
||||||
"github.com/gotk3/gotk3/glib"
|
"github.com/gotk3/gotk3/glib"
|
||||||
|
@ -40,15 +42,17 @@ func main() {
|
||||||
logger.Fatal(err.Error())
|
logger.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpDir, err := os.MkdirTemp("", "wiiudownloader")
|
client := &http.Client{
|
||||||
if err != nil {
|
Transport: &http.Transport{
|
||||||
logger.Fatal(err.Error())
|
MaxIdleConns: 1000,
|
||||||
|
MaxIdleConnsPerHost: 1000,
|
||||||
|
MaxConnsPerHost: 100,
|
||||||
|
},
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
ariaSessionPath := filepath.Join(tmpDir, "wiiudownloader.session")
|
|
||||||
|
|
||||||
app.Connect("activate", func() {
|
app.Connect("activate", func() {
|
||||||
win := NewMainWindow(app, wiiudownloader.GetTitleEntries(wiiudownloader.TITLE_CATEGORY_GAME), logger, ariaSessionPath)
|
win := NewMainWindow(app, wiiudownloader.GetTitleEntries(wiiudownloader.TITLE_CATEGORY_GAME), logger, client)
|
||||||
win.ShowAll()
|
win.ShowAll()
|
||||||
app.AddWindow(win.window)
|
app.AddWindow(win.window)
|
||||||
app.GetActiveWindow().Show()
|
app.GetActiveWindow().Show()
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -39,10 +40,10 @@ type MainWindow struct {
|
||||||
titles []wiiudownloader.TitleEntry
|
titles []wiiudownloader.TitleEntry
|
||||||
decryptContents bool
|
decryptContents bool
|
||||||
currentRegion uint8
|
currentRegion uint8
|
||||||
ariaSessionPath string
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMainWindow(app *gtk.Application, entries []wiiudownloader.TitleEntry, logger *wiiudownloader.Logger, ariaSessionPath string) *MainWindow {
|
func NewMainWindow(app *gtk.Application, entries []wiiudownloader.TitleEntry, logger *wiiudownloader.Logger, client *http.Client) *MainWindow {
|
||||||
gSettings, err := gtk.SettingsGetDefault()
|
gSettings, err := gtk.SettingsGetDefault()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err.Error())
|
logger.Error(err.Error())
|
||||||
|
@ -72,7 +73,7 @@ func NewMainWindow(app *gtk.Application, entries []wiiudownloader.TitleEntry, lo
|
||||||
currentRegion: wiiudownloader.MCP_REGION_EUROPE | wiiudownloader.MCP_REGION_JAPAN | wiiudownloader.MCP_REGION_USA,
|
currentRegion: wiiudownloader.MCP_REGION_EUROPE | wiiudownloader.MCP_REGION_JAPAN | wiiudownloader.MCP_REGION_USA,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
lastSearchText: "",
|
lastSearchText: "",
|
||||||
ariaSessionPath: ariaSessionPath,
|
client: client,
|
||||||
}
|
}
|
||||||
|
|
||||||
searchEntry.Connect("changed", mainWindow.onSearchEntryChanged)
|
searchEntry.Connect("changed", mainWindow.onSearchEntryChanged)
|
||||||
|
@ -264,7 +265,7 @@ func (mw *MainWindow) ShowAll() {
|
||||||
|
|
||||||
wiiudownloader.GenerateTicket(filepath.Join(parentDir, "title.tik"), titleID, titleKey, titleVersion)
|
wiiudownloader.GenerateTicket(filepath.Join(parentDir, "title.tik"), titleID, titleKey, titleVersion)
|
||||||
|
|
||||||
cert, err := wiiudownloader.GenerateCert(tmdData, contentCount, mw.progressWindow, context.Background(), make([]byte, 0), mw.ariaSessionPath)
|
cert, err := wiiudownloader.GenerateCert(tmdData, contentCount, mw.progressWindow, http.DefaultClient, context.Background(), make([]byte, 0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -759,7 +760,7 @@ func (mw *MainWindow) onDownloadQueueClicked(selectedPath string) error {
|
||||||
}
|
}
|
||||||
tidStr := fmt.Sprintf("%016x", title.TitleID)
|
tidStr := fmt.Sprintf("%016x", title.TitleID)
|
||||||
titlePath := filepath.Join(selectedPath, fmt.Sprintf("%s [%s] [%s]", normalizeFilename(title.Name), wiiudownloader.GetFormattedKind(title.TitleID), tidStr))
|
titlePath := filepath.Join(selectedPath, fmt.Sprintf("%s [%s] [%s]", normalizeFilename(title.Name), wiiudownloader.GetFormattedKind(title.TitleID), tidStr))
|
||||||
if err := wiiudownloader.DownloadTitle(queueCtx, tidStr, titlePath, mw.decryptContents, mw.progressWindow, mw.getDeleteEncryptedContents(), mw.logger, mw.ariaSessionPath); err != nil && err != context.Canceled {
|
if err := wiiudownloader.DownloadTitle(queueCtx, tidStr, titlePath, mw.decryptContents, mw.progressWindow, mw.getDeleteEncryptedContents(), mw.logger, mw.client); err != nil && err != context.Canceled {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/gotk3/gotk3/glib"
|
"github.com/gotk3/gotk3/glib"
|
||||||
"github.com/gotk3/gotk3/gtk"
|
"github.com/gotk3/gotk3/gtk"
|
||||||
)
|
)
|
||||||
|
|
||||||
const smoothingFactor = 0.1
|
|
||||||
|
|
||||||
type ProgressWindow struct {
|
type ProgressWindow struct {
|
||||||
Window *gtk.Window
|
Window *gtk.Window
|
||||||
box *gtk.Box
|
box *gtk.Box
|
||||||
|
@ -22,8 +19,6 @@ type ProgressWindow struct {
|
||||||
cancelFunc context.CancelFunc
|
cancelFunc context.CancelFunc
|
||||||
totalToDownload int64
|
totalToDownload int64
|
||||||
totalDownloaded int64
|
totalDownloaded int64
|
||||||
lastUpdateTime time.Time
|
|
||||||
averageSpeed float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pw *ProgressWindow) SetGameTitle(title string) {
|
func (pw *ProgressWindow) SetGameTitle(title string) {
|
||||||
|
@ -40,19 +35,8 @@ func (pw *ProgressWindow) UpdateDownloadProgress(downloaded, speed int64, filePa
|
||||||
pw.cancelButton.SetSensitive(true)
|
pw.cancelButton.SetSensitive(true)
|
||||||
currentDownload := downloaded + pw.totalDownloaded
|
currentDownload := downloaded + pw.totalDownloaded
|
||||||
pw.bar.SetFraction(float64(currentDownload) / float64(pw.totalToDownload))
|
pw.bar.SetFraction(float64(currentDownload) / float64(pw.totalToDownload))
|
||||||
|
pw.bar.SetText(fmt.Sprintf("Downloading %s (%s/%s) (%s/s)", filePath, humanize.Bytes(uint64(currentDownload)), humanize.Bytes(uint64(pw.totalToDownload)), humanize.Bytes(uint64(speed))))
|
||||||
pw.averageSpeed = smoothingFactor*float64(speed) + (1-smoothingFactor)*pw.averageSpeed
|
|
||||||
|
|
||||||
pw.bar.SetText(fmt.Sprintf("Downloading %s (%s/%s) (%s/s)",
|
|
||||||
filePath,
|
|
||||||
humanize.Bytes(uint64(currentDownload)),
|
|
||||||
humanize.Bytes(uint64(pw.totalToDownload)),
|
|
||||||
humanize.Bytes(uint64(pw.averageSpeed)),
|
|
||||||
))
|
|
||||||
|
|
||||||
pw.lastUpdateTime = time.Now()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
for gtk.EventsPending() {
|
for gtk.EventsPending() {
|
||||||
gtk.MainIteration()
|
gtk.MainIteration()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>WiiUDownloader</string>
|
<string>WiiUDownloader</string>
|
||||||
<key>CFBundleGetInfoString</key>
|
<key>CFBundleGetInfoString</key>
|
||||||
<string>2.30, Copyright 2022-2024 Xpl0itU</string>
|
<string>2.29, Copyright 2022-2023 Xpl0itU</string>
|
||||||
<key>CFBundleIconFile</key>
|
<key>CFBundleIconFile</key>
|
||||||
<string>WiiUDownloader.icns</string>
|
<string>WiiUDownloader.icns</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
|
@ -17,13 +17,13 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2.30</string>
|
<string>2.29</string>
|
||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>2.30</string>
|
<string>2.29</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright 2022-2024 Xpl0itU, GNU General Public License.</string>
|
<string>Copyright 2022-2023 Xpl0itU, GNU General Public License.</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>11.0</string>
|
<string>11.0</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
144
downloader.go
144
downloader.go
|
@ -8,14 +8,12 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jaskaranSM/aria2go"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,38 +33,6 @@ type ProgressReporter interface {
|
||||||
AddToTotalDownloaded(toAdd int64)
|
AddToTotalDownloaded(toAdd int64)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Aria2gocNotifier struct {
|
|
||||||
start chan string
|
|
||||||
complete chan bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAria2goNotifier(start chan string, complete chan bool) aria2go.Notifier {
|
|
||||||
return Aria2gocNotifier{
|
|
||||||
start: start,
|
|
||||||
complete: complete,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n Aria2gocNotifier) OnStart(gid string) {
|
|
||||||
n.start <- gid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n Aria2gocNotifier) OnPause(gid string) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n Aria2gocNotifier) OnStop(gid string) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n Aria2gocNotifier) OnComplete(gid string) {
|
|
||||||
n.complete <- false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n Aria2gocNotifier) OnError(gid string) {
|
|
||||||
n.complete <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateDownloadSpeed(downloaded int64, startTime, endTime time.Time) int64 {
|
func calculateDownloadSpeed(downloaded int64, startTime, endTime time.Time) int64 {
|
||||||
duration := endTime.Sub(startTime).Seconds()
|
duration := endTime.Sub(startTime).Seconds()
|
||||||
if duration > 0 {
|
if duration > 0 {
|
||||||
|
@ -75,75 +41,73 @@ func calculateDownloadSpeed(downloaded int64, startTime, endTime time.Time) int6
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadFile(ctx context.Context, progressReporter ProgressReporter, downloadURL, dstPath string, doRetries bool, buffer []byte, ariaSessionPath string) error {
|
func downloadFile(ctx context.Context, progressReporter ProgressReporter, client *http.Client, downloadURL, dstPath string, doRetries bool, buffer []byte) error {
|
||||||
fileName := filepath.Base(dstPath)
|
filePath := filepath.Base(dstPath)
|
||||||
|
|
||||||
var startTime time.Time
|
var startTime time.Time
|
||||||
|
|
||||||
for attempt := 1; attempt <= maxRetries; attempt++ {
|
for attempt := 1; attempt <= maxRetries; attempt++ {
|
||||||
client := aria2go.NewAria2(aria2go.Config{
|
req, err := http.NewRequestWithContext(ctx, "GET", downloadURL, nil)
|
||||||
Options: aria2go.Options{
|
|
||||||
"save-session": ariaSessionPath,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
gid, err := client.AddUri(downloadURL, aria2go.Options{
|
|
||||||
"dir": filepath.Dir(dstPath),
|
|
||||||
"out": fileName,
|
|
||||||
"continue": "true",
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
resp, err := client.Do(req)
|
||||||
defer client.Shutdown()
|
if err != nil {
|
||||||
client.Run()
|
return err
|
||||||
}()
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
startNotif := make(chan string)
|
if resp.StatusCode != http.StatusOK {
|
||||||
completeNotif := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
quit := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(quit, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
|
||||||
|
|
||||||
<-quit
|
|
||||||
completeNotif <- true
|
|
||||||
}()
|
|
||||||
client.SetNotifier(newAria2goNotifier(startNotif, completeNotif))
|
|
||||||
|
|
||||||
startTime = time.Now()
|
|
||||||
ticker := time.NewTicker(time.Millisecond * 500)
|
|
||||||
defer ticker.Stop()
|
|
||||||
loop:
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case id := <-startNotif:
|
|
||||||
gid = id
|
|
||||||
case <-ticker.C:
|
|
||||||
downloaded := client.GetDownloadInfo(gid).BytesCompleted
|
|
||||||
progressReporter.UpdateDownloadProgress(downloaded, calculateDownloadSpeed(downloaded, startTime, time.Now()), fileName)
|
|
||||||
case errored := <-completeNotif:
|
|
||||||
if errored {
|
|
||||||
if doRetries && attempt < maxRetries {
|
if doRetries && attempt < maxRetries {
|
||||||
time.Sleep(retryDelay)
|
time.Sleep(retryDelay)
|
||||||
break loop
|
continue
|
||||||
}
|
}
|
||||||
return fmt.Errorf("write error after %d attempts: %+v", attempt, client.GetDownloadInfo(gid).ErrorCode)
|
return fmt.Errorf("download error after %d attempts, status code: %d", attempt, resp.StatusCode)
|
||||||
}
|
}
|
||||||
downloaded := client.GetDownloadInfo(gid).BytesCompleted
|
|
||||||
progressReporter.UpdateDownloadProgress(downloaded, calculateDownloadSpeed(downloaded, startTime, time.Now()), fileName)
|
file, err := os.Create(dstPath)
|
||||||
return nil
|
if err != nil {
|
||||||
case <-ctx.Done():
|
return err
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var downloaded int64
|
||||||
|
|
||||||
|
startTime = time.Now()
|
||||||
|
for {
|
||||||
|
n, err := resp.Body.Read(buffer)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
if doRetries && attempt < maxRetries {
|
||||||
|
time.Sleep(retryDelay)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
return fmt.Errorf("download error after %d attempts: %+v", attempt, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.Write(buffer[:n])
|
||||||
|
if err != nil {
|
||||||
|
if doRetries && attempt < maxRetries {
|
||||||
|
time.Sleep(retryDelay)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return fmt.Errorf("write error after %d attempts: %+v", attempt, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
downloaded += int64(n)
|
||||||
|
progressReporter.UpdateDownloadProgress(downloaded, calculateDownloadSpeed(downloaded, startTime, time.Now()), filePath)
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, doDecryption bool, progressReporter ProgressReporter, deleteEncryptedContents bool, logger *Logger, ariaSessionPath string) error {
|
func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, doDecryption bool, progressReporter ProgressReporter, deleteEncryptedContents bool, logger *Logger, client *http.Client) error {
|
||||||
titleEntry := getTitleEntryFromTid(titleID)
|
titleEntry := getTitleEntryFromTid(titleID)
|
||||||
|
|
||||||
progressReporter.SetTotalDownloaded(0)
|
progressReporter.SetTotalDownloaded(0)
|
||||||
|
@ -163,7 +127,7 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
|
||||||
buffer := make([]byte, bufferSize)
|
buffer := make([]byte, bufferSize)
|
||||||
|
|
||||||
tmdPath := filepath.Join(outputDir, "title.tmd")
|
tmdPath := filepath.Join(outputDir, "title.tmd")
|
||||||
if err := downloadFile(cancelCtx, progressReporter, fmt.Sprintf("%s/%s", baseURL, "tmd"), tmdPath, true, buffer, ariaSessionPath); err != nil {
|
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%s", baseURL, "tmd"), tmdPath, true, buffer); err != nil {
|
||||||
if progressReporter.Cancelled() {
|
if progressReporter.Cancelled() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -181,7 +145,7 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
|
||||||
}
|
}
|
||||||
|
|
||||||
tikPath := filepath.Join(outputDir, "title.tik")
|
tikPath := filepath.Join(outputDir, "title.tik")
|
||||||
if err := downloadFile(cancelCtx, progressReporter, fmt.Sprintf("%s/%s", baseURL, "cetk"), tikPath, false, buffer, ariaSessionPath); err != nil {
|
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%s", baseURL, "cetk"), tikPath, false, buffer); err != nil {
|
||||||
if progressReporter.Cancelled() {
|
if progressReporter.Cancelled() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -220,7 +184,7 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
|
||||||
|
|
||||||
progressReporter.SetDownloadSize(int64(titleSize))
|
progressReporter.SetDownloadSize(int64(titleSize))
|
||||||
|
|
||||||
cert, err := GenerateCert(tmdData, contentCount, progressReporter, cancelCtx, buffer, ariaSessionPath)
|
cert, err := GenerateCert(tmdData, contentCount, progressReporter, client, cancelCtx, buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if progressReporter.Cancelled() {
|
if progressReporter.Cancelled() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -264,7 +228,7 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
filePath := filepath.Join(outputDir, fmt.Sprintf("%08X.app", id))
|
filePath := filepath.Join(outputDir, fmt.Sprintf("%08X.app", id))
|
||||||
if err := downloadFile(cancelCtx, progressReporter, fmt.Sprintf("%s/%08X", baseURL, id), filePath, true, buffer, ariaSessionPath); err != nil {
|
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%08X", baseURL, id), filePath, true, buffer); err != nil {
|
||||||
if progressReporter.Cancelled() {
|
if progressReporter.Cancelled() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -274,7 +238,7 @@ func DownloadTitle(cancelCtx context.Context, titleID, outputDirectory string, d
|
||||||
|
|
||||||
if tmdData[offset+7]&0x2 == 2 {
|
if tmdData[offset+7]&0x2 == 2 {
|
||||||
filePath = filepath.Join(outputDir, fmt.Sprintf("%08X.h3", id))
|
filePath = filepath.Join(outputDir, fmt.Sprintf("%08X.h3", id))
|
||||||
if err := downloadFile(cancelCtx, progressReporter, fmt.Sprintf("%s/%08X.h3", baseURL, id), filePath, true, buffer, ariaSessionPath); err != nil {
|
if err := downloadFile(cancelCtx, progressReporter, client, fmt.Sprintf("%s/%08X.h3", baseURL, id), filePath, true, buffer); err != nil {
|
||||||
if progressReporter.Cancelled() {
|
if progressReporter.Cancelled() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -9,15 +9,6 @@ require (
|
||||||
golang.org/x/crypto v0.17.0
|
golang.org/x/crypto v0.17.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/ianlancetaylor/cgosymbolizer v0.0.0-20231130194700-cfcb2fd150eb // indirect
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b // indirect
|
|
||||||
github.com/jaskaranSM/aria2go v0.0.0-20210417130736-a4fd19b6cb10
|
|
||||||
)
|
|
||||||
|
|
||||||
replace github.com/jaskaranSM/aria2go => ./pkg/aria2go
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d // indirect
|
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d // indirect
|
||||||
golang.org/x/sync v0.5.0
|
golang.org/x/sync v0.5.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -2,14 +2,10 @@ github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d h1:2xp1BQbqcDDaik
|
||||||
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
|
github.com/TheTitanrain/w32 v0.0.0-20200114052255-2654d97dbd3d/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
|
||||||
github.com/Xpl0itU/dialog v0.0.0-20230805114139-ec888310aded h1:GkBw5aNvID1+SKAD3xC5fU4EwMgOmkrvICy5NX3Rqvw=
|
github.com/Xpl0itU/dialog v0.0.0-20230805114139-ec888310aded h1:GkBw5aNvID1+SKAD3xC5fU4EwMgOmkrvICy5NX3Rqvw=
|
||||||
github.com/Xpl0itU/dialog v0.0.0-20230805114139-ec888310aded/go.mod h1:Yl652wzqaetwEMJ8FnDRKBK1+CisE+PU5BGJXItbYFg=
|
github.com/Xpl0itU/dialog v0.0.0-20230805114139-ec888310aded/go.mod h1:Yl652wzqaetwEMJ8FnDRKBK1+CisE+PU5BGJXItbYFg=
|
||||||
github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b h1:5JgaFtHFRnOPReItxvhMDXbvuBkjSWE+9glJyF466yw=
|
|
||||||
github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b/go.mod h1:eMD2XUcPsHYbakFEocKrWZp47G0MRJYoC60qFblGjpA=
|
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/gotk3/gotk3 v0.6.2 h1:sx/PjaKfKULJPTPq8p2kn2ZbcNFxpOJqi4VLzMbEOO8=
|
github.com/gotk3/gotk3 v0.6.2 h1:sx/PjaKfKULJPTPq8p2kn2ZbcNFxpOJqi4VLzMbEOO8=
|
||||||
github.com/gotk3/gotk3 v0.6.2/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
github.com/gotk3/gotk3 v0.6.2/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q=
|
||||||
github.com/ianlancetaylor/cgosymbolizer v0.0.0-20231130194700-cfcb2fd150eb h1:asfjGoPvNgSPvgbBiwFqMUOgWgid8xlQGCGHfgM/PAs=
|
|
||||||
github.com/ianlancetaylor/cgosymbolizer v0.0.0-20231130194700-cfcb2fd150eb/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg=
|
|
||||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
|
|
|
@ -6,13 +6,11 @@ import ssl
|
||||||
|
|
||||||
# Don't edit below this line
|
# Don't edit below this line
|
||||||
|
|
||||||
|
|
||||||
def checkAndDeleteFile(file):
|
def checkAndDeleteFile(file):
|
||||||
if os.path.exists(file):
|
if os.path.exists(file):
|
||||||
print(f"Deleting {file}")
|
print(f"Deleting {file}")
|
||||||
os.remove(file)
|
os.remove(file)
|
||||||
|
|
||||||
|
|
||||||
# Disable certificate verification
|
# Disable certificate verification
|
||||||
ssl_context = ssl.create_default_context()
|
ssl_context = ssl.create_default_context()
|
||||||
ssl_context.check_hostname = False
|
ssl_context.check_hostname = False
|
||||||
|
@ -24,25 +22,13 @@ urllib.request.install_opener(opener)
|
||||||
|
|
||||||
checkAndDeleteFile("gtitles/gtitles.c")
|
checkAndDeleteFile("gtitles/gtitles.c")
|
||||||
urllib.request.urlretrieve("https://napi.nbg01.v10lator.de/db", "gtitles/gtitles.c")
|
urllib.request.urlretrieve("https://napi.nbg01.v10lator.de/db", "gtitles/gtitles.c")
|
||||||
os.system(
|
os.system("gcc -c -Wall -fpic -Ofast -pipe -Igtitles -o gtitles/gtitles.o gtitles/gtitles.c")
|
||||||
"gcc -c -Wall -fpic -Ofast -pipe -Igtitles -o gtitles/gtitles.o gtitles/gtitles.c"
|
|
||||||
)
|
|
||||||
os.system("ar rcs libgtitles.a gtitles/gtitles.o")
|
os.system("ar rcs libgtitles.a gtitles/gtitles.o")
|
||||||
os.system("gcc -shared -o gtitles/libgtitles.so gtitles/gtitles.o")
|
os.system("gcc -shared -o gtitles/libgtitles.so gtitles/gtitles.o")
|
||||||
|
|
||||||
os.system(
|
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/aes.o cdecrypt/aes.c")
|
||||||
"gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/aes.o cdecrypt/aes.c"
|
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/cdecrypt.o cdecrypt/cdecrypt.c")
|
||||||
)
|
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/sha1.o cdecrypt/sha1.c")
|
||||||
os.system(
|
os.system("gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/util.o cdecrypt/util.c")
|
||||||
"gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/cdecrypt.o cdecrypt/cdecrypt.c"
|
|
||||||
)
|
|
||||||
os.system(
|
|
||||||
"gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/sha1.o cdecrypt/sha1.c"
|
|
||||||
)
|
|
||||||
os.system(
|
|
||||||
"gcc -c -Wall -fpic -Ofast -pipe -UNDEBUG -DAES_ROM_TABLES -D_GNU_SOURCE -Icdecrypt -o cdecrypt/util.o cdecrypt/util.c"
|
|
||||||
)
|
|
||||||
os.system("ar rcs libcdecrypt.a cdecrypt/*.o")
|
os.system("ar rcs libcdecrypt.a cdecrypt/*.o")
|
||||||
os.system("gcc -shared -o cdecrypt/libcdecrypt.so cdecrypt/*.o")
|
os.system("gcc -shared -o cdecrypt/libcdecrypt.so cdecrypt/*.o")
|
||||||
|
|
||||||
os.system("bash prepare_aria.sh")
|
|
||||||
|
|
|
@ -1,201 +0,0 @@
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright (C) 2019 Vincent Chueng (coolingfall@gmail.com)
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
|
@ -1,323 +0,0 @@
|
||||||
#include "aria2_c.h"
|
|
||||||
#include "_cgo_export.h"
|
|
||||||
#include <aria2/aria2.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
pthread_mutex_t access_mutex;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> splitBySemicolon(std::string in) {
|
|
||||||
std::string val;
|
|
||||||
std::stringstream is(in);
|
|
||||||
std::vector<std::string> out;
|
|
||||||
|
|
||||||
while (getline(is, val, ';')) {
|
|
||||||
out.push_back(val);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *toCStr(std::string in) {
|
|
||||||
int len = in.length();
|
|
||||||
char *val = new char[len + 1];
|
|
||||||
memcpy(val, in.data(), len);
|
|
||||||
val[len] = '\0';
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *getFileName(std::string dir, std::string path) {
|
|
||||||
if (path.find(dir, 0) == std::string::npos) {
|
|
||||||
return toCStr(path);
|
|
||||||
}
|
|
||||||
int index = dir.size();
|
|
||||||
std::string name = path.substr(index + 1);
|
|
||||||
return toCStr(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *toAria2goOptions(aria2::KeyVals options) {
|
|
||||||
std::vector<std::pair<std::string, std::string>>::iterator it;
|
|
||||||
|
|
||||||
std::string cOptions;
|
|
||||||
for (it = options.begin(); it != options.end(); it++) {
|
|
||||||
std::pair<std::string, std::string> p = *it;
|
|
||||||
cOptions += p.first + ";" + p.second + ";";
|
|
||||||
}
|
|
||||||
|
|
||||||
return toCStr(cOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
aria2::KeyVals toAria2Options(const char *options) {
|
|
||||||
aria2::KeyVals aria2Options;
|
|
||||||
|
|
||||||
if (options == nullptr) {
|
|
||||||
return aria2Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> o = splitBySemicolon(std::string(options));
|
|
||||||
/* key and val should be pair */
|
|
||||||
if (o.size() % 2 != 0) {
|
|
||||||
return aria2Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < (int)o.size(); i += 2) {
|
|
||||||
std::string key = o[i];
|
|
||||||
std::string val = o[i + 1];
|
|
||||||
aria2Options.push_back(std::make_pair(key, val));
|
|
||||||
}
|
|
||||||
|
|
||||||
return aria2Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FileInfo *parseFileData(aria2::DownloadHandle *dh) {
|
|
||||||
std::string dir = dh->getDir();
|
|
||||||
std::vector<aria2::FileData> files = dh->getFiles();
|
|
||||||
int numFiles = dh->getNumFiles();
|
|
||||||
struct FileInfo *allFiles = new FileInfo[numFiles];
|
|
||||||
for (int i = 0; i < numFiles; i++) {
|
|
||||||
aria2::FileData file = files[i];
|
|
||||||
struct FileInfo *fi = new FileInfo();
|
|
||||||
fi->index = file.index;
|
|
||||||
fi->name = getFileName(dir, file.path);
|
|
||||||
fi->length = file.length;
|
|
||||||
fi->completedLength = file.completedLength;
|
|
||||||
fi->selected = file.selected;
|
|
||||||
|
|
||||||
allFiles[i] = *fi;
|
|
||||||
delete fi;
|
|
||||||
}
|
|
||||||
return allFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* retrieve all BitTorrent meta information */
|
|
||||||
struct MetaInfo *parseMetaInfo(aria2::BtMetaInfoData btMetaInfo) {
|
|
||||||
struct MetaInfo *mi = new MetaInfo();
|
|
||||||
mi->name = toCStr(btMetaInfo.name);
|
|
||||||
mi->comment = toCStr(btMetaInfo.comment);
|
|
||||||
mi->creationUnix = btMetaInfo.creationDate;
|
|
||||||
std::vector<std::vector<std::string>> announceList = btMetaInfo.announceList;
|
|
||||||
std::vector<std::vector<std::string>>::iterator it;
|
|
||||||
std::string cAnnounceList;
|
|
||||||
for (it = announceList.begin(); it != announceList.end(); it++) {
|
|
||||||
std::vector<std::string>::iterator cit;
|
|
||||||
std::vector<std::string> childList = *it;
|
|
||||||
for (cit = childList.begin(); cit != childList.end(); cit++) {
|
|
||||||
cAnnounceList += *cit;
|
|
||||||
if (it != announceList.end() - 1 || cit != childList.end() - 1) {
|
|
||||||
cAnnounceList += ";";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mi->announceList = toCStr(cAnnounceList);
|
|
||||||
return mi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Global aria2 session.
|
|
||||||
*/
|
|
||||||
aria2::Session *session;
|
|
||||||
/**
|
|
||||||
* Global aria2go go pointer.
|
|
||||||
*/
|
|
||||||
uint64_t aria2goPointer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download event callback for aria2.
|
|
||||||
*/
|
|
||||||
int downloadEventCallback(aria2::Session *session, aria2::DownloadEvent event,
|
|
||||||
const aria2::A2Gid gid, void *userData) {
|
|
||||||
notifyEvent(aria2goPointer, gid, event);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initial aria2 library.
|
|
||||||
*/
|
|
||||||
int init(uint64_t pointer, const char *options) {
|
|
||||||
aria2goPointer = pointer;
|
|
||||||
int ret = aria2::libraryInit();
|
|
||||||
aria2::SessionConfig config;
|
|
||||||
config.keepRunning = true;
|
|
||||||
/* do not use signal handler, cause c will block go */
|
|
||||||
config.useSignalHandler = false;
|
|
||||||
config.downloadEventCallback = downloadEventCallback;
|
|
||||||
session = aria2::sessionNew(toAria2Options(options), config);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shutdown schedules. This will cause run finished.
|
|
||||||
*/
|
|
||||||
int shutdownSchedules(bool force) { return aria2::shutdown(session, force); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deinit aria2 library, this must be invoked when process exit(signal handler
|
|
||||||
* is not used), so aria2 will be able to save session config.
|
|
||||||
*/
|
|
||||||
int deinit() {
|
|
||||||
int ret = aria2::sessionFinal(session);
|
|
||||||
session = nullptr;
|
|
||||||
aria2::libraryDeinit();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds new HTTP(S)/FTP/BitTorrent Magnet URI. See `addUri` in aria2.
|
|
||||||
*
|
|
||||||
* @param uri uri to add
|
|
||||||
*/
|
|
||||||
uint64_t addUri(char *uri, const char *options) {
|
|
||||||
std::vector<std::string> uris = {uri};
|
|
||||||
aria2::A2Gid gid;
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
int ret = aria2::addUri(session, &gid, uris, toAria2Options(options));
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
if (ret < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add bit torrent file. See `addTorrent` in aria2.
|
|
||||||
*/
|
|
||||||
uint64_t addTorrent(char *fp, const char *options) {
|
|
||||||
aria2::A2Gid gid;
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
int ret = aria2::addTorrent(session, &gid, fp, toAria2Options(options));
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
if (ret < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change aria2 options. See `changeOption` in aria2.
|
|
||||||
*/
|
|
||||||
bool changeOptions(uint64_t gid, const char *options) {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
bool ret = aria2::changeOption(session, gid, toAria2Options(options)) == 0;
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get options for given gid. see `getOptions` in aria2.
|
|
||||||
*/
|
|
||||||
const char *getOptions(uint64_t gid) {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
aria2::DownloadHandle *dh = aria2::getDownloadHandle(session, gid);
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
if (!dh) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return toAria2goOptions(dh->getOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Change global options. See `changeGlobalOption` in aria2.
|
|
||||||
*/
|
|
||||||
bool changeGlobalOptions(const char *options) {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
bool ret = aria2::changeGlobalOption(session, toAria2Options(options));
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get global options. see `getGlobalOptions` in aria2.
|
|
||||||
*/
|
|
||||||
const char *getGlobalOptions() {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
aria2::KeyVals options = aria2::getGlobalOptions(session);
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
return toAria2goOptions(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs event polling and actions for them.
|
|
||||||
*/
|
|
||||||
int run() { return aria2::run(session, aria2::RUN_DEFAULT); }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pause an active download with given gid. This will mark the download to
|
|
||||||
* `DOWNLOAD_PAUSED`. See `resume`.
|
|
||||||
*/
|
|
||||||
bool pause(uint64_t gid) {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
bool ret = aria2::pauseDownload(session, gid) == 0;
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resume a paused download with given gid. See `pause`.
|
|
||||||
*/
|
|
||||||
bool resume(uint64_t gid) {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
bool ret = aria2::unpauseDownload(session, gid) == 0;
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a download in queue with given gid. This will stop downloading and
|
|
||||||
* seeding(for torrent).
|
|
||||||
*/
|
|
||||||
bool removeDownload(uint64_t gid) {
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
bool ret = aria2::removeDownload(session, gid) == 0;
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get download information for current download with given gid.
|
|
||||||
*/
|
|
||||||
struct DownloadInfo *getDownloadInfo(uint64_t gid) {
|
|
||||||
if (session == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&access_mutex);
|
|
||||||
aria2::DownloadHandle *dh = aria2::getDownloadHandle(session, gid);
|
|
||||||
pthread_mutex_unlock(&access_mutex);
|
|
||||||
if (!dh) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
struct DownloadInfo *di = new DownloadInfo();
|
|
||||||
di->status = dh->getStatus();
|
|
||||||
di->totalLength = dh->getTotalLength();
|
|
||||||
di->bytesCompleted = dh->getCompletedLength();
|
|
||||||
di->uploadLength = dh->getUploadLength();
|
|
||||||
di->downloadSpeed = dh->getDownloadSpeed();
|
|
||||||
di->uploadSpeed = dh->getUploadSpeed();
|
|
||||||
di->pieceLength = dh->getPieceLength();
|
|
||||||
di->numPieces = dh->getNumPieces();
|
|
||||||
di->connections = dh->getConnections();
|
|
||||||
di->numFiles = dh->getNumFiles();
|
|
||||||
di->infoHash = toCStr(dh->getInfoHash());
|
|
||||||
di->metaInfo = parseMetaInfo(dh->getBtMetaInfo());
|
|
||||||
di->files = parseFileData(dh);
|
|
||||||
di->errorCode = dh->getErrorCode();
|
|
||||||
std::vector<aria2::A2Gid> gids = dh->getFollowedBy();
|
|
||||||
if (gids.size() != 0) {
|
|
||||||
di->followedByGid = gids[0];
|
|
||||||
} else {
|
|
||||||
di->followedByGid = 0;
|
|
||||||
}
|
|
||||||
// std::cout << "status" << dh->getStatus() << std::endl;
|
|
||||||
// std::cout << "Error: " << dh->getErrorCode() << std::endl;
|
|
||||||
// std::cout << "Completed: " << dh->getCompletedLength() << std::endl;
|
|
||||||
// std::cout << "Total: " << dh->getTotalLength() << std::endl;
|
|
||||||
/* delete download handle */
|
|
||||||
aria2::deleteDownloadHandle(dh);
|
|
||||||
return di;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
#ifndef ARIA2_C_H
|
|
||||||
#define ARIA2_C_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type definition for file information in torrent.
|
|
||||||
*/
|
|
||||||
struct FileInfo {
|
|
||||||
int index;
|
|
||||||
const char *name;
|
|
||||||
int64_t length;
|
|
||||||
int64_t completedLength;
|
|
||||||
bool selected;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type definition for BitTorrent meta information.
|
|
||||||
*/
|
|
||||||
struct MetaInfo {
|
|
||||||
const char *name;
|
|
||||||
const char *comment;
|
|
||||||
int64_t creationUnix;
|
|
||||||
const char *announceList;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type definition for download information.
|
|
||||||
*/
|
|
||||||
struct DownloadInfo {
|
|
||||||
int status;
|
|
||||||
int64_t totalLength;
|
|
||||||
int64_t bytesCompleted;
|
|
||||||
int64_t uploadLength;
|
|
||||||
int downloadSpeed;
|
|
||||||
int uploadSpeed;
|
|
||||||
int pieceLength;
|
|
||||||
int numPieces;
|
|
||||||
int connections;
|
|
||||||
int numFiles;
|
|
||||||
const char *infoHash;
|
|
||||||
struct MetaInfo *metaInfo;
|
|
||||||
struct FileInfo *files;
|
|
||||||
int errorCode;
|
|
||||||
uint64_t followedByGid;
|
|
||||||
};
|
|
||||||
|
|
||||||
int init(uint64_t aria2goPointer, const char *options);
|
|
||||||
int shutdownSchedules(bool force);
|
|
||||||
int deinit();
|
|
||||||
uint64_t addUri(char *uri, const char *options);
|
|
||||||
uint64_t addTorrent(char *fp, const char *options);
|
|
||||||
bool changeOptions(uint64_t gid, const char *options);
|
|
||||||
const char *getOptions(uint64_t gid);
|
|
||||||
bool changeGlobalOptions(const char *options);
|
|
||||||
const char *getGlobalOptions();
|
|
||||||
int run();
|
|
||||||
bool pause(uint64_t gid);
|
|
||||||
bool resume(uint64_t gid);
|
|
||||||
bool removeDownload(uint64_t gid);
|
|
||||||
struct DownloadInfo *getDownloadInfo(uint64_t gid);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,7 +0,0 @@
|
||||||
module github.com/jaskaranSM/aria2go
|
|
||||||
|
|
||||||
go 1.12
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b // indirect
|
|
||||||
)
|
|
|
@ -1,2 +0,0 @@
|
||||||
github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b h1:5JgaFtHFRnOPReItxvhMDXbvuBkjSWE+9glJyF466yw=
|
|
||||||
github.com/benesch/cgosymbolizer v0.0.0-20190515212042-bec6fe6e597b/go.mod h1:eMD2XUcPsHYbakFEocKrWZp47G0MRJYoC60qFblGjpA=
|
|
|
@ -1,333 +0,0 @@
|
||||||
// Copyright (C) 2019 Vincent Chueng (coolingfall@gmail.com).
|
|
||||||
|
|
||||||
package aria2go
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo CXXFLAGS: -std=c++11 -I./aria2-lib/include -Werror -Wall
|
|
||||||
#cgo LDFLAGS: -L./aria2-lib/lib
|
|
||||||
#cgo LDFLAGS: -laria2 -lcares -lssl -lcrypto
|
|
||||||
#cgo darwin LDFLAGS: -framework Security
|
|
||||||
#cgo windows LDFLAGS: -lws2_32 -lwsock32 -lgdi32 -lwinmm -liphlpapi -lpsapi -lcrypt32 -lsecur32 -ladvapi32
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "aria2_c.h"
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
_ "github.com/benesch/cgosymbolizer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Type definition for lib aria2, it holds a notifier.
|
|
||||||
type Aria2 struct {
|
|
||||||
notifier Notifier
|
|
||||||
shutdownNotification chan bool
|
|
||||||
shouldShutdown bool
|
|
||||||
m_mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type definition of configuration for aria2.
|
|
||||||
type Config struct {
|
|
||||||
Options Options
|
|
||||||
Notifier Notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAria2 creates a new instance of aria2.
|
|
||||||
func NewAria2(config Config) *Aria2 {
|
|
||||||
a := &Aria2{
|
|
||||||
notifier: newDefaultNotifier(),
|
|
||||||
shutdownNotification: make(chan bool),
|
|
||||||
}
|
|
||||||
a.SetNotifier(config.Notifier)
|
|
||||||
|
|
||||||
C.init(C.uint64_t(uintptr(unsafe.Pointer(a))),
|
|
||||||
C.CString(a.fromOptions(config.Options)))
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown aria2, this must be invoked when process exit(signal handler is not
|
|
||||||
// used), so aria2 will be able to save session config.
|
|
||||||
func (a *Aria2) Shutdown() int {
|
|
||||||
C.shutdownSchedules(true)
|
|
||||||
a.shouldShutdown = true
|
|
||||||
|
|
||||||
// do nothing, just make thread waiting
|
|
||||||
select {
|
|
||||||
case <-a.shutdownNotification:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(C.deinit())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run starts event pooling. Note this will block current thread.
|
|
||||||
func (a *Aria2) Run() {
|
|
||||||
for {
|
|
||||||
if C.run() != 1 && a.shouldShutdown {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.shutdownNotification <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNotifier sets notifier to receive download notification from aria2.
|
|
||||||
func (a *Aria2) SetNotifier(notifier Notifier) {
|
|
||||||
if notifier == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
a.notifier = notifier
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUri adds a new download. The uris is an array of HTTP/FTP/SFTP/BitTorrent
|
|
||||||
// URIs (strings) pointing to the same resource. When adding BitTorrent Magnet
|
|
||||||
// URIs, uris must have only one element and it should be BitTorrent Magnet URI.
|
|
||||||
func (a *Aria2) AddUri(uri string, options Options) (gid string, err error) {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
cUri := C.CString(uri)
|
|
||||||
cOptions := C.CString(a.fromOptions(options))
|
|
||||||
defer C.free(unsafe.Pointer(cUri))
|
|
||||||
defer C.free(unsafe.Pointer(cOptions))
|
|
||||||
|
|
||||||
ret := C.addUri(cUri, cOptions)
|
|
||||||
if ret == 0 {
|
|
||||||
return "", errors.New("libaria2: add uri failed")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%x", uint64(ret)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTorrent adds a MetaInfo download with given torrent file path.
|
|
||||||
// This will return gid and files in torrent file if add successfully.
|
|
||||||
// User can choose specified files to download, change directory and so on.
|
|
||||||
func (a *Aria2) AddTorrent(filepath string, options Options) (gid string, err error) {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
cFilepath := C.CString(filepath)
|
|
||||||
cOptions := C.CString(a.fromOptions(options))
|
|
||||||
defer C.free(unsafe.Pointer(cFilepath))
|
|
||||||
defer C.free(unsafe.Pointer(cOptions))
|
|
||||||
|
|
||||||
ret := C.addTorrent(cFilepath, cOptions)
|
|
||||||
if ret == 0 {
|
|
||||||
return "", errors.New("libaria2: add torrent failed")
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%x", uint64(ret)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangeOptions can change the options for aria2. See available options in
|
|
||||||
// https://aria2.github.io/manual/en/html/aria2c.html#input-file.
|
|
||||||
func (a *Aria2) ChangeOptions(gid string, options Options) error {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
cOptions := C.CString(a.fromOptions(options))
|
|
||||||
defer C.free(unsafe.Pointer(cOptions))
|
|
||||||
|
|
||||||
if !C.changeOptions(a.hexToGid(gid), cOptions) {
|
|
||||||
return errors.New("libaria2: change options error")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOptions gets all options for given gid.
|
|
||||||
func (a *Aria2) GetOptions(gid string) Options {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
cOptions := C.getOptions(a.hexToGid(gid))
|
|
||||||
if cOptions == nil {
|
|
||||||
return make(Options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return a.toOptions(C.GoString(cOptions))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChangeGlobalOptions changes global options. See available options in
|
|
||||||
// https://aria2.github.io/manual/en/html/aria2c.html#input-file except for
|
|
||||||
// `checksum`, `index-out`, `out`, `pause` and `select-file`.
|
|
||||||
func (a *Aria2) ChangeGlobalOptions(options Options) error {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
cOptions := C.CString(a.fromOptions(options))
|
|
||||||
defer C.free(unsafe.Pointer(cOptions))
|
|
||||||
|
|
||||||
if !C.changeGlobalOptions(cOptions) {
|
|
||||||
return errors.New("libaria2: change global options error")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGlobalOptions gets all global options of aria2.
|
|
||||||
func (a *Aria2) GetGlobalOptions() Options {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
return a.toOptions(C.GoString(C.getGlobalOptions()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause pauses an active download for given gid. The status of the download
|
|
||||||
// will become `DOWNLOAD_PAUSED`. Use `Resume` to restart download.
|
|
||||||
func (a *Aria2) Pause(gid string) bool {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
return bool(C.pause(a.hexToGid(gid)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume resumes an paused download for given gid.
|
|
||||||
func (a *Aria2) Resume(gid string) bool {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
return bool(C.resume(a.hexToGid(gid)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes download no matter what status it was. This will stop
|
|
||||||
// downloading and stop seeding(for torrent).
|
|
||||||
func (a *Aria2) Remove(gid string) bool {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
return bool(C.removeDownload(a.hexToGid(gid)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDownloadInfo gets current download information for given gid.
|
|
||||||
func (a *Aria2) GetDownloadInfo(gid string) DownloadInfo {
|
|
||||||
a.m_mutex.Lock()
|
|
||||||
defer a.m_mutex.Unlock()
|
|
||||||
ret := C.getDownloadInfo(a.hexToGid(gid))
|
|
||||||
if ret == nil {
|
|
||||||
return DownloadInfo{}
|
|
||||||
}
|
|
||||||
defer C.free(unsafe.Pointer(ret))
|
|
||||||
|
|
||||||
// convert info hash to hex string
|
|
||||||
infoHash := fmt.Sprintf("%x", []byte(C.GoString(ret.infoHash)))
|
|
||||||
C.free(unsafe.Pointer(ret.infoHash))
|
|
||||||
// retrieve BitTorrent meta information
|
|
||||||
var metaInfo = MetaInfo{}
|
|
||||||
mi := ret.metaInfo
|
|
||||||
defer C.free(unsafe.Pointer(mi))
|
|
||||||
if mi != nil {
|
|
||||||
announceList := strings.Split(C.GoString(mi.announceList), ";")
|
|
||||||
metaInfo = MetaInfo{
|
|
||||||
Name: C.GoString(mi.name),
|
|
||||||
Comment: C.GoString(mi.comment),
|
|
||||||
CreationUnix: int64(mi.creationUnix),
|
|
||||||
AnnounceList: announceList,
|
|
||||||
}
|
|
||||||
C.free(unsafe.Pointer(mi.name))
|
|
||||||
C.free(unsafe.Pointer(mi.comment))
|
|
||||||
C.free(unsafe.Pointer(mi.announceList))
|
|
||||||
}
|
|
||||||
return DownloadInfo{
|
|
||||||
Status: int(ret.status),
|
|
||||||
TotalLength: int64(ret.totalLength),
|
|
||||||
BytesCompleted: int64(ret.bytesCompleted),
|
|
||||||
BytesUpload: int64(ret.uploadLength),
|
|
||||||
DownloadSpeed: int(ret.downloadSpeed),
|
|
||||||
UploadSpeed: int(ret.uploadSpeed),
|
|
||||||
NumPieces: int(ret.numPieces),
|
|
||||||
Connections: int(ret.connections),
|
|
||||||
InfoHash: infoHash,
|
|
||||||
MetaInfo: metaInfo,
|
|
||||||
Files: a.parseFiles(ret.files, ret.numFiles),
|
|
||||||
ErrorCode: int(ret.errorCode),
|
|
||||||
FollowedByGid: fmt.Sprintf("%x", uint64(ret.followedByGid)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fromOptions converts `Options` to string with ';' separator.
|
|
||||||
func (a *Aria2) fromOptions(options Options) string {
|
|
||||||
if options == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var cOptions string
|
|
||||||
for k, v := range options {
|
|
||||||
cOptions += k + ";"
|
|
||||||
cOptions += v + ";"
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.TrimSuffix(cOptions, ";")
|
|
||||||
}
|
|
||||||
|
|
||||||
// fromOptions converts options string with ';' separator to `Options`.
|
|
||||||
func (a *Aria2) toOptions(cOptions string) Options {
|
|
||||||
coptions := strings.Split(strings.TrimSuffix(cOptions, ";"), ";")
|
|
||||||
var options = make(Options)
|
|
||||||
var index int
|
|
||||||
for index = 0; index < len(coptions); index += 2 {
|
|
||||||
options[coptions[index]] = coptions[index+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
// hexToGid convert hex to uint64 type gid.
|
|
||||||
func (a *Aria2) hexToGid(hex string) C.uint64_t {
|
|
||||||
id, err := strconv.ParseUint(hex, 16, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return C.uint64_t(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFiles parses all files information from aria2.
|
|
||||||
func (a *Aria2) parseFiles(filesPointer *C.struct_FileInfo, length C.int) (files []File) {
|
|
||||||
cfiles := (*[1 << 20]C.struct_FileInfo)(unsafe.Pointer(filesPointer))[:length:length]
|
|
||||||
if cfiles == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range cfiles {
|
|
||||||
files = append(files, File{
|
|
||||||
Index: int(f.index),
|
|
||||||
Length: int64(f.length),
|
|
||||||
CompletedLength: int64(f.completedLength),
|
|
||||||
Name: C.GoString(f.name),
|
|
||||||
Selected: bool(f.selected),
|
|
||||||
})
|
|
||||||
C.free(unsafe.Pointer(f.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// free c pointer resource
|
|
||||||
C.free(unsafe.Pointer(filesPointer))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// noinspection GoUnusedFunction
|
|
||||||
//
|
|
||||||
//export notifyEvent
|
|
||||||
func notifyEvent(ariagoPointer uint64, id uint64, event int) {
|
|
||||||
a := (*Aria2)(unsafe.Pointer(uintptr(ariagoPointer)))
|
|
||||||
if a == nil || a.notifier == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert id to hex string
|
|
||||||
gid := fmt.Sprintf("%x", uint64(id))
|
|
||||||
|
|
||||||
switch event {
|
|
||||||
case onStart:
|
|
||||||
a.notifier.OnStart(gid)
|
|
||||||
case onPause:
|
|
||||||
a.notifier.OnPause(gid)
|
|
||||||
case onStop:
|
|
||||||
a.notifier.OnStop(gid)
|
|
||||||
case onComplete:
|
|
||||||
a.notifier.OnComplete(gid)
|
|
||||||
case onError:
|
|
||||||
a.notifier.OnError(gid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// noinspection GoUnusedFunction
|
|
||||||
//
|
|
||||||
//export goLog
|
|
||||||
func goLog(msg *C.char) {
|
|
||||||
log.Println(C.GoString(msg))
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
// Copyright (C) 2019 Vincent Chueng (coolingfall@gmail.com).
|
|
||||||
|
|
||||||
package aria2go
|
|
||||||
|
|
||||||
// Type definition for download information.
|
|
||||||
type DownloadInfo struct {
|
|
||||||
Status int
|
|
||||||
TotalLength int64
|
|
||||||
BytesCompleted int64
|
|
||||||
BytesUpload int64
|
|
||||||
DownloadSpeed int
|
|
||||||
UploadSpeed int
|
|
||||||
NumPieces int
|
|
||||||
Connections int
|
|
||||||
BitField string
|
|
||||||
InfoHash string
|
|
||||||
MetaInfo MetaInfo
|
|
||||||
Files []File
|
|
||||||
ErrorCode int
|
|
||||||
FollowedByGid string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type definition for BitTorrent meta information.
|
|
||||||
type MetaInfo struct {
|
|
||||||
Name string
|
|
||||||
AnnounceList []string
|
|
||||||
Comment string
|
|
||||||
CreationUnix int64
|
|
||||||
Mode string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type definition for file in torrent.
|
|
||||||
type File struct {
|
|
||||||
Index int
|
|
||||||
Name string
|
|
||||||
Length int64
|
|
||||||
CompletedLength int64
|
|
||||||
Selected bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Options map[string]string
|
|
||||||
|
|
||||||
// Type definition for download event, this will keep the same with aria2.
|
|
||||||
const (
|
|
||||||
onStart = iota + 1
|
|
||||||
onPause
|
|
||||||
onStop
|
|
||||||
onComplete
|
|
||||||
onError
|
|
||||||
onBTComplete
|
|
||||||
)
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright (C) 2019 Vincent Chueng (coolingfall@gmail.com).
|
|
||||||
|
|
||||||
package aria2go
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Type definition for notifier which can be used in aria2c json rpc notification.
|
|
||||||
type Notifier interface {
|
|
||||||
// OnStart will be invoked when aria2 started to download
|
|
||||||
OnStart(gid string)
|
|
||||||
|
|
||||||
// OnPause will be invoked when aria2 paused one download
|
|
||||||
OnPause(gid string)
|
|
||||||
|
|
||||||
// OnPause will be invoked when aria2 stopped one download
|
|
||||||
OnStop(gid string)
|
|
||||||
|
|
||||||
// OnComplete will be invoked when download completed
|
|
||||||
OnComplete(gid string)
|
|
||||||
|
|
||||||
// OnError will be invoked when an error occoured
|
|
||||||
OnError(gid string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type definition for default notifier which dose nothing.
|
|
||||||
type DefaultNotifier struct{}
|
|
||||||
|
|
||||||
// newDefaultNotifier creates a new instance of default Notifier.
|
|
||||||
func newDefaultNotifier() Notifier {
|
|
||||||
return DefaultNotifier{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnStart implements Notifier interface.
|
|
||||||
func (n DefaultNotifier) OnStart(gid string) {
|
|
||||||
log.Printf("on start %v", gid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnPause implements Notifier interface.
|
|
||||||
func (n DefaultNotifier) OnPause(gid string) {
|
|
||||||
log.Printf("on pause: %v", gid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnPause implements Notifier interface.
|
|
||||||
func (n DefaultNotifier) OnStop(gid string) {
|
|
||||||
log.Printf("on stop: %v", gid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnComplete implements Notifier interface.
|
|
||||||
func (n DefaultNotifier) OnComplete(gid string) {
|
|
||||||
log.Printf("on complete: %v", gid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnError implements Notifier interface.
|
|
||||||
func (n DefaultNotifier) OnError(gid string) {
|
|
||||||
log.Printf("on error: %v", gid)
|
|
||||||
}
|
|
118
prepare_aria.sh
118
prepare_aria.sh
|
@ -1,118 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# In this configuration, the following dependent libraries are compiled:
|
|
||||||
#
|
|
||||||
# * c-ares
|
|
||||||
# * openSSL
|
|
||||||
|
|
||||||
# Compiler and path
|
|
||||||
PREFIX=$PWD/pkg/aria2go/aria2-lib
|
|
||||||
C_COMPILER="gcc"
|
|
||||||
CXX_COMPILER="g++"
|
|
||||||
|
|
||||||
NPROC=`nproc`
|
|
||||||
# check if nproc is a command
|
|
||||||
if ! [[ -x "$(command -v nproc)" ]]; then
|
|
||||||
echo 'Error: nproc is not installed. Using sysctl for macOS' >&2
|
|
||||||
$NPROC=`sysctl -n hw.logicalcpu` # macOS
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check tool for download
|
|
||||||
aria2c --help > /dev/null
|
|
||||||
if [[ "$?" -eq 0 ]]; then
|
|
||||||
DOWNLOADER="aria2c --check-certificate=false"
|
|
||||||
else
|
|
||||||
DOWNLOADER="wget -c"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Remove old libs..."
|
|
||||||
rm -rf ${PREFIX}
|
|
||||||
rm -rf _obj
|
|
||||||
|
|
||||||
## Version
|
|
||||||
OPENSSL_V=1.1.1w
|
|
||||||
C_ARES_V=1.24.0
|
|
||||||
ARIA2_V=1.37.0
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
OPENSSL=http://www.openssl.org/source/openssl-${OPENSSL_V}.tar.gz
|
|
||||||
C_ARES=http://c-ares.haxx.se/download/c-ares-${C_ARES_V}.tar.gz
|
|
||||||
ARIA2=https://github.com/aria2/aria2/releases/download/release-${ARIA2_V}/aria2-${ARIA2_V}.tar.bz2
|
|
||||||
|
|
||||||
## Config
|
|
||||||
BUILD_DIRECTORY=/tmp/
|
|
||||||
|
|
||||||
## Build
|
|
||||||
cd ${BUILD_DIRECTORY}
|
|
||||||
|
|
||||||
# c-ares build
|
|
||||||
if ! [[ -e c&&res-${C_ARES_V}.tar.gz ]]; then
|
|
||||||
${DOWNLOADER} ${C_ARES}
|
|
||||||
fi
|
|
||||||
tar zxvf c-ares-${C_ARES_V}.tar.gz
|
|
||||||
cd c-ares-${C_ARES_V}
|
|
||||||
PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig/ \
|
|
||||||
LD_LIBRARY_PATH=${PREFIX}/lib/ CC="$C_COMPILER" CXX="$CXX_COMPILER" \
|
|
||||||
./configure --prefix=${PREFIX} --enable-static --disable-shared
|
|
||||||
make -j`nproc`
|
|
||||||
make install
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# openssl build
|
|
||||||
if ! [[ -e openssl-${OPENSSL_V}.tar.gz ]]; then
|
|
||||||
${DOWNLOADER} ${OPENSSL}
|
|
||||||
fi
|
|
||||||
tar zxvf openssl-${OPENSSL_V}.tar.gz
|
|
||||||
cd openssl-${OPENSSL_V}
|
|
||||||
PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig/ \
|
|
||||||
LD_LIBRARY_PATH=${PREFIX}/lib/ CC="$C_COMPILER" CXX="$CXX_COMPILER" \
|
|
||||||
./config --prefix=${PREFIX}
|
|
||||||
make -j`nproc`
|
|
||||||
make install_sw
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# build aria2 static library
|
|
||||||
if ! [[ -e aria2-${ARIA2_V}.tar.bz2 ]]; then
|
|
||||||
${DOWNLOADER} ${ARIA2}
|
|
||||||
fi
|
|
||||||
tar jxvf aria2-${ARIA2_V}.tar.bz2
|
|
||||||
cd aria2-${ARIA2_V}/
|
|
||||||
# set specific ldflags if macOS
|
|
||||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
||||||
export LDFLAGS="-L${PREFIX}/lib -framework Security"
|
|
||||||
fi
|
|
||||||
PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig/ \
|
|
||||||
LD_LIBRARY_PATH=${PREFIX}/lib/ \
|
|
||||||
CC="$C_COMPILER" \
|
|
||||||
CXX="$CXX_COMPILER" \
|
|
||||||
./configure \
|
|
||||||
--prefix=${PREFIX} \
|
|
||||||
--without-sqlite3 \
|
|
||||||
--without-libxml2 \
|
|
||||||
--without-libexpat \
|
|
||||||
--without-libgcrypt \
|
|
||||||
--without-libssh2 \
|
|
||||||
--without-libz \
|
|
||||||
--with-openssl \
|
|
||||||
--without-appletls \
|
|
||||||
--without-libnettle \
|
|
||||||
--without-gnutls \
|
|
||||||
--without-libgmp \
|
|
||||||
--enable-libaria2 \
|
|
||||||
--enable-shared=no \
|
|
||||||
--enable-static=yes
|
|
||||||
make -j`nproc`
|
|
||||||
make install
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
# cleaning
|
|
||||||
rm -rf c-ares-${C_ARES_V}
|
|
||||||
rm -rf openssl-${OPENSSL_V}
|
|
||||||
rm -rf aria2-${ARIA2_V}
|
|
||||||
rm -rf ${PREFIX}/bin
|
|
||||||
|
|
||||||
# generate files for c
|
|
||||||
cd ${PREFIX}/../
|
|
||||||
go tool cgo libaria2.go
|
|
||||||
|
|
||||||
echo "Prepare finished!"
|
|
Loading…
Add table
Add a link
Reference in a new issue