16 Commits
0.5.0 ... 0.6.0

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/ downloading/
downloaded/ downloaded/
uploadedTorrents/ uploadedTorrents/
boltBrowser/
storage.db.lock storage.db.lock
storage.db storage.db
storage.db.old storage.db.old
@@ -24,3 +25,4 @@ config.toml.old
/public/static/js/kickwebsocket-generated.js /public/static/js/kickwebsocket-generated.js
clientAuth.txt clientAuth.txt
dist 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 # Documentation
All the documentation is available [here](https://deranjer.github.io/) All the documentation is available [here](https://deranjer.github.io/goTorrentDocs/)
# Special Thanks # Special Thanks

View File

@@ -1,7 +1,7 @@
[serverConfig] [serverConfig]
ServerPort = ":8000" #leave format as is it expects a string with colon 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 LogLevel = "Info" # Options = Debug, Info, Warn, Error, Fatal, Panic
LogOutput = "stdout" #Options = file, stdout #file will print it to logs/server.log 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 #Low = ~.05MB/s, Medium = ~.5MB/s, High = ~1.5MB/s
UploadRateLimit = "Unlimited" #Options are "Low", "Medium", "High", "Unlimited" #Unlimited is default UploadRateLimit = "Unlimited" #Options are "Low", "Medium", "High", "Unlimited" #Unlimited is default
DownloadRateLimit = "Unlimited" DownloadRateLimit = "Unlimited"
#Maximum number of allowed active torrents, the rest will be queued
MaxActiveTorrents = 5
[goTorrentWebUI] [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) #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 //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() { c.AddFunc("@every 5m", func() {
Logger.WithFields(logrus.Fields{"Watch Folder": config.TorrentWatchFolder}).Info("Running the watch folder cron job") Logger.WithFields(logrus.Fields{"Watch Folder": config.TorrentWatchFolder}).Info("Running the watch folder cron job")
torrentFiles, err := ioutil.ReadDir(config.TorrentWatchFolder) 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 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") 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 //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() { c.AddFunc("@hourly", func() {
torrentHashHistory := Storage.FetchHashHistory(db) torrentHashHistory := Storage.FetchHashHistory(db)
RSSFeedStore := Storage.FetchRSSFeeds(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!") 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 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) singleFeed.Torrents = append(singleFeed.Torrents, singleRSSTorrent)
} }

View File

