EARHORNMUSIC UPnP · Android TV · Phones · Android Auto · GNU/Linux
Release 0.5.0

GenreParser.kt

Each line, with the release that last changed it.

0.5.01package dev.stave.domain.model
2
3/**
4 * Parse a genre string into individual genre tokens.
5 *
6 * UPnP/DIDL servers emit multi-genre tags with inconsistent separators across
7 * implementations and across the source files' own metadata conventions:
8 * MinimServer "Rock; Pop"
9 * iTunes / ID3v2.4 "Rock/Pop" or "Rock\Pop"
10 * Foobar2000 "Rock // Pop"
11 * Plex / BubbleUPnP "Rock, Pop"
12 * Doubled variants "Rock\\Pop" (escaped-backslash or literal double-backslash)
13 *
14 * We collapse `//` and `\\` to single delimiters first, then split on any of
15 * `,`, `;`, `/`, `\`. Tokens are trimmed; blanks are discarded. Order preserved.
16 *
17 * Returns an empty list for null/blank input. Doesn't lowercase — callers that need
18 * case-insensitive comparison should do that themselves.
19 */
20fun parseGenres(raw: String?): List<String> {
21 if (raw.isNullOrBlank()) return emptyList()
22 // Collapse doubled separators first. Without this, "Rock // Pop" split on `/` would
23 // yield ["Rock ", " ", " Pop"], and the middle empty token would pass through trim()
24 // as "" and get filtered out — slow but correct. Doing this first is cheaper and
25 // also handles the literal-double-backslash case "Rock\\Pop" which would otherwise
26 // split to ["Rock", "", "Pop"].
27 val normalized = raw
28 .replace("//", "/")
29 .replace("\\\\", "\\")
30 return normalized
31 .split(',', ';', '/', '\\')
32 .map { it.trim() }
33 .filter { it.isNotEmpty() }
34}