Skip to content

Commit

Permalink
closes #9, closes #8, closes #3, closes #4, added new notification fe…
Browse files Browse the repository at this point in the history
…atures, search torrents, change directory, force seed torrent, updated Readme
  • Loading branch information
deranjer committed Feb 3, 2018
1 parent 3ab6645 commit 0abe162
Show file tree
Hide file tree
Showing 13 changed files with 123 additions and 133 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,19 @@ Image of the frontend UI
- Automatic stop after seeding ratio reached
- Pushbullet notification on torrent complete
- Automatic move of completed torrent to new directory (leave symlink behind for seeding)
- Doesn't work on Windows yet, have to copy file for now
- Symlinks don't work on Windows yet, have to copy file for now

## Roadmap
- Early-Mid 2018

- [X] Ability to modify storage path of torrent after it has been added

- [X] Backend to frontend notification messages

- [X] Global Rate Limiting for Upload/Download Speed

- [X] Add torrents from watch folder (cron job every 5 minutes)

- [ ] Unit testing completed for a large portion of the package

- [ ] Stability/bug fixing/Optimization rewrite of some of the core structures of the WebUI and base server
Expand All @@ -40,8 +48,8 @@ Image of the frontend UI
- [ ] Ability to set priority for individual files (just added to anacrolix/torrent so coming soon, already added to my UI)

- [ ] Ability to view TOML settings from WebUI (and perhaps change a few as well)
- [ ] Ability to modify storage path of torrent after it has been added

- [ ] Authentication from client to server

- Late 2018

Expand Down Expand Up @@ -96,6 +104,12 @@ The `config.toml` file contains all of the settings for the server part of the a
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 = 'downloaded' #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"


[notifications]
Expand Down
4 changes: 2 additions & 2 deletions config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

ServerPort = ":8000" #leave format as is it expects a string with colon
ServerAddr = "" #blank will bind to default IP address, usually fine to leave be
LogLevel = "Info" # Options = Debug, Info, Warn, Error, Fatal, Panic
LogLevel = "Warn" # Options = Debug, Info, Warn, Error, Fatal, Panic
LogOutput = "file" #Options = file, stdout #file will print it to logs/server.log

SeedRatioStop = 1.50 #automatically stops the torrent after it reaches this seeding ratio
Expand All @@ -19,7 +19,7 @@

[notifications]

PushBulletToken = "o.QW6G7F6FUOKXCUKmw948fBceCUn0msFi" #add your pushbullet api token here to notify of torrent completion to pushbullet
PushBulletToken = "" #add your pushbullet api token here to notify of torrent completion to pushbullet


[EncryptionPolicy]
Expand Down
Binary file modified documentation/images/frontend.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions engine/cronJobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package engine

import (
"io/ioutil"
"os"
"path/filepath"

"github.com/anacrolix/torrent"
Expand Down Expand Up @@ -46,9 +47,9 @@ func CheckTorrentWatchFolder(c *cron.Cron, db *storm.DB, tclient *torrent.Client
continue
}

//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")
StartTorrent(clientTorrent, torrentLocalStorage, db, config.TorrentConfig.DataDir, "file", file.Name(), config.DefaultMoveFolder, "default", config.TFileUploadFolder)
StartTorrent(clientTorrent, torrentLocalStorage, db, "file", fullNewFilePathAbs, config.DefaultMoveFolder, "default", config)

}
}
Expand Down Expand Up @@ -84,7 +85,7 @@ func RefreshRSSCron(c *cron.Cron, db *storm.DB, tclient *torrent.Client, torrent
Logger.WithFields(logrus.Fields{"err": err, "Torrent": RSSTorrent.Title}).Warn("Unable to add torrent to torrent client!")
break //break out of the loop entirely for this message since we hit an error
}
StartTorrent(clientTorrent, torrentLocalStorage, db, config.TorrentConfig.DataDir, "magnet", "", config.DefaultMoveFolder, "RSS", config.TFileUploadFolder) //TODO let user specify torrent default storage location and let change on fly
StartTorrent(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)

}
Expand Down
92 changes: 23 additions & 69 deletions engine/doneTorrentActions.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package engine

