16 Commits

Author SHA1 Message Date
6e5ba2c755 Ready for new release with new engine, will start bugfixes 2018-06-08 18:19:51 -04:00
cbfcba4cbc Adding a few settings to webui, cleanup of unneeded func 2018-05-27 17:45:29 -04:00
d15bb9752a Finished Engine re-write, awaiting testing 2018-05-27 17:34:14 -04:00
35a5ac37eb Engine rewrite about 80% done, but a ton of bugs and a few new features to add, almost no testing done 2018-05-17 13:52:47 -04:00
4909429390 starting to redo the core to do queuing and better downloading 2018-04-18 22:41:32 -04:00
0fdc926cc4 adding ability to generate API keys 2018-04-07 13:07:47 -04:00
3280360d47 fixing sorting in webui 2018-04-05 16:30:44 -04:00
f69ec5b9f2 Changing permissions to walk the entire structure 2018-04-03 21:39:22 -04:00
aee3516682 fix folder permissions in donetorrentactions 2018-04-02 21:21:41 -04:00
a7881a14c7 fixing path issue with starting torrent 2018-03-30 20:05:18 -04:00
128ec774bd changing how the start API command works to start torrents 2018-03-27 15:39:02 -04:00
bc612bf5e4 removing symlink option only copy for now 2018-03-26 21:06:14 -04:00
3f1f9e7104 separate thread for torrent list 2018-03-25 23:07:22 -04:00
eeb6e102f1 fixing log not writing to file 2018-03-25 21:23:15 -04:00
0a0f0cd577 fixing notification issue, parallelizing startTorrent, verifying torrent after move 2018-03-25 09:34:32 -04:00
10399cc6e5 updating readme with new documentation link 2018-03-23 15:17:35 -04:00
22 changed files with 985 additions and 415 deletions

2
.gitignore vendored
View File

@@ -2,6 +2,7 @@ downloads/
downloading/
downloaded/
uploadedTorrents/
boltBrowser/
storage.db.lock
storage.db
storage.db.old
@@ -24,3 +25,4 @@ config.toml.old
/public/static/js/kickwebsocket-generated.js
clientAuth.txt
dist
debScripts/

3
Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM scratch
COPY goTorrent /
ENTRYPOINT [ "/goTorrent" ]

View File

