Skip to content

feat(lyrics,art): real-time synced lyrics & multi-protocol terminal album art - #33

Merged
arkalon76 merged 2 commits into
mainfrom
claude/github-issue-23-96cb0b
Sep 11, 2026
Merged

arkalon76 merged 2 commits into
mainfrom
claude/github-issue-23-96cb0b

Conversation

@arkalon76

Copy link
Copy Markdown
Contributor

Closes #23.

Adds a synced-lyrics engine and a multi-protocol terminal album art renderer, then wires both into the TUI. No new module dependencies — both packages are standard library only.

pkg/lyrics

  • LRCLIB first: exact /api/get lookup using artist, title, album and duration, then /api/search when that misses, then NetEase as a last resort.
  • LRC parsing handles [mm:ss], [mm:ss.xx], [mm:ss.xxx], several stamps sharing one line, and the [offset:NNN] tag. ActiveIndex and Progress resolve the line for a playback offset.
  • SplitTrackTitle turns dirty ICY metadata into artist and title, stripping (Official Video), [HQ], trailing | StationName and advert slugs. It returns nothing for a bare station name, so no pointless lookup is made.
  • Caching: RAM LRU plus one JSON file per track under ~/.cache/halpradio/lyrics/, with negative caching so a station full of unmatched tracks does not hammer the providers. Every disk failure is non-fatal.

pkg/art

  • Cover lookup from the iTunes Search API, Deezer, MusicBrainz with the Cover Art Archive, and Last.fm when a key is configured. Downloads are size-capped and must decode as an image before being accepted.
  • Five renderers: Kitty graphics protocol, iTerm2 inline images, Sixel (with its own quantizer), truecolor half-blocks and Braille, selected by environment detection with config and env-var overrides.
  • The layout invariant: every renderer returns exactly the requested row count, and every returned line reports the requested width through lipgloss.Width. That holds for all five protocols and is asserted per protocol in render_test.go, so an escape-sequence transport cannot shift the surrounding Bubble Tea frame.

TUI integration

  • L opens a lyrics drawer that takes its own columns rather than overlapping the station list, so the list keeps its layout and selection. The drawer auto-scrolls around the active line, fades its neighbours and draws a progress gauge.
  • j / k scroll unsynced sheets; , / . nudge the lyric sync in 0.5s steps and the correction persists to config.yaml.
  • A opens a floating cover viewer. The drawer also carries a thumbnail, but only when the terminal is tall enough to spare the rows — the sheet gets priority.
  • Lookups run as tea.Cmds keyed on a station-and-track signature, so every play path is covered (list, globe, tuner, party sync, media keys) and a result that arrives after the track changed is dropped.
  • Artwork is rasterised in the update loop on a new cover, a resize or a surface toggle, keeping component views pure per the repo's Elm discipline.

Things worth reviewing closely

  • Radio exposes no seek position, so the lyric clock starts when the station announces a title. That is the reason the sync nudge exists; the limitation is documented rather than hidden.
  • Kitty needed q=2 on the placement. Without it the terminal replies on stdin and Bubble Tea's input reader surfaces junk keypresses. A closed surface also emits the image-delete escape, because a Kitty placement survives a text repaint.
  • Inverted sign fixed in the analog tuner. FastSweepRight subtracted its step while clamping to MaxFreq, so sweeping right lowered the frequency. I was already editing that branch to share the L key, so I corrected it there. While the tuner is live L still sweeps the dial; everywhere else it toggles the drawer.

Verification

gofmt -s -l .         clean
go vet ./...          clean
go test -race ./...   all packages pass
Package Coverage
pkg/lyrics 96.1%
pkg/art 96.0%
pkg/ui/components 86.7%

Provider code is tested against httptest servers with injectable base URLs, and image fixtures are generated in memory — no test touches the network.

Beyond the unit tests I checked both engines against the live public APIs. Fetching Daft Punk's "Get Lucky" returned a 78-line synced sheet from LRCLIB and a 600x600 cover from iTunes, and all five renderers produced correct geometry from those real bytes.

Docs

New docs/LYRICS_AND_ART.md covers the providers, the protocol detection table, the layout contract, the playback-position estimate and the privacy behaviour. README, docs/CONFIGURATION.md, docs/ARCHITECTURE.md, CLAUDE.md and AGENTS.md are updated, including the new keybindings in the which-key overlay.

🤖 Generated with Claude Code

…lbum art

Closes #23.

Adds two new zero-dependency engine packages and wires them into the TUI.

pkg/lyrics
- LRCLIB exact lookup, LRCLIB search fallback, then NetEase.
- LRC parser handling [mm:ss(.xx|.xxx)], multiple stamps per line and
  [offset:NNN]; ActiveIndex/Progress resolve the line for a playback offset.
- SplitTrackTitle turns dirty ICY titles into artist/title, returning nothing
  for station names and advert slugs so no pointless lookup is made.
- RAM LRU plus a disk cache under ~/.cache/halpradio/lyrics/, with negative
  caching so unmatched stations do not hammer the providers.

pkg/art
- Cover lookup from iTunes, Deezer, MusicBrainz/Cover Art Archive and
  optionally Last.fm, capped and validated before use.
- Terminal renderers for the Kitty graphics protocol, iTerm2 inline images,
  Sixel, truecolor half-blocks and Braille, plus env-based auto-detection.
- Every renderer returns exactly `rows` lines whose lipgloss.Width equals
  `cols`, so escape-sequence transports cannot shift the Bubble Tea layout.

TUI integration
- L opens a lyrics drawer that takes its own columns rather than overlapping
  the station list, auto-scrolling around the active line with a progress
  gauge; j/k scroll unsynced sheets and , / . nudge the sync, which persists.
- A opens a floating cover art viewer; the drawer also carries a thumbnail
  when the terminal is tall enough to spare the rows.
- Lookups run as tea.Cmds keyed on a station+track signature, so every play
  path is covered and late results for a superseded track are dropped.
- Artwork is rasterised in the update loop on a new cover, a resize or a
  surface toggle, keeping component views pure; a closed surface emits the
  protocol's image-delete escape so Kitty placements do not linger.

While in the analog tuner, L keeps sweeping the dial. That branch also had
its step sign inverted, so a fast sweep right now raises the frequency.

No new module dependencies.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🧪 Test Coverage & Motivation Report

Metric Value
Base Branch (main) 67.1%
Pull Request Coverage 70.5%
Coverage Delta 📈 +3.4% (from 67.1% to 70.5%)

🏆 Awesome work! Test coverage increased by +3.4%! Your tests make halpradio rock solid for everyone. Keep up the great standards! 🚀