import (
"fmt"
"io"
"os"
"path/filepath"
"runtime"
Expand All @@ -20,22 +18,21 @@ func MoveAndLeaveSymlink(config FullClientSettings, tHash string, db *storm.DB,
tStorage := Storage.FetchTorrentFromStorage(db, tHash)
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName}).Info("Move and Create symlink started for torrent")
var oldFilePath string
if moveDone {
if moveDone { //only occurs on manual move
oldFilePathTemp := filepath.Join(oldPath, tStorage.TorrentName)
oldFilePath, err := filepath.Abs(oldFilePathTemp)
var err error
oldFilePath, err = filepath.Abs(oldFilePathTemp)
if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!")
}

fmt.Println("oldfilepath", oldFilePath)
} else {
oldFilePathTemp := filepath.Join(config.TorrentConfig.DataDir, tStorage.TorrentName)
oldFilePath, err := filepath.Abs(oldFilePathTemp)
var err error
oldFilePath, err = filepath.Abs(oldFilePathTemp)
if err != nil {
Logger.WithFields(logrus.Fields{"Torrent Name": tStorage.TorrentName, "Filepath": oldFilePath}).Error("Cannot create absolute file path!")
}
}

newFilePathTemp := filepath.Join(tStorage.StoragePath, tStorage.TorrentName)
newFilePath, err := filepath.Abs(newFilePathTemp)
if err != nil {
Expand All @@ -55,70 +52,27 @@ func MoveAndLeaveSymlink(config FullClientSettings, tHash string, db *storm.DB,
}

if oldFilePath != newFilePath {
if runtime.GOOS == "windows" { //TODO the windows symlink is broken on windows 10 creator edition, so doing a copy for now until Go 1.11
if oldFileInfo.IsDir() {
os.Mkdir(newFilePath, 0755)
if moveDone {
err := folderCopy.Copy(config.TorrentConfig.DataDir, newFilePath)
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": config.TorrentConfig.DataDir, "New File Path": newFilePath, "error": err}).Error("Error Copying Folder!")
}
} else {
err := folderCopy.Copy(oldFilePath, newFilePath) //copy the folder to the new location
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error Copying Folder!")
}

}
os.Chmod(newFilePath, 0777)
notifyUser(tStorage, config, db)
return
}
srcFile, err := os.Open(oldFilePath)
defer srcFile.Close()
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "error": err}).Error("Windows: Cannot open old file for copy")
return
}
destFile, err := os.Create(newFilePath)
defer destFile.Close()
if err != nil {
Logger.WithFields(logrus.Fields{"New File Path": newFilePath, "error": err}).Error("Windows: Cannot open new file for copying into")
return
}

bytesWritten, err := io.Copy(destFile, srcFile)
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Windows: Cannot copy old file into new")
return
}
err = destFile.Sync()
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Windows: Error syncing new file to disk")
}
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "bytesWritten": bytesWritten}).Info("Windows Torrent Copy Completed")
notifyUser(tStorage, config, db)
} else {

folderCopy.Copy(oldFilePath, newFilePath)
os.Chmod(newFilePath, 0777) //changing permissions on the new file to be permissive
newFilePathDir := filepath.Dir(newFilePath)
os.Mkdir(newFilePathDir, 0755)
err := folderCopy.Copy(oldFilePath, newFilePath) //copy the folder to the new location
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error Copying Folder!")
}
os.Chmod(newFilePath, 0777)
if runtime.GOOS != "windows" { //TODO the windows symlink is broken on windows 10 creator edition, so on the other platforms create symlink (windows will copy) until Go1.11
os.RemoveAll(oldFilePath)
if moveDone {
err := os.Symlink(newFilePath, config.TorrentConfig.DataDir)
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": config.TorrentConfig.DataDir, "New File Path": newFilePath, "error": err}).Error("Error creating symlink")
return
}
} else {
err := os.Symlink(newFilePath, oldFilePath) //For all other OS's create a symlink
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error creating symlink")
return
}
err = os.Symlink(newFilePath, oldFilePath)
if err != nil {
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath, "error": err}).Error("Error creating symlink")
}
notifyUser(tStorage, config, db)
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath}).Info("Moving completed torrent")
}
if moveDone == false {
tStorage.TorrentMoved = true //TODO error handling instead of just saying torrent was moved when it was not
notifyUser(tStorage, config, db) //Only notify if we haven't moved yet, don't want to push notify user every time user uses change storage button
}
Logger.WithFields(logrus.Fields{"Old File Path": oldFilePath, "New File Path": newFilePath}).Info("Moving completed torrent")
tStorage.StoragePath = filepath.Dir(newFilePath)
Storage.UpdateStorageTick(db, tStorage)
}

}
Expand Down
21 changes: 7 additions & 14 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -128,7 +127,7 @@ func readTorrentFileFromDB(element *Storage.TorrentLocal, tclient *torrent.Clien
}