@@ -65,7 +65,7 @@ Image of the frontend UI
# Documentation
All the documentation is available [here](https://deranjer.github.io/)
All the documentation is available [here](https://deranjer.github.io/goTorrentDocs/)
# Special Thanks

View File

@@ -1,7 +1,7 @@
[serverConfig]
ServerPort = ":8000" #leave format as is it expects a string with colon
ServerAddr = "192.168.1.100" #Put in the IP address you want to bind to
ServerAddr = "192.168.1.8" #Put in the IP address you want to bind to
LogLevel = "Info" # Options = Debug, Info, Warn, Error, Fatal, Panic
LogOutput = "stdout" #Options = file, stdout #file will print it to logs/server.log
@@ -15,6 +15,8 @@
#Low = ~.05MB/s, Medium = ~.5MB/s, High = ~1.5MB/s
UploadRateLimit = "Unlimited" #Options are "Low", "Medium", "High", "Unlimited" #Unlimited is default
DownloadRateLimit = "Unlimited"
#Maximum number of allowed active torrents, the rest will be queued
MaxActiveTorrents = 5
[goTorrentWebUI]
#Basic goTorrentWebUI authentication (not terribly secure, implemented in JS, password is hashed to SHA256, not salted, basically don't depend on this if you require very good security)

122
config.toml.bk Normal file
View File

@@ -0,0 +1,122 @@
[serverConfig]
ServerPort = ":8000" #leave format as is it expects a string with colon
ServerAddr = "192.168.1.8" #Put in the IP address you want to bind to
LogLevel = "Info" # Options = Debug, Info, Warn, Error, Fatal, Panic
LogOutput = "stdout" #Options = file, stdout #file will print it to logs/server.log
SeedRatioStop = 1.50 #automatically stops the torrent after it reaches this seeding ratio
#Relative or absolute path accepted, the server will convert any relative path to an absolute path.
DefaultMoveFolder = 'Z:\downloads' #default path that a finished torrent is symlinked to after completion. Torrents added via RSS will default here
TorrentWatchFolder = 'torrentUpload' #folder path that is watched for .torrent files and adds them automatically every 5 minutes
#Limits your upload and download speed globally, all are averages and not burst protected (usually burst on start).
#Low = ~.05MB/s, Medium = ~.5MB/s, High = ~1.5MB/s
UploadRateLimit = "Unlimited" #Options are "Low", "Medium", "High", "Unlimited" #Unlimited is default
DownloadRateLimit = "Unlimited"
[goTorrentWebUI]
#Basic goTorrentWebUI authentication (not terribly secure, implemented in JS, password is hashed to SHA256, not salted, basically don't depend on this if you require very good security)
WebUIAuth = false # bool, if false no authentication is required for the webUI
WebUIUser = "admin"
WebUIPassword = "Password1"
[notifications]
PushBulletToken = "o.8sUHemPkTCaty3u7KnyvEBN19EkeT63g" #add your pushbullet api token here to notify of torrent completion to pushbullet
[reverseProxy]
#This is for setting up goTorrent behind a reverse Proxy (with SSL, reverse proxy with no SSL will require editing the WSS connection to a WS connection manually)
ProxyEnabled = false #bool, either false or true
#URL is CASE SENSITIVE
BaseURL = "derajnet.duckdns.org/gopher/" # MUST be in the format (if you have a subdomain, and must have trailing slash) "yoursubdomain.domain.org/subroute/"
[EncryptionPolicy]
DisableEncryption = false
ForceEncryption = false
PreferNoEncryption = false
[torrentClientConfig]
DownloadDir = 'downloading' #the full OR relative path where the torrent server stores in-progress torrents
Seed = false #boolean #seed after download
# Never send chunks to peers.
NoUpload = false #boolean
#User-provided Client peer ID. If not present, one is generated automatically.
PeerID = "" #string
#The address to listen for new uTP and TCP bittorrent protocol connections. DHT shares a UDP socket with uTP unless configured otherwise.
ListenAddr = "" #Leave Blank for default, syntax "HOST:PORT"
#Don't announce to trackers. This only leaves DHT to discover peers.
DisableTrackers = false #boolean
DisablePEX = false # boolean
# Don't create a DHT.
NoDHT = false #boolean
#For the bittorrent protocol.
DisableUTP = false #bool
#For the bittorrent protocol.
DisableTCP = false #bool
#Called to instantiate storage for each added torrent. Builtin backends
# are in the storage package. If not set, the "file" implementation is used.
DefaultStorage = "storage.ClientImpl"
#encryption policy
IPBlocklist = "" #of type iplist.Ranger
DisableIPv6 = false #boolean
Debug = false #boolean
#HTTP *http.Client
HTTPUserAgent = "" # HTTPUserAgent changes default UserAgent for HTTP requests
ExtendedHandshakeClientVersion = ""
Bep20 = ""
# Overrides the default DHT configuration, see dhtServerConfig #advanced.. so be careful
DHTConfig = "" # default is "dht.ServerConfig"
[dhtServerConfig]
# Set NodeId Manually. Caller must ensure that if NodeId does not conform to DHT Security Extensions, that NoSecurity is also set.
NodeId = "" #[20]byte
Conn = "" # https:#godoc.org/net#PacketConn #not implemented
# Don't respond to queries from other nodes.
Passive = false # boolean
# the default addresses are "router.utorrent.com:6881","router.bittorrent.com:6881","dht.transmissionbt.com:6881","dht.aelitis.com:6881",
#https:#github.com/anacrolix/dht/blob/master/dht.go
StartingNodes = "dht.GlobalBootstrapAddrs"
#Disable the DHT security extension: http:#www.libtorrent.org/dht_sec.html.
NoSecurity = false
#Initial IP blocklist to use. Applied before serving and bootstrapping begins.
IPBlocklist = "" #of type iplist.Ranger
#Used to secure the server's ID. Defaults to the Conn's LocalAddr(). Set to the IP that remote nodes will see,
#as that IP is what they'll use to validate our ID.
PublicIP = "" #net.IP
#Hook received queries. Return true if you don't want to propagate to the default handlers.
OnQuery = "func(query *krpc.Msg, source net.Addr) (propagate bool)"
#Called when a peer successfully announces to us.
OnAnnouncePeer = "func(infoHash metainfo.Hash, peer Peer)"
#How long to wait before re-sending queries that haven't received a response. Defaults to a random value between 4.5 and 5.5s.
QueryResendDelay = "func() time.Duration"

View File

@@ -22,7 +22,7 @@ func InitializeCronEngine() *cron.Cron {
}
//CheckTorrentWatchFolder adds torrents from a watch folder //TODO see if you can use filepath.Abs instead of changing directory
func CheckTorrentWatchFolder(c *cron.Cron, db *storm.DB, tclient *torrent.Client, torrentLocalStorage Storage.TorrentLocal, config Settings.FullClientSettings) {
func CheckTorrentWatchFolder(c *cron.Cron, db *storm.DB, tclient *torrent.Client, torrentLocalStorage Storage.TorrentLocal, config Settings.FullClientSettings, torrentQueues Storage.TorrentQueues) {
c.AddFunc("@every 5m", func() {
Logger.WithFields(logrus.Fields{"Watch Folder": config.TorrentWatchFolder}).Info("Running the watch folder cron job")
torrentFiles, err := ioutil.ReadDir(config.TorrentWatchFolder)
@@ -50,7 +50,7 @@ func CheckTorrentWatchFolder(c *cron.Cron, db *storm.DB, tclient *torrent.Client
os.Remove(fullFilePathAbs) //delete the torrent after adding it and copying it over
Logger.WithFields(logrus.Fields{"Source Folder": fullFilePathAbs, "Destination Folder": fullNewFilePathAbs, "Torrent": file.Name()}).Info("Added torrent from watch folder, and moved torrent file")
StartTorrent(clientTorrent, torrentLocalStorage, db, "file", fullNewFilePathAbs, config.DefaultMoveFolder, "default", config)
AddTorrent(clientTorrent, torrentLocalStorage, db, "file", fullNewFilePathAbs, config.DefaultMoveFolder, "default", config)
}
}
@@ -58,7 +58,7 @@ func CheckTorrentWatchFolder(c *cron.Cron, db *storm.DB, tclient *torrent.Client
}
//RefreshRSSCron refreshes all of the RSS feeds on an hourly basis
func RefreshRSSCron(c *cron.Cron, db *storm.DB, tclient *torrent.Client, torrentLocalStorage Storage.TorrentLocal, config Settings.FullClientSettings) {
func RefreshRSSCron(c *cron.Cron, db *storm.DB, tclient *torrent.Client, torrentLocalStorage Storage.TorrentLocal, config Settings.FullClientSettings, torrentQueues Storage.TorrentQueues) {
c.AddFunc("@hourly", func() {
torrentHashHistory := Storage.FetchHashHistory(db)
RSSFeedStore := Storage.FetchRSSFeeds(db)
@@ -86,7 +86,7 @@ func RefreshRSSCron(c *cron.Cron, db *storm.DB, tclient *torrent.Client, torrent
Logger.WithFields(logrus.Fields{"err": err, "Torrent": RSSTorrent.Title}).Warn("Unable to add torrent to torrent client!")
break //break out of the loop entirely for this message since we hit an error
}
StartTorrent(clientTorrent, torrentLocalStorage, db, "magnet", "", config.DefaultMoveFolder, "RSS", config) //TODO let user specify torrent default storage location and let change on fly
AddTorrent(clientTorrent, torrentLocalStorage, db, "magnet", "", config.DefaultMoveFolder, "RSS", config) //TODO let user specify torrent default storage location and let change on fly
singleFeed.Torrents = append(singleFeed.Torrents, singleRSSTorrent)
}

View File

@@ -3,7 +3,6 @@ package engine
import (
"os"
"path/filepath"
"runtime"
"github.com/asdine/storm"
Settings "github.com/deranjer/goTorrent/settings"
@@ -15,7 +14,7 @@ import (
)
//MoveAndLeaveSymlink takes the file from the default download dir and moves it to the user specified directory and then leaves a symlink behind.
func MoveAndLeaveSymlink(config Settings.FullClientSettings, tHash string, db *storm.DB, moveDone bool, oldPath string) { //moveDone and oldPath are for moving a completed torrent
func MoveAndLeaveSymlink(config Settings.FullClientSettings, tHash string, db *storm.DB, moveDone bool, oldPath string) error { //moveDone and oldPath are for moving a completed torrent
tStorage := Storage.FetchTorrentFromStorage(db, tHash)
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName}).Info("Move and Create symlink started for torrent")
var oldFilePath string
@@ -25,6 +24,8 @@ func MoveAndLeaveSymlink(config Settings.FullClientSettings, tHash string, db *s
oldFilePath, err = filepath.Abs(oldFilePathTemp)
if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!")
moveDone = false
return err
}
} else {
oldFilePathTemp := filepath.Join(config.TorrentConfig.DataDir, tStorage.TorrentName)
@@ -32,41 +33,57 @@ func MoveAndLeaveSymlink(config Settings.FullClientSettings, tHash string, db *s
oldFilePath, err = filepath.Abs(oldFilePathTemp)
if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!")
moveDone = false
return err
}
}
newFilePathTemp := filepath.Join(tStorage.StoragePath, tStorage.TorrentName)
newFilePath, err := filepath.Abs(newFilePathTemp)
if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": newFilePath}).Error("Cannot create absolute file path for new file path!")
moveDone = false
return err
}
_, err = os.Stat(tStorage.StoragePath)
if os.IsNotExist(err) {
err := os.MkdirAll(tStorage.StoragePath, 0755)
err := os.MkdirAll(tStorage.StoragePath, 0777)
if err != nil {
Logger.WithFields(logrus.Fields{"New File Path": newFilePath, "error": err}).Error("Cannot create new directory")
moveDone = false
return err
}
}
oldFileInfo, err := os.Stat(oldFilePath)
if err != nil {
Logger.WithFields(logrus.Fields{"Old File info": oldFileInfo, "Old File Path": oldFilePath, "error": err}).Error("Cannot find the old file to copy/symlink!")
return
moveDone = false
return err
}
if oldFilePath != newFilePath {
newFilePathDir := filepath.Dir(newFilePath)
os.Mkdir(newFilePathDir, 0755)
os.Mkdir(newFilePathDir, 0777)
err := folderCopy.Copy(oldFilePath, newFilePath) //copy the folder to the new location
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error Copying Folder!")
return err
}
os.Chmod(newFilePath, 0777)
if runtime.GOOS != "windows" { //TODO the windows symlink is broken on windows 10 creator edition, so on the other platforms create symlink (windows will copy) until Go1.11
err = filepath.Walk(newFilePath, func(path string, info os.FileInfo, err error) error { //Walking the file path to change the permissions
if err != nil {
Logger.WithFields(logrus.Fields{"file": path, "error": err}).Error("Potentially non-critical error, continuing..")
}
os.Chmod(path, 0777)
return nil
})
/* if runtime.GOOS != "windows" { //TODO the windows symlink is broken on windows 10 creator edition, so on the other platforms create symlink (windows will copy) until Go1.11
os.RemoveAll(oldFilePath)
err = os.Symlink(newFilePath, oldFilePath)
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error creating symlink")
moveDone = false
return err
}
}
} */
if moveDone == false {
tStorage.TorrentMoved = true //TODO error handling instead of just saying torrent was moved when it was not
notifyUser(tStorage, config, db) //Only notify if we haven't moved yet, don't want to push notify user every time user uses change storage button
@@ -75,7 +92,7 @@ func MoveAndLeaveSymlink(config Settings.FullClientSettings, tHash string, db *s
tStorage.StoragePath = filepath.Dir(newFilePath)
Storage.UpdateStorageTick(db, tStorage)
}
return nil
}
func notifyUser(tStorage Storage.TorrentLocal, config Settings.FullClientSettings, db *storm.DB) {

View File

@@ -1,47 +0,0 @@
package engine
import (
"testing"
"github.com/asdine/storm"
Settings "github.com/deranjer/goTorrent/settings"
Storage "github.com/deranjer/goTorrent/storage"
)
func TestMoveAndLeaveSymlink(t *testing.T) {
type args struct {
config Settings.FullClientSettings
tStorage Storage.TorrentLocal
db *storm.DB
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
MoveAndLeaveSymlink(tt.args.config, tt.args.tStorage, tt.args.db)
})
}
}
func Test_notifyUser(t *testing.T) {
type args struct {
tStorage Storage.TorrentLocal
config Settings.FullClientSettings
db *storm.DB
}
tests := []struct {
name string
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
notifyUser(tt.args.tStorage, tt.args.config, tt.args.db)
})
}
}

View File

@@ -32,6 +32,10 @@ func CreateServerPushMessage(message ServerPushMessage, conn *websocket.Conn) {
conn.WriteJSON(message)
}
func QueueJSONMessage(conn *websocket.Conn){
}
//RefreshSingleRSSFeed refreshing a single RSS feed to send to the client (so no updating database) mainly by updating the torrent list to display any changes
func RefreshSingleRSSFeed(db *storm.DB, RSSFeed Storage.SingleRSSFeed) Storage.SingleRSSFeed { //Todo.. duplicate as cron job... any way to merge these to reduce duplication?
singleRSSFeed := Storage.SingleRSSFeed{URL: RSSFeed.URL, Name: RSSFeed.Name}
@@ -129,18 +133,18 @@ func readTorrentFileFromDB(element *Storage.TorrentLocal, tclient *torrent.Clien
return singleTorrent, nil
}
//StartTorrent creates the storage.db entry and starts A NEW TORRENT and adds to the running torrent array
func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.TorrentLocal, torrentDbStorage *storm.DB, torrentType, torrentFilePathAbs, torrentStoragePath, labelValue string, config Settings.FullClientSettings) {
//AddTorrent creates the storage.db entry and starts A NEW TORRENT and adds to the running torrent array
func AddTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.TorrentLocal, db *storm.DB, torrentType, torrentFilePathAbs, torrentStoragePath, labelValue string, config Settings.FullClientSettings) {
timedOut := timeOutInfo(clientTorrent, 45) //seeing if adding the torrent times out (giving 45 seconds)
if timedOut { //if we fail to add the torrent return
return
}
var TempHash metainfo.Hash
TempHash = clientTorrent.InfoHash()
allStoredTorrents := Storage.FetchAllStoredTorrents(torrentDbStorage)
allStoredTorrents := Storage.FetchAllStoredTorrents(db)
for _, runningTorrentHashes := range allStoredTorrents {
if runningTorrentHashes.Hash == TempHash.String() {
Logger.WithFields(logrus.Fields{"Hash": TempHash.String()}).Error("Torrent has duplicate hash to already running torrent... will not add to storage")
Logger.WithFields(logrus.Fields{"Hash": TempHash.String()}).Info("Torrent has duplicate hash to already running torrent... will not add to storage")
return
}
}
@@ -164,7 +168,7 @@ func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.To
}
torrentLocalStorage.TorrentFile = torrentfile //storing the entire file in to database
}
Logger.WithFields(logrus.Fields{"Storage Path": torrentStoragePath, "Torrent Name": clientTorrent.Name()}).Info("Adding Torrent with following storage path")
Logger.WithFields(logrus.Fields{"Storage Path": torrentStoragePath, "Torrent Name": clientTorrent.Name()}).Info("Adding Torrent with following storage path, to active Queue")
torrentFiles := clientTorrent.Files() //storing all of the files in the database along with the priority
var TorrentFilePriorityArray = []Storage.TorrentFilePriority{}
for _, singleFile := range torrentFiles { //creating the database setup for the file array
@@ -175,23 +179,16 @@ func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.To
TorrentFilePriorityArray = append(TorrentFilePriorityArray, torrentFilePriority)
}
torrentLocalStorage.TorrentFilePriority = TorrentFilePriorityArray
Storage.AddTorrentLocalStorage(torrentDbStorage, torrentLocalStorage) //writing all of the data to the database
clientTorrent.DownloadAll() //set all pieces to download
NumPieces := clientTorrent.NumPieces() //find the number of pieces
clientTorrent.CancelPieces(1, NumPieces) //cancel all of the pieces to use file priority
for _, singleFile := range clientTorrent.Files() { //setting all of the file priorities to normal
singleFile.SetPriority(torrent.PiecePriorityNormal)
}
fmt.Println("Downloading ALL") //starting the download
CreateServerPushMessage(ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "success", Payload: "Torrent added!"}, Conn)
//torrentQueues := Storage.FetchQueues(db)
AddTorrentToActive(&torrentLocalStorage, clientTorrent, db)
Storage.AddTorrentLocalStorage(db, torrentLocalStorage) //writing all of the data to the database
}
//CreateInitialTorrentArray adds all the torrents on program start from the database
func CreateInitialTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Storage.TorrentLocal, db *storm.DB) {
func CreateInitialTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Storage.TorrentLocal, db *storm.DB, config Settings.FullClientSettings) {
for _, singleTorrentFromStorage := range TorrentLocalArray {
var singleTorrent *torrent.Torrent
var err error
if singleTorrentFromStorage.TorrentType == "file" { //if it is a file pull it from the uploaded torrent folder
@@ -220,26 +217,62 @@ func CreateInitialTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
if err != nil {
Logger.WithFields(logrus.Fields{"torrentFile": singleTorrent.Name(), "error": err}).Error("Unable to add infobytes to the torrent!")
}
if singleTorrentFromStorage.TorrentStatus != "Completed" && singleTorrentFromStorage.TorrentStatus != "Stopped" {
fmt.Println("Starting torrent as download", singleTorrent.Name())
singleTorrent.DownloadAll() //set all of the pieces to download (piece prio is NE to file prio)
NumPieces := singleTorrent.NumPieces() //find the number of pieces
singleTorrent.CancelPieces(1, NumPieces) //cancel all of the pieces to use file priority
for _, singleFile := range singleTorrent.Files() { //setting all of the file priorities to normal
singleFile.SetPriority(torrent.PiecePriorityNormal)
torrentQueues := Storage.FetchQueues(db)
if len(torrentQueues.ActiveTorrents) == 0 && len(torrentQueues.QueuedTorrents) == 0 { // If empty, run through all the torrents and assign them
if len(torrentQueues.ActiveTorrents) < Config.MaxActiveTorrents && singleTorrentFromStorage.TorrentStatus != "Stopped" {
if singleTorrentFromStorage.TorrentStatus == "Completed" || singleTorrentFromStorage.TorrentStatus == "Seeding" {
Logger.WithFields(logrus.Fields{"Torrent Name": singleTorrentFromStorage.TorrentName}).Info("Completed Torrents have lower priority, adding to Queued")
AddTorrentToQueue(singleTorrentFromStorage, singleTorrent, db)
} else {
Logger.WithFields(logrus.Fields{"Torrent Name": singleTorrentFromStorage.TorrentName}).Info("Adding Torrent to Active Queue")
AddTorrentToActive(singleTorrentFromStorage, singleTorrent, db)
}
} else {
Logger.WithFields(logrus.Fields{"Torrent Name": singleTorrentFromStorage.TorrentName}).Info("Last resort for torrent, adding to Queued")
AddTorrentToQueue(singleTorrentFromStorage, singleTorrent, db)
}
} else { //If we already have a queue set up then assign torrents to queue
if singleTorrentFromStorage.TorrentStatus == "Queued" {
AddTorrentToQueue(singleTorrentFromStorage, singleTorrent, db)
} else {
if len(torrentQueues.ActiveTorrents) < Config.MaxActiveTorrents && singleTorrentFromStorage.TorrentStatus != "Stopped" {
AddTorrentToActive(singleTorrentFromStorage, singleTorrent, db)
} else {
AddTorrentToQueue(singleTorrentFromStorage, singleTorrent, db)
}
}
RemoveDuplicatesFromQueues(db)
}
Storage.UpdateStorageTick(db, *singleTorrentFromStorage)
}
torrentQueues := Storage.FetchQueues(db)
if len(torrentQueues.ActiveTorrents) < config.MaxActiveTorrents && len(torrentQueues.QueuedTorrents) > 0 { //after all the torrents are added, see if out active torrent list isn't full, then add from the queue
Logger.WithFields(logrus.Fields{"Max Active: ": config.MaxActiveTorrents, "Current : ": torrentQueues.ActiveTorrents}).Debug("Adding Torrents from queue to active to fill...")
maxCanSend := config.MaxActiveTorrents - len(torrentQueues.ActiveTorrents)
if maxCanSend > len(torrentQueues.QueuedTorrents) {
maxCanSend = len(torrentQueues.QueuedTorrents)
}
torrentsToStart := make([]string, maxCanSend)
copy(torrentsToStart, torrentQueues.QueuedTorrents[len(torrentsToStart)-1:])
for _, torrentStart := range torrentsToStart {
for _, singleTorrent := range tclient.Torrents() {
if singleTorrent.InfoHash().String() == torrentStart {
singleTorrentFromStorage := Storage.FetchTorrentFromStorage(db, torrentStart)
AddTorrentToActive(&singleTorrentFromStorage, singleTorrent, db)
}
}
} else {
fmt.Println("Torrent status is....", singleTorrentFromStorage.TorrentStatus)
}
}
SetFilePriority(tclient, db) //Setting the desired file priority from storage
Logger.WithFields(logrus.Fields{"Max Active: ": config.MaxActiveTorrents, "Current : ": torrentQueues.ActiveTorrents}).Debug("Queue after all initial torrents have been added")
}
//CreateRunningTorrentArray creates the entire torrent list to pass to client
func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Storage.TorrentLocal, PreviousTorrentArray []ClientDB, config Settings.FullClientSettings, db *storm.DB) (RunningTorrentArray []ClientDB) {
torrentQueues := Storage.FetchQueues(db)
Logger.WithFields(logrus.Fields{"Max Active: ": config.MaxActiveTorrents, "TorrentQueues": torrentQueues}).Debug("Current TorrentQueues")
for _, singleTorrentFromStorage := range TorrentLocalArray {
torrentQueues := Storage.FetchQueues(db)
var singleTorrent *torrent.Torrent
var TempHash metainfo.Hash
for _, liveTorrent := range tclient.Torrents() { //matching the torrent from storage to the live torrent
@@ -247,19 +280,17 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
singleTorrent = liveTorrent
}
}
tickUpdateStruct := Storage.TorrentLocal{} //we are shoving the tick updates into a torrentlocal struct to pass to storage happens at the end of the routine
fullClientDB := new(ClientDB)
//singleTorrentStorageInfo := Storage.FetchTorrentFromStorage(db, TempHash.String()) //pulling the single torrent info from storage ()
//Handling deleted torrents here
if singleTorrentFromStorage.TorrentStatus == "Dropped" {
Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting just the torrent")
singleTorrent.Drop()
Storage.DelTorrentLocalStorage(db, singleTorrentFromStorage.Hash)
}
if singleTorrentFromStorage.TorrentStatus == "DroppedData" {
Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting just the torrent")
Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting torrent and data")
singleTorrent.Drop()
Storage.DelTorrentLocalStorageAndFiles(db, singleTorrentFromStorage.Hash, Config.TorrentConfig.DataDir)
@@ -275,11 +306,20 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
TempHash = singleTorrent.InfoHash()
if (calculatedCompletedSize == singleTorrentFromStorage.TorrentSize) && (singleTorrentFromStorage.TorrentMoved == false) { //if we are done downloading and haven't moved torrent yet
Logger.WithFields(logrus.Fields{"singleTorrent": singleTorrentFromStorage.TorrentName}).Info("Torrent Completed, moving...")
go MoveAndLeaveSymlink(config, singleTorrent.InfoHash().String(), db, false, "") //can take some time to move file so running this in another thread TODO make this a goroutine and skip this block if the routine is still running
tStorage := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String()) //Todo... find a better way to do this in the go-routine currently just to make sure it doesn't trigger multiple times
tStorage.TorrentMoved = true
Storage.UpdateStorageTick(db, tStorage)
go func() { //moving torrent in separate go-routine then verifying that the data is still there and correct
err := MoveAndLeaveSymlink(config, singleTorrent.InfoHash().String(), db, false, "") //can take some time to move file so running this in another thread TODO make this a goroutine and skip this block if the routine is still running
if err != nil { //If we fail, print the error and attempt a retry
Logger.WithFields(logrus.Fields{"singleTorrent": singleTorrentFromStorage.TorrentName, "error": err}).Error("Failed to move Torrent!")
VerifyData(singleTorrent)
tStorage.TorrentMoved = false
Storage.UpdateStorageTick(db, tStorage)
}
}()
}
fullStruct := singleTorrent.Stats()
activePeersString := strconv.Itoa(fullStruct.ActivePeers) //converting to strings
totalPeersString := fmt.Sprintf("%v", fullStruct.TotalPeers)
fullClientDB.StoragePath = singleTorrentFromStorage.StoragePath
@@ -313,10 +353,23 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
}
CalculateTorrentETA(singleTorrentFromStorage.TorrentSize, calculatedCompletedSize, fullClientDB) //needs to be here since we need the speed calculated before we can estimate the eta.
if (len(torrentQueues.ActiveTorrents) < config.MaxActiveTorrents) && (len(torrentQueues.QueuedTorrents) > 0) { //If there is room for another torrent in active torrents, add it.
var newTorrentHash string
for _, torrentHash := range torrentQueues.QueuedTorrents {
if singleTorrentFromStorage.TorrentStatus != "Stopped" {
newTorrentHash = torrentHash
}
}
for _, torrent := range tclient.Torrents() {
if newTorrentHash == torrent.InfoHash().String() {
AddTorrentToActive(singleTorrentFromStorage, singleTorrent, db)
}
}
}
fullClientDB.TotalUploadedSize = HumanizeBytes(float32(fullClientDB.TotalUploadedBytes))
fullClientDB.UploadRatio = CalculateUploadRatio(singleTorrent, fullClientDB) //calculate the upload ratio
CalculateTorrentStatus(singleTorrent, fullClientDB, config, singleTorrentFromStorage, calculatedCompletedSize, calculatedTotalSize)
CalculateTorrentStatus(singleTorrent, fullClientDB, config, singleTorrentFromStorage, calculatedCompletedSize, calculatedTotalSize, torrentQueues, db) //add torrents to the queue, remove from queue, etc
tickUpdateStruct.UploadRatio = fullClientDB.UploadRatio
tickUpdateStruct.TorrentSize = calculatedTotalSize
@@ -327,6 +380,7 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
RunningTorrentArray = append(RunningTorrentArray, *fullClientDB)
}
ValidateQueues(db, config, tclient) //Ensure we don't have too many in activeQueue
return RunningTorrentArray
}

View File

@@ -23,6 +23,11 @@ func secondsToMinutes(inSeconds int64) string {
return str
}
//VerifyData just verifies the data of a torrent by hash
func VerifyData(singleTorrent *torrent.Torrent) {
singleTorrent.VerifyData()
}
//MakeRange creates a range of pieces to set their priority based on a file
func MakeRange(min, max int) []int {
a := make([]int, max-min+1)
@@ -69,7 +74,7 @@ func CopyFile(srcFile string, destFile string) { //TODO move this to our importe
}
//SetFilePriority sets the priorities for all of the files in a torrent
//SetFilePriority sets the priorities for all of the files in all of the torrents
func SetFilePriority(t *torrent.Client, db *storm.DB) {
storedTorrents := Storage.FetchAllStoredTorrents(db)
for _, singleTorrent := range t.Torrents() {
@@ -179,18 +184,177 @@ func CalculateUploadRatio(t *torrent.Torrent, c *ClientDB) string {
return uploadRatio
}
//StopTorrent stops the torrent, updates the database and sends a message. Since stoptorrent is called by each loop (individually) no need to call an array
func StopTorrent(singleTorrent *torrent.Torrent, torrentLocalStorage *Storage.TorrentLocal, db *storm.DB) {
torrentQueues := Storage.FetchQueues(db)
if torrentLocalStorage.TorrentStatus == "Stopped" { //if we are already stopped
Logger.WithFields(logrus.Fields{"Torrent Name": torrentLocalStorage.TorrentName}).Info("Torrent Already Stopped, returning...")
return
}
torrentLocalStorage.TorrentStatus = "Stopped"
torrentLocalStorage.MaxConnections = 0
singleTorrent.SetMaxEstablishedConns(0)
for _, torrentHash := range torrentQueues.ActiveTorrents { //pulling it out of activetorrents
if torrentHash == singleTorrent.InfoHash().String() {
DeleteTorrentFromQueues(singleTorrent.InfoHash().String(), db)
}
}
for _, torrentHash := range torrentQueues.QueuedTorrents { //pulling it out of queuedTorrent
if torrentHash == singleTorrent.InfoHash().String() {
DeleteTorrentFromQueues(singleTorrent.InfoHash().String(), db)
}
}
Storage.UpdateStorageTick(db, *torrentLocalStorage)
CreateServerPushMessage(ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "success", Payload: "Torrent Stopped!"}, Conn)
return
}
//AddTorrentToActive adds a torrent to the active slice
func AddTorrentToActive(torrentLocalStorage *Storage.TorrentLocal, singleTorrent *torrent.Torrent, db *storm.DB) {
torrentQueues := Storage.FetchQueues(db)
for _, torrentHash := range torrentQueues.ActiveTorrents {
if torrentHash == singleTorrent.InfoHash().String() { //If torrent already in active skip
return
}
}
for index, queuedTorrentHash := range torrentQueues.QueuedTorrents { //Removing from the queued torrents if in queued torrents
if queuedTorrentHash == singleTorrent.InfoHash().String() {
torrentQueues.QueuedTorrents = append(torrentQueues.QueuedTorrents[:index], torrentQueues.QueuedTorrents[index+1:]...)
}
}
singleTorrent.NewReader()
singleTorrent.SetMaxEstablishedConns(80)
torrentQueues.ActiveTorrents = append(torrentQueues.ActiveTorrents, singleTorrent.InfoHash().String())
torrentLocalStorage.TorrentStatus = "Running"
torrentLocalStorage.MaxConnections = 80
for _, file := range singleTorrent.Files() {
for _, sentFile := range torrentLocalStorage.TorrentFilePriority {
if file.DisplayPath() == sentFile.TorrentFilePath {
switch sentFile.TorrentFilePriority {
case "High":
file.SetPriority(torrent.PiecePriorityHigh)
case "Normal":
file.SetPriority(torrent.PiecePriorityNormal)
case "Cancel":
file.SetPriority(torrent.PiecePriorityNone)
default:
file.SetPriority(torrent.PiecePriorityNormal)
}
}
}
}
Logger.WithFields(logrus.Fields{"Torrent Name": torrentLocalStorage.TorrentName}).Info("Adding Torrent to Active Queue")
Storage.UpdateQueues(db, torrentQueues)
}
//RemoveTorrentFromActive forces a torrent to be removed from the active list if the max limit is already there and user forces a new torrent to be added
func RemoveTorrentFromActive(torrentLocalStorage *Storage.TorrentLocal, singleTorrent *torrent.Torrent, db *storm.DB) {
torrentQueues := Storage.FetchQueues(db)
for x, torrentHash := range torrentQueues.ActiveTorrents {
if torrentHash == singleTorrent.InfoHash().String() {
torrentQueues.ActiveTorrents = append(torrentQueues.ActiveTorrents[:x], torrentQueues.ActiveTorrents[x+1:]...)
torrentQueues.QueuedTorrents = append(torrentQueues.QueuedTorrents, torrentHash)
torrentLocalStorage.TorrentStatus = "Queued"
torrentLocalStorage.MaxConnections = 0
singleTorrent.SetMaxEstablishedConns(0)
Storage.UpdateQueues(db, torrentQueues)
//AddTorrentToQueue(torrentLocalStorage, singleTorrent, db) //Adding the lasttorrent from active to queued
Storage.UpdateStorageTick(db, *torrentLocalStorage)
}
}
}
//DeleteTorrentFromQueues deletes the torrent from all queues (for a stop or delete action)
func DeleteTorrentFromQueues(torrentHash string, db *storm.DB) {
torrentQueues := Storage.FetchQueues(db)
for x, torrentHashActive := range torrentQueues.ActiveTorrents {
if torrentHash == torrentHashActive {
torrentQueues.ActiveTorrents = append(torrentQueues.ActiveTorrents[:x], torrentQueues.ActiveTorrents[x+1:]...)
Storage.UpdateQueues(db, torrentQueues)
} else {
for x, torrentHashQueued := range torrentQueues.QueuedTorrents {
if torrentHash == torrentHashQueued {
torrentQueues.QueuedTorrents = append(torrentQueues.QueuedTorrents[:x], torrentQueues.QueuedTorrents[x+1:]...)
Storage.UpdateQueues(db, torrentQueues)
}
}
}
}
Logger.WithFields(logrus.Fields{"Torrent Hash": torrentHash}).Info("Removing Torrent from all Queues")
}
//AddTorrentToQueue adds a torrent to the queue
func AddTorrentToQueue(torrentLocalStorage *Storage.TorrentLocal, singleTorrent *torrent.Torrent, db *storm.DB) {
torrentQueues := Storage.FetchQueues(db)
for _, torrentHash := range torrentQueues.QueuedTorrents {
if singleTorrent.InfoHash().String() == torrentHash { //don't add duplicate to que but do everything else (TODO, maybe find a better way?)
singleTorrent.SetMaxEstablishedConns(0)
torrentLocalStorage.MaxConnections = 0
torrentLocalStorage.TorrentStatus = "Queued"
Logger.WithFields(logrus.Fields{"TorrentName": torrentLocalStorage.TorrentName}).Info("Adding torrent to the queue, not active")
Storage.UpdateStorageTick(db, *torrentLocalStorage)
return
}
}
torrentQueues.QueuedTorrents = append(torrentQueues.QueuedTorrents, singleTorrent.InfoHash().String())
singleTorrent.SetMaxEstablishedConns(0)
torrentLocalStorage.MaxConnections = 0
torrentLocalStorage.TorrentStatus = "Queued"
Logger.WithFields(logrus.Fields{"TorrentName": torrentLocalStorage.TorrentName}).Info("Adding torrent to the queue, not active")
Storage.UpdateQueues(db, torrentQueues)
Storage.UpdateStorageTick(db, *torrentLocalStorage)
}
//RemoveDuplicatesFromQueues removes any duplicates from torrentQueues.QueuedTorrents (which will happen if it is read in from DB)
func RemoveDuplicatesFromQueues(db *storm.DB) {
torrentQueues := Storage.FetchQueues(db)
for _, torrentHash := range torrentQueues.ActiveTorrents {
for i, queuedHash := range torrentQueues.QueuedTorrents {
if torrentHash == queuedHash {
torrentQueues.QueuedTorrents = append(torrentQueues.QueuedTorrents[:i], torrentQueues.QueuedTorrents[i+1:]...)
}
}
}
Storage.UpdateQueues(db, torrentQueues)
}
//ValidateQueues is a sanity check that runs every tick to make sure the queues are in order... tried to avoid this but seems to be required
func ValidateQueues(db *storm.DB, config Settings.FullClientSettings, tclient *torrent.Client) {
torrentQueues := Storage.FetchQueues(db)
for len(torrentQueues.ActiveTorrents) > config.MaxActiveTorrents {
removeTorrent := torrentQueues.ActiveTorrents[:1]
for _, singleTorrent := range tclient.Torrents() {
if singleTorrent.InfoHash().String() == removeTorrent[0] {
singleTorrentFromStorage := Storage.FetchTorrentFromStorage(db, removeTorrent[0])
RemoveTorrentFromActive(&singleTorrentFromStorage, singleTorrent, db)
}
}
}
}
//CalculateTorrentStatus is used to determine what the STATUS column of the frontend will display ll2
func CalculateTorrentStatus(t *torrent.Torrent, c *ClientDB, config Settings.FullClientSettings, tFromStorage *storage.TorrentLocal, bytesCompleted int64, totalSize int64) {
if (tFromStorage.TorrentStatus == "Stopped") || (float64(c.TotalUploadedBytes)/float64(bytesCompleted) >= config.SeedRatioStop && tFromStorage.TorrentUploadLimit == true) { //If storage shows torrent stopped or if it is over the seeding ratio AND is under the global limit
func CalculateTorrentStatus(t *torrent.Torrent, c *ClientDB, config Settings.FullClientSettings, tFromStorage *storage.TorrentLocal, bytesCompleted int64, totalSize int64, torrentQueues Storage.TorrentQueues, db *storm.DB) {
if tFromStorage.TorrentStatus == "Stopped" {
c.Status = "Stopped"
c.MaxConnections = 0
t.SetMaxEstablishedConns(0)
return
}
if float64(c.TotalUploadedBytes)/float64(bytesCompleted) >= config.SeedRatioStop && tFromStorage.TorrentUploadLimit == true { //If storage shows torrent stopped or if it is over the seeding ratio AND is under the global limit
StopTorrent(t, tFromStorage, db)
} else { //Only has 2 states in storage, stopped or running, so we know it should be running, and the websocket request handled updating the database with connections and status
for _, torrentHash := range torrentQueues.QueuedTorrents {
if tFromStorage.Hash == torrentHash {
c.Status = "Queued"
return
}
}
if len(torrentQueues.ActiveTorrents) < config.MaxActiveTorrents && tFromStorage.TorrentStatus == "Queued" {
AddTorrentToActive(tFromStorage, t, db)
}
bytesMissing := totalSize - bytesCompleted
c.MaxConnections = 80
t.SetMaxEstablishedConns(80)
//t.DownloadAll() //ensure that we are setting the torrent to download
if t.Seeding() && t.Stats().ActivePeers > 0 && bytesMissing == 0 {
c.Status = "Seeding"
} else if t.Stats().ActivePeers > 0 && bytesMissing > 0 {

View File

@@ -21,6 +21,7 @@ let serverMessage = [];
let serverPushMessage = [];
let webSocketState = false;
let settingsFile = [];
let tokenReturn = "";
var torrentListRequest = {
MessageType: "torrentListRequest"
@@ -29,7 +30,7 @@ var torrentListRequest = {
//websocket is started in kickwebsocket.js and is picked up here so "ws" is already defined 22
//websocket is started in kickwebsocket.js and is picked up here so "ws" is already defined
ws.onmessage = function (evt) { //When we recieve a message from the websocket
var serverMessage = JSON.parse(evt.data)
console.log("message", serverMessage.MessageType)
@@ -134,6 +135,10 @@ ws.onmessage = function (evt) { //When we recieve a message from the websocket
settingsFile = [];
console.log("Settings File Returned", serverMessage)
settingsFile = serverMessage.Config
case "TokenReturn":
tokenReturn = serverMessage.TokenReturn
console.log("Token Returned", serverMessage)
}
}
@@ -222,10 +227,15 @@ class BackendSocket extends React.Component {
console.log("PROPSSERVER", this.props.serverPushMessage, "SERVERPUSH", serverPushMessage)
this.props.newServerMessage(serverPushMessage)
}
if (this.props.settingsModalOpen) { //TODO don't really need to updaate every tick currently until we can edit config
if (this.props.settingsModalOpen) { //TODO don't really need to update every tick currently until we can edit config
this.props.newSettingsFile(settingsFile)
}
if (tokenReturn != ""){ //If we get a return token
console.log("Dispatching token return", tokenReturn)
this.props.newTokenReturn(tokenReturn)
}
ws.send(JSON.stringify(torrentListRequest))//talking to the server to get the torrent list
if (ws.readyState === ws.CLOSED){ //if our websocket gets closed inform the user
webSocketState = false
@@ -264,6 +274,9 @@ class BackendSocket extends React.Component {
if (nextProps.selectionHashes.length === 1){ //if we have a selection pass it on for the tabs to verify
this.selectionHandler(nextProps.selectionHashes, nextProps.selectedTab)
}
if (nextProps.tokenReturn != this.props.tokenReturn){ //clearing out the token if we switch from the API tab
tokenReturn = nextProps.tokenReturn
}
}
@@ -287,6 +300,7 @@ const mapStateToProps = state => {
RSSTorrentList: state.RSSTorrentList,
serverPushMessage: state.serverPushMessage,
settingsModalOpen: state.settingsModalOpen,
tokenReturn: state.tokenReturn,
};
}
@@ -301,9 +315,8 @@ const mapDispatchToProps = dispatch => {
RSSTorrentList: (RSSTorrentList) => dispatch({type: actionTypes.RSS_TORRENT_LIST, RSSTorrentList}),
newServerMessage: (serverPushMessage) => dispatch({type: actionTypes.SERVER_MESSAGE, serverPushMessage}),
webSocketStateUpdate: (webSocketState) => dispatch({type: actionTypes.WEBSOCKET_STATE, webSocketState}),
newSettingsFile: (settingsFile) => dispatch({type: actionTypes.NEW_SETTINGS_FILE, settingsFile})
//changeSelection: (selection) => dispatch({type: actionTypes.CHANGE_SELECTION, selection}),//forcing an update to the buttons
newSettingsFile: (settingsFile) => dispatch({type: actionTypes.NEW_SETTINGS_FILE, settingsFile}),
newTokenReturn: (tokenReturn) => dispatch({type: actionTypes.TOKEN_RETURN, tokenReturn}),
}
}

View File

@@ -3,14 +3,18 @@ import ReactDOM from 'react-dom';
import { withStyles } from 'material-ui/styles';
import Paper from 'material-ui/Paper';
import Grid from 'material-ui/Grid';
import Button from 'material-ui/Button';
import TextField from 'material-ui/TextField';
import {connect} from 'react-redux';
import * as actionTypes from '../../../../store/actions';
const styles = theme => ({
root: {
flexGrow: 1,
marginTop: 0,
padding: 10,
},
paper: {
padding: 16,
@@ -23,19 +27,48 @@ const styles = theme => ({
});
class APISettingsTab extends React.PureComponent {
class APISettingsTab extends React.Component {
state = {
clientName: "",
};
requestNewKey = (keyName) => {
generateKey = (event) => {
let newAuthTokenRequest = {
MessageType: "newAuthToken",
Payload: {"ClientName": this.state.clientName}
}
console.log("Sending New Auth Request: ", newAuthTokenRequest);
ws.send(JSON.stringify(newAuthTokenRequest));
this.setState({clientName: ""})
}
setClientName = (event) => {
this.setState({clientName: event.target.value})
}
componentWillUnmount = () => {
this.props.newTokenReturn("")
}
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
Not yet implemented!
<TextField style ={{width: '50%', paddingRight: '10px'}} id="clientName" type="text" label="Client Name" placeholder="Client Name associated with the key" onChange={this.setClientName} />
<Button variant="raised" color="primary" onClick={this.generateKey}>
Generate Key
</Button>
<Paper style = {{padding: '10px'}}> <span className={classes.floatLeft}>{this.props.tokenReturn} </span></Paper>
<Grid container spacing={16}>
<Grid item xs={12} sm={4}>
</Grid>
</Grid>
</div>
);
}
@@ -44,9 +77,15 @@ class APISettingsTab extends React.PureComponent {
const mapStateToProps = state => {
return {
settingsFile: state.settingsFile,
tokenReturn: state.tokenReturn,
};
}
export default withStyles(styles)(connect(mapStateToProps)(APISettingsTab))
const mapDispatchToProps = dispatch => {
return {
newTokenReturn: (tokenReturn) => dispatch({type: actionTypes.TOKEN_RETURN, tokenReturn}),
}
}
export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(APISettingsTab))

View File

@@ -35,6 +35,7 @@ class ClientSettingsTab extends React.PureComponent {
<Paper className={classes.paper}>HTTP Port: <span className={classes.floatRight}>{this.props.settingsFile["HTTPAddr"]} </span> </Paper>
<Paper className={classes.paper}>Use Proxy: <span className={classes.floatRight}>{String(this.props.settingsFile["UseProxy"])} </span> </Paper>
<Paper className={classes.paper}>Base URL: <span className={classes.floatRight}>{this.props.settingsFile["BaseURL"]} </span> </Paper>
<Paper className={classes.paper}>Max Active Torrents: <span className={classes.floatRight}>{this.props.settingsFile["MaxActiveTorrents"]} </span> </Paper>
</Grid>

View File

@@ -53,7 +53,7 @@ class LoggingSettingsTab extends React.Component {
<Grid container spacing={8}>
<Grid item xs={12} sm={4}>
<Paper className={classes.paper}>Logging Output: <span className={classes.floatRight}>{this.props.settingsFile["LoggingOutput"]} </span></Paper>
<Paper className={classes.paper}>Logging Level: <span className={classes.floatRight}>{logLevel} </span> </Paper>
<Paper className={classes.paper}>Logging Level: <span className={classes.floatRight}>{this.props.settingsFile["LoggingLevel"]} </span> </Paper>
</Grid>

View File

@@ -15,3 +15,4 @@ export const NEW_SETTINGS_FILE = 'NEW_SETTINGS_FILE';
export const RSS_TORRENT_LIST = 'RSS_TORRENT_LIST';
export const SERVER_MESSAGE = 'SERVER_MESSAGE';
export const WEBSOCKET_STATE = 'WEBSOCKET_STATE';
export const TOKEN_RETURN = 'TOKEN_RETURN';

View File

@@ -130,6 +130,13 @@ const reducer = (state = initialState, action) => {
serverPushMessage: action.serverPushMessage
}
case actionTypes.TOKEN_RETURN:
console.log("New token return", action.tokenReturn)
return {
... state,
tokenReturn: action.tokenReturn
}
case actionTypes.SET_BUTTON_STATE:
if (action.buttonState.length === 0) { //if selection is empty buttons will be default and selectionHashes will be blanked out and pushed to redux
let buttonStateFinal = state.buttonStateDefault //if no selection dispatch that to redux
@@ -144,10 +151,9 @@ const reducer = (state = initialState, action) => {
selectedRows.push(state.torrentList[element]) //pushing the selected rows out of torrentlist
});
let buttonStateTest = selectedRows.filter(element => { //TODO fix this bad mess... we literally just need to filter for stopped and go from there
let buttonStateTest = selectedRows.filter(element => {
let result = []
if (element.Status === "Downloading" || element.Status === "Awaiting Peers" || element.Status === "Seeding" || element.Status === "Completed"){
if (element.Status === "Downloading" || element.Status === "Awaiting Peers" || element.Status === "Seeding" || element.Status === "Completed" || element.Status === "Queued"){
result.push(element.Status)
return result
}
@@ -157,7 +163,6 @@ const reducer = (state = initialState, action) => {
if (buttonStateTest.length > 0 && buttonStateTest2.length === 0){
let buttonStateFinal = [{startButton: "default", stopButton: "primary", deleteButton: "secondary", fSeedButton: "default", fRecheckButton: "primary"}]
console.log("ButtonStateFil")
return {
...state,
buttonState: buttonStateFinal

View File

@@ -136,7 +136,7 @@ class TorrentListTable extends React.Component {
<TableColumnReordering order={this.state.columnOrder} onOrderChange={this.changeColumnOrder} />
<IntegratedSelection />
<TableSelection selectByRowClick highlightSelected showSelectAll />
<TableHeaderRow allowSorting allowResizing allowDragging />
<TableHeaderRow showSortingControls allowSorting allowResizing allowDragging />
</Grid>
</Paper>
);

160
main.go
View File

@@ -51,7 +51,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
func handleAuthentication(conn *websocket.Conn, db *storm.DB) {
msg := Engine.Message{}
err := conn.ReadJSON(&msg)
conn.WriteJSON(msg) //TODO just for testing, remove
//conn.WriteJSON(msg) //TODO just for testing, remove
payloadData, ok := msg.Payload.(map[string]interface{})
clientAuthToken, tokenOk := payloadData["ClientAuthString"].(string)
fmt.Println("ClientAuthToken:", clientAuthToken, "TokenOkay", tokenOk, "PayloadData", payloadData, "PayloadData Okay?", ok)
@@ -94,6 +94,7 @@ func main() {
Engine.Logger = Logger //Injecting the logger into all the packages
Storage.Logger = Logger
Settings.Logger = Logger
var torrentQueues = Storage.TorrentQueues{}
Config := Settings.FullClientSettingsNew() //grabbing from settings.go
Engine.Config = Config
if Config.LoggingOutput == "file" {
@@ -103,16 +104,17 @@ func main() {
if err != nil {
fmt.Println("Unable to create 'log' folder for logging.... please check permissions.. forcing output to stdout", err)
Logger.Out = os.Stdout
} else {
os.Remove("logs/server.log") //cleanup the old log on every restart
file, err := os.OpenFile("logs/server.log", os.O_CREATE|os.O_WRONLY, 0755) //creating the log file
defer file.Close() //TODO.. since we write to this constantly how does close work?
if err != nil {
fmt.Println("Unable to create file for logging.... please check permissions.. forcing output to stdout")
Logger.Out = os.Stdout
}
Logger.Out = file
}
} else {
os.Remove("logs/server.log") //cleanup the old log on every restart
file, err := os.OpenFile("logs/server.log", os.O_CREATE|os.O_WRONLY, 0755) //creating the log file
//defer file.Close() //TODO.. since we write to this constantly how does close work?
if err != nil {
fmt.Println("Unable to create file for logging.... please check permissions.. forcing output to stdout")
Logger.Out = os.Stdout
}
fmt.Println("Logging to file logs/server.log")
Logger.Out = file
}
} else {
Logger.Out = os.Stdout
@@ -122,26 +124,33 @@ func main() {
httpAddr := Config.HTTPAddr
os.MkdirAll(Config.TFileUploadFolder, 0755) //creating a directory to store uploaded torrent files
os.MkdirAll(Config.TorrentWatchFolder, 0755) //creating a directory to watch for added .torrent files
//Logger.WithFields(logrus.Fields{"Config": Config}).Info("Torrent Client Config has been generated...")
Logger.WithFields(logrus.Fields{"Config": Config}).Info("Torrent Client Config has been generated...")
tclient, err := torrent.NewClient(&Config.TorrentConfig) //pulling out the torrent specific config to use
if err != nil {
Logger.WithFields(logrus.Fields{"error": err}).Fatalf("Error creating torrent client: %s")
}
fmt.Printf("%+v\n", Config.TorrentConfig)
//fmt.Printf("%+v\n", Config.TorrentConfig)
db, err := storm.Open("storage.db") //initializing the boltDB store that contains all the added torrents
if err != nil {
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Error opening/creating storage.db")
} else {
Logger.WithFields(logrus.Fields{"error": err}).Info("Opening or creating storage.db...")
}
defer db.Close() //defering closing the database until the program closes
err = db.One("ID", 5, &torrentQueues)
if err != nil { //Create the torrent que database
Logger.WithFields(logrus.Fields{"error": err}).Info("No Queue database found, assuming first run, creating database")
torrentQueues.ID = 5
db.Save(&torrentQueues)
}
tokens := Storage.IssuedTokensList{} //if first run setting up the authentication tokens
var signingKey []byte
err = db.One("ID", 3, &tokens)
if err != nil {
Logger.WithFields(logrus.Fields{"RSSFeedStore": tokens, "error": err}).Info("No Tokens database found, assuming first run, generating token...")
fmt.Println("Error", err)
fmt.Println("MAIN TOKEN: %+v\n", tokens)
tokens.ID = 3 //creating the initial store
claims := Settings.GoTorrentClaims{
"goTorrentWebUI",
@@ -188,12 +197,13 @@ func main() {
TorrentLocalArray := Storage.FetchAllStoredTorrents(db) //pulling in all the already added torrents - this is an array of ALL of the local storage torrents, they will be added back in via hash
if TorrentLocalArray != nil { //the first creation of the running torrent array //since we are adding all of them in we use a coroutine... just allows the web ui to load then it will load in the torrents
go Engine.CreateInitialTorrentArray(tclient, TorrentLocalArray, db) //adding all of the stored torrents into the torrent client
Engine.CreateInitialTorrentArray(tclient, TorrentLocalArray, db, Config) //adding all of the stored torrents into the torrent client
//TODO add GO to this
} else {
Logger.Info("Database is empty, no torrents loaded")
}
Engine.CheckTorrentWatchFolder(cronEngine, db, tclient, torrentLocalStorage, Config) //Every 5 minutes the engine will check the specified folder for new .torrent files
Engine.RefreshRSSCron(cronEngine, db, tclient, torrentLocalStorage, Config) // Refresing the RSS feeds on an hourly basis to add torrents that show up in the RSS feed
Engine.CheckTorrentWatchFolder(cronEngine, db, tclient, torrentLocalStorage, Config, torrentQueues) //Every 5 minutes the engine will check the specified folder for new .torrent files
Engine.RefreshRSSCron(cronEngine, db, tclient, torrentLocalStorage, Config, torrentQueues) // Refresing the RSS feeds on an hourly basis to add torrents that show up in the RSS feed
router := mux.NewRouter() //setting up the handler for the web backend
router.HandleFunc("/", serveHome) //Serving the main page for our SPA
@@ -264,7 +274,7 @@ func main() {
Logger.WithFields(logrus.Fields{"clientName": payloadData["ClientName"].(string)}).Info("New Auth Token creation request")
fmt.Println("Signing Key", signingKey)
token := Settings.GenerateToken(claims, signingKey)
tokenReturn := Settings.TokenReturn{TokenReturn: token}
tokenReturn := Settings.TokenReturn{MessageType: "TokenReturn", TokenReturn: token}
tokensDB := Storage.FetchJWTTokens(db)
tokensDB.TokenNames = append(tokens.TokenNames, Storage.SingleToken{payloadData["ClientName"].(string)})
db.Update(&tokensDB) //adding the new token client name to the database
@@ -272,27 +282,30 @@ func main() {
case "torrentListRequest":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested TorrentList Update")
TorrentLocalArray = Storage.FetchAllStoredTorrents(db) //Required to re-read th database since we write to the DB and this will pull the changes from it
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well
PreviousTorrentArray = RunningTorrentArray
torrentlistArray := Engine.TorrentList{MessageType: "torrentList", ClientDBstruct: RunningTorrentArray, Totaltorrents: len(RunningTorrentArray)}
Logger.WithFields(logrus.Fields{"torrentList": torrentlistArray, "previousTorrentList": PreviousTorrentArray}).Debug("Previous and Current Torrent Lists for sending to client")
conn.WriteJSON(torrentlistArray)
go func() { //running updates in separate thread so can still accept commands
TorrentLocalArray = Storage.FetchAllStoredTorrents(db) //Required to re-read the database since we write to the DB and this will pull the changes from it
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well
PreviousTorrentArray = RunningTorrentArray
torrentlistArray := Engine.TorrentList{MessageType: "torrentList", ClientDBstruct: RunningTorrentArray, Totaltorrents: len(RunningTorrentArray)}
Logger.WithFields(logrus.Fields{"torrentList": torrentlistArray, "previousTorrentList": PreviousTorrentArray}).Debug("Previous and Current Torrent Lists for sending to client")
conn.WriteJSON(torrentlistArray)
}()
case "torrentFileListRequest": //client requested a filelist update
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested FileList Update")
Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested FileList Update")
fileListArrayRequest := payloadData["FileListHash"].(string)
FileListArray := Engine.CreateFileListArray(tclient, fileListArrayRequest, db, Config)
conn.WriteJSON(FileListArray) //writing the JSON to the client
case "torrentPeerListRequest":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested PeerList Update")
Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested PeerList Update")
peerListArrayRequest := payloadData["PeerListHash"].(string)
torrentPeerList := Engine.CreatePeerListArray(tclient, peerListArrayRequest)
conn.WriteJSON(torrentPeerList)
case "fetchTorrentsByLabel": //TODO test this to make sure it works
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Torrents by Label")
case "fetchTorrentsByLabel":
Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested Torrents by Label")
label := payloadData["Label"].(string)
torrentsByLabel := Storage.FetchTorrentsByLabel(db, label)
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db)
@@ -307,7 +320,7 @@ func main() {
conn.WriteJSON(labelRunningArray)
case "changeStorageValue":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Storage Location Update")
Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested Storage Location Update")
newStorageLocation := payloadData["StorageValue"].(string)
hashes := payloadData["ChangeStorageHashes"].([]interface{})
for _, singleHash := range hashes {
@@ -328,12 +341,12 @@ func main() {
}
case "settingsFileRequest":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Settings File")
Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested Settings File")
clientSettingsFile := Engine.SettingsFile{MessageType: "settingsFile", Config: Config}
conn.WriteJSON(clientSettingsFile)
case "rssFeedRequest":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested RSS Update")
Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested RSS Update")
RSSList := Storage.FetchRSSFeeds(db)
RSSJSONFeed := Engine.RSSJSONList{MessageType: "rssList", TotalRSSFeeds: len(RSSList.RSSFeeds)}
RSSsingleFeed := Engine.RSSFeedsNames{}
@@ -346,9 +359,9 @@ func main() {
case "addRSSFeed":
newRSSFeed := payloadData["RSSURL"].(string)
Logger.WithFields(logrus.Fields{"message": newRSSFeed}).Debug("Client Added RSS Feed")
Logger.WithFields(logrus.Fields{"message": newRSSFeed}).Info("Client Added RSS Feed")
fullRSSFeeds := Storage.FetchRSSFeeds(db)
Logger.WithFields(logrus.Fields{"RSSFeeds": fullRSSFeeds}).Debug("Pulled Full RSS Feeds")
Logger.WithFields(logrus.Fields{"RSSFeeds": fullRSSFeeds}).Info("Pulled Full RSS Feeds")
for _, singleFeed := range fullRSSFeeds.RSSFeeds {
if newRSSFeed == singleFeed.URL || newRSSFeed == "" {
Logger.WithFields(logrus.Fields{"RSSFeed": newRSSFeed}).Warn("Empty URL or Duplicate RSS URL to one already in database! Rejecting submission")
@@ -375,7 +388,7 @@ func main() {
case "deleteRSSFeed":
deleteRSSFeed := payloadData["RSSURL"].(string)
Logger.WithFields(logrus.Fields{"message": deleteRSSFeed}).Debug("Deleting RSS Feed")
Logger.WithFields(logrus.Fields{"message": deleteRSSFeed}).Info("Deleting RSS Feed")
Storage.DeleteRSSFeed(db, deleteRSSFeed)
fullRSSFeeds := Storage.FetchRSSFeeds(db)
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Deleting RSS feed..."}, conn)
@@ -386,7 +399,7 @@ func main() {
Logger.WithFields(logrus.Fields{"RSSFeed": RSSFeedURL}).Info("Requesting torrentList for feed..")
UpdatedRSSFeed := Engine.RefreshSingleRSSFeed(db, Storage.FetchSpecificRSSFeed(db, RSSFeedURL))
TorrentRSSList := Engine.SingleRSSFeedMessage{MessageType: "rssTorrentList", URL: RSSFeedURL, Name: UpdatedRSSFeed.Name, TotalTorrents: len(UpdatedRSSFeed.Torrents), Torrents: UpdatedRSSFeed.Torrents}
Logger.WithFields(logrus.Fields{"TorrentRSSList": TorrentRSSList}).Debug("Returning Torrent list from RSSFeed to client")
Logger.WithFields(logrus.Fields{"TorrentRSSList": TorrentRSSList}).Info("Returning Torrent list from RSSFeed to client")
conn.WriteJSON(TorrentRSSList)
case "magnetLinkSubmit": //if we detect a magnet link we will be adding a magnet torrent
@@ -418,8 +431,18 @@ func main() {
}
Logger.WithFields(logrus.Fields{"clientTorrent": clientTorrent, "magnetLink": magnetLink}).Info("Adding torrent to client!")
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received MagnetLink"}, conn)
Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, "magnet", "", storageValue, labelValue, Config) //starting the torrent and creating local DB entry
if len(torrentQueues.ActiveTorrents) > Config.MaxActiveTorrents {
Logger.WithFields(logrus.Fields{"Name: ": clientTorrent.Name()}).Info("Adding New torrent to active, pushing other torrent to queue")
removeTorrent := torrentQueues.ActiveTorrents[:1]
for _, singleTorrent := range runningTorrents {
if singleTorrent.InfoHash().String() == removeTorrent[0] {
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
Engine.RemoveTorrentFromActive(&oldTorrentInfo, singleTorrent, db)
Storage.UpdateStorageTick(db, oldTorrentInfo)
}
}
}
go Engine.AddTorrent(clientTorrent, torrentLocalStorage, db, "magnet", "", storageValue, labelValue, Config) //starting the torrent and creating local DB entry
}
case "torrentFileSubmit":
@@ -465,7 +488,18 @@ func main() {
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "error", Payload: "Unable to add Torrent to torrent server"}, conn)
}
Logger.WithFields(logrus.Fields{"clienttorrent": clientTorrent.Name(), "filename": filePathAbs}).Info("Added torrent")
Engine.StartTorrent(clientTorrent, torrentLocalStorage, db, "file", filePathAbs, storageValue, labelValue, Config)
if len(torrentQueues.ActiveTorrents) >= Config.MaxActiveTorrents {
Logger.WithFields(logrus.Fields{"Name: ": clientTorrent.Name()}).Info("Adding New torrent to active, pushing other torrent to queue")
removeTorrent := torrentQueues.ActiveTorrents[:1]
for _, singleTorrent := range runningTorrents {
if singleTorrent.InfoHash().String() == removeTorrent[0] {
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
Engine.RemoveTorrentFromActive(&oldTorrentInfo, singleTorrent, db)
Storage.UpdateStorageTick(db, oldTorrentInfo)
go Engine.AddTorrent(clientTorrent, torrentLocalStorage, db, "file", filePathAbs, storageValue, labelValue, Config)
}
}
}
case "stopTorrents":
torrentHashes := payloadData["TorrentHashes"].([]interface{})
@@ -475,9 +509,16 @@ func main() {
if singleTorrent.InfoHash().String() == singleSelection {
Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for stopping torrents")
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
oldTorrentInfo.TorrentStatus = "Stopped"
oldTorrentInfo.MaxConnections = 0
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
Engine.StopTorrent(singleTorrent, &oldTorrentInfo, db)
if len(torrentQueues.QueuedTorrents) > 1 {
addTorrent := torrentQueues.QueuedTorrents[:1]
for _, singleTorrent := range runningTorrents {
if singleTorrent.InfoHash().String() == addTorrent[0] {
Engine.AddTorrentToActive(&torrentLocalStorage, singleTorrent, db)
}
}
}
}
}
}
@@ -491,6 +532,20 @@ func main() {
for _, singleSelection := range torrentHashes {
if singleTorrent.InfoHash().String() == singleSelection {
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
torrentQueues = Storage.FetchQueues(db)
for index, activeTorrentHash := range torrentQueues.ActiveTorrents { //If torrent is in the active slice, pull it
if singleTorrent.InfoHash().String() == activeTorrentHash {
singleTorrent.SetMaxEstablishedConns(0)
torrentQueues.ActiveTorrents = append(torrentQueues.ActiveTorrents[:index], torrentQueues.ActiveTorrents[index+1:]...)
}
}
for index, queuedTorrentHash := range torrentQueues.QueuedTorrents { //If torrent is in the queued slice, pull it
if singleTorrent.InfoHash().String() == queuedTorrentHash {
torrentQueues.QueuedTorrents = append(torrentQueues.QueuedTorrents[:index], torrentQueues.QueuedTorrents[index+1:]...)
}
}
Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for deleting torrents")
if withData {
oldTorrentInfo.TorrentStatus = "DroppedData" //Will be cleaned up the next engine loop since deleting a torrent mid loop can cause issues
@@ -498,6 +553,7 @@ func main() {
oldTorrentInfo.TorrentStatus = "Dropped"
}
Storage.UpdateStorageTick(db, oldTorrentInfo)
Storage.UpdateQueues(db, torrentQueues)
}
}
}
@@ -509,17 +565,28 @@ func main() {
for _, singleTorrent := range runningTorrents {
for _, singleSelection := range torrentHashes {
if singleTorrent.InfoHash().String() == singleSelection {
Logger.WithFields(logrus.Fields{"infoHash": singleTorrent.InfoHash().String()}).Debug("Found matching torrent to start")
Logger.WithFields(logrus.Fields{"infoHash": singleTorrent.InfoHash().String()}).Info("Found matching torrent to start")
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
oldTorrentInfo.TorrentStatus = "Running"
oldTorrentInfo.MaxConnections = 80
Engine.AddTorrentToActive(&oldTorrentInfo, singleTorrent, db)
Logger.WithFields(logrus.Fields{"Torrent": oldTorrentInfo.TorrentName}).Info("Changing database to torrent running with 80 max connections")
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
}
torrentQueues = Storage.FetchQueues(db)
if len(torrentQueues.ActiveTorrents) > Config.MaxActiveTorrents { //Since we are starting a new torrent stop the first torrent in the que if running is full
//removeTorrent := torrentQueues.ActiveTorrents[len(torrentQueues.ActiveTorrents)-1]
removeTorrent := torrentQueues.ActiveTorrents[:1]
for _, singleTorrent := range runningTorrents {
if singleTorrent.InfoHash().String() == removeTorrent[0] {
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
Engine.RemoveTorrentFromActive(&oldTorrentInfo, singleTorrent, db)
Storage.UpdateStorageTick(db, oldTorrentInfo)
}
}
}
}
}
case "forceUploadTorrents":
case "forceUploadTorrents": //TODO allow force to override total limit of queued torrents?
torrentHashes := payloadData["TorrentHashes"].([]interface{})
Logger.WithFields(logrus.Fields{"selection": msg.Payload}).Info("Matched for force Uploading Torrents")
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Received Force Start Request"}, conn)
@@ -531,7 +598,6 @@ func main() {
oldTorrentInfo.TorrentUploadLimit = false // no upload limit for this torrent
oldTorrentInfo.TorrentStatus = "Running"
oldTorrentInfo.MaxConnections = 80
fmt.Println("OldtorrentinfoName", oldTorrentInfo.TorrentName)
Logger.WithFields(logrus.Fields{"NewMax": oldTorrentInfo.MaxConnections, "Torrent": oldTorrentInfo.TorrentName}).Info("Setting max connection from zero to 80")
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
}
@@ -596,7 +662,7 @@ func main() {
} else {
err := http.ListenAndServe(httpAddr, nil) //Can't send proxy headers if not used since that can be a security issue
if err != nil {
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Unable to listen on the http Server with no proxy headers!")
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Unable to listen on the http Server! (Maybe wrong IP in config, port already in use?) (Config: Not using proxy, see error for more details)")
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,12 +8,15 @@ import (
"github.com/sirupsen/logrus"
)
//AuthRequest is a struct sent by a client with an authstring (JWT Token) to validate they have access to the server
type AuthRequest struct {
MessageType string `json:"MessageType"`
AuthString string `json:"AuthString"`
}
//TokenReturn is a struct sent by the server to a client with a new generated authstring
type TokenReturn struct {
MessageType string `json:"MessageType"`
TokenReturn string `json:"TokenReturn"`
}

View File

@@ -40,6 +40,7 @@ type FullClientSettings struct {
DefaultMoveFolder string
TorrentWatchFolder string
ClientConnectSettings
MaxActiveTorrents int
}
//default is called if there is a parsing error
@@ -70,6 +71,8 @@ func dhtServerSettings(dhtConfig dht.ServerConfig) dht.ServerConfig {
func calculateRateLimiters(uploadRate, downloadRate string) (*rate.Limiter, *rate.Limiter) { //TODO reorg
var uploadRateLimiterSize int
var downloadRateLimiterSize int
var downloadRateLimiter *rate.Limiter
var uploadRateLimiter *rate.Limiter
switch uploadRate {
case "Low":
@@ -79,8 +82,8 @@ func calculateRateLimiters(uploadRate, downloadRate string) (*rate.Limiter, *rat
case "High":
uploadRateLimiterSize = 1500000
default:
downloadRateLimiter := rate.NewLimiter(rate.Inf, 0)
uploadRateLimiter := rate.NewLimiter(rate.Inf, 0)
downloadRateLimiter = rate.NewLimiter(rate.Inf, 0)
uploadRateLimiter = rate.NewLimiter(rate.Inf, 0)
return downloadRateLimiter, uploadRateLimiter
}
@@ -89,17 +92,16 @@ func calculateRateLimiters(uploadRate, downloadRate string) (*rate.Limiter, *rat
downloadRateLimiterSize = 50000
case "Medium":
downloadRateLimiterSize = 500000
fmt.Println("Medium Rate Limit...")
case "High":
downloadRateLimiterSize = 1500000
default:
downloadRateLimiter := rate.NewLimiter(rate.Inf, 0)
uploadRateLimiter := rate.NewLimiter(rate.Inf, 0)
downloadRateLimiter = rate.NewLimiter(rate.Inf, 0)
uploadRateLimiter = rate.NewLimiter(rate.Inf, 0)
return downloadRateLimiter, uploadRateLimiter
}
var limitPerSecondUl = rate.Limit(uploadRateLimiterSize)
uploadRateLimiter := rate.NewLimiter(limitPerSecondUl, uploadRateLimiterSize)
var limitPerSecondDl = rate.Limit(uploadRateLimiterSize)
downloadRateLimiter := rate.NewLimiter(limitPerSecondDl, downloadRateLimiterSize)
uploadRateLimiter = rate.NewLimiter(rate.Limit(uploadRateLimiterSize), uploadRateLimiterSize)
downloadRateLimiter = rate.NewLimiter(rate.Limit(downloadRateLimiterSize), downloadRateLimiterSize)
return downloadRateLimiter, uploadRateLimiter
}
@@ -177,9 +179,9 @@ func FullClientSettingsNew() FullClientSettings {
//Rate Limiters
//var uploadRateLimiter *rate.Limiter
//var downloadRateLimiter *rate.Limiter
//uploadRate := viper.GetString("serverConfig.UploadRateLimit")
//downloadRate := viper.GetString("serverConfig.DownloadRateLimit")
//downloadRateLimiter, uploadRateLimiter = calculateRateLimiters(uploadRate, downloadRate)
uploadRate := viper.GetString("serverConfig.UploadRateLimit")
downloadRate := viper.GetString("serverConfig.DownloadRateLimit")
downloadRateLimiter, uploadRateLimiter := calculateRateLimiters(uploadRate, downloadRate)
//Internals
dataDir := filepath.ToSlash(viper.GetString("torrentClientConfig.DownloadDir")) //Converting the string literal into a filepath
dataDirAbs, err := filepath.Abs(dataDir) //Converting to an absolute file path
@@ -191,6 +193,7 @@ func FullClientSettingsNew() FullClientSettings {
noDHT := viper.GetBool("torrentClientConfig.NoDHT")
noUpload := viper.GetBool("torrentClientConfig.NoUpload")
seed := viper.GetBool("torrentClientConfig.Seed")
maxActiveTorrents := viper.GetInt("serverConfig.MaxActiveTorrents")
peerID := viper.GetString("torrentClientConfig.PeerID")
disableUTP := viper.GetBool("torrentClientConfig.DisableUTP")
@@ -213,21 +216,21 @@ func FullClientSettingsNew() FullClientSettings {
}
tConfig := torrent.Config{
DataDir: dataDirAbs,
ListenAddr: listenAddr,
DisablePEX: disablePex,
NoDHT: noDHT,
DHTConfig: dhtServerConfig,
NoUpload: noUpload,
Seed: seed,
//UploadRateLimiter: uploadRateLimiter,
//DownloadRateLimiter: downloadRateLimiter,
PeerID: peerID,
DisableUTP: disableUTP,
DisableTCP: disableTCP,
DisableIPv6: disableIPv6,
Debug: debug,
EncryptionPolicy: encryptionPolicy,
DataDir: dataDirAbs,
ListenAddr: listenAddr,
DisablePEX: disablePex,
NoDHT: noDHT,
DHTConfig: dhtServerConfig,
NoUpload: noUpload,
Seed: seed,
UploadRateLimiter: uploadRateLimiter,
DownloadRateLimiter: downloadRateLimiter,
PeerID: peerID,
DisableUTP: disableUTP,
DisableTCP: disableTCP,
DisableIPv6: disableIPv6,
Debug: debug,
EncryptionPolicy: encryptionPolicy,
}
Config := FullClientSettings{
@@ -248,6 +251,7 @@ func FullClientSettingsNew() FullClientSettings {
TorrentConfig: tConfig,
DefaultMoveFolder: defaultMoveFolderAbs,
TorrentWatchFolder: torrentWatchFolderAbs,
MaxActiveTorrents: maxActiveTorrents,
}
return Config

View File

@@ -16,6 +16,13 @@ var Logger *logrus.Logger
//Conn is the global websocket connection used to push server notification messages
var Conn *websocket.Conn
//TorrentQueues contains the active and queued torrent hashes in slices
type TorrentQueues struct {
ID int `storm:"id,unique"` //storm requires unique ID (will be 5)
ActiveTorrents []string
QueuedTorrents []string
}
//IssuedTokensList contains a slice of all the tokens issues to applications
type IssuedTokensList struct {
ID int `storm:"id,unique"` //storm requires unique ID (will be 3) to save although there will only be one of these
@@ -94,6 +101,26 @@ func SaveConfig(torrentStorage *storm.DB, config Settings.FullClientSettings) {
}
}
//UpdateQueues Saves the slice of hashes that contain the active Torrents
func UpdateQueues(db *storm.DB, torrentQueues TorrentQueues) {
torrentQueues.ID = 5
err := db.Save(&torrentQueues)
if err != nil {
Logger.WithFields(logrus.Fields{"database": db, "error": err}).Error("Unable to write Queues to database!")
}
}
//FetchQueues fetches the activetorrent and queuedtorrent slices from the database
func FetchQueues(db *storm.DB) TorrentQueues {
torrentQueues := TorrentQueues{}
err := db.One("ID", 5, &torrentQueues)
if err != nil {
Logger.WithFields(logrus.Fields{"database": db, "error": err}).Error("Unable to read Database into torrentQueues!")
return torrentQueues
}
return torrentQueues
}
//FetchConfig fetches the client config from the database
func FetchConfig(torrentStorage *storm.DB) (Settings.FullClientSettings, error) {
config := Settings.FullClientSettings{}
@@ -166,6 +193,8 @@ func UpdateStorageTick(torrentStorage *storm.DB, torrentLocal TorrentLocal) {
err := torrentStorage.Update(&torrentLocal)
if err != nil {
Logger.WithFields(logrus.Fields{"UpdateContents": torrentLocal, "error": err}).Error("Error performing tick update to database!")
} else {
Logger.WithFields(logrus.Fields{"UpdateContents": torrentLocal, "error": err}).Debug("Performed Update to database!")
}
}