diff --git a/engine/engine.go b/engine/engine.go index aaf08588..30c0003f 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -21,6 +21,9 @@ import ( //Logger is the injected variable for global logger var Logger *logrus.Logger +//Config is the injected variable for the torrent config +var Config Settings.FullClientSettings + //Conn is the injected variable for the websocket connection var Conn *websocket.Conn @@ -85,7 +88,6 @@ func timeOutInfo(clientTorrent *torrent.Torrent, seconds time.Duration) (deleted select { case <-clientTorrent.GotInfo(): //attempting to retrieve info for torrent Logger.WithFields(logrus.Fields{"clientTorrentName": clientTorrent.Name()}).Debug("Received torrent info for torrent") - clientTorrent.DownloadAll() return false case <-timeout: // getting info for torrent has timed out so purging the torrent Logger.WithFields(logrus.Fields{"clientTorrentName": clientTorrent.Name()}).Error("Forced to drop torrent from timeout waiting for info") @@ -173,34 +175,37 @@ func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.To TorrentFilePriorityArray = append(TorrentFilePriorityArray, torrentFilePriority) } + torrentLocalStorage.TorrentFilePriority = TorrentFilePriorityArray Storage.AddTorrentLocalStorage(torrentDbStorage, torrentLocalStorage) //writing all of the data to the database - clientTorrent.DownloadAll() //starting the download + clientTorrent.DownloadAll() //set all pieces to download + NumPieces := clientTorrent.NumPieces() //find the number of pieces + clientTorrent.CancelPieces(1, NumPieces) //cancel all of the pieces to use file priority + for _, singleFile := range clientTorrent.Files() { //setting all of the file priorities to normal + singleFile.SetPriority(torrent.PiecePriorityNormal) + } + fmt.Println("Downloading ALL") //starting the download + CreateServerPushMessage(ServerPushMessage{MessageType: "serverPushMessage", MessageLevel: "success", Payload: "Torrent added!"}, Conn) } -//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) { - +//CreateInitialTorrentArray adds all the torrents on program start from the database +func CreateInitialTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Storage.TorrentLocal, db *storm.DB) { for _, singleTorrentFromStorage := range TorrentLocalArray { var singleTorrent *torrent.Torrent - var TempHash metainfo.Hash - tickUpdateStruct := Storage.TorrentLocal{} //we are shoving the tick updates into a torrentlocal struct to pass to storage happens at the end of the routine - - fullClientDB := new(ClientDB) - //singleTorrentStorageInfo := Storage.FetchTorrentFromStorage(db, TempHash.String()) //pulling the single torrent info from storage () - + var err error if singleTorrentFromStorage.TorrentType == "file" { //if it is a file pull it from the uploaded torrent folder - var err error singleTorrent, err = readTorrentFileFromDB(singleTorrentFromStorage, tclient, db) if err != nil { continue } - fullClientDB.SourceType = "Torrent File" } else { singleTorrentFromStorageMagnet := "magnet:?xt=urn:btih:" + singleTorrentFromStorage.Hash //For magnet links just need to prepend the magnet part to the hash to readd - singleTorrent, _ = tclient.AddMagnet(singleTorrentFromStorageMagnet) - fullClientDB.SourceType = "Magnet Link" + singleTorrent, err = tclient.AddMagnet(singleTorrentFromStorageMagnet) + if err != nil { + continue + } + } if len(singleTorrentFromStorage.InfoBytes) == 0 { //TODO.. kind of a fringe scenario.. not sure if needed since the db should always have the infobytes timeOut := timeOutInfo(singleTorrent, 45) @@ -211,16 +216,66 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto singleTorrentFromStorage.InfoBytes = singleTorrent.Metainfo().InfoBytes } - err := singleTorrent.SetInfoBytes(singleTorrentFromStorage.InfoBytes) //setting the infobytes back into the torrent + err = singleTorrent.SetInfoBytes(singleTorrentFromStorage.InfoBytes) //setting the infobytes back into the torrent if err != nil { Logger.WithFields(logrus.Fields{"torrentFile": singleTorrent.Name(), "error": err}).Error("Unable to add infobytes to the torrent!") } + if singleTorrentFromStorage.TorrentStatus != "Completed" && singleTorrentFromStorage.TorrentStatus != "Stopped" { + fmt.Println("Starting torrent as download", singleTorrent.Name()) + singleTorrent.DownloadAll() //set all of the pieces to download (piece prio is NE to file prio) + NumPieces := singleTorrent.NumPieces() //find the number of pieces + singleTorrent.CancelPieces(1, NumPieces) //cancel all of the pieces to use file priority + for _, singleFile := range singleTorrent.Files() { //setting all of the file priorities to normal + singleFile.SetPriority(torrent.PiecePriorityNormal) + } + } else { + fmt.Println("Torrent status is....", singleTorrentFromStorage.TorrentStatus) + } + + } + SetFilePriority(tclient, db) //Setting the desired file priority from storage +} + +//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) { + + for _, singleTorrentFromStorage := range TorrentLocalArray { + var singleTorrent *torrent.Torrent + var TempHash metainfo.Hash + for _, liveTorrent := range tclient.Torrents() { //matching the torrent from storage to the live torrent + if singleTorrentFromStorage.Hash == liveTorrent.InfoHash().String() { + singleTorrent = liveTorrent + } + } + + tickUpdateStruct := Storage.TorrentLocal{} //we are shoving the tick updates into a torrentlocal struct to pass to storage happens at the end of the routine + + fullClientDB := new(ClientDB) + //singleTorrentStorageInfo := Storage.FetchTorrentFromStorage(db, TempHash.String()) //pulling the single torrent info from storage () + + if singleTorrentFromStorage.TorrentStatus == "Dropped" { + Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting just the torrent") + singleTorrent.Drop() + Storage.DelTorrentLocalStorage(db, singleTorrentFromStorage.Hash) + } + if singleTorrentFromStorage.TorrentStatus == "DroppedData" { + Logger.WithFields(logrus.Fields{"selection": singleTorrentFromStorage.TorrentName}).Info("Deleting just the torrent") + singleTorrent.Drop() + Storage.DelTorrentLocalStorageAndFiles(db, singleTorrentFromStorage.Hash, Config.TorrentConfig.DataDir) + + } + if singleTorrentFromStorage.TorrentType == "file" { //if it is a file pull it from the uploaded torrent folder + fullClientDB.SourceType = "Torrent File" + } else { + fullClientDB.SourceType = "Magnet Link" + } + calculatedTotalSize := CalculateDownloadSize(singleTorrentFromStorage, singleTorrent) calculatedCompletedSize := CalculateCompletedSize(singleTorrentFromStorage, singleTorrent) TempHash = singleTorrent.InfoHash() if (calculatedCompletedSize == singleTorrentFromStorage.TorrentSize) && (singleTorrentFromStorage.TorrentMoved == false) { //if we are done downloading and haven't moved torrent yet Logger.WithFields(logrus.Fields{"singleTorrent": singleTorrentFromStorage.TorrentName}).Info("Torrent Completed, moving...") - 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 + 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 } fullStruct := singleTorrent.Stats() diff --git a/engine/engineHelpers.go b/engine/engineHelpers.go index fb6c5aa3..39c560d7 100644 --- a/engine/engineHelpers.go +++ b/engine/engineHelpers.go @@ -7,6 +7,7 @@ import ( "time" "github.com/anacrolix/torrent" + "github.com/asdine/storm" Settings "github.com/deranjer/goTorrent/settings" "github.com/deranjer/goTorrent/storage" Storage "github.com/deranjer/goTorrent/storage" @@ -22,6 +23,15 @@ func secondsToMinutes(inSeconds int64) string { return str } +//MakeRange creates a range of pieces to set their priority based on a file +func MakeRange(min, max int) []int { + a := make([]int, max-min+1) + for i := range a { + a[i] = min + i + } + return a +} + //HumanizeBytes returns a nice humanized version of bytes in either GB or MB func HumanizeBytes(bytes float32) string { if bytes < 1000000 { //if we have less than 1MB in bytes convert to KB @@ -59,10 +69,36 @@ func CopyFile(srcFile string, destFile string) { //TODO move this to our importe } +//SetFilePriority sets the priorities for all of the files in a torrent +func SetFilePriority(t *torrent.Client, db *storm.DB) { + storedTorrents := Storage.FetchAllStoredTorrents(db) + for _, singleTorrent := range t.Torrents() { + for _, storedTorrent := range storedTorrents { + if storedTorrent.Hash == singleTorrent.InfoHash().String() { + for _, file := range singleTorrent.Files() { + for _, storedFile := range storedTorrent.TorrentFilePriority { + if storedFile.TorrentFilePath == file.DisplayPath() { + switch storedFile.TorrentFilePriority { + case "High": + file.SetPriority(torrent.PiecePriorityHigh) + case "Normal": + file.SetPriority(torrent.PiecePriorityNormal) + case "Cancel": + file.SetPriority(torrent.PiecePriorityNone) + default: + file.SetPriority(torrent.PiecePriorityNormal) + } + } + } + } + } + } + } +} + //CalculateTorrentSpeed is used to calculate the torrent upload and download speed over time c is current clientdb, oc is last client db to calculate speed over time func CalculateTorrentSpeed(t *torrent.Torrent, c *ClientDB, oc ClientDB, completedSize int64) { now := time.Now() - //bytes := t.BytesCompleted() bytes := completedSize bytesUpload := t.Stats().BytesWrittenData dt := float32(now.Sub(oc.UpdatedAt)) // get the delta time length between now and last updated @@ -113,6 +149,9 @@ func CalculateCompletedSize(tFromStorage *Storage.TorrentLocal, activeTorrent *t } } downloadedLength := activeTorrent.BytesCompleted() - discardByteLength + if downloadedLength < 0 { + downloadedLength = 0 + } return downloadedLength } @@ -146,11 +185,12 @@ func CalculateTorrentStatus(t *torrent.Torrent, c *ClientDB, config Settings.Ful c.Status = "Stopped" c.MaxConnections = 0 t.SetMaxEstablishedConns(0) + } 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 bytesMissing := totalSize - bytesCompleted c.MaxConnections = 80 - t.SetMaxEstablishedConns(80) //TODO this should not be needed but apparently is needed - t.DownloadAll() //ensure that we are setting the torrent to download + t.SetMaxEstablishedConns(80) + //t.DownloadAll() //ensure that we are setting the torrent to download if t.Seeding() && t.Stats().ActivePeers > 0 && bytesMissing == 0 { c.Status = "Seeding" } else if t.Stats().ActivePeers > 0 && bytesMissing > 0 { diff --git a/goTorrentWebUI/src/BottomMenu/Tabs/fileTab.js b/goTorrentWebUI/src/BottomMenu/Tabs/fileTab.js index e281d684..3edd5839 100644 --- a/goTorrentWebUI/src/BottomMenu/Tabs/fileTab.js +++ b/goTorrentWebUI/src/BottomMenu/Tabs/fileTab.js @@ -7,7 +7,7 @@ import { ProgressBarCell } from '../../CustomCells/progressBarCell'; import { - SortingState, IntegratedSorting, VirtualTableLayout, SelectionState, + SortingState, IntegratedSorting, IntegratedSelection, VirtualTableLayout, SelectionState, } from '@devexpress/dx-react-grid'; import { @@ -131,7 +131,8 @@ class FileTab extends React.Component { - + + diff --git a/goTorrentWebUI/src/TopMenu/Modals/RSSModal/RSSTorrentList.js b/goTorrentWebUI/src/TopMenu/Modals/RSSModal/RSSTorrentList.js index 7e282b87..92969e1e 100644 --- a/goTorrentWebUI/src/TopMenu/Modals/RSSModal/RSSTorrentList.js +++ b/goTorrentWebUI/src/TopMenu/Modals/RSSModal/RSSTorrentList.js @@ -4,7 +4,7 @@ import Button from 'material-ui/Button'; import Paper from 'material-ui/Paper'; import { - SortingState, IntegratedSorting, VirtualTableLayout, SelectionState, + SortingState, IntegratedSorting, IntegratedSelection, VirtualTableLayout, SelectionState, } from '@devexpress/dx-react-grid'; import { @@ -98,7 +98,8 @@ class RSSTorrentList extends React.Component { - + + diff --git a/goTorrentWebUI/src/login.js b/goTorrentWebUI/src/login.js index 0b2f5088..69fa67f1 100644 --- a/goTorrentWebUI/src/login.js +++ b/goTorrentWebUI/src/login.js @@ -88,7 +88,7 @@ export default class Login extends React.Component { render() { const { classes, onClose, handleRequestClose, handleSubmit } = this.props; return ( - + Login Here @@ -106,9 +106,10 @@ export default class Login extends React.Component { type="text" placeholder="Username" fullWidth + required onChange={this.setUserNameValue} /> - +