ShowShark Release Notes v2026.04.23
Just some quick thoughts. I know I've said this before about ShowShark, and thought this before about countless other projects, but I don't want to bloat ShowShark with unnecessary complexity, and I feel like it is approaching maturity. It works and it works well. There's always minor polish to improve the user experience, particularly on the tvOS side of things, which is consistently the most challenging user interface to get Just Right(tm).
If I do decide to explore additional features, the only major ones I've got in mind at the moment are:
- IPTV – incorporate broadcast TV sources in ShowShark Server
- S3 – use an S3 service (such as minio) for media access
They're both solid features, but I just don't have a feel for the user demand being adequate to justify the "bloat". I'll keep pondering it.
Overview
- Profiles are now a first-class concept across the client and server. Instead of play history, playlists, and watch stats being tied to each device, they now belong to a person who can move between devices freely. The Server ships with a new Profiles tab for creating, editing, merging, and deleting profiles, and the Client lets you switch profiles from the tab bar and from the Settings tab.
- A new Default Profile for New Devices toggle in the Server Profiles tab lets you designate one profile to be automatically assigned to any new device that connects, so you don't have to create a fresh profile for every newcomer.
- The first device to connect to a newly-set-up server is now granted admin access automatically, eliminating the chicken-and-egg problem of needing an admin to promote the first admin.
- Profile avatars replace the old SF Symbol icons with a bundled set of custom artwork that is served from the server and cached on each client.
- Admin-configurable server display name lets the server admin override the machine hostname shown to clients in server lists, login responses, and watch party invites.
- tvOS focus polish extends the green-glow focus idiom introduced in the last release across the entire app: Library, Playlists, Discover, Show Detail, Movie Detail, By Feel, Preference Rounds, Profile Editor, Watch Party sheets, and more. Buttons no longer scale up or invert colors on focus; they glow with a subtle green halo instead.
- tvOS Now Playing button appears beside the profile avatar in the tab bar when background audio is playing, providing quick access to the music player or stop controls.
- Watch Party sheets on tvOS now present full-screen so text fields are readable while typing, and the Channels tab no longer shows a permanent empty Watch Parties header when no parties exist.
- Empty playlists on tvOS now show Edit, Icon, and Delete buttons so you can manage them without having to add content first.
- Several bug fixes for channel auto-advance, pre-playback subtitle selection, tvOS track pickers, album track-number layout, the visualizer style picker on macOS, and missing watch time on playback that ends naturally.
Profiles
- Profile-scoped state separates the "who is watching" from the "what device are they using". Play history, playlists, favorites, and watch stats now belong to a profile instead of the device, so switching profiles on any device instantly loads the right data. The database migrates existing device-scoped data to profile-scoped tables on first launch with the new Server build; the migration is transparent and wraps everything in a single transaction for safety
- Server Profiles tab in the sidebar lets you view, create, edit, merge, and delete profiles. Each profile row shows a consolidated three-item stats strip (movies, shows, and music, each displaying count and cumulative watch time together) along with the active session indicator and a "Default for N devices" subline
- Profile editor lets you rename a profile, pick a new avatar, designate it as the server's default for new devices, merge it into another profile, or delete it. Deleting a profile takes you through a confirmation dialog and cascades to remove its play history, playlists, and watch stats; devices using the deleted profile as default get a fresh profile auto-created on next connect
- Profile merge consolidates two profiles into one, moving all history, playlists, watch stats, and device bindings from the source into the destination. Host profile IDs on active watch parties are rewritten atomically, and conflicting music album history rows (where both profiles have history for the same album) are resolved by keeping the destination and dropping the duplicate source row
- Client profile switcher appears as avatar cards in the Client Settings tab and as a dedicated button on the tvOS tab bar. Tapping a profile card switches to that profile with a confirmation dialog; tapping the currently-active card opens the profile editor. Profile switching now immediately reloads play history, watch stats, and playlists so the Playlists and History tabs never transiently disappear after a switch
- Default Profile for New Devices is a new toggle in the server's Profile editor. When enabled on a profile, any new device that connects to the server is automatically assigned to that profile rather than getting a fresh auto-generated one. A green pill-shaped "Default" badge appears on the profile row in the Profiles tab so you can see which profile is currently the default. Disable the toggle on any profile, or delete the default profile entirely, and the legacy auto-create behavior resumes
- First device is admin — when no devices have ever connected to a freshly-installed server, the very first device to log in is granted admin privileges automatically. Subsequent devices are regular users and can be promoted from the Devices tab as before. This eliminates the chicken-and-egg problem of needing admin access to create the first admin
- Bundled profile avatars replace the old SF Symbol icons with a curated set of custom artwork. Avatars are served from the server over the existing protobuf message channel, cached in memory and on disk under the client's Caches directory, and assigned deterministically to new profiles via a stable FNV-1a hash of the profile ID (so the same profile always gets the same default avatar). The catalog now includes additional avatar options beyond the initial set
- Profile avatar picker on both Server and Client lets you choose from the bundled avatar catalog. On tvOS, the picker is a full-screen grid of large focusable avatar tiles with the green glow focus effect. On other platforms, the picker uses enlarged tiles without labels or a search bar, keeping the interface clean and browsable
- Missing watch time fix for playback that ends naturally: previously, watch_stats increments were tied to the client sending a stop request, but the server's stream loops would often tear down the session before the client got a chance. Full-track and full-video completions would silently skip stats updates, which was especially noticeable for album-style listening. EOS-time stats persistence now runs for audio, video, BDMV, and HLS streaming paths with a per-session claim guard so stop and EOS paths never double-count the same playback
- Active profile binding at login resolves each session to a valid profile via a shared helper used by both the WebSocket and HTTP poll login paths. If a device's stored default profile has been deleted, the stale reference is cleared and a fresh profile is created. Duplicate profile creation on concurrent logins for the same device is prevented by a per-device async lock
Server
- Admin-configurable server display name — a new "Server Name" field in Server Settings lets the admin override the default machine hostname that clients see in server discovery lists, login responses, and watch party invites. The override takes effect immediately for wire-protocol responses; Bonjour advertising and tailnet hostname update on the next server restart, with an in-app caption noting when a restart is needed. Clearing the field reverts to the machine hostname
Client
- Tab-contextual refresh — the toolbar refresh button and pull-to-refresh now only fetch data relevant to the active tab instead of reloading everything unconditionally. The Library tab refreshes the directory listing, Channels refreshes channels and watch parties, Playlists refreshes playlists, History refreshes play history and watch stats, Search refreshes genre lists and library stats, and Settings refreshes profile data. This is both faster and enables refreshing profile information from the Settings tab, which was previously not possible
- Play button icon distinction — play buttons that navigate to the playback view now use a different SF Symbol from the play buttons within the playback view that actually start playback, making their purpose clearer at a glance
- Watch progress indicator improvements — the pie-arc watch progress indicator now enforces a visual minimum of 15% fill when any progress exists, so episodes watched for only a few seconds still show a clearly visible marker. The indicator also scales proportionally to its adjacent text using font metrics rather than a hardcoded size, keeping it properly sized across iOS, tvOS, macOS, and visionOS
tvOS
- Standardized green focus glow across every grid, list, and chip throughout the app. The double-stacked shadow idiom (a tight inner halo plus a wider outer bloom, both in
MediaStyle.focusGlowColor) now applies to posters, playlists, library folders, discovery cards, preference round candidates, By Feel mood cards, profile avatars, genre chips, season chips, episode chips, and more. Buttons no longer scale up or invert colors on focus, which was disorienting on screens full of them - Show Detail episode list replaces the previous full-width row layout with compact wrapping episode chips inside each season section. Each chip shows the episode number, title, and watch progress indicator; chips flow horizontally and wrap onto new lines as needed. This reclaims enormous vertical space for shows with many episodes
- Now Playing button in tab bar — when background audio is playing, a purple music-note button appears beside the profile avatar in the tvOS tab bar, providing one-press access to the music player or stop controls. This replaces the previous Now Playing button which was only visible while scrolling content
- Profile editor full-screen presentation on tvOS instead of a sheet. Sheets on tvOS blur text field contents while focused because of how the system composites the vibrancy layer; switching to
.fullScreenCovereliminates that and makes the name field readable while typing. The same fix was applied to the Watch Party creation and editing sheets - Profile editor auto-save on dismiss for tvOS, matching the Settings pattern — changes commit when you back out instead of requiring a separate Save button that was unreliable to focus from the form
- Library and Playlists grid alignment — both tabs now share the same cell structure and focus glow treatment. The previous inconsistency (Library had single-layer glow, Playlists had no glow at all and used the default card style) has been resolved. A latent crash mode in the SwiftUI focus engine when custom button styles wrapped a GeometryReader label plus sibling text has been fixed along the way
- Library grid item backgrounds simplified from a heavy two-color wash to a subtle blue gradient matching the Playlists tiles, so the focus glow dominates when a cell is focused instead of competing with the background
- Discover tab — the main Discover hub cards (Movies / TV Shows / Music navigation) and the Recently Added grid now use the standard green focus glow. A previous inconsistency where items with posters and items without posters got different focus treatments has been unified
- Server list auto-focus — when the tvOS server management view appears, the most recently connected saved server is automatically focused so you can press Select to reconnect without first swiping through the shelf
- Track selection picker fix — the pre-playback audio track, subtitle track, starting chapter, and output resolution pickers on tvOS now correctly apply the selected value. Previously, choosing an option and pressing Menu would dismiss the entire playback view before the picker binding could commit, because the back-navigation handler intercepted the Menu press. The pickers now use inline button-based selection rows that apply immediately on tap
Watch Party
- Empty playlists show management buttons on tvOS — you can now edit, change the icon, or delete an empty playlist without having to add content first. The Play button is hidden when the playlist has no items (rather than disabled)
- Full-screen sheets on tvOS for Watch Party creation and editing, fixing the focus-blur issue where text fields became unreadable while being typed into. The invite share view and the edit flow both get the same green focus glow treatment as the rest of the app
- Channels tab cleanup — tvOS no longer shows a permanent empty Watch Parties header plus a Join Watch Party button when no active parties exist. The section now appears only when there are active parties, matching the behavior on iOS and macOS. The tvOS-specific join button has been removed from that location
- App Store link fix on the Join Watch Party web page that guests see when they open a shareable invite link
Bug Fixes
- Channel program auto-advance no longer interrupts the last 30 seconds of playback — a monitoring timer was prematurely triggering program transitions based on wall-clock estimates, causing a stop-restart cycle every 10 seconds during the final half-minute of each channel program (which also reset adaptive bitrate back to 2 Mbps each time). The redundant timer has been removed; the existing end-of-stream handler already transitions correctly when the program finishes naturally
- Pre-playback subtitle selection now applies to client-side text subtitles — choosing a subtitle track before starting playback correctly activates the client-side overlay for text-based subtitles (SRT/ASS/SSA). Previously, the selection was sent to the server but the client overlay was not informed, resulting in no visible subtitles despite the data being streamed and buffered
- Album track number layout on tvOS no longer wraps for multi-digit track numbers. The track number column now uses intrinsic layout sizing based on the widest track number in the album, so 100-track albums display cleanly with all numbers on a single line and right-aligned consistently across rows
- Visualizer style picker on macOS now opens at a usable height instead of collapsing to just the title bar, and has a Cancel button in the toolbar matching the iOS and visionOS behavior
- Profile card selection ring spacing in Client Settings — the selection outline sits slightly outside the avatar bounds now, so the active profile ring reads more cleanly instead of feeling cramped against the artwork
- Avatar outlines removed across Server and Client — the default faint rounded-rect stroke on profile avatars has been removed since the artwork already has its own framing