Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ Center all three lines:
| `server_url` | string | No | - | Plex server address, e.g. `http://192.168.1.100:32400`. Empty: found through your Plex account |
| `token` | string | No | - | An X-Plex-Token to use instead of **Sign in with Plex**. When set, it wins |
| `server_name` | string | No | - | Which server to use when your account has several and `server_url` is empty |
| `plex_user` | string | No | - | Only show this Plex user's streams |
| `plex_user` | string | No | - | Only show these Plex users' streams (comma-separated) |
| `plex_player` | string | No | - | Only show streams on these devices (comma-separated), e.g. `Living Room TV` |
| `show_accents` | boolean | No | `true` | Frame the year or season/episode line with yellow tiles |
| `hold_seconds` | integer | No | `60` | After playback stops, keep showing it this long before "Nothing playing" (0-600, 0 turns it off) |
| `refresh_seconds` | integer | No | `30` | How often to ask Plex what's playing (10-600) |
Expand All @@ -118,7 +119,7 @@ With no `server_url`, the plugin asks plex.tv for the servers on your account (`
- Display lines that adapt to the board's width: Note, Flagship, and any Note Array
- Long titles wrap across two lines; the rest of the layout moves to make room
- Optional Plex yellow accent tiles
- Filter to a single Plex user
- Filter to some Plex users, some devices, or both (e.g. whatever is playing on the TV the board hangs over)
- Every active stream available as `sessions`, plus a stream count
- No blank board between episodes: the last stream stays up through Plex's Up Next countdown
- Playback state color-coded: green playing, yellow paused, orange buffering
Expand Down
12 changes: 9 additions & 3 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ def _extra_lines(
return extra


def name_set(value: Any) -> set:
"""A comma-separated settings value as a set of lower-cased names; empty means no filter."""
return {name.strip().lower() for name in str(value or "").split(",") if name.strip()}


def pick_primary(sessions: List[Dict[str, Any]]) -> Dict[str, Any]:
"""The session to feature: playing beats buffering beats paused, then Plex's order."""
return min(sessions, key=lambda s: STATE_PRIORITY.index(s["state"]))
Expand Down Expand Up @@ -414,9 +419,10 @@ def fetch_data(self) -> PluginResult:
# A session with no title is one Plex has not loaded yet: nothing to show
sessions = [parse_session(item) for item in items if item.get("title")]

plex_user = (self.config.get("plex_user") or "").strip().lower()
if plex_user:
sessions = [s for s in sessions if s["user"].lower() == plex_user]
for field, setting in (("user", "plex_user"), ("player", "plex_player")):
wanted = name_set(self.config.get(setting))
if wanted:
sessions = [s for s in sessions if s[field].lower() in wanted]
sessions = self._hold_through_gaps(sessions)

primary = pick_primary(sessions) if sessions else None
Expand Down
6 changes: 4 additions & 2 deletions docs/SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Connect FiestaBoard to your Plex Media Server so your board shows what's playing

2. **Sign in** — In the plugin's settings, click **Sign in with Plex**. Plex's sign-in page opens in a new tab; sign in and approve **FiestaBoard**. The settings show you are connected once Plex confirms, usually within a few seconds.

3. **Server (usually nothing to do)** — Leave **Plex Server URL** empty and the plugin finds your server through your Plex account, trying its home-network address first. If your account can reach several servers, enter the one you want in **Plex Server Name**. If the board cannot reach the server that way, enter its address in **Plex Server URL** (for example `http://192.168.1.100:32400`). Optionally set a **Plex User** to only show that person's streams.
3. **Server (usually nothing to do)** — Leave **Plex Server URL** empty and the plugin finds your server through your Plex account, trying its home-network address first. If your account can reach several servers, enter the one you want in **Plex Server Name**. If the board cannot reach the server that way, enter its address in **Plex Server URL** (for example `http://192.168.1.100:32400`). Optionally set **Plex User** to only show some people's streams, or **Plex Player** to only show what's playing on some devices (for example the TV the board hangs over, whoever is watching).

4. **Template** — Create a page from the plugin's demo, or add the display lines to your own page:

Expand Down Expand Up @@ -76,7 +76,8 @@ This is how the plugin worked before 1.1.0, and it still does. A pasted token is
| Plex Server URL | No | - | Your server's address, including the port. Empty: found through your Plex account |
| Plex Token | No | - | An X-Plex-Token, used instead of Sign in with Plex when set |
| Plex Server Name | No | - | Which server to use when the URL is empty and your account has several |
| Plex User | No | - | Only show streams from this Plex user (case-insensitive) |
| Plex User | No | - | Only show streams from these Plex users, separated by commas (case-insensitive) |
| Plex Player | No | - | Only show streams on these devices, separated by commas (case-insensitive), e.g. `Living Room TV` |
| Plex yellow accents | No | On | Frame the year or season/episode line with two yellow tiles on each side |
| Hold Between Episodes | No | 60 seconds | After playback stops, keep showing it this long before "Nothing playing" (0-600 seconds, 0 turns it off) |
| Refresh Interval | No | 30 seconds | How often to check Plex (10-600 seconds) |
Expand Down Expand Up @@ -121,6 +122,7 @@ This is how the plugin worked before 1.1.0, and it still does. A pasted token is
### The board says "Nothing playing" while something is playing

- If **Plex User** is set, it must match the name Plex shows for that user.
- If **Plex Player** is set, it must match the device name Plex shows. Put `{{plex.player}}` on a page with Plex Player empty to see it.
- The board updates on the refresh interval, so wait up to 30 seconds.

### The board goes blank between episodes
Expand Down
11 changes: 8 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "plex",
"name": "Plex Now Playing",
"version": "1.2.0",
"version": "1.3.0",
"description": "Show what's playing on your Plex Media Server: movies, TV episodes and music, laid out to fit any board",
"author": "FiestaBoard Team",
"repository": "https://github.com/Fiestaboard/fiestaboard-plugin--plex",
Expand Down Expand Up @@ -36,7 +36,12 @@
"plex_user": {
"type": "string",
"title": "Plex User (optional)",
"description": "Only show streams from this Plex user. Leave empty to show anyone's."
"description": "Only show streams from these Plex users, separated by commas. Leave empty to show anyone's."
},
"plex_player": {
"type": "string",
"title": "Plex Player (optional)",
"description": "Only show streams on these devices, separated by commas, using the names Plex shows (e.g. Living Room TV). Leave empty to show every device."
},
"show_accents": {
"type": "boolean",
Expand Down Expand Up @@ -200,7 +205,7 @@
"example": "18"
},
"stream_count": {
"description": "Number of active streams (after the Plex User filter)",
"description": "Number of active streams (after the Plex User and Plex Player filters)",
"type": "number",
"max_length": 2,
"group": "playback",
Expand Down
38 changes: 38 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,44 @@ def test_user_filter_without_match_is_idle(self):
assert result.data["state"] == "Idle"
assert result.data["stream_count"] == 0

def test_user_filter_accepts_several_users(self):
kim = episode_item(User={"title": "Kim"})
result, _ = fetch(make_plugin(plex_user="Sam, kim"), movie_item(), track_item(), kim)
assert sorted(s["user"] for s in result.data["sessions"]) == ["Kim", "Sam"]

def test_user_filter_ignores_empty_entries(self):
result, _ = fetch(make_plugin(plex_user=" , "), movie_item())
assert result.data["stream_count"] == 1

def test_player_filter_shows_only_that_device(self):
bedroom = episode_item(User={"title": "Kim"}, Player={"title": "Bedroom", "state": "playing"})
result, _ = fetch(make_plugin(plex_player="living room tv"), bedroom, movie_item())
assert result.data["title"] == "Interstellar"
assert result.data["stream_count"] == 1

def test_player_filter_follows_whoever_uses_the_device(self):
kim = episode_item(User={"title": "Kim"})
result, _ = fetch(make_plugin(plex_player="Living Room TV"), kim)
assert result.data["user"] == "Kim"

def test_player_filter_accepts_several_devices(self):
kitchen = track_item(Player={"title": "Kitchen", "state": "playing"})
bedroom = episode_item(Player={"title": "Bedroom", "state": "playing"})
result, _ = fetch(make_plugin(plex_player="Kitchen, Living Room TV"), kitchen, bedroom, movie_item())
assert sorted(s["player"] for s in result.data["sessions"]) == ["Kitchen", "Living Room TV"]

def test_player_filter_without_match_is_idle(self):
result, _ = fetch(make_plugin(plex_player="Bedroom"), movie_item())
assert result.data["state"] == "Idle"

def test_user_and_player_filters_both_apply(self):
kim_on_tv = episode_item(User={"title": "Kim"})
alex_in_bedroom = track_item(User={"title": "Alex"}, Player={"title": "Bedroom", "state": "playing"})
result, _ = fetch(
make_plugin(plex_user="Alex", plex_player="Living Room TV"), kim_on_tv, alex_in_bedroom, movie_item()
)
assert [(s["user"], s["player"]) for s in result.data["sessions"]] == [("Alex", "Living Room TV")]

def test_show_accents_setting(self):
result, _ = fetch(make_plugin(show_accents=False), episode_item(), board=NOTE)
assert result.data["line_2"] == "S05 E14"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_sign_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def test_requires_core_with_plex_pin(self):

def test_existing_settings_keys_kept_and_optional(self):
props = MANIFEST["settings_schema"]["properties"]
for key in ("server_url", "token", "plex_user", "show_accents", "refresh_seconds"):
for key in ("server_url", "token", "plex_user", "plex_player", "show_accents", "refresh_seconds"):
assert key in props
assert "token" not in MANIFEST["settings_schema"].get("required", [])
assert "server_url" not in MANIFEST["settings_schema"].get("required", [])
Expand Down
Loading