Files
gronod 7bcf5322e1 feat: emby-mcp 1.0.16 player owner filter and HA links
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.
2026-09-25 19:58:58 +00:00

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
}