// Package output renders summaries to user-visible formats. Markdown is // passed through; Spotify HTML uses the small tag subset that Spotify for // Podcasters' show-notes editor accepts (b, i, a, ul/ol/li, paragraphs). package output import ( "regexp" "strings" ) var ( reBoldStar = regexp.MustCompile(`\*\*([^*\n]+)\*\*`) reBoldUnder = regexp.MustCompile(`__([^_\n]+)__`) reItalicStar = regexp.MustCompile(`\*([^*\n]+)\*`) reItalicUnder = regexp.MustCompile(`(^|[\s(])_([^_\n]+)_($|[\s).,!?;:])`) reLink = regexp.MustCompile(`\[([^\]]+)\]\(([^)\s]+)\)`) reInlineCode = regexp.MustCompile("`([^`\n]+)`") ) // MarkdownToSpotifyHTML converts a markdown summary into the limited HTML // subset Spotify for Podcasters renders. Unknown markdown structures degrade // to plain text rather than producing rejected tags. func MarkdownToSpotifyHTML(md string) string { lines := strings.Split(strings.ReplaceAll(md, "\r\n", "\n"), "\n") var out strings.Builder listKind := "" // "ul" or "ol" while we're inside a list flushList := func() { if listKind != "" { out.WriteString("" + listKind + ">\n") listKind = "" } } openList := func(kind string) { if listKind != kind { flushList() out.WriteString("<" + kind + ">\n") listKind = kind } } paragraph := []string{} flushPara := func() { if len(paragraph) == 0 { return } text := strings.Join(paragraph, " ") out.WriteString("
" + inline(text) + "
\n") paragraph = paragraph[:0] } for _, raw := range lines { line := strings.TrimRight(raw, " \t") trim := strings.TrimSpace(line) // Blank line: end current paragraph/list block. if trim == "" { flushPara() flushList() continue } // Horizontal rule. if trim == "---" || trim == "***" || trim == "___" { flushPara() flushList() continue } // Heading -> bold paragraph. if h := headingText(trim); h != "" { flushPara() flushList() out.WriteString("" + inline(h) + "
\n") continue } // Blockquote -> italic paragraph. if strings.HasPrefix(trim, "> ") { flushPara() flushList() out.WriteString("" + inline(strings.TrimPrefix(trim, "> ")) + "
\n") continue } // Unordered list item. if strings.HasPrefix(trim, "- ") || strings.HasPrefix(trim, "* ") || strings.HasPrefix(trim, "+ ") { flushPara() openList("ul") out.WriteString("