0.2.57 - Traliers bug fixes

This commit is contained in:
ponzischeme89
2026-08-12 13:08:53 +12:00
parent f2d052dbf6
commit 64f19aeef2
45 changed files with 1215 additions and 681 deletions
+52 -12
View File
@@ -25,6 +25,12 @@ type Client struct {
baseURL string
publicURL string
clientName string
// gatewayClientName identifies the requests the gateway makes on its own behalf, so
// Emby's device list separates a television from the server standing behind it. The
// admin and installer sign-ins are the ones an operator sees: those are the gateway
// asking, not a set in a living room, and reporting them as a television made the
// list claim a device that does not exist.
gatewayClientName string
// gatewayVersion labels the requests the gateway makes on its own behalf — the
// library sync, the health probe, device cleanup — which belong to no television and
// so have no app version to report.
@@ -42,6 +48,12 @@ type Credentials struct {
// as it reported in X-Memby-Version. Emby shows it beside the device, so a blank one
// makes every set in the house look like the same build.
ClientVersion string
// Gateway marks a request the gateway makes for itself — the library sync, the health
// probe, device cleanup, an operator signing into the admin console — rather than on
// behalf of a television. It is stated rather than inferred from a missing token or
// version: an old app reports neither, and misreading one as the server would put a
// television in Emby's list under the wrong name.
Gateway bool
}
type Device struct {
@@ -133,12 +145,24 @@ func (e *APIError) Error() string {
return fmt.Sprintf("emby: status %d: %s", e.StatusCode, e.Body)
}
func New(baseURL, publicURL, clientName string, timeout time.Duration) *Client {
// DefaultGatewayClientName is what Emby records for a request the gateway makes for
// itself. It is deliberately not the product name: this travels to whatever Emby does
// with its own logs, and it must never read as one of the household's televisions.
const DefaultGatewayClientName = "MbyGateway"
// New builds a client. clientName identifies a television on the wire to Emby and
// gatewayClientName identifies the gateway itself; a blank gateway name falls back to
// DefaultGatewayClientName rather than borrowing the television's.
func New(baseURL, publicURL, clientName, gatewayClientName string, timeout time.Duration) *Client {
if strings.TrimSpace(gatewayClientName) == "" {
gatewayClientName = DefaultGatewayClientName
}
return &Client{
baseURL: strings.TrimRight(baseURL, "/"),
publicURL: strings.TrimRight(publicURL, "/"),
clientName: clientName,
gatewayVersion: buildinfo.Version(),
baseURL: strings.TrimRight(baseURL, "/"),
publicURL: strings.TrimRight(publicURL, "/"),
clientName: clientName,
gatewayClientName: gatewayClientName,
gatewayVersion: buildinfo.Version(),
http: &http.Client{
Timeout: timeout,
Transport: &http.Transport{
@@ -150,16 +174,21 @@ func New(baseURL, publicURL, clientName string, timeout time.Duration) *Client {
}
}
// Authenticate signs a television in. clientVersion is the app version that set
// reported; it is what Emby stamps on the device record it creates here, so an empty one
// leaves the entry claiming the gateway's own build.
func (c *Client) Authenticate(ctx context.Context, username, password, deviceID, deviceName, clientVersion string) (*AuthResult, error) {
// Authenticate signs a television in. cred carries the device the record is created for
// and that set's ClientVersion — Emby stamps it on the record, so an empty one leaves the
// entry claiming the gateway's own build. cred.Gateway marks a sign-in the gateway is
// making for itself (the admin console and the web installer), which Emby then records
// under the gateway's own client name rather than as a television.
func (c *Client) Authenticate(ctx context.Context, username, password string, cred Credentials) (*AuthResult, error) {
body, err := json.Marshal(map[string]string{"Username": username, "Pw": password})
if err != nil {
return nil, err
}
req, err := c.newRequest(ctx, http.MethodPost, "/Users/AuthenticateByName", nil,
Credentials{DeviceID: deviceID, DeviceName: deviceName, ClientVersion: clientVersion},
Credentials{
DeviceID: cred.DeviceID, DeviceName: cred.DeviceName,
ClientVersion: cred.ClientVersion, Gateway: cred.Gateway,
},
bytes.NewReader(body))
if err != nil {
return nil, err
@@ -610,7 +639,8 @@ func (c *Client) SubtitleURL(cred Credentials, itemID, mediaSourceID string, ind
// Ping checks that Emby is reachable, for readiness probes.
func (c *Client) Ping(ctx context.Context) error {
req, err := c.newRequest(ctx, http.MethodGet, "/System/Info/Public", nil, Credentials{}, nil)
req, err := c.newRequest(ctx, http.MethodGet, "/System/Info/Public", nil,
Credentials{Gateway: true}, nil)
if err != nil {
return err
}
@@ -657,7 +687,13 @@ func (c *Client) newRequest(ctx context.Context, method, path string, params url
}
deviceName := strings.TrimSpace(cred.DeviceName)
if deviceName == "" {
// A gateway request is not a television, so it must not fall back to the
// unnamed-set placeholder: that put the server in Emby's device list wearing a
// name that reads as somebody's TV.
deviceName = "Memby TV"
if cred.Gateway {
deviceName = c.gatewayClientName
}
}
// The version Emby records is the television's app version, not a constant: every
// device in the dashboard read as one build before this, so there was no way to tell
@@ -666,10 +702,14 @@ func (c *Client) newRequest(ctx context.Context, method, path string, params url
if version == "" {
version = c.gatewayVersion
}
clientName := c.clientName
if cred.Gateway {
clientName = c.gatewayClientName
}
req.Header.Set("Accept", "application/json")
req.Header.Set("X-Emby-Authorization", fmt.Sprintf(
`MediaBrowser Client="%s", Device="%s", DeviceId="%s", Version="%s"`,
c.clientName, strings.ReplaceAll(deviceName, `"`, ""), deviceID,
clientName, strings.ReplaceAll(deviceName, `"`, ""), deviceID,
strings.ReplaceAll(version, `"`, ""),
))
if cred.Token != "" {