//StartTorrent creates the storage.db entry and starts A NEW TORRENT and adds to the running torrent array
func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.TorrentLocal, torrentDbStorage *storm.DB, dataDir, torrentType, torrentFileName, torrentStoragePath, labelValue, tFileUploadFolder string) {
func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.TorrentLocal, torrentDbStorage *storm.DB, torrentType, torrentFilePathAbs, torrentStoragePath, labelValue string, config FullClientSettings) {
timedOut := timeOutInfo(clientTorrent, 45) //seeing if adding the torrent times out (giving 45 seconds)
if timedOut { //if we fail to add the torrent return
return
Expand All @@ -147,15 +146,14 @@ func StartTorrent(clientTorrent *torrent.Torrent, torrentLocalStorage Storage.To
torrentLocalStorage.Label = labelValue
torrentLocalStorage.DateAdded = time.Now().Format("Jan _2 2006")
torrentLocalStorage.StoragePath = torrentStoragePath
torrentLocalStorage.TempStoragePath = config.TorrentConfig.DataDir
torrentLocalStorage.TorrentName = clientTorrent.Name()
torrentLocalStorage.TorrentUploadLimit = true //by default all of the torrents will stop uploading after the global rate is set.
torrentLocalStorage.TorrentMoved = false //by default the torrent has no been moved.
torrentLocalStorage.TorrentStatus = "Running" //by default start all the torrents as downloading.
torrentLocalStorage.TorrentType = torrentType //either "file" or "magnet" maybe more in the future
torrentLocalStorage.TorrentSize = clientTorrent.Length() //Length will change as we cancel files so store it in DB
if torrentType == "file" { //if it is a file read the entire file into the database for us to spit out later
torrentFilePath := filepath.Join(tFileUploadFolder, torrentFileName)
torrentFilePathAbs, err := filepath.Abs(torrentFilePath)
torrentfile, err := ioutil.ReadFile(torrentFilePathAbs)
torrentLocalStorage.TorrentFileName = torrentFilePathAbs
if err != nil {
Expand Down Expand Up @@ -236,11 +234,10 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
PercentDone := fmt.Sprintf("%.2f", float32(singleTorrent.BytesCompleted())/float32(singleTorrentFromStorage.TorrentSize))
fullClientDB.TorrentHash = TempHash
fullClientDB.PercentDone = PercentDone
fullClientDB.DataBytesRead = fullStruct.ConnStats.DataBytesRead //used for calculations not passed to client calculating up/down speed
fullClientDB.DataBytesWritten = fullStruct.ConnStats.DataBytesWritten //used for calculations not passed to client calculating up/down speed
fullClientDB.DataBytesRead = fullStruct.ConnStats.BytesReadData //used for calculations not passed to client calculating up/down speed
fullClientDB.DataBytesWritten = fullStruct.ConnStats.BytesWrittenData //used for calculations not passed to client calculating up/down speed
fullClientDB.ActivePeers = activePeersString + " / (" + totalPeersString + ")"
fullClientDB.TorrentHashString = TempHash.String()
fullClientDB.StoragePath = singleTorrentFromStorage.StoragePath
fullClientDB.TorrentName = singleTorrentFromStorage.TorrentName
fullClientDB.DateAdded = singleTorrentFromStorage.DateAdded
fullClientDB.TorrentLabel = singleTorrentFromStorage.Label
Expand All @@ -252,7 +249,7 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
TempHash := singleTorrent.InfoHash()
if previousElement.TorrentHashString == TempHash.String() { //matching previous to new
CalculateTorrentSpeed(singleTorrent, fullClientDB, previousElement)
fullClientDB.TotalUploadedBytes = singleTorrentFromStorage.UploadedBytes + (fullStruct.ConnStats.DataBytesWritten - previousElement.DataBytesWritten)
fullClientDB.TotalUploadedBytes = singleTorrentFromStorage.UploadedBytes + (fullStruct.ConnStats.BytesWrittenData - previousElement.DataBytesWritten)
}
}
}
Expand All @@ -276,7 +273,7 @@ func CreateRunningTorrentArray(tclient *torrent.Client, TorrentLocalArray []*Sto
}

//CreateFileListArray creates a file list for a single torrent that is selected and sent to the server
func CreateFileListArray(tclient *torrent.Client, selectedHash string, db *storm.DB) TorrentFileList {
func CreateFileListArray(tclient *torrent.Client, selectedHash string, db *storm.DB, config FullClientSettings) TorrentFileList {
runningTorrents := tclient.Torrents() //don't need running torrent array since we aren't adding or deleting from storage
torrentFileListStorage := Storage.FetchTorrentFromStorage(db, selectedHash)
TorrentFileListSelected := TorrentFileList{}
Expand All @@ -289,11 +286,7 @@ func CreateFileListArray(tclient *torrent.Client, selectedHash string, db *storm
for _, singleFile := range torrentFilesRaw {
TorrentFileStruct.TorrentHashString = tempHash
TorrentFileStruct.FileName = singleFile.DisplayPath()
absFilePath, err := filepath.Abs(singleFile.Path())
if err != nil {
Logger.WithFields(logrus.Fields{"file": singleFile.Path()}).Debug("Unable to create absolute path")
}
TorrentFileStruct.FilePath = absFilePath
TorrentFileStruct.FilePath = singleFile.Path()
PieceState := singleFile.State()
var downloadedBytes int64
for _, piece := range PieceState {
Expand Down
2 changes: 1 addition & 1 deletion engine/engineHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func CopyFile(srcFile string, destFile string) {
func CalculateTorrentSpeed(t *torrent.Torrent, c *ClientDB, oc ClientDB) {
now := time.Now()
bytes := t.BytesCompleted()
bytesUpload := t.Stats().DataBytesWritten
bytesUpload := t.Stats().BytesWrittenData
dt := float32(now.Sub(oc.UpdatedAt)) // get the delta time length between now and last updated
db := float32(bytes - oc.BytesCompleted) //getting the delta bytes
rate := db * (float32(time.Second) / dt) // converting into seconds
Expand Down
15 changes: 5 additions & 10 deletions goTorrentWebUI/src/TopMenu/Modals/RSSModal/RSSModalLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ const inlineStyle = {
{i: 'b', x: 0, y: 1, w: 1, h: 5, static: true},
{i: 'c', x: 1, y: 1, w: 5, h: 5, minW: 5, minH: 3, static: true},
];
this.state = { layout };
this.state = {
layout ,
textValue: "",
};


};
Expand All @@ -103,7 +106,6 @@ const inlineStyle = {
}

handleAddRSSFeed = () => {
this.setState({ textValue: "Clear"}) //clearing out the text submitted
let RSSURLSubmit = {
MessageType: "addRSSFeed",
Payload: [this.state.textValue]
Expand All @@ -113,20 +115,13 @@ const inlineStyle = {
MessageType: "rssFeedRequest",
}
ws.send(JSON.stringify(RSSRequest)) //Immediatly request an update of the feed when you add a new URL
this.setState({textValue: ""})
}

setTextValue = (event) => {
this.setState({ textValue: event.target.value });
}

componentWillReceiveProps (nextProps) {
console.log("nextprops", nextProps, "Modal", nextProps.RSSModalOpen)
}
componentWillMount () {
console.log("Mounting grid")
}


render() {
return (
<div style={inlineStyle}>
Expand Down
4 changes: 2 additions & 2 deletions goTorrentWebUI/src/TopMenu/Modals/deleteTorrentModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class DeleteTorrentModal extends React.Component {
let selection = []
let deleteTorrentHashes = {
MessageType: "deleteTorrents",
MessageDetail: "true",
MessageDetail: "false", //delete with data
Payload: this.props.selectionHashes
}
console.log("Deleting Torrents", deleteTorrentHashes)
Expand All @@ -56,7 +56,7 @@ class DeleteTorrentModal extends React.Component {

let deleteTorrentHashes = {
MessageType: "deleteTorrents",
MessageDetail: "true",
MessageDetail: "true", //delete with data
Payload: this.props.selectionHashes,
}
console.log("Deleting Torrents and Data", deleteTorrentHashes)
Expand Down
Loading

0 comments on commit 0abe162

Please sign in to comment.