📋 Detailed Package Coverage Breakdown
github.com/halpworld/halpradio/main.go:12:					main					0.0%
github.com/halpworld/halpradio/pkg/app/app.go:42:				RunPluginCLI				36.8%
github.com/halpworld/halpradio/pkg/app/app.go:209:				formatPlaybackInfo			78.1%
github.com/halpworld/halpradio/pkg/app/app.go:253:				RunCurrent				66.7%
github.com/halpworld/halpradio/pkg/app/app.go:386:				RunStatus				77.8%
github.com/halpworld/halpradio/pkg/app/app.go:441:				RunRemote				64.7%
github.com/halpworld/halpradio/pkg/app/app.go:530:				RunHelp					100.0%
github.com/halpworld/halpradio/pkg/app/app.go:535:				SetupApp				59.0%
github.com/halpworld/halpradio/pkg/app/app.go:863:				Run					0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:26:				RunParty				71.4%
github.com/halpworld/halpradio/pkg/app/cli_party.go:54:				runPartyCreate				0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:158:			runPartyJoin				55.6%
github.com/halpworld/halpradio/pkg/app/cli_party.go:250:			runPartyStatus				28.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:327:			runPartyLeave				0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:361:			runPartyReact				22.2%
github.com/halpworld/halpradio/pkg/app/cli_party.go:393:			runPartyChat				30.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:410:			executeHeadlessParty			0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:545:			setupPartyApp				42.1%
github.com/halpworld/halpradio/pkg/app/cli_play.go:32:				RunPlay					81.6%
github.com/halpworld/halpradio/pkg/app/cli_play.go:152:				resolveStation				96.6%
github.com/halpworld/halpradio/pkg/app/cli_play.go:221:				executeHeadlessPlay			72.9%
github.com/halpworld/halpradio/pkg/app/cli_play.go:309:				printPlayHelp				0.0%
github.com/halpworld/halpradio/pkg/app/cli_play.go:315:				reorderFlagsFirst			100.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:33:			RunStations				77.8%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:67:			loadStore				100.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:74:			runStationsList				87.8%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:138:			runStationsSearch			61.4%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:227:			runStationsFav				93.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:297:			runStationsAdd				73.3%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:353:			renderStationsJSON			87.5%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:382:			renderStationsPlain			90.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:406:			renderStationsTable			87.5%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:455:			printStationsHelp			0.0%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:17:				PrintThemeHelp				100.0%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:49:				RunThemeCLI				80.5%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:279:			RenderTerminalThemePreview		100.0%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:370:			truncate				80.0%
github.com/halpworld/halpradio/pkg/app/cli_volume.go:15:			RunVolume				75.4%
github.com/halpworld/halpradio/pkg/app/help.go:44:				IsHelpArg				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:54:				HasHelpFlag				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:64:				RenderHelpHeader			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:69:				RenderHelpUsage				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:78:				RenderHelpItems				90.9%
github.com/halpworld/halpradio/pkg/app/help.go:117:				formatItemLeft				93.8%
github.com/halpworld/halpradio/pkg/app/help.go:144:				RenderHelpExamples			85.0%
github.com/halpworld/halpradio/pkg/app/help.go:184:				RenderHelpFooter			80.0%
github.com/halpworld/halpradio/pkg/app/help.go:195:				PrintRootHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:268:				PrintPlayHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:305:				PrintStationsHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:349:				PrintStationsListHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:376:				PrintStationsSearchHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:403:				PrintStationsFavHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:422:				PrintStationsAddHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:449:				PrintVolumeHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:480:				PrintCurrentHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:512:				PrintStatusHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:532:				PrintRemoteHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:562:				PrintPluginHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:587:				PrintUpdateStationsHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:601:				PrintPlaybackControlHelp		100.0%
github.com/halpworld/halpradio/pkg/app/help.go:610:				PrintPartyHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:652:				RouteHelp				93.5%
github.com/halpworld/halpradio/pkg/app/help.go:741:				SuggestCommand				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:779:				levenshteinDistance			89.5%
github.com/halpworld/halpradio/pkg/app/help.go:814:				minInt					100.0%
github.com/halpworld/halpradio/pkg/art/braille.go:23:				renderBraille				100.0%
github.com/halpworld/halpradio/pkg/art/braille.go:78:				otsuThreshold				97.5%
github.com/halpworld/halpradio/pkg/art/cache.go:36:				expired					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:56:				cacheKey				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:61:				hashKey					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:73:				normalizeText				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:93:				stripGroups				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:136:				collapseSpace				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:156:				memGet					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:176:				memPut					88.2%
github.com/halpworld/halpradio/pkg/art/cache.go:206:				diskPaths				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:217:				diskGet					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:255:				diskPut					69.2%
github.com/halpworld/halpradio/pkg/art/client.go:54:				clone					100.0%
github.com/halpworld/halpradio/pkg/art/client.go:85:				NewClient				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:101:				CacheDir				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:114:				Fetch					97.1%
github.com/halpworld/halpradio/pkg/art/client.go:178:				fetchITunes				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:220:				fetchDeezer				88.2%
github.com/halpworld/halpradio/pkg/art/client.go:268:				fetchCoverArtArchive			90.9%
github.com/halpworld/halpradio/pkg/art/client.go:323:				fetchLastFM				83.3%
github.com/halpworld/halpradio/pkg/art/client.go:383:				lastFMSizeRank				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:402:				getJSON					92.9%
github.com/halpworld/halpradio/pkg/art/client.go:427:				download				81.0%
github.com/halpworld/halpradio/pkg/art/client.go:462:				httpClient				66.7%
github.com/halpworld/halpradio/pkg/art/client.go:470:				contentType				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:479:				isImageContentType			100.0%
github.com/halpworld/halpradio/pkg/art/client.go:487:				isMBID					90.9%
github.com/halpworld/halpradio/pkg/art/client.go:511:				baseOr					100.0%
github.com/halpworld/halpradio/pkg/art/client.go:520:				firstNonEmpty				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:43:				Label					100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:64:				Graphical				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:75:				DetectEnv				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:133:				Detect					100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:140:				Resolve					100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:150:				parseProtocol				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:171:				truthy					100.0%
github.com/halpworld/halpradio/pkg/art/halfblock.go:19:				renderHalfBlock				100.0%
github.com/halpworld/halpradio/pkg/art/halfblock.go:54:				writeSGRColor				100.0%
github.com/halpworld/halpradio/pkg/art/halfblock.go:67:				rgbKey					100.0%
github.com/halpworld/halpradio/pkg/art/iterm2.go:10:				renderITerm2				75.0%
github.com/halpworld/halpradio/pkg/art/kitty.go:21:				renderKitty				95.7%
github.com/halpworld/halpradio/pkg/art/render.go:20:				NewRenderer				100.0%
github.com/halpworld/halpradio/pkg/art/render.go:31:				Render					89.3%
github.com/halpworld/halpradio/pkg/art/render.go:86:				Clear					100.0%
github.com/halpworld/halpradio/pkg/art/render.go:100:				escapeLines				100.0%
github.com/halpworld/halpradio/pkg/art/render.go:112:				encodePNG				75.0%
github.com/halpworld/halpradio/pkg/art/resize.go:24:				decodeImage				87.5%
github.com/halpworld/halpradio/pkg/art/resize.go:43:				subCellGrid				85.7%
github.com/halpworld/halpradio/pkg/art/resize.go:62:				fitDimensions				93.3%
github.com/halpworld/halpradio/pkg/art/resize.go:89:				resampleBox				94.4%
github.com/halpworld/halpradio/pkg/art/resize.go:144:				prepare					100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:153:				prepareImage				100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:176:				pixelAt					100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:185:				luminance				100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:189:				maxInt					100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:196:				clampInt				100.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:30:				renderSixel				98.2%
github.com/halpworld/halpradio/pkg/art/sixel.go:119:				writeSixelRun				100.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:148:				quantizeSixel				96.1%
github.com/halpworld/halpradio/pkg/art/sixel.go:226:				medianCut				91.7%
github.com/halpworld/halpradio/pkg/art/sixel.go:282:				widestAxis				100.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:308:				sortByAxis				90.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:328:				quantKey				100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:38:			Enabled					100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:45:			Path					100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:53:			EnvEnabled				100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:64:			Init					89.5%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:96:			rotateLocked				100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:105:			Close					100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:121:			Logf					72.7%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:141:			Header					87.5%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:158:			sortedKeys				100.0%
github.com/halpworld/halpradio/pkg/debuglog/watchdog.go:40:			Watch					100.0%
github.com/halpworld/halpradio/pkg/debuglog/watchdog.go:75:			watchdogLoop				88.9%
github.com/halpworld/halpradio/pkg/debuglog/watchdog.go:105:			DumpStacks				69.2%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:53:			isPartyAction				66.7%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:63:			NewManager				40.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:182:			UpdatePlayback				100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:187:			UpdatePlaybackFull			86.2%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:294:			NotifySong				87.5%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:308:			GetPlaybackInfo				80.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:350:			SetPartyHandler				0.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:360:			SetPartyInfo				0.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:370:			SetNotificationsEnabled			100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:378:			SetNotifierRunner			100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:386:			SetDiscordClient			100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:396:			Close					88.2%
github.com/halpworld/halpradio/pkg/desktop/discord.go:52:			GetDiscordDJAsset			100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:131:			NewDiscordRPCClient			100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:141:			SetDialer				100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:148:			UpdateActivity				83.9%
github.com/halpworld/halpradio/pkg/desktop/discord.go:224:			ClearActivity				100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:229:			Close					100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:252:			closeConnLocked				80.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:261:			ensureConnectedLocked			75.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:315:			sendFrameLocked				72.7%
github.com/halpworld/halpradio/pkg/desktop/discord.go:336:			readDiscordFrame			100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:357:			dialDiscordSocket			45.5%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:20:				SanitizeString				86.5%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:109:				SplitArtistTitle			100.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:161:				GetDefaultSocketPath			50.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:176:				StartIPCServerWithPayload		80.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:204:				StartIPCServer				75.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:213:				serve					90.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:230:				handleConn				94.3%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:298:				Close					100.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:317:				SendIPCCommand				100.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:322:				SendIPCCommandWithPayload		70.4%
github.com/halpworld/halpradio/pkg/desktop/mediakeys.go:31:			NormalizeKey				100.0%
github.com/halpworld/halpradio/pkg/desktop/mediakeys.go:54:			ParseAction				86.4%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:54:				MapStatusToMPRIS			100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:68:				BuildMPRISMetadata			100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:112:			Raise					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:116:			Quit					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:128:			Next					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:135:			Previous				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:142:			Pause					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:149:			PlayPause				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:156:			Stop					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:163:			Play					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:170:			SetPosition				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:174:			OpenUri					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:179:			StartMPRISServer			0.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:269:			onRemoteVolume				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:288:			setProp					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:299:			UpdatePlaybackState			90.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:330:			Close					20.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:23:			defaultCommandRunner			0.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:40:			NewNotifier				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:49:			SetRunner				71.4%
github.com/halpworld/halpradio/pkg/desktop/notify.go:62:			GetRunner				80.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:72:			SetEnabled				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:82:			IsEnabled				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:92:			ResetDeduplication			100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:104:			FormatSongNotification			100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:124:			NotifySong				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:161:			Notify					100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:179:			sanitizeNotificationString		90.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:196:			dispatchOSNotification			90.9%
github.com/halpworld/halpradio/pkg/desktop/ownership_unix.go:11:		isOwnedByCurrentUser			66.7%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:55:				newMemCache				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:76:				get					100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:105:				putSheet				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:114:				putNegative				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:118:				put					88.2%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:151:				len					80.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:161:				clear					83.3%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:196:				newDiskSheet				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:216:				toSheet					100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:240:				diskPath				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:247:				readDisk				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:280:				writeDisk				62.5%
github.com/halpworld/halpradio/pkg/lyrics/client.go:62:				NewClient				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:74:				CacheDir				66.7%
github.com/halpworld/halpradio/pkg/lyrics/client.go:89:				Fetch					100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:167:			applyRequestMetadata			100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:184:			memCacheRef				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:193:			httpClient				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:200:			lrclibBase				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:207:			netEaseBase				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:216:			getJSON					100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:258:			hasLyrics				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:264:			isSynced				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:269:			toSheet					100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:298:			lrclibGet				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:328:			lrclibSearch				86.7%
github.com/halpworld/halpradio/pkg/lyrics/client.go:357:			pickLRCLibCandidate			100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:374:			betterLRCLibCandidate			100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:385:			absFloat				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:405:			artistName				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:429:			netEase					92.5%
github.com/halpworld/halpradio/pkg/lyrics/client.go:503:			pickNetEaseSong				96.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:550:			absDuration				66.7%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:29:				ParseLRC				100.0%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:79:				leadingTimestamps			90.5%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:116:				fractionToDuration			88.9%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:136:				plainToLines				92.9%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:35:				ActiveIndex				100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:58:				Progress				100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:85:				IsEmpty					100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:98:				PlainLines				100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:110:				clone					100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:75:				SplitTrackTitle				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:99:				cleanStreamTitle			100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:128:				stripUnprintable			100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:144:				collapseSpace				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:149:				unwrapQuotes				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:162:				splitOnDash				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:189:				splitOnBy				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:199:				finishPart				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:207:				isUnusablePart				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:225:				normalizeQuery				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:248:				normalizeKey				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:254:				cacheKey				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:37:				NewTCPPeerConn				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:44:				RemotePeerID				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:50:				SetRemotePeerID				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:56:				RemoteAddr				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:63:				Send					83.3%
github.com/halpworld/halpradio/pkg/party/mesh.go:82:				Close					87.5%
github.com/halpworld/halpradio/pkg/party/mesh.go:104:				NewWebRTCPeerConn			0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:112:				RemotePeerID				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:118:				RemoteAddr				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:122:				Send					0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:131:				Close					0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:176:				NewMeshNetwork				75.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:206:				PeerID					100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:210:				Nickname				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:216:				SetNickname				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:226:				RoomCode				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:230:				ListenPort				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:236:				SetPacketHandler			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:242:				SetPeerJoinedHandler			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:248:				SetPeerLeftHandler			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:255:				StartListening				90.9%
github.com/halpworld/halpradio/pkg/party/mesh.go:272:				acceptLoop				81.2%
github.com/halpworld/halpradio/pkg/party/mesh.go:299:				handleIncomingTCP			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:304:				readFramedPackets			88.9%
github.com/halpworld/halpradio/pkg/party/mesh.go:358:				ConnectPeer				75.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:390:				addPeer					100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:396:				removePeer				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:413:				dispatchPacket				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:424:				SendDirect				71.4%
github.com/halpworld/halpradio/pkg/party/mesh.go:451:				Broadcast				95.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:482:				PeerCount				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:489:				PeerIDs					0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:500:				StartLANDiscovery			44.3%
github.com/halpworld/halpradio/pkg/party/mesh.go:618:				Close					88.2%
github.com/halpworld/halpradio/pkg/party/mesh.go:647:				min					0.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:59:			GenerateRoomCode			87.5%
github.com/halpworld/halpradio/pkg/party/protocol.go:73:			NormalizeRoomCode			100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:80:			FormatRoomCode				75.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:89:			HashRoomCode				100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:97:			ComputeBeaconToken			100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:106:			VerifyBeaconToken			100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:119:			DeriveRoomKey				83.3%
github.com/halpworld/halpradio/pkg/party/protocol.go:211:			SanitizeString				63.9%
github.com/halpworld/halpradio/pkg/party/protocol.go:271:			EncryptPacket				68.8%
github.com/halpworld/halpradio/pkg/party/protocol.go:301:			DecryptPacket				75.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:342:			Next					100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:346:			Current					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:23:				Age					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:85:				NewPartySession				84.6%
github.com/halpworld/halpradio/pkg/party/sync.go:154:				SetHandlers				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:170:				RoomCode				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:176:				FormattedCode				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:180:				RoomName				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:186:				IsHost					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:192:				HostNickname				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:198:				DJPass					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:204:				PeerCount				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:210:				ListenPort				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:214:				PeerID					100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:218:				Nickname				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:222:				IsActive				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:229:				CanChangeStation			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:239:				ToggleDJPass				61.1%
github.com/halpworld/halpradio/pkg/party/sync.go:270:				BroadcastStationChange			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:299:				broadcastSync				80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:313:				CurrentSync				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:324:				BroadcastReaction			90.5%
github.com/halpworld/halpradio/pkg/party/sync.go:372:				ActiveReactions				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:388:				BroadcastChat				80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:427:				RecentChat				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:436:				ConnectDirect				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:442:				PeerRoster				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:463:				Tick					58.3%
github.com/halpworld/halpradio/pkg/party/sync.go:506:				heartbeatLoop				62.5%
github.com/halpworld/halpradio/pkg/party/sync.go:527:				handleIncomingPacket			47.9%
github.com/halpworld/halpradio/pkg/party/sync.go:752:				applySync				90.9%
github.com/halpworld/halpradio/pkg/party/sync.go:788:				calcHostWinnerLocked			80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:805:				handleHostElection			84.2%
github.com/halpworld/halpradio/pkg/party/sync.go:843:				handlePeerJoined			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:859:				handlePeerLeft				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:880:				makeRosterPayload			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:894:				notifyPeerChange			88.9%
github.com/halpworld/halpradio/pkg/party/sync.go:909:				flash					80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:920:				Close					80.0%
github.com/halpworld/halpradio/pkg/player/autopause_stub.go:7:			startAutoPause				100.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:25:		NewLRUCache				60.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:41:		Get					71.4%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:65:		Put					94.7%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:99:		Len					0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:106:		Clear					0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:18:		CaptureAudioSample			0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:103:		decodeMP3ToMono				0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:124:		Read					0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:134:		WriteWAVFile				88.9%
github.com/halpworld/halpradio/pkg/player/fingerprint/client.go:56:		NewClient				40.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/client.go:73:		Identify				0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/client.go:145:		queryAcoustID				75.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/fingerprint.go:25:	ComputeFingerprint			25.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/fingerprint.go:43:	generatePureGoFingerprint		83.6%
github.com/halpworld/halpradio/pkg/player/fingerprint/fingerprint.go:153:	encodeChromaprint			100.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:23:		Badge					66.7%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:39:		ShortBadge				66.7%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:51:		SimpleTitle				42.9%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:65:		FullDisplay				43.8%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:90:		ConfidenceBar				60.0%
github.com/halpworld/halpradio/pkg/player/mock.go:30:				NewMockPlayer				66.7%
github.com/halpworld/halpradio/pkg/player/mock.go:43:				Play					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:70:				Stop					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:80:				Pause					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:90:				Resume					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:100:				SetVolume				77.8%
github.com/halpworld/halpradio/pkg/player/mock.go:115:				Volume					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:125:				ToggleMute				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:133:				IsMuted					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:140:				Status					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:147:				CurrentStation				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:154:				CurrentTrack				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:161:				ActiveBackend				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:168:				Error					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:175:				SetTrack				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:192:				SetStatus				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:199:				SetTunerMode				0.0%
github.com/halpworld/halpradio/pkg/player/mock.go:209:				UpdateTunerSignal			0.0%
github.com/halpworld/halpradio/pkg/player/mock.go:218:				IsTunerActive				0.0%
github.com/halpworld/halpradio/pkg/player/mock.go:225:				TunerSignal				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:42:				Cleanup					100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:67:				newMPVControl				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:73:				connectLoop				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:98:				attach					80.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:122:			SetVolume				100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:129:			SetMute					100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:136:			setProperty				63.2%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:166:			Close					100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:194:			SetVolume				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:203:			SetMute					0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:216:			Close					0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc_unix.go:18:			newMPVIPCEndpoint			66.7%
github.com/halpworld/halpradio/pkg/player/mpvipc_unix.go:31:			dialMPVIPC				0.0%
github.com/halpworld/halpradio/pkg/player/native_oto.go:17:			playNative				18.6%
github.com/halpworld/halpradio/pkg/player/player.go:95:				NewManager				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:112:			SetOnAutoPause				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:120:			SetAutoPause				45.5%
github.com/halpworld/halpradio/pkg/player/player.go:135:			autoPauseOnDeviceLoss			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:152:			Close					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:164:			detectBackend				80.0%
github.com/halpworld/halpradio/pkg/player/player.go:184:			ActiveBackend				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:190:			Status					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:196:			CurrentStation				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:202:			CurrentTrack				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:208:			Error					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:214:			Volume					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:223:			IsMuted					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:229:			SetVolume				84.6%
github.com/halpworld/halpradio/pkg/player/player.go:268:			ToggleMute				76.2%
github.com/halpworld/halpradio/pkg/player/player.go:298:			Stop					77.8%
github.com/halpworld/halpradio/pkg/player/player.go:335:			Pause					77.3%
github.com/halpworld/halpradio/pkg/player/player.go:367:			Resume					50.0%
github.com/halpworld/halpradio/pkg/player/player.go:390:			IsValidStreamURL			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:403:			sanitizeTrackTitle			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:413:			Play					79.4%
github.com/halpworld/halpradio/pkg/player/player.go:464:			clampVolume				60.0%
github.com/halpworld/halpradio/pkg/player/player.go:477:			buildExternalArgs			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:507:			playExternal				0.0%
github.com/halpworld/halpradio/pkg/player/player.go:600:			setError				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:612:			startICYListener			83.8%
github.com/halpworld/halpradio/pkg/player/player.go:744:			ioReadFull				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:757:			SetTunerMode				94.7%
github.com/halpworld/halpradio/pkg/player/player.go:787:			UpdateTunerSignal			94.1%
github.com/halpworld/halpradio/pkg/player/player.go:815:			IsTunerActive				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:45:				NewStaticSynthesizer			66.7%
github.com/halpworld/halpradio/pkg/player/static.go:62:				SetEnabled				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:69:				IsEnabled				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:76:				SetParams				75.0%
github.com/halpworld/halpradio/pkg/player/static.go:100:			SetNearestStationCarrier		100.0%
github.com/halpworld/halpradio/pkg/player/static.go:107:			SetVolume				75.0%
github.com/halpworld/halpradio/pkg/player/static.go:120:			nextFloat				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:131:			Read					95.2%
github.com/halpworld/halpradio/pkg/player/static_oto.go:9:			startOrStopStaticPlayer			95.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:41:			NewManager				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:56:			SetNotifyHandler			100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:63:			SetFlashHandler				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:70:			SetLogHandler				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:76:			log					100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:86:			Init					75.7%
github.com/halpworld/halpradio/pkg/plugin/manager.go:159:			startPluginLocked			52.6%
github.com/halpworld/halpradio/pkg/plugin/manager.go:198:			stopPluginLocked			100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:206:			loadSavedStates				33.3%
github.com/halpworld/halpradio/pkg/plugin/manager.go:222:			saveStatesLocked			87.5%
github.com/halpworld/halpradio/pkg/plugin/manager.go:236:			GetPlugins				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:257:			GetPlugin				85.7%
github.com/halpworld/halpradio/pkg/plugin/manager.go:278:			EnablePlugin				88.9%
github.com/halpworld/halpradio/pkg/plugin/manager.go:295:			DisablePlugin				87.5%
github.com/halpworld/halpradio/pkg/plugin/manager.go:310:			ApprovePermissions			72.7%
github.com/halpworld/halpradio/pkg/plugin/manager.go:330:			RegistryClient				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:335:			InstallFromRegistry			0.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:389:			UninstallPlugin				88.9%
github.com/halpworld/halpradio/pkg/plugin/manager.go:406:			Close					83.3%
github.com/halpworld/halpradio/pkg/plugin/manager.go:420:			DispatchTrackChange			71.4%
github.com/halpworld/halpradio/pkg/plugin/manager.go:446:			DispatchPlaybackChange			71.4%
github.com/halpworld/halpradio/pkg/plugin/manager.go:471:			DispatchTimerTick			50.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:500:			ToRegistryPlugin			100.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:21:			init					100.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:52:			IsPrivateOrLoopbackIP			75.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:75:			HasStorage				75.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:85:			HasEvent				83.3%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:98:			IsIPAllowed				0.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:142:			CanAccessNetwork			82.4%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:260:			Validate				88.9%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:295:			LoadManifest				75.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:304:			ParseManifest				70.0%
github.com/halpworld/halpradio/pkg/plugin/registry.go:29:			NewRegistryClient			100.0%
github.com/halpworld/halpradio/pkg/plugin/registry.go:42:			FetchRegistry				76.5%
github.com/halpworld/halpradio/pkg/plugin/registry.go:73:			DownloadAndInstall			67.3%
github.com/halpworld/halpradio/pkg/plugin/registry.go:174:			fallbackRegistry			100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:42:			sanitizeString				100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:58:			newSandboxedHTTPTransport		10.5%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:105:			NewSandbox				47.6%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:168:			log					0.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:174:			registerHostFunctions			7.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:401:			Start					87.5%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:435:			Stop					0.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:447:			Close					100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:460:			UpdateState				100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:467:			InvokeHook				100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:473:			invokeHookLocked			14.7%
github.com/halpworld/halpradio/pkg/plugin/storage.go:20:			ValidateStorageKey			88.9%
github.com/halpworld/halpradio/pkg/plugin/storage.go:45:			NewStorage				88.9%
github.com/halpworld/halpradio/pkg/plugin/storage.go:66:			load					87.5%
github.com/halpworld/halpradio/pkg/plugin/storage.go:79:			save					71.4%
github.com/halpworld/halpradio/pkg/plugin/storage.go:93:			Get					100.0%
github.com/halpworld/halpradio/pkg/plugin/storage.go:104:			Set					83.3%
github.com/halpworld/halpradio/pkg/plugin/storage.go:136:			Delete					83.3%
github.com/halpworld/halpradio/pkg/plugin/storage.go:147:			GetAll					100.0%
github.com/halpworld/halpradio/pkg/plugin/storage.go:158:			Dir					100.0%
github.com/halpworld/halpradio/pkg/plugin/verifier.go:12:			CalculateSHA256				100.0%
github.com/halpworld/halpradio/pkg/plugin/verifier.go:18:			VerifyChecksum				85.7%
github.com/halpworld/halpradio/pkg/plugin/verifier.go:33:			VerifySignature				70.6%
github.com/halpworld/halpradio/pkg/radio/activity.go:54:			GetActivityByID				0.0%
github.com/halpworld/halpradio/pkg/radio/activity.go:65:			MatchesActivity				57.1%
github.com/halpworld/halpradio/pkg/radio/broadcasters.go:17:			normalizeStationName			100.0%
github.com/halpworld/halpradio/pkg/radio/broadcasters.go:106:			LookupVerifiedBroadcaster		15.5%
github.com/halpworld/halpradio/pkg/radio/broadcasters.go:254:			EnrichStation				93.3%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:43:			NewCatalogUpdater			60.0%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:62:			LoadMetadata				100.0%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:72:			SaveMetadata				80.0%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:82:			ShouldCheck				83.3%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:95:			CheckAndUpdate				72.0%
github.com/halpworld/halpradio/pkg/radio/geo.go:166:				GeocodeStation				78.3%
github.com/halpworld/halpradio/pkg/radio/geo.go:212:				HaversineDistance			100.0%
github.com/halpworld/halpradio/pkg/radio/geo.go:229:				BuildStationClusters			90.5%
github.com/halpworld/halpradio/pkg/radio/geo.go:275:				FindNearestCluster			91.7%
github.com/halpworld/halpradio/pkg/radio/geo.go:301:				ExtractOrAssignFrequency		96.3%
github.com/halpworld/halpradio/pkg/radio/geo.go:352:				LookupNearestLocation			81.5%
github.com/halpworld/halpradio/pkg/radio/health.go:12:				CheckStreamHealth			80.0%
github.com/halpworld/halpradio/pkg/radio/history.go:30:				ParseArtistAndTitle			100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:69:				FullDisplay				46.2%
github.com/halpworld/halpradio/pkg/radio/history.go:89:				FormatBookmarkLine			100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:95:				AddIdentifiedHistory			0.0%
github.com/halpworld/halpradio/pkg/radio/history.go:146:			AddHistory				100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:184:			GetHistory				100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:194:			ClearHistory				100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:201:			SaveTrackBookmark			88.9%
github.com/halpworld/halpradio/pkg/radio/history.go:217:			GetBookmarkedTracks			76.9%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:48:			NewRadioBrowserClient			100.0%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:70:			ParseSearchQuery			96.4%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:160:			SearchSmart				70.0%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:187:			SearchByCountry				66.7%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:200:			SearchByLocation			66.7%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:214:			SearchWithParams			81.2%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:355:			Search					100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:54:			SanitizeTrackTitle			86.7%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:225:			IsDirtyOrGeneric			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:230:			stripAdSegments				91.7%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:250:			isPureSlogan				100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:269:			isStationSelfReference			90.9%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:290:			isPureNoiseOrURL			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:304:			cleanCompositeNoise			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:319:			stripPunctuation			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:341:			ValidateCandidateWithMusicBrainz	0.0%
github.com/halpworld/halpradio/pkg/radio/somafm.go:27:				NewSomaFMClient				0.0%
github.com/halpworld/halpradio/pkg/radio/somafm.go:36:				FetchChannels				0.0%
github.com/halpworld/halpradio/pkg/radio/station.go:67:				CountryNameToCode			23.1%
github.com/halpworld/halpradio/pkg/radio/station.go:144:			CountryFlagForCode			100.0%
github.com/halpworld/halpradio/pkg/radio/station.go:157:			CountryFlag				100.0%
github.com/halpworld/halpradio/pkg/radio/station.go:161:			CountryName				50.0%
github.com/halpworld/halpradio/pkg/radio/station.go:172:			LocationString				50.0%
github.com/halpworld/halpradio/pkg/radio/station.go:185:			IsTerrestrial				100.0%
github.com/halpworld/halpradio/pkg/radio/station.go:195:			BroadcastType				46.7%
github.com/halpworld/halpradio/pkg/radio/station.go:220:			BroadcastBadge				41.7%
github.com/halpworld/halpradio/pkg/radio/station.go:244:			ShortBroadcastBadge			57.1%
github.com/halpworld/halpradio/pkg/radio/station.go:261:			DisplayLocationAndBand			80.0%
github.com/halpworld/halpradio/pkg/radio/station.go:271:			ToYAMLSnippet				88.9%
github.com/halpworld/halpradio/pkg/radio/store.go:25:				NewStore				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:37:				Load					60.0%
github.com/halpworld/halpradio/pkg/radio/store.go:88:				ReloadBundledFromCache			85.7%
github.com/halpworld/halpradio/pkg/radio/store.go:112:				LoadSomaFM				0.0%
github.com/halpworld/halpradio/pkg/radio/store.go:125:				syncFavoritesLocked			63.6%
github.com/halpworld/halpradio/pkg/radio/store.go:143:				SyncFavorites				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:149:				SaveFavorites				83.3%
github.com/halpworld/halpradio/pkg/radio/store.go:159:				SaveLocalStations			85.7%
github.com/halpworld/halpradio/pkg/radio/store.go:170:				AddOrUpdateLocalStation			100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:192:				DeleteLocalStation			100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:203:				ToggleFavorite				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:220:				GetAllStations				68.4%
github.com/halpworld/halpradio/pkg/radio/store.go:252:				FindStationByID				0.0%
github.com/halpworld/halpradio/pkg/radio/store.go:265:				GetFavorites				83.3%
github.com/halpworld/halpradio/pkg/radio/store.go:276:				GetCategories				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:304:				GetCountries				94.7%
github.com/halpworld/halpradio/pkg/radio/store.go:342:				GetCities				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:366:				Filter					100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:370:				FilterWithActivity			100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:374:				FilterWithLocation			89.2%
github.com/halpworld/halpradio/pkg/theme/registry.go:49:			ToTheme					100.0%
github.com/halpworld/halpradio/pkg/theme/registry.go:86:			NewRegistryClient			100.0%
github.com/halpworld/halpradio/pkg/theme/registry.go:100:			FetchRegistry				62.5%
github.com/halpworld/halpradio/pkg/theme/registry.go:145:			sanitizeRegistryThemes			94.1%
github.com/halpworld/halpradio/pkg/theme/registry.go:168:			DownloadAndInstall			46.2%
github.com/halpworld/halpradio/pkg/theme/registry.go:235:			Uninstall				81.8%
github.com/halpworld/halpradio/pkg/theme/registry.go:255:			fallbackRegistry			100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:175:				copyBuiltins				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:184:				ResetCustomThemes			100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:192:				RegisterTheme				82.4%
github.com/halpworld/halpradio/pkg/theme/theme.go:221:				UnregisterTheme				88.9%
github.com/halpworld/halpradio/pkg/theme/theme.go:238:				GetTheme				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:270:				GetAllThemes				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:294:				GetThemeIDs				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:312:				LoadCustomThemes			80.5%
github.com/halpworld/halpradio/pkg/theme/theme.go:385:				EnsureExampleTheme			83.3%
github.com/halpworld/halpradio/pkg/theme/theme.go:399:				ExportActiveTheme			63.6%
github.com/halpworld/halpradio/pkg/theme/theme.go:421:				ExportTheme				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:427:				FormatThemeYAML				90.9%
github.com/halpworld/halpradio/pkg/theme/theme.go:483:				SampleThemeYAML				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:513:				normalizeID				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:519:				prettifyName				0.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:531:				isValidColor				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:558:				sanitizeColor				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:566:				sanitizeString				87.5%
github.com/halpworld/halpradio/pkg/theme/theme.go:581:				sanitizeThemeTokens			90.3%
github.com/halpworld/halpradio/pkg/timer/notify.go:24:				SetBellWriterForTesting			100.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:39:				SetNotificationRunnerForTesting		100.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:54:				SetHookRunnerForTesting			0.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:68:				DispatchEvent				64.3%
github.com/halpworld/halpradio/pkg/timer/notify.go:95:				RingTerminalBell			100.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:105:				defaultDesktopNotification		0.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:131:				defaultCommandHookRunner		90.9%
github.com/halpworld/halpradio/pkg/timer/notify.go:161:				runCommandHook				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:79:				DefaultPomodoroConfig			100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:105:				DefaultSleepConfig			100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:130:				NewTimer				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:142:				StartSleep				75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:179:				StartPomodoro				78.9%
github.com/halpworld/halpradio/pkg/timer/timer.go:218:				Pause					75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:232:				Resume					75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:246:				TogglePause				80.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:255:				Stop					100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:274:				ResetCurrentInterval			27.3%
github.com/halpworld/halpradio/pkg/timer/timer.go:293:				AddMinutes				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:309:				SkipPhase				75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:318:				advancePomodoroPhase			90.6%
github.com/halpworld/halpradio/pkg/timer/timer.go:438:				Tick					95.5%
github.com/halpworld/halpradio/pkg/timer/timer.go:489:				BadgeText				82.4%
github.com/halpworld/halpradio/pkg/timer/timer.go:526:				WindowTitleBadge			91.7%
github.com/halpworld/halpradio/pkg/timer/timer.go:551:				FormattedTime				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:556:				Progress				72.7%
github.com/halpworld/halpradio/pkg/timer/timer.go:575:				PhaseDescription			100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:595:				IsActive				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:599:				timerTypeName				50.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:611:				FormatDuration				100.0%
github.com/halpworld/halpradio/pkg/ui/components/art.go:26:			RenderAlbumArtModal			97.8%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:25:		NewBrailleCanvas			75.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:48:		SetDot					100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:88:		MarkHotspot				90.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:105:		RenderGlobeView				92.8%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:415:		latDirection				100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:422:		lonDirection				100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/mapdata.go:24:		init					100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/mapdata.go:28:		initWorldBitmap				57.1%
github.com/halpworld/halpradio/pkg/ui/components/globe/mapdata.go:52:		IsLand					63.6%
github.com/halpworld/halpradio/pkg/ui/components/header.go:12:			RenderHeader				91.7%
github.com/halpworld/halpradio/pkg/ui/components/history.go:13:			RenderHistoryList			90.6%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:43:			RenderLyricsDrawer			90.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:99:			renderLyricsBody			88.3%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:223:			lyricGauge				66.7%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:243:			lyricsFooter				100.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:277:			signedSeconds				75.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:287:			wrapSheet				75.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:313:			firstRowFor				62.5%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:329:			wrapPlain				87.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:367:			splitAtWidth				71.4%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:383:			centerLine				80.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:392:			absInt					100.0%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:15:			RenderPRExportModal			81.2%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:72:			RenderThemePickerModal			79.6%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:385:			renderThemePreviewCard			75.0%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:487:			RenderAddStationModal			91.2%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:586:			RenderTimerModal			92.6%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:866:			RenderPluginManagerModal		77.5%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:1066:		RenderPermissionApprovalModal		84.2%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:1129:		RenderPartyManagerModal			0.0%
github.com/halpworld/halpradio/pkg/ui/components/partybar.go:17:		RenderPartyBar				81.7%
github.com/halpworld/halpradio/pkg/ui/components/playerbar.go:14:		RenderPlayerBar				88.5%
github.com/halpworld/halpradio/pkg/ui/components/sidebar.go:11:			RenderSidebar				81.5%
github.com/halpworld/halpradio/pkg/ui/components/stationlist.go:12:		RenderStationList			87.3%
github.com/halpworld/halpradio/pkg/ui/components/stationlist.go:221:		padRight				85.7%
github.com/halpworld/halpradio/pkg/ui/components/stationlist.go:233:		truncate				66.7%
github.com/halpworld/halpradio/pkg/ui/components/statusbar.go:10:		RenderStatusBar				85.0%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:55:		NextBand				100.0%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:67:		CalculateSignalRSSI			94.1%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:97:		FindNearestStation			100.0%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:116:		FormatSMeter				59.1%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:145:		RenderAnalogTunerView			85.9%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:502:		generateStaticPattern			100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:38:		normalizeMode				88.9%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:59:		NewVisualizer				100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:66:		Tick					100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:95:		CycleMode				71.4%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:107:		Render					100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:131:		renderStopped				81.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:177:		getAnimalPoses				100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:217:		renderAnimalDJ				96.9%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:327:		ensureBins				83.3%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:338:		updateBarPhysics			100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:391:		renderBars				88.6%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:445:		renderWave				88.9%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:503:		renderSpectrum				95.2%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:541:		renderMinimal				93.3%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:566:		clamp					66.7%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:573:		clampFloat				100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:583:		clampRange				60.0%
github.com/halpworld/halpradio/pkg/ui/components/whichkey.go:11:		RenderWhichKeyOverlay			84.0%
github.com/halpworld/halpradio/pkg/ui/components/whichkey.go:192:		PlaceOverlay				100.0%
github.com/halpworld/halpradio/pkg/ui/keymap.go:62:				DefaultKeyMap				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:258:				NewModel				95.5%
github.com/halpworld/halpradio/pkg/ui/model.go:354:				SwitchTab				87.5%
github.com/halpworld/halpradio/pkg/ui/model.go:378:				RefreshStations				90.6%
github.com/halpworld/halpradio/pkg/ui/model.go:439:				WindowTitle				81.5%
github.com/halpworld/halpradio/pkg/ui/model.go:483:				Init					100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:494:				checkCatalogUpdateCmd			16.7%
github.com/halpworld/halpradio/pkg/ui/model.go:509:				tickCmd					50.0%
github.com/halpworld/halpradio/pkg/ui/model.go:516:				SetDesktop				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:521:				SetPluginManager			100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:526:				SetMsgSender				0.0%
github.com/halpworld/halpradio/pkg/ui/model.go:531:				SetPartySession				0.0%
github.com/halpworld/halpradio/pkg/ui/model.go:539:				SetupPartyHandlers			42.9%
github.com/halpworld/halpradio/pkg/ui/model.go:573:				openPartyModal				90.9%
github.com/halpworld/halpradio/pkg/ui/model.go:590:				SyncDesktop				76.9%
github.com/halpworld/halpradio/pkg/ui/model.go:658:				PlayNextStation				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:679:				PlayPrevStation				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:700:				TogglePlayPause				55.6%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:39:				nowPlayingTrack				50.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:50:				nowPlayingParts				54.5%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:71:				lyricElapsed				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:87:				nowPlayingSignature			77.8%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:103:			coverSourceLabel			66.7%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:112:			resetNowPlaying				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:129:			syncNowPlaying				25.9%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:172:			fetchLyricsCmd				0.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:198:			fetchCoverCmd				0.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:226:			LyricsDrawerWidth			87.5%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:242:			artTarget				82.4%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:304:			drawerArtBudget				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:314:			cellRowsFor				75.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:323:			cellColsFor				75.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:334:			renderArt				73.3%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:361:			dropArt					100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:371:			ArtClearSequence			100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:380:			toggleLyricsDrawer			95.2%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:413:			toggleArtModal				76.9%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:434:			scrollLyrics				70.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:453:			nudgeLyricsSync				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:463:			formatOffset				100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:33:				describeUpdateMsg			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:48:				Update					49.1%
github.com/halpworld/halpradio/pkg/ui/update.go:1757:				openTimerModal				100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1775:				applyTheme				100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1784:				getFilteredRegistryThemes		100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1800:				fetchThemeRegistryCmd			10.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1824:				installThemeCmd				10.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1842:				deleteThemeCmd				0.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1854:				handleThemePickerKey			48.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2143:				handleTimerEvent			44.4%
github.com/halpworld/halpradio/pkg/ui/update.go:2188:				tuneToStationByID			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2198:				handleTimerModalKey			31.8%
github.com/halpworld/halpradio/pkg/ui/update.go:2406:				startPomodoroFromMenu			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2413:				startSleepFromMenu			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2427:				handleAddModalKey			66.7%
github.com/halpworld/halpradio/pkg/ui/update.go:2494:				searchRadioBrowserCmd			0.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2501:				handlePermissionApprovalKey		75.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2521:				handlePluginModalKey			25.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2636:				fetchRegistryCmd			11.1%
github.com/halpworld/halpradio/pkg/ui/update.go:2651:				installPluginCmd			0.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2663:				cycleGlobeStation			81.8%
github.com/halpworld/halpradio/pkg/ui/update.go:2682:				seekTunerStation			84.8%
github.com/halpworld/halpradio/pkg/ui/update.go:2729:				onTunerFreqChanged			84.6%
github.com/halpworld/halpradio/pkg/ui/update.go:2773:				identifyTrackCmd			56.2%
github.com/halpworld/halpradio/pkg/ui/update.go:2802:				handlePartyChatKey			61.5%
github.com/halpworld/halpradio/pkg/ui/update.go:2826:				handlePartyModalKey			34.3%
github.com/halpworld/halpradio/pkg/ui/view.go:14:				View					80.0%
github.com/halpworld/halpradio/pkg/util/browser.go:20:				defaultURLOpener			0.0%
github.com/halpworld/halpradio/pkg/util/browser.go:37:				SetURLOpenerForTesting			100.0%
github.com/halpworld/halpradio/pkg/util/browser.go:51:				IsValidHTTPURL				100.0%
github.com/halpworld/halpradio/pkg/util/browser.go:64:				BuildSearchURL				100.0%
github.com/halpworld/halpradio/pkg/util/browser.go:86:				OpenURL					87.5%
github.com/halpworld/halpradio/pkg/util/clipboard.go:19:			SetClipboardWriterForTesting		100.0%
github.com/halpworld/halpradio/pkg/util/clipboard.go:33:			CopyToClipboard				83.3%
github.com/halpworld/halpradio/pkg/util/config.go:54:				DefaultConfig				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:100:				GetConfigDir				42.9%
github.com/halpworld/halpradio/pkg/util/config.go:113:				EnsureConfigDir				75.0%
github.com/halpworld/halpradio/pkg/util/config.go:121:				GetThemesDir				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:125:				GetLocalStationsFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:129:				GetCatalogCacheFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:133:				GetCatalogMetadataFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:137:				GetFavoritesFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:141:				GetSavedTracksFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:147:				GetDebugLogFile				0.0%
github.com/halpworld/halpradio/pkg/util/config.go:151:				GetConfigFile				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:158:				GetCacheDir				0.0%
github.com/halpworld/halpradio/pkg/util/config.go:171:				GetLyricsCacheDir			0.0%
github.com/halpworld/halpradio/pkg/util/config.go:176:				GetAlbumArtCacheDir			0.0%
github.com/halpworld/halpradio/pkg/util/config.go:182:				EnsureCacheDir				0.0%
github.com/halpworld/halpradio/pkg/util/config.go:189:				GetPluginsDir				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:193:				GetPluginsDataDir			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:197:				GetPluginsConfigFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:202:				LoadConfig				90.9%
github.com/halpworld/halpradio/pkg/util/config.go:242:				SaveConfig				66.7%
total:										(statements)				70.5%

