mirror of
https://github.com/Xpl0itU/WiiUDownloader.git
synced 2025-05-21 10:45:26 -04:00
333 lines
8.8 KiB
Go
333 lines
8.8 KiB
Go
// 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
|
|
#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))
|
|
}
|