feat: restore premiere_date on item results (1.0.15) #1
@@ -1,5 +1,26 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.0.15
|
||||||
|
|
||||||
|
- Restore `premiere_date` on item-shaped results (search, episode/season
|
||||||
|
lists, next-episode, playlist items, play queue). Emby's `PremiereDate`
|
||||||
|
is the first air date for episodes/series and the release date for
|
||||||
|
movies; it is emitted as `YYYY-MM-DD` and omitted when metadata is empty
|
||||||
|
- Request `PremiereDate` again in upstream `Fields`
|
||||||
|
- Conversation-agent system prompt in DOCS.md now tells the model to read
|
||||||
|
`premiere_date` for airdate questions
|
||||||
|
|
||||||
|
### Potential breaking change
|
||||||
|
|
||||||
|
- Item JSON grows a `premiere_date` key versus the 1.0.11 slim shape.
|
||||||
|
Home Assistant function *parameters* are unchanged (`value_template`
|
||||||
|
still returns `value_json.result`). Re-paste the DOCS.md system prompt
|
||||||
|
so the agent looks for `premiere_date`; the Functions YAML block does
|
||||||
|
not need to be replaced unless you also want the updated prompt text
|
||||||
|
nearby. Clients that reject unknown item keys must allow
|
||||||
|
`premiere_date`.
|
||||||
|
|
||||||
|
|
||||||
## 1.0.14
|
## 1.0.14
|
||||||
|
|
||||||
- `control_media_player` PlayNow now honours `time_milliseconds` as a start
|
- `control_media_player` PlayNow now honours `time_milliseconds` as a start
|
||||||
|
|||||||
+6
-1
@@ -101,7 +101,7 @@ These steps assume the add-on has Emby credentials configured and
|
|||||||
Add this line to the conversation agent's instructions:
|
Add this line to the conversation agent's instructions:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
- Media & Emby: Use `emby_search` to find films, TV series, episodes, or music tracks. Use `emby_episode_list` to list or count a series' episodes (seasons via `emby_season_list`). Use `emby_list_players` to find active playback sessions, and `emby_playback_control` to send playback commands (PlayNow, Pause, Unpause, Stop, NextTrack). Use `emby_next_episode` when asked what episode to watch next.
|
- Media & Emby: Never use execute_services for Emby. Use `emby_search` to find films, TV series, episodes, or music tracks. Use `emby_episode_list` to list or count a series' episodes (seasons via `emby_season_list`). Each episode item includes `premiere_date` (YYYY-MM-DD first air date) when Emby has that metadata; use it for airdate questions. Use `emby_list_players` to find active playback sessions, and `emby_playback_control` to send playback commands (PlayNow, Pause, Unpause, Stop, NextTrack). Use `emby_next_episode` when asked what episode to watch next.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Functions
|
### Functions
|
||||||
@@ -285,6 +285,11 @@ Paste the following into the conversation agent's **Functions** list
|
|||||||
If you changed the add-on port, replace `8085` in every `resource_template`.
|
If you changed the add-on port, replace `8085` in every `resource_template`.
|
||||||
If **Restrict to localhost** is off, send an `Authorization` header on each request.
|
If **Restrict to localhost** is off, send an `Authorization` header on each request.
|
||||||
|
|
||||||
|
Item-shaped tool results (search, episode list, next episode, queues) include
|
||||||
|
`premiere_date` as `YYYY-MM-DD` when Emby metadata has a first air / release
|
||||||
|
date. The Functions YAML above does not declare that field — it arrives inside
|
||||||
|
`value_json.result`. Update the system prompt so the model reads it.
|
||||||
|
|
||||||
## Standalone usage (outside Home Assistant)
|
## Standalone usage (outside Home Assistant)
|
||||||
|
|
||||||
The same Go binary can run outside the add-on, e.g. on a desktop or NAS.
|
The same Go binary can run outside the add-on, e.g. on a desktop or NAS.
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ LABEL \
|
|||||||
io.hass.name="Emby MCP" \
|
io.hass.name="Emby MCP" \
|
||||||
io.hass.description="Emby Model Context Protocol server with REST bridge" \
|
io.hass.description="Emby Model Context Protocol server with REST bridge" \
|
||||||
io.hass.type="addon" \
|
io.hass.type="addon" \
|
||||||
io.hass.version="1.0.14" \
|
io.hass.version="1.0.15" \
|
||||||
org.opencontainers.image.title="emby-mcp" \
|
org.opencontainers.image.title="emby-mcp" \
|
||||||
org.opencontainers.image.source="https://git.i3omb.com/gronod/ha-gronod-addons"
|
org.opencontainers.image.source="https://git.i3omb.com/gronod/ha-gronod-addons"
|
||||||
CMD [ "/run.sh" ]
|
CMD [ "/run.sh" ]
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.0.14
|
1.0.15
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: "Emby MCP"
|
|||||||
description: >-
|
description: >-
|
||||||
Emby Model Context Protocol server with a Home Assistant REST bridge.
|
Emby Model Context Protocol server with a Home Assistant REST bridge.
|
||||||
Serves streamable MCP at /mcp and REST tool calls at /call/{tool}.
|
Serves streamable MCP at /mcp and REST tool calls at /call/{tool}.
|
||||||
version: "1.0.14"
|
version: "1.0.15"
|
||||||
slug: "emby_mcp"
|
slug: "emby_mcp"
|
||||||
init: false
|
init: false
|
||||||
startup: application
|
startup: application
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type MediaItem struct {
|
|||||||
DiskNumber any `json:"disk_number,omitempty"`
|
DiskNumber any `json:"disk_number,omitempty"`
|
||||||
TrackNumber any `json:"track_number,omitempty"`
|
TrackNumber any `json:"track_number,omitempty"`
|
||||||
ProductionYear any `json:"production_year,omitempty"`
|
ProductionYear any `json:"production_year,omitempty"`
|
||||||
|
PremiereDate string `json:"premiere_date,omitempty"`
|
||||||
Genres []string `json:"genres,omitempty"`
|
Genres []string `json:"genres,omitempty"`
|
||||||
RunTime string `json:"run_time,omitempty"`
|
RunTime string `json:"run_time,omitempty"`
|
||||||
Played bool `json:"played"`
|
Played bool `json:"played"`
|
||||||
@@ -52,7 +53,7 @@ type ItemQuery struct {
|
|||||||
EnableUserData bool // include per-user watch state
|
EnableUserData bool // include per-user watch state
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemExtraFields = "Genres,ProductionYear,ParentIndexNumber,IndexNumber,SeriesName"
|
const itemExtraFields = "Genres,ProductionYear,PremiereDate,ParentIndexNumber,IndexNumber,SeriesName"
|
||||||
|
|
||||||
// GetItems queries Audio/Video items in a library (or all libraries when
|
// GetItems queries Audio/Video items in a library (or all libraries when
|
||||||
// libraryID is empty).
|
// libraryID is empty).
|
||||||
@@ -172,9 +173,26 @@ func toMediaItem(it *BaseItemDto) MediaItem {
|
|||||||
mi.DiskNumber = intOrEmpty(it.ParentIndexNumber)
|
mi.DiskNumber = intOrEmpty(it.ParentIndexNumber)
|
||||||
mi.TrackNumber = intOrEmpty(it.IndexNumber)
|
mi.TrackNumber = intOrEmpty(it.IndexNumber)
|
||||||
mi.ProductionYear = intOrEmpty(it.ProductionYear)
|
mi.ProductionYear = intOrEmpty(it.ProductionYear)
|
||||||
|
mi.PremiereDate = formatISODate(it.PremiereDate)
|
||||||
return mi
|
return mi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// formatISODate keeps YYYY-MM-DD from an Emby date-time and drops empty values
|
||||||
|
// so premiere_date is omitted from JSON when metadata is missing.
|
||||||
|
func formatISODate(s string) string {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if i := strings.IndexByte(s, 'T'); i > 0 {
|
||||||
|
return s[:i]
|
||||||
|
}
|
||||||
|
if len(s) >= 10 && s[4] == '-' && s[7] == '-' {
|
||||||
|
return s[:10]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// ticksToHMS converts Emby ticks (100 ns) to "hh:mm:ss".
|
// ticksToHMS converts Emby ticks (100 ns) to "hh:mm:ss".
|
||||||
func ticksToHMS(ticks int64) string {
|
func ticksToHMS(ticks int64) string {
|
||||||
if ticks <= 0 {
|
if ticks <= 0 {
|
||||||
|
|||||||
@@ -75,3 +75,79 @@ func TestGetItemsQueryTranslation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToMediaItemPremiereDate(t *testing.T) {
|
||||||
|
it := &BaseItemDto{
|
||||||
|
ID: "e1", Name: "Pilot", Type: "Episode",
|
||||||
|
PremiereDate: "2025-03-31T00:00:00.0000000Z",
|
||||||
|
}
|
||||||
|
year := 2025
|
||||||
|
it.ProductionYear = &year
|
||||||
|
mi := toMediaItem(it)
|
||||||
|
if mi.PremiereDate != "2025-03-31" {
|
||||||
|
t.Fatalf("premiere_date = %q", mi.PremiereDate)
|
||||||
|
}
|
||||||
|
if mi.ProductionYear != 2025 {
|
||||||
|
t.Fatalf("production_year = %v", mi.ProductionYear)
|
||||||
|
}
|
||||||
|
empty := toMediaItem(&BaseItemDto{ID: "e2", Name: "Unknown"})
|
||||||
|
if empty.PremiereDate != "" {
|
||||||
|
t.Fatalf("empty premiere_date = %q", empty.PremiereDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetItemsRequestsPremiereDateField(t *testing.T) {
|
||||||
|
var q map[string][]string
|
||||||
|
c, srv := newTestClient(t, itemsHandler(t, nil, &q))
|
||||||
|
defer srv.Close()
|
||||||
|
if _, err := c.GetItems(context.Background(), "u1", "", ItemQuery{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fields := ""
|
||||||
|
if got := q["Fields"]; len(got) == 1 {
|
||||||
|
fields = got[0]
|
||||||
|
}
|
||||||
|
if fields == "" || !containsCSV(fields, "PremiereDate") {
|
||||||
|
t.Fatalf("Fields = %q, want PremiereDate", fields)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatISODate(t *testing.T) {
|
||||||
|
cases := map[string]string{
|
||||||
|
"": "",
|
||||||
|
"2025-03-31T00:00:00.0000000Z": "2025-03-31",
|
||||||
|
"2025-03-31": "2025-03-31",
|
||||||
|
" 2024-12-01T15:04:05+01:00 ": "2024-12-01",
|
||||||
|
}
|
||||||
|
for in, want := range cases {
|
||||||
|
if got := formatISODate(in); got != want {
|
||||||
|
t.Errorf("formatISODate(%q) = %q, want %q", in, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsCSV(csv, needle string) bool {
|
||||||
|
for _, p := range splitComma(csv) {
|
||||||
|
if p == needle {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitComma(s string) []string {
|
||||||
|
var out []string
|
||||||
|
cur := ""
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] == ',' {
|
||||||
|
out = append(out, cur)
|
||||||
|
cur = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cur += string(s[i])
|
||||||
|
}
|
||||||
|
if cur != "" || len(s) > 0 && s[len(s)-1] == ',' {
|
||||||
|
out = append(out, cur)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ type PlaylistItem struct {
|
|||||||
DiskNumber any `json:"disk_number,omitempty"`
|
DiskNumber any `json:"disk_number,omitempty"`
|
||||||
TrackNumber any `json:"track_number,omitempty"`
|
TrackNumber any `json:"track_number,omitempty"`
|
||||||
ProductionYear any `json:"production_year,omitempty"`
|
ProductionYear any `json:"production_year,omitempty"`
|
||||||
|
PremiereDate string `json:"premiere_date,omitempty"`
|
||||||
Genres []string `json:"genres,omitempty"`
|
Genres []string `json:"genres,omitempty"`
|
||||||
RunTime string `json:"run_time,omitempty"`
|
RunTime string `json:"run_time,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -129,6 +130,7 @@ func (c *Client) GetPlaylistItems(ctx context.Context, userID, playlistID string
|
|||||||
DiskNumber: mi.DiskNumber,
|
DiskNumber: mi.DiskNumber,
|
||||||
TrackNumber: mi.TrackNumber,
|
TrackNumber: mi.TrackNumber,
|
||||||
ProductionYear: mi.ProductionYear,
|
ProductionYear: mi.ProductionYear,
|
||||||
|
PremiereDate: mi.PremiereDate,
|
||||||
Genres: mi.Genres,
|
Genres: mi.Genres,
|
||||||
RunTime: mi.RunTime,
|
RunTime: mi.RunTime,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ type PlayQueueItem struct {
|
|||||||
DiskNumber any `json:"disk_number,omitempty"`
|
DiskNumber any `json:"disk_number,omitempty"`
|
||||||
TrackNumber any `json:"track_number,omitempty"`
|
TrackNumber any `json:"track_number,omitempty"`
|
||||||
ProductionYear any `json:"production_year,omitempty"`
|
ProductionYear any `json:"production_year,omitempty"`
|
||||||
|
PremiereDate string `json:"premiere_date,omitempty"`
|
||||||
Genres []string `json:"genres,omitempty"`
|
Genres []string `json:"genres,omitempty"`
|
||||||
RunTime string `json:"run_time,omitempty"`
|
RunTime string `json:"run_time,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -151,6 +152,7 @@ func (c *Client) GetPlayQueueItems(ctx context.Context, sessionID string) ([]Pla
|
|||||||
DiskNumber: mi.DiskNumber,
|
DiskNumber: mi.DiskNumber,
|
||||||
TrackNumber: mi.TrackNumber,
|
TrackNumber: mi.TrackNumber,
|
||||||
ProductionYear: mi.ProductionYear,
|
ProductionYear: mi.ProductionYear,
|
||||||
|
PremiereDate: mi.PremiereDate,
|
||||||
Genres: mi.Genres,
|
Genres: mi.Genres,
|
||||||
RunTime: mi.RunTime,
|
RunTime: mi.RunTime,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ var mediaItemRespFields = []string{
|
|||||||
"Items", "TotalRecordCount",
|
"Items", "TotalRecordCount",
|
||||||
"Items.Name", "Items.Artists", "Items.Album", "Items.AlbumId",
|
"Items.Name", "Items.Artists", "Items.Album", "Items.AlbumId",
|
||||||
"Items.AlbumArtist", "Items.ParentIndexNumber", "Items.IndexNumber",
|
"Items.AlbumArtist", "Items.ParentIndexNumber", "Items.IndexNumber",
|
||||||
"Items.ProductionYear",
|
"Items.ProductionYear", "Items.PremiereDate",
|
||||||
"Items.Genres", "Items.MediaType", "Items.RunTimeTicks",
|
"Items.Genres", "Items.MediaType", "Items.RunTimeTicks",
|
||||||
"Items.Id", "Items.Type",
|
"Items.Id", "Items.Type",
|
||||||
"Items.SeriesName", "Items.LocationType",
|
"Items.SeriesName", "Items.LocationType",
|
||||||
@@ -191,7 +191,7 @@ var endpointUsages = []endpointUsage{
|
|||||||
{tool: "stop_sharing_playlist", callSite: "playlists.go:243", method: "post", specPath: "/Items/{Id}/MakePrivate", path: []string{"Id"}},
|
{tool: "stop_sharing_playlist", callSite: "playlists.go:243", method: "post", specPath: "/Items/{Id}/MakePrivate", path: []string{"Id"}},
|
||||||
{tool: "share_playlist_user_access", callSite: "playlists.go:253", method: "post", specPath: "/Items/Access", bodyFields: []string{"ItemIds", "UserIds", "ItemAccess"}, enumUse: map[string][]string{"UserItemShareLevel": {"None", "Read", "Write", "Manage", "ManageDelete"}}},
|
{tool: "share_playlist_user_access", callSite: "playlists.go:253", method: "post", specPath: "/Items/Access", bodyFields: []string{"ItemIds", "UserIds", "ItemAccess"}, enumUse: map[string][]string{"UserItemShareLevel": {"None", "Read", "Write", "Manage", "ManageDelete"}}},
|
||||||
{tool: "retrieve_player_list", callSite: "sessions.go:39", method: "get", specPath: "/Sessions", query: []string{"ControllableByUserId"}, respFields: []string{"Client", "Id", "DeviceId", "DeviceName", "RemoteEndPoint", "PlayableMediaTypes", "NowPlayingItem.Name", "NowPlayingItem.Artists", "NowPlayingItem.Album", "NowPlayingItem.IndexNumber", "NowPlayingItem.ParentIndexNumber", "NowPlayingItem.Id", "NowPlayingItem.RunTimeTicks", "PlayState.PositionTicks", "PlayState.IsPaused"}},
|
{tool: "retrieve_player_list", callSite: "sessions.go:39", method: "get", specPath: "/Sessions", query: []string{"ControllableByUserId"}, respFields: []string{"Client", "Id", "DeviceId", "DeviceName", "RemoteEndPoint", "PlayableMediaTypes", "NowPlayingItem.Name", "NowPlayingItem.Artists", "NowPlayingItem.Album", "NowPlayingItem.IndexNumber", "NowPlayingItem.ParentIndexNumber", "NowPlayingItem.Id", "NowPlayingItem.RunTimeTicks", "PlayState.PositionTicks", "PlayState.IsPaused"}},
|
||||||
{tool: "retrieve_player_queue", callSite: "sessions.go:136", method: "get", specPath: "/Sessions/PlayQueue", query: []string{"Id", "Fields"}, respFields: []string{"Items", "Items.Name", "Items.Artists", "Items.Album", "Items.AlbumId", "Items.AlbumArtist", "Items.ParentIndexNumber", "Items.IndexNumber", "Items.ProductionYear", "Items.Genres", "Items.MediaType", "Items.RunTimeTicks", "Items.Id", "Items.PlaylistItemId", "TotalRecordCount"}},
|
{tool: "retrieve_player_queue", callSite: "sessions.go:136", method: "get", specPath: "/Sessions/PlayQueue", query: []string{"Id", "Fields"}, respFields: []string{"Items", "Items.Name", "Items.Artists", "Items.Album", "Items.AlbumId", "Items.AlbumArtist", "Items.ParentIndexNumber", "Items.IndexNumber", "Items.ProductionYear", "Items.PremiereDate", "Items.Genres", "Items.MediaType", "Items.RunTimeTicks", "Items.Id", "Items.PlaylistItemId", "TotalRecordCount"}},
|
||||||
{tool: "control_media_player (PlayNow)", callSite: "sessions.go:246", method: "post", specPath: "/Sessions/{Id}/Playing", path: []string{"Id"}, query: []string{"ItemIds", "PlayCommand"}, bodyFields: []string{"PlayCommand", "ControllingUserId"}, enumUse: map[string][]string{"PlayCommand": {"PlayNow"}}},
|
{tool: "control_media_player (PlayNow)", callSite: "sessions.go:246", method: "post", specPath: "/Sessions/{Id}/Playing", path: []string{"Id"}, query: []string{"ItemIds", "PlayCommand"}, bodyFields: []string{"PlayCommand", "ControllingUserId"}, enumUse: map[string][]string{"PlayCommand": {"PlayNow"}}},
|
||||||
{tool: "control_media_player (other commands)", callSite: "sessions.go:267", method: "post", specPath: "/Sessions/{Id}/Playing/{Command}", path: []string{"Id", "Command"}, bodyFields: []string{"Command", "SeekPositionTicks", "ControllingUserId"}, enumUse: map[string][]string{"PlaystateCommand": {"Stop", "Pause", "Unpause", "NextTrack", "PreviousTrack", "Seek", "Rewind", "FastForward", "PlayPause", "SeekRelative"}}},
|
{tool: "control_media_player (other commands)", callSite: "sessions.go:267", method: "post", specPath: "/Sessions/{Id}/Playing/{Command}", path: []string{"Id", "Command"}, bodyFields: []string{"Command", "SeekPositionTicks", "ControllingUserId"}, enumUse: map[string][]string{"PlaystateCommand": {"Stop", "Pause", "Unpause", "NextTrack", "PreviousTrack", "Seek", "Rewind", "FastForward", "PlayPause", "SeekRelative"}}},
|
||||||
{tool: "retrieve_now_playing, set_subtitle, set_audio_track", callSite: "sessions.go:169", method: "get", specPath: "/Sessions", respFields: []string{"Id", "SupportedCommands", "NowPlayingItem.Id", "NowPlayingItem.Name", "NowPlayingItem.SeriesName", "NowPlayingItem.MediaType", "NowPlayingItem.MediaSources.MediaStreams.Index", "NowPlayingItem.MediaSources.MediaStreams.Type", "NowPlayingItem.MediaSources.MediaStreams.Language", "NowPlayingItem.MediaSources.MediaStreams.Codec", "NowPlayingItem.MediaSources.MediaStreams.Title", "NowPlayingItem.MediaSources.MediaStreams.DisplayTitle", "NowPlayingItem.MediaSources.MediaStreams.IsDefault", "NowPlayingItem.MediaSources.MediaStreams.IsForced", "NowPlayingItem.MediaSources.MediaStreams.IsExternal", "PlayState.PositionTicks", "PlayState.IsPaused", "PlayState.CanSeek", "PlayState.AudioStreamIndex", "PlayState.SubtitleStreamIndex"}},
|
{tool: "retrieve_now_playing, set_subtitle, set_audio_track", callSite: "sessions.go:169", method: "get", specPath: "/Sessions", respFields: []string{"Id", "SupportedCommands", "NowPlayingItem.Id", "NowPlayingItem.Name", "NowPlayingItem.SeriesName", "NowPlayingItem.MediaType", "NowPlayingItem.MediaSources.MediaStreams.Index", "NowPlayingItem.MediaSources.MediaStreams.Type", "NowPlayingItem.MediaSources.MediaStreams.Language", "NowPlayingItem.MediaSources.MediaStreams.Codec", "NowPlayingItem.MediaSources.MediaStreams.Title", "NowPlayingItem.MediaSources.MediaStreams.DisplayTitle", "NowPlayingItem.MediaSources.MediaStreams.IsDefault", "NowPlayingItem.MediaSources.MediaStreams.IsForced", "NowPlayingItem.MediaSources.MediaStreams.IsExternal", "PlayState.PositionTicks", "PlayState.IsPaused", "PlayState.CanSeek", "PlayState.AudioStreamIndex", "PlayState.SubtitleStreamIndex"}},
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ type BaseItemDto struct {
|
|||||||
ParentIndexNumber *int `json:"ParentIndexNumber"`
|
ParentIndexNumber *int `json:"ParentIndexNumber"`
|
||||||
IndexNumber *int `json:"IndexNumber"`
|
IndexNumber *int `json:"IndexNumber"`
|
||||||
ProductionYear *int `json:"ProductionYear"`
|
ProductionYear *int `json:"ProductionYear"`
|
||||||
|
PremiereDate string `json:"PremiereDate"`
|
||||||
Genres []string `json:"Genres"`
|
Genres []string `json:"Genres"`
|
||||||
MediaSources []MediaSource `json:"MediaSources"`
|
MediaSources []MediaSource `json:"MediaSources"`
|
||||||
MediaType string `json:"MediaType"`
|
MediaType string `json:"MediaType"`
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Name = "HA Emby MCP"
|
Name = "HA Emby MCP"
|
||||||
Version = "1.0.14"
|
Version = "1.0.15"
|
||||||
Purpose = `These MCP tools allow you to control an Emby media server. Using them you can retrieve
|
Purpose = `These MCP tools allow you to control an Emby media server. Using them you can retrieve
|
||||||
a list of libraries, genres, playlists, audio & video items, and player sessions.
|
a list of libraries, genres, playlists, audio & video items, and player sessions.
|
||||||
You can browse series, seasons and episodes, find the next episode to watch,
|
You can browse series, seasons and episodes, find the next episode to watch,
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ Returns:
|
|||||||
disk_number (int): the disk or series number of the item.
|
disk_number (int): the disk or series number of the item.
|
||||||
track_number (int): the track or episode number of the item.
|
track_number (int): the track or episode number of the item.
|
||||||
production_year (int): the release / broadcast year of the item.
|
production_year (int): the release / broadcast year of the item.
|
||||||
|
premiere_date (str): first air date for episodes/series, or release date for movies, as YYYY-MM-DD when metadata has it.
|
||||||
genres (list of str): the genres tagged to the item
|
genres (list of str): the genres tagged to the item
|
||||||
run_time (str): the run time / play length of the item as hh:mm:ss.
|
run_time (str): the run time / play length of the item as hh:mm:ss.
|
||||||
played (bool): whether the item has been fully played.
|
played (bool): whether the item has been fully played.
|
||||||
|
|||||||
Reference in New Issue
Block a user