Skip to content

Commit

Permalink
Added streaming endpoint and refactored
Browse files Browse the repository at this point in the history
  • Loading branch information
glblduh committed May 7, 2022
1 parent b30e47a commit 1606665
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
torrservedl/
torrenttpdl/
*.exe
Binary file added .torrent.bolt.db
Binary file not shown.
71 changes: 26 additions & 45 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,24 @@ import (
"github.com/boltdb/bolt"
)

func getDB() (*bolt.DB, error) {
db, err := bolt.Open(
func (DB *specDb) initDB() error {
var err error
DB.db, err = bolt.Open(
filepath.Join(btEngine.ClientConfig.DataDir, ".torrserve.db"),
0600,
nil)
return db, err
return err
}

func createSpecBucket() error {
db, dberr := getDB()
if dberr != nil {
return dberr
}
defer db.Close()
return db.Update(func(tx *bolt.Tx) error {
_, berr := tx.CreateBucketIfNotExists([]byte("TorrSpecs"))
return berr
func (DB *specDb) createSpecBucket() {
DB.db.Update(func(tx *bolt.Tx) error {
tx.CreateBucketIfNotExists([]byte("TorrSpecs"))
return nil
})
}

// Saves torrent spec to database file
func saveSpec(spec *torrent.TorrentSpec) error {
func (DB *specDb) saveSpec(spec *torrent.TorrentSpec) error {
json, err := json.Marshal(persistentSpec{
Trackers: spec.Trackers,
InfoHash: spec.InfoHash.String(),
Expand All @@ -49,33 +45,28 @@ func saveSpec(spec *torrent.TorrentSpec) error {
if err != nil {
return err
}
return specToDB(spec.InfoHash.String(), json)
return DB.specToDB(spec.InfoHash.String(), json)
}

// Commit a persistentSpec to DB
func specToDB(infohash string, json []byte) error {
db, dberr := getDB()
if dberr != nil {
return dberr
}
defer db.Close()
return db.Update(func(tx *bolt.Tx) error {
func (DB *specDb) specToDB(infohash string, json []byte) error {
return DB.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("TorrSpecs"))
return b.Put([]byte(strings.ToLower(infohash)), json)
})
}

// Loads all persistentSpec to BitTorrent client
func loadPersist() error {
specs, err := getSpecs()
func (DB *specDb) loadPersist() error {
specs, err := DB.getSpecs()
if err != nil {
return err
}
for _, spec := range specs {
t, terr := addTorrent(persistSpecToTorrentSpec(spec), true)
t, terr := btEngine.addTorrent(persistSpecToTorrentSpec(spec), true)
if terr != nil {
Warn.Printf("Cannot load spec \"%s\": %s\n", spec.InfoHash, terr)
rmerr := removeSpec(spec.InfoHash)
rmerr := DB.removeSpec(spec.InfoHash)
if rmerr != nil {
Warn.Printf("Cannot remove spec \"%s\": %s\n", spec.InfoHash, rmerr)
}
Expand All @@ -98,14 +89,9 @@ func loadPersist() error {
}

// Returns all persistentSpec in DB
func getSpecs() ([]persistentSpec, error) {
db, dberr := getDB()
if dberr != nil {
return nil, dberr
}
defer db.Close()
func (DB *specDb) getSpecs() ([]persistentSpec, error) {
specs := []persistentSpec{}
verr := db.View(func(tx *bolt.Tx) error {
verr := DB.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("TorrSpecs"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
Expand All @@ -122,8 +108,8 @@ func getSpecs() ([]persistentSpec, error) {
}

// Get specific persistentSpec from infohash
func getSpec(infohash string) (persistentSpec, error) {
specs, err := getSpecs()
func (DB *specDb) getSpec(infohash string) (persistentSpec, error) {
specs, err := DB.getSpecs()
if err != nil {
return persistentSpec{}, err
}
Expand All @@ -135,25 +121,20 @@ func getSpec(infohash string) (persistentSpec, error) {
return persistentSpec{}, errors.New("Torrent spec not found")
}

func removeSpec(infohash string) error {
db, dberr := getDB()
if dberr != nil {
return dberr
}
defer db.Close()
return db.Update(func(tx *bolt.Tx) error {
func (DB *specDb) removeSpec(infohash string) error {
return DB.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("TorrSpecs"))
return b.Delete([]byte(strings.ToLower(infohash)))
})
}

// Adds selected files of torrent to DB for persistence
func saveSpecFiles(infohash string, allfiles bool, files []string) error {
spec, err := getSpec(infohash)
func (DB *specDb) saveSpecFiles(infohash string, allfiles bool, files []string) error {
spec, err := DB.getSpec(infohash)
if err != nil {
return err
}
rmerr := removeSpec(infohash)
rmerr := DB.removeSpec(infohash)
if rmerr != nil {
return rmerr
}
Expand All @@ -163,5 +144,5 @@ func saveSpecFiles(infohash string, allfiles bool, files []string) error {
if jerr != nil {
return jerr
}
return specToDB(infohash, json)
return DB.specToDB(infohash, json)
}
26 changes: 23 additions & 3 deletions endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package main

import (
"net/http"
"time"

"github.com/anacrolix/torrent"
"github.com/gorilla/mux"
)

// Endpoint handler for torrent adding to client
Expand All @@ -30,11 +32,11 @@ func apiAddTorrent(w http.ResponseWriter, r *http.Request) {
// If manual metainfo is present
if body.Magnet == "" && body.InfoHash != "" && body.DisplayName != "" {
spec = makeTorrentSpec(body.InfoHash, body.DisplayName, body.Trackers)
addTorrent(spec, false)
btEngine.addTorrent(spec, false)
}

var terr error
t, terr = addTorrent(spec, false)
t, terr = btEngine.addTorrent(spec, false)
if terr != nil {
errorRes(w, terr.Error(), http.StatusInternalServerError)
return
Expand Down Expand Up @@ -70,7 +72,7 @@ func apiTorrentSelectFile(w http.ResponseWriter, r *http.Request) {
}

/* Gets torrent handler from client */
t, err := getTorrHandle(body.InfoHash)
t, err := btEngine.getTorrHandle(body.InfoHash)
if err != nil {
errorRes(w, err.Error(), http.StatusInternalServerError)
return
Expand Down Expand Up @@ -115,3 +117,21 @@ func apiTorrentSelectFile(w http.ResponseWriter, r *http.Request) {
encodeRes(w, &res)
return
}

// Endpoint for streaming a file
func apiStreamTorrentFile(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
t, err := btEngine.getTorrHandle(vars["infohash"])
if err != nil {
errorRes(w, "Torrent not found: "+err.Error(), http.StatusNotFound)
}
f, ferr := getTorrentFile(t, vars["file"])
if ferr != nil {
errorRes(w, "File not found: "+err.Error(), http.StatusNotFound)
}
reader := f.NewReader()
defer reader.Close()
reader.SetReadahead(f.Length() / 100)
http.ServeContent(w, r, f.DisplayPath(), time.Now(), reader)
return
}
66 changes: 38 additions & 28 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,47 @@ import (

"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"github.com/dustin/go-humanize"
)

// Creates the BitTorrent client
func initBTClient(opts *torrent.ClientConfig) {
btEngine.ClientConfig = opts
func (Engine *btEng) initialize(opts *torrent.ClientConfig) {
Engine.ClientConfig = opts
var err error
btEngine.Client, err = torrent.NewClient(btEngine.ClientConfig)
Engine.Client, err = torrent.NewClient(Engine.ClientConfig)
if err != nil {
Error.Fatalf("Cannot initialize BitTorrent client: %s", err)
}
}

// Create config for BitTorrent client with confs from args
func newBtCliConfs(dir string, noup bool) *torrent.ClientConfig {
opts := torrent.NewDefaultClientConfig()
opts.DataDir = dir
opts.NoUpload = noup
return opts
Engine.Torrents = make(map[string]torrentHandle)
}

// Add torrent to client
func addTorrent(spec *torrent.TorrentSpec, noSave bool) (*torrent.Torrent, error) {
t, new, err := btEngine.Client.AddTorrentSpec(spec)
func (Engine *btEng) addTorrent(spec *torrent.TorrentSpec, noSave bool) (*torrent.Torrent, error) {
t, new, err := Engine.Client.AddTorrentSpec(spec)
if err != nil {
Warn.Printf("Cannot add torrent spec: %s\n", err)
return nil, err
}
if new && !noSave {
sserr := saveSpec(spec)
sserr := specDB.saveSpec(spec)
if sserr != nil {
Warn.Printf("Cannot save torrent spec: %s\n", sserr)
}
}

btEngine.addTorrentHandle(t, spec)
Engine.addTorrentHandle(t, spec)

<-t.GotInfo()
return t, nil
}

// Get *torrent.Torrent from infohash
func getTorrHandle(infohash string) (*torrent.Torrent, error) {
func (Engine *btEng) getTorrHandle(infohash string) (*torrent.Torrent, error) {
if len(infohash) != 40 {
Warn.Println("Invalid infohash")
return nil, errors.New("Invalid infohash")
}
t, ok := btEngine.Client.Torrent(metainfo.NewHashFromHex(infohash))
t, ok := Engine.Client.Torrent(metainfo.NewHashFromHex(infohash))
if !ok {
Warn.Println("Torrent not found")
return nil, errors.New("Torrent not found")
Expand All @@ -62,26 +56,42 @@ func getTorrHandle(infohash string) (*torrent.Torrent, error) {
}

// Removes torrent from BitTorrent client and removes it's persistence spec
func dropTorrent(infohash string) error {
t, err := getTorrHandle(infohash)
func (Engine *btEng) dropTorrent(infohash string) error {
t, err := Engine.getTorrHandle(infohash)
if err != nil {
return err
}
t.Drop()
btEngine.removeTorrentHandle(infohash)
rmerr := removeSpec(t.InfoHash().String())
Engine.removeTorrentHandle(infohash)
rmerr := specDB.removeSpec(t.InfoHash().String())
if rmerr != nil {
Warn.Printf("Cannot remove spec from DB: %s\n", rmerr)
}
return rmerr
}

// Get the file handle inside the torrent
func getTorrentFile(t *torrent.Torrent, filename string) (*torrent.File, error) {
for _, f := range t.Files() {
if f.DisplayPath() == filename {
return f, nil
}
// Adds torrent handle to custom torrent handler
func (Engine *btEng) addTorrentHandle(t *torrent.Torrent, spec *torrent.TorrentSpec) {
Engine.Torrents[t.InfoHash().String()] = torrentHandle{
Torrent: t,
Spec: spec,
Name: t.Name(),
InfoHash: t.InfoHash(),
InfoHashString: t.InfoHash().String(),
}
return nil, errors.New("File not found")
}

// Remove torrent handle from custom torrent handle
func (Engine *btEng) removeTorrentHandle(infohash string) {
delete(Engine.Torrents, infohash)
}

func (Engine *btEng) calculateSpeeds(infohash string) {
handle := Engine.Torrents[infohash]

/* Download speed */
curprog := handle.Torrent.BytesCompleted()
handle.DlSpeedBytes = curprog - handle.DlLastProgress
handle.DlSpeedReadable = humanize.Bytes(uint64(handle.DlSpeedBytes)) + "/s"
handle.DlLastProgress = curprog
}
40 changes: 14 additions & 26 deletions functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ package main

import (
"encoding/json"
"errors"
"io"
"net/http"
"net/url"

"github.com/anacrolix/torrent"
"github.com/anacrolix/torrent/metainfo"
"github.com/dustin/go-humanize"
)

// Function for sending error message as JSON response
Expand Down Expand Up @@ -74,32 +74,20 @@ func createFileLink(infohash string, filename string) string {
return "/api/getfile?infohash=" + infohash + "&file=" + url.QueryEscape(filename)
}

/* Functions with receivers */

/* btEng functions */

// Adds torrent handle to custom torrent handler
func (Engine btEng) addTorrentHandle(t *torrent.Torrent, spec *torrent.TorrentSpec) {
Engine.Torrents[t.InfoHash().String()] = torrentHandle{
Torrent: t,
Spec: spec,
Name: t.Name(),
InfoHash: t.InfoHash(),
InfoHashString: t.InfoHash().String(),
// Get the file handle inside the torrent
func getTorrentFile(t *torrent.Torrent, filename string) (*torrent.File, error) {
for _, f := range t.Files() {
if f.DisplayPath() == filename {
return f, nil
}
}
return nil, errors.New("File not found")
}

// Remove torrent handle from custom torrent handle
func (Engine btEng) removeTorrentHandle(infohash string) {
delete(Engine.Torrents, infohash)
}

func (Engine btEng) calculateSpeeds(infohash string) {
handle := Engine.Torrents[infohash]

/* Download speed */
curprog := handle.Torrent.BytesCompleted()
handle.DlSpeedBytes = curprog - handle.DlLastProgress
handle.DlSpeedReadable = humanize.Bytes(uint64(handle.DlSpeedBytes)) + "/s"
handle.DlLastProgress = curprog
// Create config for BitTorrent client with confs from args
func newBtCliConfs(dir string, noup bool) *torrent.ClientConfig {
opts := torrent.NewDefaultClientConfig()
opts.DataDir = dir
opts.NoUpload = noup
return opts
}
Loading

0 comments on commit 1606665

Please sign in to comment.