@@ -3,7 +3,6 @@ package engine
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"github.com/asdine/storm" "github.com/asdine/storm"
Settings "github.com/deranjer/goTorrent/settings" 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. //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) tStorage := Storage.FetchTorrentFromStorage(db, tHash)
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName}).Info("Move and Create symlink started for torrent") Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName}).Info("Move and Create symlink started for torrent")
var oldFilePath string var oldFilePath string
@@ -25,6 +24,8 @@ func MoveAndLeaveSymlink(config Settings.FullClientSettings, tHash string, db *s
oldFilePath, err = filepath.Abs(oldFilePathTemp) oldFilePath, err = filepath.Abs(oldFilePathTemp)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!") Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!")
moveDone = false
return err
} }
} else { } else {
oldFilePathTemp := filepath.Join(config.TorrentConfig.DataDir, tStorage.TorrentName) 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) oldFilePath, err = filepath.Abs(oldFilePathTemp)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!") 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) newFilePathTemp := filepath.Join(tStorage.StoragePath, tStorage.TorrentName)
newFilePath, err := filepath.Abs(newFilePathTemp) newFilePath, err := filepath.Abs(newFilePathTemp)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": newFilePath}).Error("Cannot create absolute file path for new file path!") 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) _, err = os.Stat(tStorage.StoragePath)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err := os.MkdirAll(tStorage.StoragePath, 0755) err := os.MkdirAll(tStorage.StoragePath, 0777)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"New File Path": newFilePath, "error": err}).Error("Cannot create new directory") Logger.WithFields(logrus.Fields{"New File Path": newFilePath, "error": err}).Error("Cannot create new directory")
moveDone = false
return err
} }
} }
oldFileInfo, err := os.Stat(oldFilePath) oldFileInfo, err := os.Stat(oldFilePath)
if err != nil { 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!") 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 { if oldFilePath != newFilePath {
newFilePathDir := filepath.Dir(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 err := folderCopy.Copy(oldFilePath, newFilePath) //copy the folder to the new location
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error Copying Folder!") Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error Copying Folder!")
return err
} }
os.Chmod(newFilePath, 0777) err = filepath.Walk(newFilePath, func(path string, info os.FileInfo, err error) error { //Walking the file path to change the permissions
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 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) os.RemoveAll(oldFilePath)
err = os.Symlink(newFilePath, oldFilePath) err = os.Symlink(newFilePath, oldFilePath)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error creating symlink") 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 { if moveDone == false {
tStorage.TorrentMoved = true //TODO error handling instead of just saying torrent was moved when it was not 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 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) tStorage.StoragePath = filepath.Dir(newFilePath)
Storage.UpdateStorageTick(db, tStorage) Storage.UpdateStorageTick(db, tStorage)
} }
return nil
} }
func notifyUser(tStorage Storage.TorrentLocal, config Settings.FullClientSettings, db *storm.DB) { 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) 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 //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? 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} singleRSSFeed := Storage.SingleRSSFeed{URL: RSSFeed.URL, Name: RSSFeed.Name}
@@ -129,18 +133,18 @@ func readTorrentFileFromDB(element *Storage.TorrentLocal, tclient *torrent.Clien
return singleTorrent, nil return singleTorrent, nil
} }
//StartTorrent creates the storage.db entry and starts A NEW TORRENT and adds to the running torrent array //AddTorrent 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) { 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) timedOut := timeOutInfo(clientTorrent, 45) //seeing if adding the torrent times out (giving 45 seconds)
if timedOut { //if we fail to add the torrent return if timedOut { //if we fail to add the torrent return
return return
} }
var TempHash metainfo.Hash var TempHash metainfo.Hash
TempHash = clientTorrent.InfoHash() TempHash = clientTorrent.InfoHash()
allStoredTorrents := Storage.FetchAllStoredTorrents(torrentDbStorage) allStoredTorrents := Storage.FetchAllStoredTorrents(db)
for _, runningTorrentHashes := range allStoredTorrents { for _, runningTorrentHashes := range allStoredTorrents {
if runningTorrentHashes.Hash == TempHash.String() { 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 return
} }
} }
@@ -164,7 +168,7 @@ func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.To
} }
torrentLocalStorage.TorrentFile = torrentfile //storing the entire file in to database 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 torrentFiles := clientTorrent.Files() //storing all of the files in the database along with the priority
var TorrentFilePriorityArray = []Storage.TorrentFilePriority{} var TorrentFilePriorityArray = []Storage.TorrentFilePriority{}
for _, singleFile := range torrentFiles { //creating the database setup for the file array 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) TorrentFilePriorityArray = append(TorrentFilePriorityArray, torrentFilePriority)
} }
torrentLocalStorage.TorrentFilePriority = TorrentFilePriorityArray torrentLocalStorage.TorrentFilePriority = TorrentFilePriorityArray
Storage.AddTorrentLocalStorage(torrentDbStorage, torrentLocalStorage) //writing all of the data to the database //torrentQueues := Storage.FetchQueues(db)
clientTorrent.DownloadAll() //set all pieces to download AddTorrentToActive(&torrentLocalStorage, clientTorrent, db)
NumPieces := clientTorrent.NumPieces() //find the number of pieces Storage.AddTorrentLocalStorage(db, torrentLocalStorage) //writing all of the data to the database
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)
} }
//CreateInitialTorrentArray adds all the torrents on program start from 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 { for _, singleTorrentFromStorage := range TorrentLocalArray {
var singleTorrent *torrent.Torrent var singleTorrent *torrent.Torrent
var err error var err error
if singleTorrentFromStorage.TorrentType == "file" { //if it is a file pull it from the uploaded torrent folder 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 { if err != nil {
Logger.WithFields(logrus.Fields{"torrentFile": singleTorrent.Name(), "error": err}).Error("Unable to add infobytes to the torrent!") Logger.WithFields(logrus.Fields{"torrentFile": singleTorrent.Name(), "error": err}).Error("Unable to add infobytes to the torrent!")
} }
if singleTorrentFromStorage.TorrentStatus != "Completed" && singleTorrentFromStorage.TorrentStatus != "Stopped" { torrentQueues := Storage.FetchQueues(db)
fmt.Println("Starting torrent as download", singleTorrent.Name()) if len(torrentQueues.ActiveTorrents) == 0 && len(torrentQueues.QueuedTorrents) == 0 { // If empty, run through all the torrents and assign them
singleTorrent.DownloadAll() //set all of the pieces to download (piece prio is NE to file prio) if len(torrentQueues.ActiveTorrents) < Config.MaxActiveTorrents && singleTorrentFromStorage.TorrentStatus != "Stopped" {
NumPieces := singleTorrent.NumPieces() //find the number of pieces if singleTorrentFromStorage.TorrentStatus == "Completed" || singleTorrentFromStorage.TorrentStatus == "Seeding" {
singleTorrent.CancelPieces(1, NumPieces) //cancel all of the pieces to use file priority Logger.WithFields(logrus.Fields{"Torrent Name": singleTorrentFromStorage.TorrentName}).Info("Completed Torrents have lower priority, adding to Queued")
for _, singleFile := range singleTorrent.Files() { //setting all of the file priorities to normal AddTorrentToQueue(singleTorrentFromStorage, singleTorrent, db)
singleFile.SetPriority(torrent.PiecePriorityNormal) } else {
Logger.WithFields(logrus.Fields{"Torrent Name": singleTorrentFromStorage.TorrentName}).Info("Adding Torrent to Active Queue")
AddTorrentToActive(singleTorrentFromStorage, singleTorrent, db)
} }
} else { } else {
fmt.Println("Torrent status is....", singleTorrentFromStorage.TorrentStatus) 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)
}
}
} }
} }
SetFilePriority(tclient, db) //Setting the desired file priority from storage 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 //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) { 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 { for _, singleTorrentFromStorage := range TorrentLocalArray {
torrentQueues := Storage.FetchQueues(db)
var singleTorrent *torrent.Torrent var singleTorrent *torrent.Torrent
var TempHash metainfo.Hash var TempHash metainfo.Hash
for _, liveTorrent := range tclient.Torrents() { //matching the torrent from storage to the live torrent 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 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 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) fullClientDB := new(ClientDB)
//singleTorrentStorageInfo := Storage.FetchTorrentFromStorage(db, TempHash.String()) //pulling the single torrent info from storage () //Handling deleted torrents here
if singleTorrentFromStorage.TorrentStatus == "Dropped" { if singleTorrentFromStorage.TorrentStatus == "Dropped" {
Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting just the torrent") Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting just the torrent")
singleTorrent.Drop() singleTorrent.Drop()
Storage.DelTorrentLocalStorage(db, singleTorrentFromStorage.Hash) Storage.DelTorrentLocalStorage(db, singleTorrentFromStorage.Hash)
} }
if singleTorrentFromStorage.TorrentStatus == "DroppedData" { 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() singleTorrent.Drop()
Storage.DelTorrentLocalStorageAndFiles(db, singleTorrentFromStorage.Hash, Config.TorrentConfig.DataDir) Storage.DelTorrentLocalStorageAndFiles(db, singleTorrentFromStorage.Hash, Config.TorrentConfig.DataDir)
@@ -275,11 +306,20 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
TempHash = singleTorrent.InfoHash() TempHash = singleTorrent.InfoHash()
if (calculatedCompletedSize == singleTorrentFromStorage.TorrentSize) && (singleTorrentFromStorage.TorrentMoved == false) { //if we are done downloading and haven't moved torrent yet 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...") 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() fullStruct := singleTorrent.Stats()
activePeersString := strconv.Itoa(fullStruct.ActivePeers) //converting to strings activePeersString := strconv.Itoa(fullStruct.ActivePeers) //converting to strings
totalPeersString := fmt.Sprintf("%v", fullStruct.TotalPeers) totalPeersString := fmt.Sprintf("%v", fullStruct.TotalPeers)
fullClientDB.StoragePath = singleTorrentFromStorage.StoragePath 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. 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.TotalUploadedSize = HumanizeBytes(float32(fullClientDB.TotalUploadedBytes))
fullClientDB.UploadRatio = CalculateUploadRatio(singleTorrent, fullClientDB) //calculate the upload ratio 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.UploadRatio = fullClientDB.UploadRatio
tickUpdateStruct.TorrentSize = calculatedTotalSize tickUpdateStruct.TorrentSize = calculatedTotalSize
@@ -327,6 +380,7 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
RunningTorrentArray = append(RunningTorrentArray, *fullClientDB) RunningTorrentArray = append(RunningTorrentArray, *fullClientDB)
} }
ValidateQueues(db, config, tclient) //Ensure we don't have too many in activeQueue
return RunningTorrentArray return RunningTorrentArray
} }

