Expose session/last-used user and device IP on player rows. Filter retrieve_player_list by users, merge GET /Devices when include_offline is set, and resolve spoken names to HA media_player entities via player_links (device_id or IP). PlayNow still requires a live session.
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
package emby
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
// DeviceInfo is a subset of GET /Devices.
|
|
type DeviceInfo struct {
|
|
ID string `json:"Id"`
|
|
Name string `json:"Name"`
|
|
AppName string `json:"AppName"`
|
|
LastUserName string `json:"LastUserName"`
|
|
LastUserID string `json:"LastUserId"`
|
|
DateLastActivity string `json:"DateLastActivity"`
|
|
IPAddress string `json:"IpAddress"`
|
|
}
|
|
|
|
// GetDevices lists known Emby client devices (last user, last activity).
|
|
func (c *Client) GetDevices(ctx context.Context) ([]DeviceInfo, error) {
|
|
var raw json.RawMessage
|
|
if err := c.Get(ctx, "/Devices", nil, &raw); err != nil {
|
|
return nil, err
|
|
}
|
|
raw = json.RawMessage(strings.TrimSpace(string(raw)))
|
|
if len(raw) == 0 {
|
|
return nil, nil
|
|
}
|
|
if raw[0] == '[' {
|
|
var items []DeviceInfo
|
|
if err := json.Unmarshal(raw, &items); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
var wrap struct {
|
|
Items []DeviceInfo `json:"Items"`
|
|
}
|
|
if err := json.Unmarshal(raw, &wrap); err != nil {
|
|
return nil, err
|
|
}
|
|
return wrap.Items, nil
|
|
}
|
|
|
|
// MergeOfflineDevices appends Devices that have no live session, using last-used
|
|
// user as owner. Session rows win when DeviceID matches.
|
|
func MergeOfflineDevices(live []PlayerSession, devices []DeviceInfo) []PlayerSession {
|
|
seen := map[string]struct{}{}
|
|
for _, p := range live {
|
|
if p.DeviceID != "" {
|
|
seen[p.DeviceID] = struct{}{}
|
|
}
|
|
}
|
|
out := append([]PlayerSession(nil), live...)
|
|
for _, d := range devices {
|
|
id := d.ID
|
|
if id == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
out = append(out, PlayerSession{
|
|
ClientName: d.AppName,
|
|
DeviceID: id,
|
|
DeviceName: d.Name,
|
|
DeviceIPAddress: HostOf(d.IPAddress),
|
|
UserID: d.LastUserID,
|
|
UserName: d.LastUserName,
|
|
Online: false,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
// HostOf returns the host part of an address (strips port). IPv6 brackets are removed.
|
|
func HostOf(addr string) string {
|
|
addr = strings.TrimSpace(addr)
|
|
if addr == "" {
|
|
return ""
|
|
}
|
|
if strings.HasPrefix(addr, "[") {
|
|
if host, _, err := net.SplitHostPort(addr); err == nil {
|
|
return host
|
|
}
|
|
return strings.Trim(addr, "[]")
|
|
}
|
|
if host, port, err := net.SplitHostPort(addr); err == nil && port != "" {
|
|
return host
|
|
}
|
|
return addr
|
|
}
|