Below 80 columns the lyrics drawer declined to open, because the station
list will not render under 28 columns beside an 18 column sidebar. The only
feedback was one dim status-bar line, so pressing L on a narrow terminal
looked like a dead key.

The sheet now has two surfaces. At 80 columns or wider it stays a drawer
beside the station list; below that it takes over the content area as a
full-width overlay. Resizing moves an open sheet between the two instead of
making it vanish, and the width refusal is gone entirely.

The status bar also carries [L] Lyrics and [A] Art on every station tab.
Previously the keys appeared only in the which-key overlay, so nothing in
the default view hinted the feature existed. The legend is clipped to the
terminal width, so its order is now a priority list and the two new entries
sit near the front; y/Yank and a/Add move to the widest tier only.

Verified against the real binary in a pty at 70 columns: L opens the
overlay and the legend shows both keys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🧪 Test Coverage & Motivation Report

Metric Value
Base Branch (main) 67.0%
Pull Request Coverage 70.5%
Coverage Delta 📈 +3.5% (from 67.0% to 70.5%)

🏆 Awesome work! Test coverage increased by +3.5%! Your tests make halpradio rock solid for everyone. Keep up the great standards! 🚀

📋 Detailed Package Coverage Breakdown
github.com/halpworld/halpradio/main.go:12:					main					0.0%
github.com/halpworld/halpradio/pkg/app/app.go:42:				RunPluginCLI				36.8%
github.com/halpworld/halpradio/pkg/app/app.go:209:				formatPlaybackInfo			78.1%
github.com/halpworld/halpradio/pkg/app/app.go:253:				RunCurrent				66.7%
github.com/halpworld/halpradio/pkg/app/app.go:386:				RunStatus				77.8%
github.com/halpworld/halpradio/pkg/app/app.go:441:				RunRemote				64.7%
github.com/halpworld/halpradio/pkg/app/app.go:530:				RunHelp					100.0%
github.com/halpworld/halpradio/pkg/app/app.go:535:				SetupApp				59.0%
github.com/halpworld/halpradio/pkg/app/app.go:863:				Run					0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:26:				RunParty				71.4%
github.com/halpworld/halpradio/pkg/app/cli_party.go:54:				runPartyCreate				0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:158:			runPartyJoin				55.6%
github.com/halpworld/halpradio/pkg/app/cli_party.go:250:			runPartyStatus				28.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:327:			runPartyLeave				0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:361:			runPartyReact				22.2%
github.com/halpworld/halpradio/pkg/app/cli_party.go:393:			runPartyChat				30.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:410:			executeHeadlessParty			0.0%
github.com/halpworld/halpradio/pkg/app/cli_party.go:545:			setupPartyApp				42.1%
github.com/halpworld/halpradio/pkg/app/cli_play.go:32:				RunPlay					81.6%
github.com/halpworld/halpradio/pkg/app/cli_play.go:152:				resolveStation				96.6%
github.com/halpworld/halpradio/pkg/app/cli_play.go:221:				executeHeadlessPlay			72.9%
github.com/halpworld/halpradio/pkg/app/cli_play.go:309:				printPlayHelp				0.0%
github.com/halpworld/halpradio/pkg/app/cli_play.go:315:				reorderFlagsFirst			100.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:33:			RunStations				77.8%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:67:			loadStore				100.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:74:			runStationsList				87.8%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:138:			runStationsSearch			61.4%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:227:			runStationsFav				93.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:297:			runStationsAdd				73.3%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:353:			renderStationsJSON			87.5%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:382:			renderStationsPlain			90.0%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:406:			renderStationsTable			87.5%
github.com/halpworld/halpradio/pkg/app/cli_stations.go:455:			printStationsHelp			0.0%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:17:				PrintThemeHelp				100.0%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:49:				RunThemeCLI				80.5%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:279:			RenderTerminalThemePreview		100.0%
github.com/halpworld/halpradio/pkg/app/cli_theme.go:370:			truncate				80.0%
github.com/halpworld/halpradio/pkg/app/cli_volume.go:15:			RunVolume				75.4%
github.com/halpworld/halpradio/pkg/app/help.go:44:				IsHelpArg				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:54:				HasHelpFlag				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:64:				RenderHelpHeader			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:69:				RenderHelpUsage				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:78:				RenderHelpItems				90.9%
github.com/halpworld/halpradio/pkg/app/help.go:117:				formatItemLeft				93.8%
github.com/halpworld/halpradio/pkg/app/help.go:144:				RenderHelpExamples			85.0%
github.com/halpworld/halpradio/pkg/app/help.go:184:				RenderHelpFooter			80.0%
github.com/halpworld/halpradio/pkg/app/help.go:195:				PrintRootHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:268:				PrintPlayHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:305:				PrintStationsHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:349:				PrintStationsListHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:376:				PrintStationsSearchHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:403:				PrintStationsFavHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:422:				PrintStationsAddHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:449:				PrintVolumeHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:480:				PrintCurrentHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:512:				PrintStatusHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:532:				PrintRemoteHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:562:				PrintPluginHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:587:				PrintUpdateStationsHelp			100.0%
github.com/halpworld/halpradio/pkg/app/help.go:601:				PrintPlaybackControlHelp		100.0%
github.com/halpworld/halpradio/pkg/app/help.go:610:				PrintPartyHelp				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:652:				RouteHelp				93.5%
github.com/halpworld/halpradio/pkg/app/help.go:741:				SuggestCommand				100.0%
github.com/halpworld/halpradio/pkg/app/help.go:779:				levenshteinDistance			89.5%
github.com/halpworld/halpradio/pkg/app/help.go:814:				minInt					100.0%
github.com/halpworld/halpradio/pkg/art/braille.go:23:				renderBraille				100.0%
github.com/halpworld/halpradio/pkg/art/braille.go:78:				otsuThreshold				97.5%
github.com/halpworld/halpradio/pkg/art/cache.go:36:				expired					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:56:				cacheKey				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:61:				hashKey					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:73:				normalizeText				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:93:				stripGroups				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:136:				collapseSpace				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:156:				memGet					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:176:				memPut					88.2%
github.com/halpworld/halpradio/pkg/art/cache.go:206:				diskPaths				100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:217:				diskGet					100.0%
github.com/halpworld/halpradio/pkg/art/cache.go:255:				diskPut					69.2%
github.com/halpworld/halpradio/pkg/art/client.go:54:				clone					100.0%
github.com/halpworld/halpradio/pkg/art/client.go:85:				NewClient				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:101:				CacheDir				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:114:				Fetch					97.1%
github.com/halpworld/halpradio/pkg/art/client.go:178:				fetchITunes				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:220:				fetchDeezer				88.2%
github.com/halpworld/halpradio/pkg/art/client.go:268:				fetchCoverArtArchive			90.9%
github.com/halpworld/halpradio/pkg/art/client.go:323:				fetchLastFM				83.3%
github.com/halpworld/halpradio/pkg/art/client.go:383:				lastFMSizeRank				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:402:				getJSON					92.9%
github.com/halpworld/halpradio/pkg/art/client.go:427:				download				81.0%
github.com/halpworld/halpradio/pkg/art/client.go:462:				httpClient				66.7%
github.com/halpworld/halpradio/pkg/art/client.go:470:				contentType				100.0%
github.com/halpworld/halpradio/pkg/art/client.go:479:				isImageContentType			100.0%
github.com/halpworld/halpradio/pkg/art/client.go:487:				isMBID					90.9%
github.com/halpworld/halpradio/pkg/art/client.go:511:				baseOr					100.0%
github.com/halpworld/halpradio/pkg/art/client.go:520:				firstNonEmpty				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:43:				Label					100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:64:				Graphical				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:75:				DetectEnv				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:133:				Detect					100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:140:				Resolve					100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:150:				parseProtocol				100.0%
github.com/halpworld/halpradio/pkg/art/detect.go:171:				truthy					100.0%
github.com/halpworld/halpradio/pkg/art/halfblock.go:19:				renderHalfBlock				100.0%
github.com/halpworld/halpradio/pkg/art/halfblock.go:54:				writeSGRColor				100.0%
github.com/halpworld/halpradio/pkg/art/halfblock.go:67:				rgbKey					100.0%
github.com/halpworld/halpradio/pkg/art/iterm2.go:10:				renderITerm2				75.0%
github.com/halpworld/halpradio/pkg/art/kitty.go:21:				renderKitty				95.7%
github.com/halpworld/halpradio/pkg/art/render.go:20:				NewRenderer				100.0%
github.com/halpworld/halpradio/pkg/art/render.go:31:				Render					89.3%
github.com/halpworld/halpradio/pkg/art/render.go:86:				Clear					100.0%
github.com/halpworld/halpradio/pkg/art/render.go:100:				escapeLines				100.0%
github.com/halpworld/halpradio/pkg/art/render.go:112:				encodePNG				75.0%
github.com/halpworld/halpradio/pkg/art/resize.go:24:				decodeImage				87.5%
github.com/halpworld/halpradio/pkg/art/resize.go:43:				subCellGrid				85.7%
github.com/halpworld/halpradio/pkg/art/resize.go:62:				fitDimensions				93.3%
github.com/halpworld/halpradio/pkg/art/resize.go:89:				resampleBox				94.4%
github.com/halpworld/halpradio/pkg/art/resize.go:144:				prepare					100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:153:				prepareImage				100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:176:				pixelAt					100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:185:				luminance				100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:189:				maxInt					100.0%
github.com/halpworld/halpradio/pkg/art/resize.go:196:				clampInt				100.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:30:				renderSixel				98.2%
github.com/halpworld/halpradio/pkg/art/sixel.go:119:				writeSixelRun				100.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:148:				quantizeSixel				96.1%
github.com/halpworld/halpradio/pkg/art/sixel.go:226:				medianCut				91.7%
github.com/halpworld/halpradio/pkg/art/sixel.go:282:				widestAxis				100.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:308:				sortByAxis				90.0%
github.com/halpworld/halpradio/pkg/art/sixel.go:328:				quantKey				100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:38:			Enabled					100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:45:			Path					100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:53:			EnvEnabled				100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:64:			Init					89.5%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:96:			rotateLocked				100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:105:			Close					100.0%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:121:			Logf					72.7%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:141:			Header					87.5%
github.com/halpworld/halpradio/pkg/debuglog/debuglog.go:158:			sortedKeys				100.0%
github.com/halpworld/halpradio/pkg/debuglog/watchdog.go:40:			Watch					100.0%
github.com/halpworld/halpradio/pkg/debuglog/watchdog.go:75:			watchdogLoop				88.9%
github.com/halpworld/halpradio/pkg/debuglog/watchdog.go:105:			DumpStacks				69.2%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:53:			isPartyAction				66.7%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:63:			NewManager				40.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:182:			UpdatePlayback				100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:187:			UpdatePlaybackFull			86.2%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:294:			NotifySong				87.5%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:308:			GetPlaybackInfo				80.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:350:			SetPartyHandler				0.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:360:			SetPartyInfo				0.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:370:			SetNotificationsEnabled			100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:378:			SetNotifierRunner			100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:386:			SetDiscordClient			100.0%
github.com/halpworld/halpradio/pkg/desktop/desktop.go:396:			Close					88.2%
github.com/halpworld/halpradio/pkg/desktop/discord.go:52:			GetDiscordDJAsset			100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:131:			NewDiscordRPCClient			100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:141:			SetDialer				100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:148:			UpdateActivity				83.9%
github.com/halpworld/halpradio/pkg/desktop/discord.go:224:			ClearActivity				100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:229:			Close					100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:252:			closeConnLocked				80.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:261:			ensureConnectedLocked			75.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:315:			sendFrameLocked				72.7%
github.com/halpworld/halpradio/pkg/desktop/discord.go:336:			readDiscordFrame			100.0%
github.com/halpworld/halpradio/pkg/desktop/discord.go:357:			dialDiscordSocket			45.5%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:20:				SanitizeString				86.5%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:109:				SplitArtistTitle			100.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:161:				GetDefaultSocketPath			50.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:176:				StartIPCServerWithPayload		80.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:204:				StartIPCServer				75.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:213:				serve					90.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:230:				handleConn				94.3%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:298:				Close					100.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:317:				SendIPCCommand				100.0%
github.com/halpworld/halpradio/pkg/desktop/ipc.go:322:				SendIPCCommandWithPayload		70.4%
github.com/halpworld/halpradio/pkg/desktop/mediakeys.go:31:			NormalizeKey				100.0%
github.com/halpworld/halpradio/pkg/desktop/mediakeys.go:54:			ParseAction				86.4%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:54:				MapStatusToMPRIS			100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:68:				BuildMPRISMetadata			100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:112:			Raise					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:116:			Quit					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:128:			Next					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:135:			Previous				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:142:			Pause					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:149:			PlayPause				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:156:			Stop					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:163:			Play					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:170:			SetPosition				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:174:			OpenUri					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:179:			StartMPRISServer			0.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:269:			onRemoteVolume				100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:288:			setProp					100.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:299:			UpdatePlaybackState			90.0%
github.com/halpworld/halpradio/pkg/desktop/mpris.go:330:			Close					20.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:23:			defaultCommandRunner			0.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:40:			NewNotifier				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:49:			SetRunner				71.4%
github.com/halpworld/halpradio/pkg/desktop/notify.go:62:			GetRunner				80.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:72:			SetEnabled				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:82:			IsEnabled				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:92:			ResetDeduplication			100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:104:			FormatSongNotification			100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:124:			NotifySong				100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:161:			Notify					100.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:179:			sanitizeNotificationString		90.0%
github.com/halpworld/halpradio/pkg/desktop/notify.go:196:			dispatchOSNotification			90.9%
github.com/halpworld/halpradio/pkg/desktop/ownership_unix.go:11:		isOwnedByCurrentUser			66.7%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:55:				newMemCache				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:76:				get					100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:105:				putSheet				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:114:				putNegative				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:118:				put					88.2%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:151:				len					80.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:161:				clear					83.3%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:196:				newDiskSheet				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:216:				toSheet					100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:240:				diskPath				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:247:				readDisk				100.0%
github.com/halpworld/halpradio/pkg/lyrics/cache.go:280:				writeDisk				62.5%
github.com/halpworld/halpradio/pkg/lyrics/client.go:62:				NewClient				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:74:				CacheDir				66.7%
github.com/halpworld/halpradio/pkg/lyrics/client.go:89:				Fetch					100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:167:			applyRequestMetadata			100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:184:			memCacheRef				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:193:			httpClient				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:200:			lrclibBase				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:207:			netEaseBase				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:216:			getJSON					100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:258:			hasLyrics				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:264:			isSynced				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:269:			toSheet					100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:298:			lrclibGet				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:328:			lrclibSearch				86.7%
github.com/halpworld/halpradio/pkg/lyrics/client.go:357:			pickLRCLibCandidate			100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:374:			betterLRCLibCandidate			100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:385:			absFloat				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:405:			artistName				100.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:429:			netEase					92.5%
github.com/halpworld/halpradio/pkg/lyrics/client.go:503:			pickNetEaseSong				96.0%
github.com/halpworld/halpradio/pkg/lyrics/client.go:550:			absDuration				66.7%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:29:				ParseLRC				100.0%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:79:				leadingTimestamps			90.5%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:116:				fractionToDuration			88.9%
github.com/halpworld/halpradio/pkg/lyrics/lrc.go:136:				plainToLines				92.9%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:35:				ActiveIndex				100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:58:				Progress				100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:85:				IsEmpty					100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:98:				PlainLines				100.0%
github.com/halpworld/halpradio/pkg/lyrics/sheet.go:110:				clone					100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:75:				SplitTrackTitle				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:99:				cleanStreamTitle			100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:128:				stripUnprintable			100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:144:				collapseSpace				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:149:				unwrapQuotes				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:162:				splitOnDash				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:189:				splitOnBy				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:199:				finishPart				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:207:				isUnusablePart				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:225:				normalizeQuery				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:248:				normalizeKey				100.0%
github.com/halpworld/halpradio/pkg/lyrics/title.go:254:				cacheKey				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:37:				NewTCPPeerConn				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:44:				RemotePeerID				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:50:				SetRemotePeerID				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:56:				RemoteAddr				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:63:				Send					83.3%
github.com/halpworld/halpradio/pkg/party/mesh.go:82:				Close					87.5%
github.com/halpworld/halpradio/pkg/party/mesh.go:104:				NewWebRTCPeerConn			0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:112:				RemotePeerID				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:118:				RemoteAddr				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:122:				Send					0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:131:				Close					0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:176:				NewMeshNetwork				75.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:206:				PeerID					100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:210:				Nickname				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:216:				SetNickname				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:226:				RoomCode				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:230:				ListenPort				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:236:				SetPacketHandler			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:242:				SetPeerJoinedHandler			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:248:				SetPeerLeftHandler			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:255:				StartListening				90.9%
github.com/halpworld/halpradio/pkg/party/mesh.go:272:				acceptLoop				81.2%
github.com/halpworld/halpradio/pkg/party/mesh.go:299:				handleIncomingTCP			100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:304:				readFramedPackets			88.9%
github.com/halpworld/halpradio/pkg/party/mesh.go:358:				ConnectPeer				75.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:390:				addPeer					100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:396:				removePeer				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:413:				dispatchPacket				100.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:424:				SendDirect				71.4%
github.com/halpworld/halpradio/pkg/party/mesh.go:451:				Broadcast				95.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:482:				PeerCount				0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:489:				PeerIDs					0.0%
github.com/halpworld/halpradio/pkg/party/mesh.go:500:				StartLANDiscovery			44.3%
github.com/halpworld/halpradio/pkg/party/mesh.go:618:				Close					88.2%
github.com/halpworld/halpradio/pkg/party/mesh.go:647:				min					0.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:59:			GenerateRoomCode			87.5%
github.com/halpworld/halpradio/pkg/party/protocol.go:73:			NormalizeRoomCode			100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:80:			FormatRoomCode				75.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:89:			HashRoomCode				100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:97:			ComputeBeaconToken			100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:106:			VerifyBeaconToken			100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:119:			DeriveRoomKey				83.3%
github.com/halpworld/halpradio/pkg/party/protocol.go:211:			SanitizeString				63.9%
github.com/halpworld/halpradio/pkg/party/protocol.go:271:			EncryptPacket				68.8%
github.com/halpworld/halpradio/pkg/party/protocol.go:301:			DecryptPacket				75.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:342:			Next					100.0%
github.com/halpworld/halpradio/pkg/party/protocol.go:346:			Current					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:23:				Age					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:85:				NewPartySession				84.6%
github.com/halpworld/halpradio/pkg/party/sync.go:154:				SetHandlers				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:170:				RoomCode				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:176:				FormattedCode				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:180:				RoomName				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:186:				IsHost					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:192:				HostNickname				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:198:				DJPass					0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:204:				PeerCount				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:210:				ListenPort				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:214:				PeerID					100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:218:				Nickname				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:222:				IsActive				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:229:				CanChangeStation			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:239:				ToggleDJPass				61.1%
github.com/halpworld/halpradio/pkg/party/sync.go:270:				BroadcastStationChange			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:299:				broadcastSync				80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:313:				CurrentSync				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:324:				BroadcastReaction			90.5%
github.com/halpworld/halpradio/pkg/party/sync.go:372:				ActiveReactions				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:388:				BroadcastChat				80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:427:				RecentChat				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:436:				ConnectDirect				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:442:				PeerRoster				0.0%
github.com/halpworld/halpradio/pkg/party/sync.go:463:				Tick					58.3%
github.com/halpworld/halpradio/pkg/party/sync.go:506:				heartbeatLoop				62.5%
github.com/halpworld/halpradio/pkg/party/sync.go:527:				handleIncomingPacket			47.9%
github.com/halpworld/halpradio/pkg/party/sync.go:752:				applySync				90.9%
github.com/halpworld/halpradio/pkg/party/sync.go:788:				calcHostWinnerLocked			80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:805:				handleHostElection			84.2%
github.com/halpworld/halpradio/pkg/party/sync.go:843:				handlePeerJoined			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:859:				handlePeerLeft				100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:880:				makeRosterPayload			100.0%
github.com/halpworld/halpradio/pkg/party/sync.go:894:				notifyPeerChange			88.9%
github.com/halpworld/halpradio/pkg/party/sync.go:909:				flash					80.0%
github.com/halpworld/halpradio/pkg/party/sync.go:920:				Close					80.0%
github.com/halpworld/halpradio/pkg/player/autopause_stub.go:7:			startAutoPause				100.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:25:		NewLRUCache				60.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:41:		Get					71.4%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:65:		Put					94.7%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:99:		Len					0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/cache.go:106:		Clear					0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:18:		CaptureAudioSample			0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:103:		decodeMP3ToMono				0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:124:		Read					0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/capture.go:134:		WriteWAVFile				88.9%
github.com/halpworld/halpradio/pkg/player/fingerprint/client.go:56:		NewClient				40.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/client.go:73:		Identify				0.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/client.go:145:		queryAcoustID				75.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/fingerprint.go:25:	ComputeFingerprint			25.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/fingerprint.go:43:	generatePureGoFingerprint		83.6%
github.com/halpworld/halpradio/pkg/player/fingerprint/fingerprint.go:153:	encodeChromaprint			100.0%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:23:		Badge					66.7%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:39:		ShortBadge				66.7%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:51:		SimpleTitle				42.9%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:65:		FullDisplay				43.8%
github.com/halpworld/halpradio/pkg/player/fingerprint/result.go:90:		ConfidenceBar				60.0%
github.com/halpworld/halpradio/pkg/player/mock.go:30:				NewMockPlayer				66.7%
github.com/halpworld/halpradio/pkg/player/mock.go:43:				Play					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:70:				Stop					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:80:				Pause					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:90:				Resume					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:100:				SetVolume				77.8%
github.com/halpworld/halpradio/pkg/player/mock.go:115:				Volume					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:125:				ToggleMute				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:133:				IsMuted					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:140:				Status					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:147:				CurrentStation				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:154:				CurrentTrack				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:161:				ActiveBackend				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:168:				Error					100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:175:				SetTrack				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:192:				SetStatus				100.0%
github.com/halpworld/halpradio/pkg/player/mock.go:199:				SetTunerMode				0.0%
github.com/halpworld/halpradio/pkg/player/mock.go:209:				UpdateTunerSignal			0.0%
github.com/halpworld/halpradio/pkg/player/mock.go:218:				IsTunerActive				0.0%
github.com/halpworld/halpradio/pkg/player/mock.go:225:				TunerSignal				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:42:				Cleanup					100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:67:				newMPVControl				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:73:				connectLoop				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:98:				attach					80.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:122:			SetVolume				100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:129:			SetMute					100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:136:			setProperty				63.2%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:166:			Close					100.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:194:			SetVolume				0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:203:			SetMute					0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc.go:216:			Close					0.0%
github.com/halpworld/halpradio/pkg/player/mpvipc_unix.go:18:			newMPVIPCEndpoint			66.7%
github.com/halpworld/halpradio/pkg/player/mpvipc_unix.go:31:			dialMPVIPC				0.0%
github.com/halpworld/halpradio/pkg/player/native_oto.go:17:			playNative				18.6%
github.com/halpworld/halpradio/pkg/player/player.go:95:				NewManager				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:112:			SetOnAutoPause				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:120:			SetAutoPause				45.5%
github.com/halpworld/halpradio/pkg/player/player.go:135:			autoPauseOnDeviceLoss			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:152:			Close					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:164:			detectBackend				80.0%
github.com/halpworld/halpradio/pkg/player/player.go:184:			ActiveBackend				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:190:			Status					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:196:			CurrentStation				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:202:			CurrentTrack				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:208:			Error					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:214:			Volume					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:223:			IsMuted					100.0%
github.com/halpworld/halpradio/pkg/player/player.go:229:			SetVolume				84.6%
github.com/halpworld/halpradio/pkg/player/player.go:268:			ToggleMute				76.2%
github.com/halpworld/halpradio/pkg/player/player.go:298:			Stop					77.8%
github.com/halpworld/halpradio/pkg/player/player.go:335:			Pause					77.3%
github.com/halpworld/halpradio/pkg/player/player.go:367:			Resume					50.0%
github.com/halpworld/halpradio/pkg/player/player.go:390:			IsValidStreamURL			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:403:			sanitizeTrackTitle			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:413:			Play					79.4%
github.com/halpworld/halpradio/pkg/player/player.go:464:			clampVolume				60.0%
github.com/halpworld/halpradio/pkg/player/player.go:477:			buildExternalArgs			100.0%
github.com/halpworld/halpradio/pkg/player/player.go:507:			playExternal				0.0%
github.com/halpworld/halpradio/pkg/player/player.go:600:			setError				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:612:			startICYListener			83.8%
github.com/halpworld/halpradio/pkg/player/player.go:744:			ioReadFull				100.0%
github.com/halpworld/halpradio/pkg/player/player.go:757:			SetTunerMode				94.7%
github.com/halpworld/halpradio/pkg/player/player.go:787:			UpdateTunerSignal			94.1%
github.com/halpworld/halpradio/pkg/player/player.go:815:			IsTunerActive				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:45:				NewStaticSynthesizer			66.7%
github.com/halpworld/halpradio/pkg/player/static.go:62:				SetEnabled				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:69:				IsEnabled				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:76:				SetParams				75.0%
github.com/halpworld/halpradio/pkg/player/static.go:100:			SetNearestStationCarrier		100.0%
github.com/halpworld/halpradio/pkg/player/static.go:107:			SetVolume				75.0%
github.com/halpworld/halpradio/pkg/player/static.go:120:			nextFloat				100.0%
github.com/halpworld/halpradio/pkg/player/static.go:131:			Read					95.2%
github.com/halpworld/halpradio/pkg/player/static_oto.go:9:			startOrStopStaticPlayer			95.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:41:			NewManager				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:56:			SetNotifyHandler			100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:63:			SetFlashHandler				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:70:			SetLogHandler				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:76:			log					100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:86:			Init					75.7%
github.com/halpworld/halpradio/pkg/plugin/manager.go:159:			startPluginLocked			52.6%
github.com/halpworld/halpradio/pkg/plugin/manager.go:198:			stopPluginLocked			100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:206:			loadSavedStates				33.3%
github.com/halpworld/halpradio/pkg/plugin/manager.go:222:			saveStatesLocked			87.5%
github.com/halpworld/halpradio/pkg/plugin/manager.go:236:			GetPlugins				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:257:			GetPlugin				85.7%
github.com/halpworld/halpradio/pkg/plugin/manager.go:278:			EnablePlugin				88.9%
github.com/halpworld/halpradio/pkg/plugin/manager.go:295:			DisablePlugin				87.5%
github.com/halpworld/halpradio/pkg/plugin/manager.go:310:			ApprovePermissions			72.7%
github.com/halpworld/halpradio/pkg/plugin/manager.go:330:			RegistryClient				100.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:335:			InstallFromRegistry			0.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:389:			UninstallPlugin				88.9%
github.com/halpworld/halpradio/pkg/plugin/manager.go:406:			Close					83.3%
github.com/halpworld/halpradio/pkg/plugin/manager.go:420:			DispatchTrackChange			71.4%
github.com/halpworld/halpradio/pkg/plugin/manager.go:446:			DispatchPlaybackChange			71.4%
github.com/halpworld/halpradio/pkg/plugin/manager.go:471:			DispatchTimerTick			50.0%
github.com/halpworld/halpradio/pkg/plugin/manager.go:500:			ToRegistryPlugin			100.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:21:			init					100.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:52:			IsPrivateOrLoopbackIP			75.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:75:			HasStorage				75.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:85:			HasEvent				83.3%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:98:			IsIPAllowed				0.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:142:			CanAccessNetwork			82.4%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:260:			Validate				88.9%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:295:			LoadManifest				75.0%
github.com/halpworld/halpradio/pkg/plugin/manifest.go:304:			ParseManifest				70.0%
github.com/halpworld/halpradio/pkg/plugin/registry.go:29:			NewRegistryClient			100.0%
github.com/halpworld/halpradio/pkg/plugin/registry.go:42:			FetchRegistry				76.5%
github.com/halpworld/halpradio/pkg/plugin/registry.go:73:			DownloadAndInstall			67.3%
github.com/halpworld/halpradio/pkg/plugin/registry.go:174:			fallbackRegistry			100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:42:			sanitizeString				100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:58:			newSandboxedHTTPTransport		10.5%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:105:			NewSandbox				47.6%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:168:			log					0.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:174:			registerHostFunctions			7.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:401:			Start					87.5%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:435:			Stop					0.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:447:			Close					100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:460:			UpdateState				100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:467:			InvokeHook				100.0%
github.com/halpworld/halpradio/pkg/plugin/sandbox.go:473:			invokeHookLocked			14.7%
github.com/halpworld/halpradio/pkg/plugin/storage.go:20:			ValidateStorageKey			88.9%
github.com/halpworld/halpradio/pkg/plugin/storage.go:45:			NewStorage				88.9%
github.com/halpworld/halpradio/pkg/plugin/storage.go:66:			load					87.5%
github.com/halpworld/halpradio/pkg/plugin/storage.go:79:			save					71.4%
github.com/halpworld/halpradio/pkg/plugin/storage.go:93:			Get					100.0%
github.com/halpworld/halpradio/pkg/plugin/storage.go:104:			Set					83.3%
github.com/halpworld/halpradio/pkg/plugin/storage.go:136:			Delete					83.3%
github.com/halpworld/halpradio/pkg/plugin/storage.go:147:			GetAll					100.0%
github.com/halpworld/halpradio/pkg/plugin/storage.go:158:			Dir					100.0%
github.com/halpworld/halpradio/pkg/plugin/verifier.go:12:			CalculateSHA256				100.0%
github.com/halpworld/halpradio/pkg/plugin/verifier.go:18:			VerifyChecksum				85.7%
github.com/halpworld/halpradio/pkg/plugin/verifier.go:33:			VerifySignature				70.6%
github.com/halpworld/halpradio/pkg/radio/activity.go:54:			GetActivityByID				0.0%
github.com/halpworld/halpradio/pkg/radio/activity.go:65:			MatchesActivity				57.1%
github.com/halpworld/halpradio/pkg/radio/broadcasters.go:17:			normalizeStationName			100.0%
github.com/halpworld/halpradio/pkg/radio/broadcasters.go:106:			LookupVerifiedBroadcaster		15.5%
github.com/halpworld/halpradio/pkg/radio/broadcasters.go:254:			EnrichStation				93.3%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:43:			NewCatalogUpdater			60.0%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:62:			LoadMetadata				100.0%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:72:			SaveMetadata				80.0%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:82:			ShouldCheck				83.3%
github.com/halpworld/halpradio/pkg/radio/catalog_updater.go:95:			CheckAndUpdate				72.0%
github.com/halpworld/halpradio/pkg/radio/geo.go:166:				GeocodeStation				78.3%
github.com/halpworld/halpradio/pkg/radio/geo.go:212:				HaversineDistance			100.0%
github.com/halpworld/halpradio/pkg/radio/geo.go:229:				BuildStationClusters			90.5%
github.com/halpworld/halpradio/pkg/radio/geo.go:275:				FindNearestCluster			91.7%
github.com/halpworld/halpradio/pkg/radio/geo.go:301:				ExtractOrAssignFrequency		96.3%
github.com/halpworld/halpradio/pkg/radio/geo.go:352:				LookupNearestLocation			81.5%
github.com/halpworld/halpradio/pkg/radio/health.go:12:				CheckStreamHealth			80.0%
github.com/halpworld/halpradio/pkg/radio/history.go:30:				ParseArtistAndTitle			100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:69:				FullDisplay				46.2%
github.com/halpworld/halpradio/pkg/radio/history.go:89:				FormatBookmarkLine			100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:95:				AddIdentifiedHistory			0.0%
github.com/halpworld/halpradio/pkg/radio/history.go:146:			AddHistory				100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:184:			GetHistory				100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:194:			ClearHistory				100.0%
github.com/halpworld/halpradio/pkg/radio/history.go:201:			SaveTrackBookmark			88.9%
github.com/halpworld/halpradio/pkg/radio/history.go:217:			GetBookmarkedTracks			76.9%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:48:			NewRadioBrowserClient			100.0%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:70:			ParseSearchQuery			96.4%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:160:			SearchSmart				70.0%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:187:			SearchByCountry				66.7%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:200:			SearchByLocation			66.7%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:214:			SearchWithParams			81.2%
github.com/halpworld/halpradio/pkg/radio/radiobrowser.go:355:			Search					100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:54:			SanitizeTrackTitle			86.7%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:225:			IsDirtyOrGeneric			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:230:			stripAdSegments				91.7%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:250:			isPureSlogan				100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:269:			isStationSelfReference			90.9%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:290:			isPureNoiseOrURL			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:304:			cleanCompositeNoise			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:319:			stripPunctuation			100.0%
github.com/halpworld/halpradio/pkg/radio/sanitizer.go:341:			ValidateCandidateWithMusicBrainz	0.0%
github.com/halpworld/halpradio/pkg/radio/somafm.go:27:				NewSomaFMClient				0.0%
github.com/halpworld/halpradio/pkg/radio/somafm.go:36:				FetchChannels				0.0%
github.com/halpworld/halpradio/pkg/radio/station.go:67:				CountryNameToCode			23.1%
github.com/halpworld/halpradio/pkg/radio/station.go:144:			CountryFlagForCode			100.0%
github.com/halpworld/halpradio/pkg/radio/station.go:157:			CountryFlag				100.0%
github.com/halpworld/halpradio/pkg/radio/station.go:161:			CountryName				50.0%
github.com/halpworld/halpradio/pkg/radio/station.go:172:			LocationString				50.0%
github.com/halpworld/halpradio/pkg/radio/station.go:185:			IsTerrestrial				100.0%
github.com/halpworld/halpradio/pkg/radio/station.go:195:			BroadcastType				46.7%
github.com/halpworld/halpradio/pkg/radio/station.go:220:			BroadcastBadge				41.7%
github.com/halpworld/halpradio/pkg/radio/station.go:244:			ShortBroadcastBadge			57.1%
github.com/halpworld/halpradio/pkg/radio/station.go:261:			DisplayLocationAndBand			80.0%
github.com/halpworld/halpradio/pkg/radio/station.go:271:			ToYAMLSnippet				88.9%
github.com/halpworld/halpradio/pkg/radio/store.go:25:				NewStore				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:37:				Load					60.0%
github.com/halpworld/halpradio/pkg/radio/store.go:88:				ReloadBundledFromCache			85.7%
github.com/halpworld/halpradio/pkg/radio/store.go:112:				LoadSomaFM				0.0%
github.com/halpworld/halpradio/pkg/radio/store.go:125:				syncFavoritesLocked			63.6%
github.com/halpworld/halpradio/pkg/radio/store.go:143:				SyncFavorites				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:149:				SaveFavorites				83.3%
github.com/halpworld/halpradio/pkg/radio/store.go:159:				SaveLocalStations			85.7%
github.com/halpworld/halpradio/pkg/radio/store.go:170:				AddOrUpdateLocalStation			100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:192:				DeleteLocalStation			100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:203:				ToggleFavorite				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:220:				GetAllStations				68.4%
github.com/halpworld/halpradio/pkg/radio/store.go:252:				FindStationByID				0.0%
github.com/halpworld/halpradio/pkg/radio/store.go:265:				GetFavorites				83.3%
github.com/halpworld/halpradio/pkg/radio/store.go:276:				GetCategories				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:304:				GetCountries				94.7%
github.com/halpworld/halpradio/pkg/radio/store.go:342:				GetCities				100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:366:				Filter					100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:370:				FilterWithActivity			100.0%
github.com/halpworld/halpradio/pkg/radio/store.go:374:				FilterWithLocation			89.2%
github.com/halpworld/halpradio/pkg/theme/registry.go:49:			ToTheme					100.0%
github.com/halpworld/halpradio/pkg/theme/registry.go:86:			NewRegistryClient			100.0%
github.com/halpworld/halpradio/pkg/theme/registry.go:100:			FetchRegistry				62.5%
github.com/halpworld/halpradio/pkg/theme/registry.go:145:			sanitizeRegistryThemes			94.1%
github.com/halpworld/halpradio/pkg/theme/registry.go:168:			DownloadAndInstall			46.2%
github.com/halpworld/halpradio/pkg/theme/registry.go:235:			Uninstall				81.8%
github.com/halpworld/halpradio/pkg/theme/registry.go:255:			fallbackRegistry			100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:175:				copyBuiltins				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:184:				ResetCustomThemes			100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:192:				RegisterTheme				82.4%
github.com/halpworld/halpradio/pkg/theme/theme.go:221:				UnregisterTheme				88.9%
github.com/halpworld/halpradio/pkg/theme/theme.go:238:				GetTheme				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:270:				GetAllThemes				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:294:				GetThemeIDs				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:312:				LoadCustomThemes			80.5%
github.com/halpworld/halpradio/pkg/theme/theme.go:385:				EnsureExampleTheme			83.3%
github.com/halpworld/halpradio/pkg/theme/theme.go:399:				ExportActiveTheme			63.6%
github.com/halpworld/halpradio/pkg/theme/theme.go:421:				ExportTheme				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:427:				FormatThemeYAML				90.9%
github.com/halpworld/halpradio/pkg/theme/theme.go:483:				SampleThemeYAML				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:513:				normalizeID				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:519:				prettifyName				0.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:531:				isValidColor				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:558:				sanitizeColor				100.0%
github.com/halpworld/halpradio/pkg/theme/theme.go:566:				sanitizeString				87.5%
github.com/halpworld/halpradio/pkg/theme/theme.go:581:				sanitizeThemeTokens			90.3%
github.com/halpworld/halpradio/pkg/timer/notify.go:24:				SetBellWriterForTesting			100.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:39:				SetNotificationRunnerForTesting		100.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:54:				SetHookRunnerForTesting			0.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:68:				DispatchEvent				64.3%
github.com/halpworld/halpradio/pkg/timer/notify.go:95:				RingTerminalBell			100.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:105:				defaultDesktopNotification		0.0%
github.com/halpworld/halpradio/pkg/timer/notify.go:131:				defaultCommandHookRunner		90.9%
github.com/halpworld/halpradio/pkg/timer/notify.go:161:				runCommandHook				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:79:				DefaultPomodoroConfig			100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:105:				DefaultSleepConfig			100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:130:				NewTimer				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:142:				StartSleep				75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:179:				StartPomodoro				78.9%
github.com/halpworld/halpradio/pkg/timer/timer.go:218:				Pause					75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:232:				Resume					75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:246:				TogglePause				80.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:255:				Stop					100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:274:				ResetCurrentInterval			27.3%
github.com/halpworld/halpradio/pkg/timer/timer.go:293:				AddMinutes				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:309:				SkipPhase				75.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:318:				advancePomodoroPhase			90.6%
github.com/halpworld/halpradio/pkg/timer/timer.go:438:				Tick					95.5%
github.com/halpworld/halpradio/pkg/timer/timer.go:489:				BadgeText				82.4%
github.com/halpworld/halpradio/pkg/timer/timer.go:526:				WindowTitleBadge			91.7%
github.com/halpworld/halpradio/pkg/timer/timer.go:551:				FormattedTime				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:556:				Progress				72.7%
github.com/halpworld/halpradio/pkg/timer/timer.go:575:				PhaseDescription			100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:595:				IsActive				100.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:599:				timerTypeName				50.0%
github.com/halpworld/halpradio/pkg/timer/timer.go:611:				FormatDuration				100.0%
github.com/halpworld/halpradio/pkg/ui/components/art.go:26:			RenderAlbumArtModal			97.8%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:25:		NewBrailleCanvas			75.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:48:		SetDot					100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:88:		MarkHotspot				90.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:105:		RenderGlobeView				92.8%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:415:		latDirection				100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/globe.go:422:		lonDirection				100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/mapdata.go:24:		init					100.0%
github.com/halpworld/halpradio/pkg/ui/components/globe/mapdata.go:28:		initWorldBitmap				57.1%
github.com/halpworld/halpradio/pkg/ui/components/globe/mapdata.go:52:		IsLand					63.6%
github.com/halpworld/halpradio/pkg/ui/components/header.go:12:			RenderHeader				91.7%
github.com/halpworld/halpradio/pkg/ui/components/history.go:13:			RenderHistoryList			90.6%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:43:			RenderLyricsDrawer			90.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:99:			renderLyricsBody			88.3%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:223:			lyricGauge				66.7%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:243:			lyricsFooter				100.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:277:			signedSeconds				75.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:287:			wrapSheet				75.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:313:			firstRowFor				62.5%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:329:			wrapPlain				87.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:367:			splitAtWidth				71.4%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:383:			centerLine				80.0%
github.com/halpworld/halpradio/pkg/ui/components/lyrics.go:392:			absInt					100.0%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:15:			RenderPRExportModal			81.2%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:72:			RenderThemePickerModal			79.6%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:385:			renderThemePreviewCard			75.0%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:487:			RenderAddStationModal			91.2%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:586:			RenderTimerModal			92.6%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:866:			RenderPluginManagerModal		77.5%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:1066:		RenderPermissionApprovalModal		84.2%
github.com/halpworld/halpradio/pkg/ui/components/modals.go:1129:		RenderPartyManagerModal			0.0%
github.com/halpworld/halpradio/pkg/ui/components/partybar.go:17:		RenderPartyBar				81.7%
github.com/halpworld/halpradio/pkg/ui/components/playerbar.go:14:		RenderPlayerBar				88.5%
github.com/halpworld/halpradio/pkg/ui/components/sidebar.go:11:			RenderSidebar				81.5%
github.com/halpworld/halpradio/pkg/ui/components/stationlist.go:12:		RenderStationList			87.3%
github.com/halpworld/halpradio/pkg/ui/components/stationlist.go:221:		padRight				85.7%
github.com/halpworld/halpradio/pkg/ui/components/stationlist.go:233:		truncate				66.7%
github.com/halpworld/halpradio/pkg/ui/components/statusbar.go:10:		RenderStatusBar				85.7%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:55:		NextBand				100.0%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:67:		CalculateSignalRSSI			94.1%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:97:		FindNearestStation			100.0%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:116:		FormatSMeter				59.1%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:145:		RenderAnalogTunerView			85.9%
github.com/halpworld/halpradio/pkg/ui/components/tuner/tuner.go:502:		generateStaticPattern			100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:38:		normalizeMode				88.9%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:59:		NewVisualizer				100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:66:		Tick					100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:95:		CycleMode				71.4%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:107:		Render					100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:131:		renderStopped				81.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:177:		getAnimalPoses				100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:217:		renderAnimalDJ				96.9%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:327:		ensureBins				83.3%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:338:		updateBarPhysics			100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:391:		renderBars				88.6%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:445:		renderWave				88.9%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:503:		renderSpectrum				95.2%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:541:		renderMinimal				93.3%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:566:		clamp					66.7%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:573:		clampFloat				100.0%
github.com/halpworld/halpradio/pkg/ui/components/visualizer.go:583:		clampRange				60.0%
github.com/halpworld/halpradio/pkg/ui/components/whichkey.go:11:		RenderWhichKeyOverlay			84.0%
github.com/halpworld/halpradio/pkg/ui/components/whichkey.go:192:		PlaceOverlay				100.0%
github.com/halpworld/halpradio/pkg/ui/keymap.go:62:				DefaultKeyMap				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:258:				NewModel				95.5%
github.com/halpworld/halpradio/pkg/ui/model.go:354:				SwitchTab				87.5%
github.com/halpworld/halpradio/pkg/ui/model.go:378:				RefreshStations				90.6%
github.com/halpworld/halpradio/pkg/ui/model.go:439:				WindowTitle				81.5%
github.com/halpworld/halpradio/pkg/ui/model.go:483:				Init					100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:494:				checkCatalogUpdateCmd			16.7%
github.com/halpworld/halpradio/pkg/ui/model.go:509:				tickCmd					50.0%
github.com/halpworld/halpradio/pkg/ui/model.go:516:				SetDesktop				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:521:				SetPluginManager			100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:526:				SetMsgSender				0.0%
github.com/halpworld/halpradio/pkg/ui/model.go:531:				SetPartySession				0.0%
github.com/halpworld/halpradio/pkg/ui/model.go:539:				SetupPartyHandlers			42.9%
github.com/halpworld/halpradio/pkg/ui/model.go:573:				openPartyModal				90.9%
github.com/halpworld/halpradio/pkg/ui/model.go:590:				SyncDesktop				76.9%
github.com/halpworld/halpradio/pkg/ui/model.go:658:				PlayNextStation				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:679:				PlayPrevStation				100.0%
github.com/halpworld/halpradio/pkg/ui/model.go:700:				TogglePlayPause				55.6%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:39:				nowPlayingTrack				50.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:50:				nowPlayingParts				54.5%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:71:				lyricElapsed				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:87:				nowPlayingSignature			77.8%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:103:			coverSourceLabel			66.7%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:112:			resetNowPlaying				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:129:			syncNowPlaying				25.9%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:172:			fetchLyricsCmd				0.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:198:			fetchCoverCmd				0.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:228:			LyricsDrawerWidth			87.5%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:256:			lyricsSurface				87.5%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:272:			artTarget				84.4%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:331:			drawerArtBudget				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:341:			cellRowsFor				75.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:350:			cellColsFor				75.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:361:			renderArt				73.3%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:388:			dropArt					100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:398:			ArtClearSequence			100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:407:			toggleLyricsDrawer			94.4%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:435:			toggleArtModal				76.9%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:456:			scrollLyrics				70.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:475:			nudgeLyricsSync				100.0%
github.com/halpworld/halpradio/pkg/ui/nowplaying.go:485:			formatOffset				100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:33:				describeUpdateMsg			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:48:				Update					49.1%
github.com/halpworld/halpradio/pkg/ui/update.go:1757:				openTimerModal				100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1775:				applyTheme				100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1784:				getFilteredRegistryThemes		100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1800:				fetchThemeRegistryCmd			10.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1824:				installThemeCmd				10.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1842:				deleteThemeCmd				0.0%
github.com/halpworld/halpradio/pkg/ui/update.go:1854:				handleThemePickerKey			48.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2143:				handleTimerEvent			44.4%
github.com/halpworld/halpradio/pkg/ui/update.go:2188:				tuneToStationByID			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2198:				handleTimerModalKey			31.8%
github.com/halpworld/halpradio/pkg/ui/update.go:2406:				startPomodoroFromMenu			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2413:				startSleepFromMenu			100.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2427:				handleAddModalKey			66.7%
github.com/halpworld/halpradio/pkg/ui/update.go:2494:				searchRadioBrowserCmd			0.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2501:				handlePermissionApprovalKey		75.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2521:				handlePluginModalKey			25.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2636:				fetchRegistryCmd			11.1%
github.com/halpworld/halpradio/pkg/ui/update.go:2651:				installPluginCmd			0.0%
github.com/halpworld/halpradio/pkg/ui/update.go:2663:				cycleGlobeStation			81.8%
github.com/halpworld/halpradio/pkg/ui/update.go:2682:				seekTunerStation			84.8%
github.com/halpworld/halpradio/pkg/ui/update.go:2729:				onTunerFreqChanged			84.6%
github.com/halpworld/halpradio/pkg/ui/update.go:2773:				identifyTrackCmd			56.2%
github.com/halpworld/halpradio/pkg/ui/update.go:2802:				handlePartyChatKey			61.5%
github.com/halpworld/halpradio/pkg/ui/update.go:2826:				handlePartyModalKey			34.3%
github.com/halpworld/halpradio/pkg/ui/view.go:14:				View					81.4%
github.com/halpworld/halpradio/pkg/util/browser.go:20:				defaultURLOpener			0.0%
github.com/halpworld/halpradio/pkg/util/browser.go:37:				SetURLOpenerForTesting			100.0%
github.com/halpworld/halpradio/pkg/util/browser.go:51:				IsValidHTTPURL				100.0%
github.com/halpworld/halpradio/pkg/util/browser.go:64:				BuildSearchURL				100.0%
github.com/halpworld/halpradio/pkg/util/browser.go:86:				OpenURL					87.5%
github.com/halpworld/halpradio/pkg/util/clipboard.go:19:			SetClipboardWriterForTesting		100.0%
github.com/halpworld/halpradio/pkg/util/clipboard.go:33:			CopyToClipboard				83.3%
github.com/halpworld/halpradio/pkg/util/config.go:54:				DefaultConfig				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:100:				GetConfigDir				42.9%
github.com/halpworld/halpradio/pkg/util/config.go:113:				EnsureConfigDir				75.0%
github.com/halpworld/halpradio/pkg/util/config.go:121:				GetThemesDir				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:125:				GetLocalStationsFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:129:				GetCatalogCacheFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:133:				GetCatalogMetadataFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:137:				GetFavoritesFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:141:				GetSavedTracksFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:147:				GetDebugLogFile				0.0%
github.com/halpworld/halpradio/pkg/util/config.go:151:				GetConfigFile				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:158:				GetCacheDir				0.0%
github.com/halpworld/halpradio/pkg/util/config.go:171:				GetLyricsCacheDir			0.0%
github.com/halpworld/halpradio/pkg/util/config.go:176:				GetAlbumArtCacheDir			0.0%
github.com/halpworld/halpradio/pkg/util/config.go:182:				EnsureCacheDir				0.0%
github.com/halpworld/halpradio/pkg/util/config.go:189:				GetPluginsDir				100.0%
github.com/halpworld/halpradio/pkg/util/config.go:193:				GetPluginsDataDir			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:197:				GetPluginsConfigFile			100.0%
github.com/halpworld/halpradio/pkg/util/config.go:202:				LoadConfig				90.9%
github.com/halpworld/halpradio/pkg/util/config.go:242:				SaveConfig				66.7%
total:										(statements)				70.5%

@arkalon76
arkalon76 merged commit d3a93cb into main Sep 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Real-Time Synced Karaoke Lyrics & Multi-Protocol Terminal Album Art

1 participant