View File

@@ -23,6 +23,11 @@ func secondsToMinutes(inSeconds int64) string {
return str 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 //MakeRange creates a range of pieces to set their priority based on a file
func MakeRange(min, max int) []int { func MakeRange(min, max int) []int {
a := make([]int, max-min+1) 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) { func SetFilePriority(t *torrent.Client, db *storm.DB) {
storedTorrents := Storage.FetchAllStoredTorrents(db) storedTorrents := Storage.FetchAllStoredTorrents(db)
for _, singleTorrent := range t.Torrents() { for _, singleTorrent := range t.Torrents() {
@@ -179,18 +184,177 @@ func CalculateUploadRatio(t *torrent.Torrent, c *ClientDB) string {
return uploadRatio 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 //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) { 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") || (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 if tFromStorage.TorrentStatus == "Stopped" {
c.Status = "Stopped" c.Status = "Stopped"
c.MaxConnections = 0 return
t.SetMaxEstablishedConns(0) }
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 } 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 bytesMissing := totalSize - bytesCompleted
c.MaxConnections = 80 c.MaxConnections = 80
t.SetMaxEstablishedConns(80) t.SetMaxEstablishedConns(80)
//t.DownloadAll() //ensure that we are setting the torrent to download
if t.Seeding() && t.Stats().ActivePeers > 0 && bytesMissing == 0 { if t.Seeding() && t.Stats().ActivePeers > 0 && bytesMissing == 0 {
c.Status = "Seeding" c.Status = "Seeding"
} else if t.Stats().ActivePeers > 0 && bytesMissing > 0 { } else if t.Stats().ActivePeers > 0 && bytesMissing > 0 {

View File

@@ -21,6 +21,7 @@ let serverMessage = [];
let serverPushMessage = []; let serverPushMessage = [];
let webSocketState = false; let webSocketState = false;
let settingsFile = []; let settingsFile = [];
let tokenReturn = "";
var torrentListRequest = { var torrentListRequest = {
MessageType: "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 ws.onmessage = function (evt) { //When we recieve a message from the websocket
var serverMessage = JSON.parse(evt.data) var serverMessage = JSON.parse(evt.data)
console.log("message", serverMessage.MessageType) console.log("message", serverMessage.MessageType)
@@ -134,6 +135,10 @@ ws.onmessage = function (evt) { //When we recieve a message from the websocket
settingsFile = []; settingsFile = [];
console.log("Settings File Returned", serverMessage) console.log("Settings File Returned", serverMessage)
settingsFile = serverMessage.Config 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) console.log("PROPSSERVER", this.props.serverPushMessage, "SERVERPUSH", serverPushMessage)
this.props.newServerMessage(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) 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 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 if (ws.readyState === ws.CLOSED){ //if our websocket gets closed inform the user
webSocketState = false 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 if (nextProps.selectionHashes.length === 1){ //if we have a selection pass it on for the tabs to verify
this.selectionHandler(nextProps.selectionHashes, nextProps.selectedTab) 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, RSSTorrentList: state.RSSTorrentList,
serverPushMessage: state.serverPushMessage, serverPushMessage: state.serverPushMessage,
settingsModalOpen: state.settingsModalOpen, settingsModalOpen: state.settingsModalOpen,
tokenReturn: state.tokenReturn,
}; };
} }
@@ -301,9 +315,8 @@ const mapDispatchToProps = dispatch => {
RSSTorrentList: (RSSTorrentList) => dispatch({type: actionTypes.RSS_TORRENT_LIST, RSSTorrentList}), RSSTorrentList: (RSSTorrentList) => dispatch({type: actionTypes.RSS_TORRENT_LIST, RSSTorrentList}),
newServerMessage: (serverPushMessage) => dispatch({type: actionTypes.SERVER_MESSAGE, serverPushMessage}), newServerMessage: (serverPushMessage) => dispatch({type: actionTypes.SERVER_MESSAGE, serverPushMessage}),
webSocketStateUpdate: (webSocketState) => dispatch({type: actionTypes.WEBSOCKET_STATE, webSocketState}), webSocketStateUpdate: (webSocketState) => dispatch({type: actionTypes.WEBSOCKET_STATE, webSocketState}),
newSettingsFile: (settingsFile) => dispatch({type: actionTypes.NEW_SETTINGS_FILE, settingsFile}) newSettingsFile: (settingsFile) => dispatch({type: actionTypes.NEW_SETTINGS_FILE, settingsFile}),
//changeSelection: (selection) => dispatch({type: actionTypes.CHANGE_SELECTION, selection}),//forcing an update to the buttons 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 { withStyles } from 'material-ui/styles';
import Paper from 'material-ui/Paper'; import Paper from 'material-ui/Paper';
import Grid from 'material-ui/Grid'; import Grid from 'material-ui/Grid';
import Button from 'material-ui/Button';
import TextField from 'material-ui/TextField';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import * as actionTypes from '../../../../store/actions';
const styles = theme => ({ const styles = theme => ({
root: { root: {
flexGrow: 1, flexGrow: 1,
marginTop: 0, marginTop: 0,
padding: 10,
}, },
paper: { paper: {
padding: 16, 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() { render() {
const { classes } = this.props; const { classes } = this.props;
return ( return (
<div className={classes.root}> <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> </div>
); );
} }
@@ -44,9 +77,15 @@ class APISettingsTab extends React.PureComponent {
const mapStateToProps = state => { const mapStateToProps = state => {
return { 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}>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}>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}>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> </Grid>

View File

@@ -53,7 +53,7 @@ class LoggingSettingsTab extends React.Component {
<Grid container spacing={8}> <Grid container spacing={8}>
<Grid item xs={12} sm={4}> <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 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> </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 RSS_TORRENT_LIST = 'RSS_TORRENT_LIST';
export const SERVER_MESSAGE = 'SERVER_MESSAGE'; export const SERVER_MESSAGE = 'SERVER_MESSAGE';
export const WEBSOCKET_STATE = 'WEBSOCKET_STATE'; 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 serverPushMessage: action.serverPushMessage
} }
case actionTypes.TOKEN_RETURN:
console.log("New token return", action.tokenReturn)
return {
... state,
tokenReturn: action.tokenReturn
}
case actionTypes.SET_BUTTON_STATE: 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 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 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 selectedRows.push(state.torrentList[element]) //pushing the selected rows out of torrentlist
}); });
let buttonStateTest = selectedRows.filter(element => {
let buttonStateTest = selectedRows.filter(element => { //TODO fix this bad mess... we literally just need to filter for stopped and go from there
let result = [] 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) result.push(element.Status)
return result return result
} }
@@ -157,7 +163,6 @@ const reducer = (state = initialState, action) => {
if (buttonStateTest.length > 0 && buttonStateTest2.length === 0){ if (buttonStateTest.length > 0 && buttonStateTest2.length === 0){
let buttonStateFinal = [{startButton: "default", stopButton: "primary", deleteButton: "secondary", fSeedButton: "default", fRecheckButton: "primary"}] let buttonStateFinal = [{startButton: "default", stopButton: "primary", deleteButton: "secondary", fSeedButton: "default", fRecheckButton: "primary"}]
console.log("ButtonStateFil")
return { return {
...state, ...state,
buttonState: buttonStateFinal buttonState: buttonStateFinal

View File

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

136
main.go
View File

@@ -51,7 +51,7 @@ func serveHome(w http.ResponseWriter, r *http.Request) {
func handleAuthentication(conn *websocket.Conn, db *storm.DB) { func handleAuthentication(conn *websocket.Conn, db *storm.DB) {
msg := Engine.Message{} msg := Engine.Message{}
err := conn.ReadJSON(&msg) 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{}) payloadData, ok := msg.Payload.(map[string]interface{})
clientAuthToken, tokenOk := payloadData["ClientAuthString"].(string) clientAuthToken, tokenOk := payloadData["ClientAuthString"].(string)
fmt.Println("ClientAuthToken:", clientAuthToken, "TokenOkay", tokenOk, "PayloadData", payloadData, "PayloadData Okay?", ok) 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 Engine.Logger = Logger //Injecting the logger into all the packages
Storage.Logger = Logger Storage.Logger = Logger
Settings.Logger = Logger Settings.Logger = Logger
var torrentQueues = Storage.TorrentQueues{}
Config := Settings.FullClientSettingsNew() //grabbing from settings.go Config := Settings.FullClientSettingsNew() //grabbing from settings.go
Engine.Config = Config Engine.Config = Config
if Config.LoggingOutput == "file" { if Config.LoggingOutput == "file" {
@@ -103,17 +104,18 @@ func main() {
if err != nil { if err != nil {
fmt.Println("Unable to create 'log' folder for logging.... please check permissions.. forcing output to stdout", err) fmt.Println("Unable to create 'log' folder for logging.... please check permissions.. forcing output to stdout", err)
Logger.Out = os.Stdout Logger.Out = os.Stdout
}
} else { } else {
os.Remove("logs/server.log") //cleanup the old log on every restart 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 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? //defer file.Close() //TODO.. since we write to this constantly how does close work?
if err != nil { if err != nil {
fmt.Println("Unable to create file for logging.... please check permissions.. forcing output to stdout") fmt.Println("Unable to create file for logging.... please check permissions.. forcing output to stdout")
Logger.Out = os.Stdout Logger.Out = os.Stdout
} }
fmt.Println("Logging to file logs/server.log")
Logger.Out = file Logger.Out = file
} }
}
} else { } else {
Logger.Out = os.Stdout Logger.Out = os.Stdout
} }
@@ -122,26 +124,33 @@ func main() {
httpAddr := Config.HTTPAddr httpAddr := Config.HTTPAddr
os.MkdirAll(Config.TFileUploadFolder, 0755) //creating a directory to store uploaded torrent files 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 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 tclient, err := torrent.NewClient(&Config.TorrentConfig) //pulling out the torrent specific config to use
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"error": err}).Fatalf("Error creating torrent client: %s") 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 db, err := storm.Open("storage.db") //initializing the boltDB store that contains all the added torrents
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"error": err}).Fatal("Error opening/creating storage.db") 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 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 tokens := Storage.IssuedTokensList{} //if first run setting up the authentication tokens
var signingKey []byte var signingKey []byte
err = db.One("ID", 3, &tokens) err = db.One("ID", 3, &tokens)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"RSSFeedStore": tokens, "error": err}).Info("No Tokens database found, assuming first run, generating token...") 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 tokens.ID = 3 //creating the initial store
claims := Settings.GoTorrentClaims{ claims := Settings.GoTorrentClaims{
"goTorrentWebUI", "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 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 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 { } else {
Logger.Info("Database is empty, no torrents loaded") 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.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) // Refresing the RSS feeds on an hourly basis to add torrents that show up in the RSS feed 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 := mux.NewRouter() //setting up the handler for the web backend
router.HandleFunc("/", serveHome) //Serving the main page for our SPA 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") Logger.WithFields(logrus.Fields{"clientName": payloadData["ClientName"].(string)}).Info("New Auth Token creation request")
fmt.Println("Signing Key", signingKey) fmt.Println("Signing Key", signingKey)
token := Settings.GenerateToken(claims, signingKey) token := Settings.GenerateToken(claims, signingKey)
tokenReturn := Settings.TokenReturn{TokenReturn: token} tokenReturn := Settings.TokenReturn{MessageType: "TokenReturn", TokenReturn: token}
tokensDB := Storage.FetchJWTTokens(db) tokensDB := Storage.FetchJWTTokens(db)
tokensDB.TokenNames = append(tokens.TokenNames, Storage.SingleToken{payloadData["ClientName"].(string)}) tokensDB.TokenNames = append(tokens.TokenNames, Storage.SingleToken{payloadData["ClientName"].(string)})
db.Update(&tokensDB) //adding the new token client name to the database db.Update(&tokensDB) //adding the new token client name to the database
@@ -272,27 +282,30 @@ func main() {
case "torrentListRequest": case "torrentListRequest":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested TorrentList Update") 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
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 RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) //Updates the RunningTorrentArray with the current client data as well
PreviousTorrentArray = RunningTorrentArray PreviousTorrentArray = RunningTorrentArray
torrentlistArray := Engine.TorrentList{MessageType: "torrentList", ClientDBstruct: RunningTorrentArray, Totaltorrents: len(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") Logger.WithFields(logrus.Fields{"torrentList": torrentlistArray, "previousTorrentList": PreviousTorrentArray}).Debug("Previous and Current Torrent Lists for sending to client")
conn.WriteJSON(torrentlistArray) conn.WriteJSON(torrentlistArray)
}()
case "torrentFileListRequest": //client requested a filelist update 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) fileListArrayRequest := payloadData["FileListHash"].(string)
FileListArray := Engine.CreateFileListArray(tclient, fileListArrayRequest, db, Config) FileListArray := Engine.CreateFileListArray(tclient, fileListArrayRequest, db, Config)
conn.WriteJSON(FileListArray) //writing the JSON to the client conn.WriteJSON(FileListArray) //writing the JSON to the client
case "torrentPeerListRequest": 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) peerListArrayRequest := payloadData["PeerListHash"].(string)
torrentPeerList := Engine.CreatePeerListArray(tclient, peerListArrayRequest) torrentPeerList := Engine.CreatePeerListArray(tclient, peerListArrayRequest)
conn.WriteJSON(torrentPeerList) conn.WriteJSON(torrentPeerList)
case "fetchTorrentsByLabel": //TODO test this to make sure it works case "fetchTorrentsByLabel":
Logger.WithFields(logrus.Fields{"message": msg}).Debug("Client Requested Torrents by Label") Logger.WithFields(logrus.Fields{"message": msg}).Info("Client Requested Torrents by Label")
label := payloadData["Label"].(string) label := payloadData["Label"].(string)
torrentsByLabel := Storage.FetchTorrentsByLabel(db, label) torrentsByLabel := Storage.FetchTorrentsByLabel(db, label)
RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db) RunningTorrentArray = Engine.CreateRunningTorrentArray(tclient, TorrentLocalArray, PreviousTorrentArray, Config, db)
@@ -307,7 +320,7 @@ func main() {
conn.WriteJSON(labelRunningArray) conn.WriteJSON(labelRunningArray)
case "changeStorageValue": 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) newStorageLocation := payloadData["StorageValue"].(string)
hashes := payloadData["ChangeStorageHashes"].([]interface{}) hashes := payloadData["ChangeStorageHashes"].([]interface{})
for _, singleHash := range hashes { for _, singleHash := range hashes {
@@ -328,12 +341,12 @@ func main() {
} }
case "settingsFileRequest": 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} clientSettingsFile := Engine.SettingsFile{MessageType: "settingsFile", Config: Config}
conn.WriteJSON(clientSettingsFile) conn.WriteJSON(clientSettingsFile)
case "rssFeedRequest": 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) RSSList := Storage.FetchRSSFeeds(db)
RSSJSONFeed := Engine.RSSJSONList{MessageType: "rssList", TotalRSSFeeds: len(RSSList.RSSFeeds)} RSSJSONFeed := Engine.RSSJSONList{MessageType: "rssList", TotalRSSFeeds: len(RSSList.RSSFeeds)}
RSSsingleFeed := Engine.RSSFeedsNames{} RSSsingleFeed := Engine.RSSFeedsNames{}
@@ -346,9 +359,9 @@ func main() {
case "addRSSFeed": case "addRSSFeed":
newRSSFeed := payloadData["RSSURL"].(string) 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) 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 { for _, singleFeed := range fullRSSFeeds.RSSFeeds {
if newRSSFeed == singleFeed.URL || newRSSFeed == "" { 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") 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": case "deleteRSSFeed":
deleteRSSFeed := payloadData["RSSURL"].(string) 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) Storage.DeleteRSSFeed(db, deleteRSSFeed)
fullRSSFeeds := Storage.FetchRSSFeeds(db) fullRSSFeeds := Storage.FetchRSSFeeds(db)
Engine.CreateServerPushMessage(Engine.ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "info", Payload: "Deleting RSS feed..."}, conn) 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..") Logger.WithFields(logrus.Fields{"RSSFeed": RSSFeedURL}).Info("Requesting torrentList for feed..")
UpdatedRSSFeed := Engine.RefreshSingleRSSFeed(db, Storage.FetchSpecificRSSFeed(db, RSSFeedURL)) 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} 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) conn.WriteJSON(TorrentRSSList)
case "magnetLinkSubmit": //if we detect a magnet link we will be adding a magnet torrent 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!") 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.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": 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) 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") 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": case "stopTorrents":
torrentHashes := payloadData["TorrentHashes"].([]interface{}) torrentHashes := payloadData["TorrentHashes"].([]interface{})
@@ -475,9 +509,16 @@ func main() {
if singleTorrent.InfoHash().String() == singleSelection { if singleTorrent.InfoHash().String() == singleSelection {
Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for stopping torrents") Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for stopping torrents")
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String()) oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
oldTorrentInfo.TorrentStatus = "Stopped" Engine.StopTorrent(singleTorrent, &oldTorrentInfo, db)
oldTorrentInfo.MaxConnections = 0 if len(torrentQueues.QueuedTorrents) > 1 {
Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status 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 { for _, singleSelection := range torrentHashes {
if singleTorrent.InfoHash().String() == singleSelection { if singleTorrent.InfoHash().String() == singleSelection {
oldTorrentInfo := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String()) 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") Logger.WithFields(logrus.Fields{"selection": singleSelection}).Info("Matched for deleting torrents")
if withData { if withData {
oldTorrentInfo.TorrentStatus = "DroppedData" //Will be cleaned up the next engine loop since deleting a torrent mid loop can cause issues 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" oldTorrentInfo.TorrentStatus = "Dropped"
} }
Storage.UpdateStorageTick(db, oldTorrentInfo) Storage.UpdateStorageTick(db, oldTorrentInfo)
Storage.UpdateQueues(db, torrentQueues)
} }
} }
} }
@@ -509,17 +565,28 @@ func main() {
for _, singleTorrent := range runningTorrents { for _, singleTorrent := range runningTorrents {
for _, singleSelection := range torrentHashes { for _, singleSelection := range torrentHashes {
if singleTorrent.InfoHash().String() == singleSelection { 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 := Storage.FetchTorrentFromStorage(db, singleTorrent.InfoHash().String())
oldTorrentInfo.TorrentStatus = "Running" Engine.AddTorrentToActive(&oldTorrentInfo, singleTorrent, db)
oldTorrentInfo.MaxConnections = 80
Logger.WithFields(logrus.Fields{"Torrent": oldTorrentInfo.TorrentName}).Info("Changing database to torrent running with 80 max connections") 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 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{}) torrentHashes := payloadData["TorrentHashes"].([]interface{})
Logger.WithFields(logrus.Fields{"selection": msg.Payload}).Info("Matched for force Uploading Torrents") 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) 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.TorrentUploadLimit = false // no upload limit for this torrent
oldTorrentInfo.TorrentStatus = "Running" oldTorrentInfo.TorrentStatus = "Running"
oldTorrentInfo.MaxConnections = 80 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") 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 Storage.UpdateStorageTick(db, oldTorrentInfo) //Updating the torrent status
} }
@@ -596,7 +662,7 @@ func main() {
} else { } else {
err := http.ListenAndServe(httpAddr, nil) //Can't send proxy headers if not used since that can be a security issue err := http.ListenAndServe(httpAddr, nil) //Can't send proxy headers if not used since that can be a security issue
if err != nil { 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" "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 { type AuthRequest struct {
MessageType string `json:"MessageType"` MessageType string `json:"MessageType"`
AuthString string `json:"AuthString"` AuthString string `json:"AuthString"`
} }
//TokenReturn is a struct sent by the server to a client with a new generated authstring
type TokenReturn struct { type TokenReturn struct {
MessageType string `json:"MessageType"`
TokenReturn string `json:"TokenReturn"` TokenReturn string `json:"TokenReturn"`
} }

View File

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

View File

@@ -16,6 +16,13 @@ var Logger *logrus.Logger
//Conn is the global websocket connection used to push server notification messages //Conn is the global websocket connection used to push server notification messages
var Conn *websocket.Conn 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 //IssuedTokensList contains a slice of all the tokens issues to applications
type IssuedTokensList struct { 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 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 //FetchConfig fetches the client config from the database
func FetchConfig(torrentStorage *storm.DB) (Settings.FullClientSettings, error) { func FetchConfig(torrentStorage *storm.DB) (Settings.FullClientSettings, error) {
config := Settings.FullClientSettings{} config := Settings.FullClientSettings{}
@@ -166,6 +193,8 @@ func UpdateStorageTick(torrentStorage *storm.DB, torrentLocal TorrentLocal) {
err := torrentStorage.Update(&torrentLocal) err := torrentStorage.Update(&torrentLocal)
if err != nil { if err != nil {
Logger.WithFields(logrus.Fields{"UpdateContents": torrentLocal, "error": err}).Error("Error performing tick update to database!") 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!")
} }
} }