diff --git a/server/dlna/dlna.go b/server/dlna/dlna.go index 1465267..9bdd7e4 100644 --- a/server/dlna/dlna.go +++ b/server/dlna/dlna.go @@ -13,7 +13,6 @@ import ( "github.com/anacrolix/dms/dlna/dms" "server/log" - "server/utils" "server/web/pages/template" ) @@ -30,7 +29,7 @@ func Start() { } for _, i := range ifaces { // interface flags seem to always be 0 on Windows - if runtime.GOOS != "windows" && (i.Flags&net.FlagLoopback != 0 || i.Flags&net.FlagUp == 0 || i.Flags&net.FlagMulticast == 0) || !utils.IsPhysicalInterface(i.HardwareAddr.String()) { + if runtime.GOOS != "windows" && (i.Flags&net.FlagLoopback != 0 || i.Flags&net.FlagUp == 0 || i.Flags&net.FlagMulticast == 0) { continue } ifs = append(ifs, i) @@ -168,7 +167,7 @@ func getDefaultFriendlyName() string { var list []string for _, i := range ifaces { // interface flags seem to always be 0 on Windows - if runtime.GOOS != "windows" && (i.Flags&net.FlagLoopback != 0 || i.Flags&net.FlagUp == 0 || i.Flags&net.FlagMulticast == 0) || !utils.IsPhysicalInterface(i.HardwareAddr.String()) { + if runtime.GOOS != "windows" && (i.Flags&net.FlagLoopback != 0 || i.Flags&net.FlagUp == 0 || i.Flags&net.FlagMulticast == 0) { continue } addrs, _ := i.Addrs() diff --git a/server/dlna/list.go b/server/dlna/list.go index 2098bc5..4a4673b 100644 --- a/server/dlna/list.go +++ b/server/dlna/list.go @@ -13,6 +13,7 @@ import ( "github.com/anacrolix/dms/upnpav" "server/log" + mt "server/mimetype" "server/settings" "server/torr" "server/torr/state" @@ -72,7 +73,7 @@ func getTorrents() (ret []interface{}) { Class: "object.container.storageFolder", Date: upnpav.Timestamp{Time: time.Now()}, } - cnt := upnpav.Container{Object: obj, ChildCount: 1} + cnt := upnpav.Container{Object: obj, ChildCount: 0} ret = append(ret, cnt) } return @@ -114,50 +115,6 @@ func getTorrent(path, host string) (ret []interface{}) { } func getTorrentMeta(path, host string) (ret interface{}) { - // https://github.com/1100101/minidlna/blob/ca6dbba18390ad6f8b8d7b7dbcf797dbfd95e2db/upnpsoap.c#L1237-L1243 - if path == "/" { - rootObj := upnpav.Object{ - ID: "0", - ParentID: "-1", - Restricted: 1, - Searchable: 1, - Title: "TorrServer", - Date: upnpav.Timestamp{Time: time.Now()}, - Class: "object.container", - SearchXML: ` object.container.album.musicAlbum - object.container.genre.musicGenre - object.container.person.musicArtist - object.container.playlistContainer - object.container.storageFolder - object.item.audioItem.musicTrack - object.item.imageItem.photo - object.container.album - object.container.genre - object.container - object.item.audioItem - object.item.imageItem - object.item.videoItem -`, - } - // add Root Object - meta := upnpav.Container{Object: rootObj} - return meta - } else if 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", - } - //vol := len(torr.ListTorrent()) - meta := upnpav.Container{Object: trObj} - return meta - } - // find torrent without load torrs := torr.ListTorrent() var torr *torr.Torrent @@ -172,7 +129,34 @@ func getTorrentMeta(path, host string) (ret interface{}) { } // Meta object - if isHashPath(path) { + 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 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(), @@ -180,15 +164,11 @@ func getTorrentMeta(path, host string) (ret interface{}) { Restricted: 1, Title: torr.Title, Date: upnpav.Timestamp{Time: time.Now()}, - Class: "object.container", } - meta := upnpav.Container{Object: obj} + meta := upnpav.Container{Object: obj, ChildCount: 1} return meta } else if filepath.Base(path) == "LD" { parent := url.PathEscape(filepath.Dir(path)) - if settings.BTsets.EnableDebug { - log.TLogln("getTorrentMeta parent for LD", parent) - } // LD object meta obj := upnpav.Object{ ID: parent + "%2FLD", @@ -197,17 +177,13 @@ func getTorrentMeta(path, host string) (ret interface{}) { Searchable: 1, Title: "Load Torrents", Date: upnpav.Timestamp{Time: time.Now()}, - Class: "object.container", } - meta := upnpav.Container{Object: obj} + meta := upnpav.Container{Object: obj, ChildCount: 1} return meta } else { file := filepath.Base(path) id := url.PathEscape(path) parent := url.PathEscape(filepath.Dir(path)) - if settings.BTsets.EnableDebug { - log.TLogln("getTorrentMeta id:", id, "parent:", parent) - } // file object meta obj := upnpav.Object{ ID: id, @@ -216,12 +192,11 @@ func getTorrentMeta(path, host string) (ret interface{}) { Searchable: 1, Title: file, Date: upnpav.Timestamp{Time: time.Now()}, - Class: "object.container", } - meta := upnpav.Container{Object: obj} + meta := upnpav.Container{Object: obj, ChildCount: 1} return meta } - // for error response + return nil } @@ -277,7 +252,7 @@ func getLink(host, path string) string { func getObjFromTorrent(path, parent, host string, torr *torr.Torrent, file *state.TorrentFileStat) (ret interface{}) { - mime, err := MimeTypeByPath(file.Path) + mime, err := mt.MimeTypeByPath(file.Path) if err != nil { if settings.BTsets.EnableDebug { log.TLogln("Can't detect mime type", err) diff --git a/server/go.mod b/server/go.mod index ed0bd58..bca1c92 100644 --- a/server/go.mod +++ b/server/go.mod @@ -2,8 +2,7 @@ module server go 1.17 -replace github.com/anacrolix/dms v1.2.2 => github.com/tsynik/dms v0.0.0-20210913075730-e41ecd88369c - +replace github.com/anacrolix/dms v1.2.2 => github.com/tsynik/dms v0.0.0-20210911171915-d3d89ee99163 exclude ( github.com/willf/bitset v1.2.0 @@ -21,7 +20,6 @@ require ( github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 github.com/pkg/errors v0.9.1 go.etcd.io/bbolt v1.3.6 - golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac ) @@ -92,6 +90,7 @@ require ( golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e // indirect golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect + golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e // indirect golang.org/x/text v0.3.6 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/protobuf v1.27.1 // indirect diff --git a/server/go.sum b/server/go.sum index 934c142..dec591a 100644 --- a/server/go.sum +++ b/server/go.sum @@ -156,6 +156,8 @@ github.com/anacrolix/torrent v1.25.1-0.20210224024805-693c30dd889e/go.mod h1:d4V github.com/anacrolix/torrent v1.28.1-0.20210622065255-582f0ccd48a0/go.mod h1:15VRIA5/DwqbqETbKo3fzlC4aSbB0iMoo10ng3mzAbE= github.com/anacrolix/torrent v1.29.0/go.mod h1:40Hf2bWxFqTbTWbrdig57JnmYLCjShbWWjdbB3VN5n4= github.com/anacrolix/torrent v1.30.2/go.mod h1:vbNxKfaGiNq8edcCaQI1oSNJwh4GMqtMUMF9qOdZ6C0= +github.com/anacrolix/torrent v1.31.1-0.20210912041407-4691b0b8d194 h1:U1h0guD86azvKs/sepNTrYUacoofSLxRQJwfd54IxgY= +github.com/anacrolix/torrent v1.31.1-0.20210912041407-4691b0b8d194/go.mod h1:akZJHHFN8aWH2lcPZQ0I3etujnenwYpUvj36HV9uvAI= github.com/anacrolix/upnp v0.1.1/go.mod h1:LXsbsp5h+WGN7YR+0A7iVXm5BL1LYryDev1zuJMWYQo= github.com/anacrolix/upnp v0.1.2-0.20200416075019-5e9378ed1425 h1:/Wi6l2ONI1FUFWN4cBwHOO90V4ylp4ud/eov6GUcVFk= github.com/anacrolix/upnp v0.1.2-0.20200416075019-5e9378ed1425/go.mod h1:Pz94W3kl8rf+wxH3IbCa9Sq+DTJr8OSbV2Q3/y51vYs= @@ -720,8 +722,8 @@ github.com/tinylib/msgp v1.1.1/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDW github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tsynik/dms v0.0.0-20210913075730-e41ecd88369c h1:4yU1p5MmRu+233Hs2OhR8Kx9gOWw20KR2AY1py5pLvA= -github.com/tsynik/dms v0.0.0-20210913075730-e41ecd88369c/go.mod h1:oWW4QbQ9YGl+FJ1X8xcrUYVObfA/KdipoeBuTC4ltG8= +github.com/tsynik/dms v0.0.0-20210911171915-d3d89ee99163 h1:qBZPOad8wOmvFe6rIx1d0U7VpymFalvbM5kjHeEx8Gs= +github.com/tsynik/dms v0.0.0-20210911171915-d3d89ee99163/go.mod h1:oWW4QbQ9YGl+FJ1X8xcrUYVObfA/KdipoeBuTC4ltG8= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= diff --git a/server/dlna/mimetype.go b/server/mimetype/mimetype.go similarity index 99% rename from server/dlna/mimetype.go rename to server/mimetype/mimetype.go index c6f8f1b..ebdf3f8 100644 --- a/server/dlna/mimetype.go +++ b/server/mimetype/mimetype.go @@ -1,4 +1,4 @@ -package dlna +package mimetype import ( "log" diff --git a/server/torr/stream.go b/server/torr/stream.go index da3ca3a..1bfebb3 100644 --- a/server/torr/stream.go +++ b/server/torr/stream.go @@ -12,6 +12,7 @@ import ( "github.com/anacrolix/missinggo/httptoo" "github.com/anacrolix/torrent" + mt "server/mimetype" sets "server/settings" "server/torr/state" ) @@ -61,8 +62,12 @@ func (t *Torrent) Stream(fileID int, req *http.Request, resp http.ResponseWriter resp.Header().Set("Connection", "close") resp.Header().Set("ETag", httptoo.EncodeQuotedString(fmt.Sprintf("%s/%s", t.Hash().HexString(), file.Path()))) + // DLNA headers resp.Header().Set("transferMode.dlna.org", "Streaming") - + mime, err := mt.MimeTypeByPath(file.Path()) + if err == nil && mime.IsMedia() { + resp.Header().Set("content-type", mime.String()) + } if req.Header.Get("getContentFeatures.dlna.org") != "" { resp.Header().Set("contentFeatures.dlna.org", dlna.ContentFeatures{ SupportRange: true, diff --git a/server/utils/net_interface_filter.go b/server/utils/net_interface_filter.go deleted file mode 100644 index 23bdb7b..0000000 --- a/server/utils/net_interface_filter.go +++ /dev/null @@ -1,38 +0,0 @@ -package utils - -import ( - "strings" -) - -type PhysicalInterface struct { - MACAddress string - Name string - FriendlyName string -} - -// Mac Address parts to look for, and identify non physical devices. There may be more, update me! -var macAddrPartsToFilter []string = []string{ - "00:03:FF", // Microsoft Hyper-V, Virtual Server, Virtual PC - "0A:00:27", // VirtualBox - "00:00:00:00:00", // Teredo Tunneling Pseudo-Interface - "00:50:56", // VMware ESX 3, Server, Workstation, Player - "00:1C:14", // VMware ESX 3, Server, Workstation, Player - "00:0C:29", // VMware ESX 3, Server, Workstation, Player - "00:05:69", // VMware ESX 3, Server, Workstation, Player - "00:1C:42", // Microsoft Hyper-V, Virtual Server, Virtual PC - "00:0F:4B", // Virtual Iron 4 - "00:16:3E", // Red Hat Xen, Oracle VM, XenSource, Novell Xen - "08:00:27", // Sun xVM VirtualBox - "7A:79", // Hamachi -} - -// Filters the possible physical interface address by comparing it to known popular VM Software adresses -// and Teredo Tunneling Pseudo-Interface. -func IsPhysicalInterface(addr string) bool { - for _, macPart := range macAddrPartsToFilter { - if strings.HasPrefix(strings.ToLower(addr), strings.ToLower(macPart)) { - return false - } - } - return true -} diff --git a/server/utils/net_interface_filter_nix_darwin.go b/server/utils/net_interface_filter_nix_darwin.go deleted file mode 100644 index ecebcef..0000000 --- a/server/utils/net_interface_filter_nix_darwin.go +++ /dev/null @@ -1,29 +0,0 @@ -//go:build linux || darwin -// +build linux darwin - -package utils - -import ( - "fmt" - "net" -) - -// Gets all physical interfaces based on filter results, ignoring all VM, Loopback and Tunnel interfaces. -func GetAllPhysicalInterfaces() []PhysicalInterface { - ifaces, err := net.Interfaces() - - if err != nil { - fmt.Println(err) - return nil - } - - var outInterfaces []PhysicalInterface - - for _, element := range ifaces { - if element.Flags&net.FlagLoopback == 0 && element.Flags&net.FlagUp == 1 && IsPhysicalInterface(element.HardwareAddr.String()) { - outInterfaces = append(outInterfaces, PhysicalInterface{MACAddress: element.HardwareAddr.String(), Name: element.Name, FriendlyName: element.Name}) - } - } - - return outInterfaces -} diff --git a/server/utils/net_interface_filter_win.go b/server/utils/net_interface_filter_win.go deleted file mode 100644 index 0620d59..0000000 --- a/server/utils/net_interface_filter_win.go +++ /dev/null @@ -1,105 +0,0 @@ -//go:build windows -// +build windows - -package utils - -import ( - "os" - "syscall" - "unicode/utf16" - "unsafe" - - "golang.org/x/sys/windows" -) - -const ( - IfOperStatusUp = 1 - IF_TYPE_SOFTWARE_LOOPBACK = 24 - IF_TYPE_TUNNEL = 131 -) - -const hexDigit = "0123456789abcdef" - -func adapterAddresses() ([]*windows.IpAdapterAddresses, error) { - var b []byte - l := uint32(15000) // recommended initial size - for { - b = make([]byte, l) - err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l) - if err == nil { - if l == 0 { - return nil, nil - } - break - } - if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW { - return nil, os.NewSyscallError("getadaptersaddresses", err) - } - if l <= uint32(len(b)) { - return nil, os.NewSyscallError("getadaptersaddresses", err) - } - } - var aas []*windows.IpAdapterAddresses - for aa := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); aa != nil; aa = aa.Next { - aas = append(aas, aa) - } - return aas, nil -} - -func bytePtrToString(p *uint8) string { - a := (*[10000]uint8)(unsafe.Pointer(p)) - i := 0 - for a[i] != 0 { - i++ - } - return string(a[:i]) -} - -func physicalAddrToString(physAddr [8]byte) string { - if len(physAddr) == 0 { - return "" - } - buf := make([]byte, 0, len(physAddr)*3-1) - for i, b := range physAddr { - if i > 0 { - buf = append(buf, ':') - } - buf = append(buf, hexDigit[b>>4]) - buf = append(buf, hexDigit[b&0xF]) - } - return string(buf) -} - -func cStringToString(cs *uint16) (s string) { - if cs != nil { - us := make([]uint16, 0, 256) - for p := uintptr(unsafe.Pointer(cs)); ; p += 2 { - u := *(*uint16)(unsafe.Pointer(p)) - if u == 0 { - return string(utf16.Decode(us)) - } - us = append(us, u) - } - } - return "" -} - -// Gets all physical interfaces based on filter results, ignoring all VM, Loopback and Tunnel interfaces. -func GetAllPhysicalInterfaces() []PhysicalInterface { - aa, _ := adapterAddresses() - - var outInterfaces []PhysicalInterface - - for _, pa := range aa { - mac := physicalAddrToString(pa.PhysicalAddress) - name := "\\Device\\NPF_" + bytePtrToString(pa.AdapterName) - - if pa.IfType != uint32(IF_TYPE_SOFTWARE_LOOPBACK) && pa.IfType != uint32(IF_TYPE_TUNNEL) && - pa.OperStatus == uint32(IfOperStatusUp) && IsPhysicalInterface(mac) { - - outInterfaces = append(outInterfaces, PhysicalInterface{MACAddress: mac, Name: name, FriendlyName: cStringToString(pa.FriendlyName)}) - } - } - - return outInterfaces -}