Files
TorrServerJellyfin/server/dlna/list.go
2022-04-27 23:06:51 +03:00

294 lines
6.7 KiB
Go

package dlna
import (
"fmt"
"net/url"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"github.com/anacrolix/dms/dlna"
"github.com/anacrolix/dms/upnpav"
"server/log"
mt "server/mimetype"
"server/settings"
"server/torr"
"server/torr/state"
)
func getRoot() (ret []interface{}) {
// Torrents Object
tObj := upnpav.Object{
ID: "%2FTR",
ParentID: "0",
Restricted: 1,
Title: "Torrents",
Class: "object.container.storageFolder",
Date: upnpav.Timestamp{Time: time.Now()},
}
// add Torrents Object
vol := len(torr.ListTorrent())
cnt := upnpav.Container{Object: tObj, ChildCount: vol}
ret = append(ret, cnt)
return
}
func getTorrents() (ret []interface{}) {
torrs := torr.ListTorrent()
// sort by title as in cds SortCaps
sort.Slice(torrs, func(i, j int) bool {
return torrs[i].Title < torrs[j].Title
})
var vol = 0
for _, t := range torrs {
vol++
obj := upnpav.Object{
ID: "%2F" + t.TorrentSpec.InfoHash.HexString(),
ParentID: "%2FTR",
Restricted: 1,
Title: strings.ReplaceAll(t.Title, "/", "|"),
Class: "object.container.storageFolder",
Icon: t.Poster,
AlbumArtURI: t.Poster,
Date: upnpav.Timestamp{Time: time.Now()},
}
cnt := upnpav.Container{Object: obj, ChildCount: 1}
ret = append(ret, cnt)
}
if vol == 0 {
obj := upnpav.Object{
ID: "%2FNT",
ParentID: "%2FTR",
Restricted: 1,
Title: "No Torrents",
Class: "object.container.storageFolder",
Date: upnpav.Timestamp{Time: time.Now()},
}
cnt := upnpav.Container{Object: obj, ChildCount: 0}
ret = append(ret, cnt)
}
return
}
func getTorrent(path, host string) (ret []interface{}) {
// find torrent without load
torrs := torr.ListTorrent()
var torr *torr.Torrent
for _, t := range torrs {
if strings.Contains(path, t.TorrentSpec.InfoHash.HexString()) {
torr = t
break
}
}
if torr == nil {
return nil
}
// get content from torrent
parent := "%2F" + torr.TorrentSpec.InfoHash.HexString()
// if torrent not loaded, get button for load
if torr.Files() == nil {
obj := upnpav.Object{
ID: parent + "%2FLD",
ParentID: parent,
Restricted: 1,
Title: "Load Torrent",
Class: "object.container.storageFolder",
Date: upnpav.Timestamp{Time: time.Now()},
}
cnt := upnpav.Container{Object: obj, ChildCount: 1}
ret = append(ret, cnt)
return
}
ret = loadTorrent(path, host)
return
}
func getTorrentMeta(path, host string) (ret interface{}) {
// find torrent without load
torrs := torr.ListTorrent()
var torr *torr.Torrent
for _, t := range torrs {
if strings.Contains(path, t.TorrentSpec.InfoHash.HexString()) {
torr = t
break
}
}
if torr == nil {
return nil
}
// Meta object
if path == "/" {
// root object meta
rootObj := upnpav.Object{
ID: "0",
ParentID: "-1",
Restricted: 1,
Searchable: 1,
Title: "TorrServer",
Date: upnpav.Timestamp{Time: time.Now()},
Class: "object.container.storageFolder",
}
meta := upnpav.Container{Object: rootObj, ChildCount: 1}
return meta
} else if filepath.Base(path) == "TR" {
// TR Object Meta
trObj := upnpav.Object{
ID: "%2FTR",
ParentID: "0",
Restricted: 1,
Searchable: 1,
Title: "Torrents",
Date: upnpav.Timestamp{Time: time.Now()},
Class: "object.container.storageFolder",
}
vol := len(torrs)
meta := upnpav.Container{Object: trObj, ChildCount: vol}
return meta
} else if isHashPath(path) {
// hash object meta
obj := upnpav.Object{
ID: "%2F" + torr.TorrentSpec.InfoHash.HexString(),
ParentID: "%2FTR",
Restricted: 1,
Title: torr.Title,
Date: upnpav.Timestamp{Time: time.Now()},
}
meta := upnpav.Container{Object: obj, ChildCount: 1}
return meta
} else if filepath.Base(path) == "LD" {
parent := url.PathEscape(filepath.Dir(path))
// LD object meta
obj := upnpav.Object{
ID: parent + "%2FLD",
ParentID: parent,
Restricted: 1,
Searchable: 1,
Title: "Load Torrents",
Date: upnpav.Timestamp{Time: time.Now()},
}
meta := upnpav.Container{Object: obj, ChildCount: 1}
return meta
} else {
file := filepath.Base(path)
id := url.PathEscape(path)
parent := url.PathEscape(filepath.Dir(path))
// file object meta
obj := upnpav.Object{
ID: id,
ParentID: parent,
Restricted: 1,
Searchable: 1,
Title: file,
Date: upnpav.Timestamp{Time: time.Now()},
}
meta := upnpav.Container{Object: obj, ChildCount: 1}
return meta
}
return nil
}
func loadTorrent(path, host string) (ret []interface{}) {
hash := filepath.Base(filepath.Dir(path))
if hash == "/" {
hash = filepath.Base(path)
}
if len(hash) != 40 {
return
}
tor := torr.GetTorrent(hash)
if tor == nil {
log.TLogln("Dlna error get info from torrent", hash)
return
}
if len(tor.Files()) == 0 {
time.Sleep(time.Millisecond * 200)
timeout := time.Now().Add(time.Second * 60)
for {
tor = torr.GetTorrent(hash)
if len(tor.Files()) > 0 {
break
}
time.Sleep(time.Millisecond * 200)
if time.Now().After(timeout) {
return
}
}
}
parent := "%2F" + tor.TorrentSpec.InfoHash.HexString()
files := tor.Status().FileStats
for _, f := range files {
obj := getObjFromTorrent(path, parent, host, tor, f)
if obj != nil {
ret = append(ret, obj)
}
}
return
}
func getLink(host, path string) string {
if !strings.HasPrefix(host, "http") {
host = "http://" + host
}
pos := strings.LastIndex(host, ":")
if pos > 7 {
host = host[:pos]
}
return host + ":" + settings.Port + "/" + path
}
func getObjFromTorrent(path, parent, host string, torr *torr.Torrent, file *state.TorrentFileStat) (ret interface{}) {
mime, err := mt.MimeTypeByPath(file.Path)
if err != nil {
if settings.BTsets.EnableDebug {
log.TLogln("Can't detect mime type", err)
}
return
}
// TODO: handle subtitles for media
if !mime.IsMedia() {
return
}
if settings.BTsets.EnableDebug {
log.TLogln("mime type", mime.String(), file.Path)
}
obj := upnpav.Object{
ID: parent + "%2F" + url.PathEscape(file.Path),
ParentID: parent,
Restricted: 1,
Title: file.Path,
Class: "object.item." + mime.Type() + "Item",
Date: upnpav.Timestamp{Time: time.Now()},
}
item := upnpav.Item{
Object: obj,
Res: make([]upnpav.Resource, 0, 1),
}
pathPlay := "stream/" + url.PathEscape(file.Path) + "?link=" + torr.TorrentSpec.InfoHash.HexString() + "&play&index=" + strconv.Itoa(file.Id)
item.Res = append(item.Res, upnpav.Resource{
URL: getLink(host, pathPlay),
ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", mime, dlna.ContentFeatures{
SupportRange: true,
SupportTimeSeek: true,
}.String()),
Size: uint64(file.Length),
})
return item
}