import truncateHtml from "truncate-html"; import { convert } from "html-to-text"; import { parseCountryField } from "./countries.js"; import { formatDate, md } from "./formatters.js"; import { ICON_MAP } from "./icons.js"; const warningBanner = ``; export const generateMetadata = (data, type, globals) => { let title = globals["site_name"]; let description = data["description"] || globals["site_description"]; const canonicalUrl = data["url"] ? `${globals["url"]}${data["url"]}` : globals["url"]; const ogImage = `${globals["cdn_url"]}${(data["backdrop"] ? data["backdrop"] : data["image"]) || globals["avatar"]}?class=w800`; description = convert( truncateHtml(md.render(description), 100, { byWords: true, ellipsis: "...", }), { wordwrap: false, selectors: [ { selector: "a", options: { ignoreHref: true } }, { selector: "h1", options: { uppercase: false } }, { selector: "h2", options: { uppercase: false } }, { selector: "h3", options: { uppercase: false } }, { selector: "*", format: "block" }, ], } ) .replace(/\s+/g, " ") .trim(); switch (type) { case "artist": title = `Artists / ${data["name"]} / ${globals["site_name"]}`; break; case "genre": title = `Genre / ${data["name"]} / ${globals["site_name"]}`; break; case "book": title = `Books / ${data["title"]} by ${data["author"]} / ${globals["site_name"]}`; break; case "movie": title = `Movies / ${data["title"]} (${data["year"]}) / ${globals["site_name"]}`; break; case "show": title = `Shows / ${data["title"]} / ${globals["site_name"]}`; break; default: title = `${data["title"] || globals["site_name"]}`; } return { title, description, "og:title": title, "og:description": description, "og:image": ogImage, "og:url": canonicalUrl, canonical: canonicalUrl, }; }; const generateAssociatedMediaHTML = (data, isGenre = false) => { const sections = [ { key: "artists", icon: "headphones", category: "music", title: "Related Artist(s)", }, { key: "related_artists", icon: "headphones", category: "music", title: "Related Artist(s)", }, { key: "genres", icon: "headphones", category: "music", title: "Related Genre(s)", }, { key: "movies", icon: "film", category: "movies", title: "Related Movie(s)", }, { key: "related_movies", icon: "film", category: "movies", title: "Related Movie(s)", }, { key: "shows", icon: "deviceTvOld", category: "tv", title: "Related Show(s)", }, { key: "related_shows", icon: "deviceTvOld", category: "tv", title: "Related Show(s)", }, { key: "posts", icon: "article", category: "article", title: "Related Post(s)", }, { key: "books", icon: "books", category: "books", title: "Related Book(s)", }, { key: "related_books", icon: "books", category: "books", title: "Related Book(s)", }, ]; return sections .filter(({ key }) => !(isGenre && key === "artists")) .map(({ key, category, icon, title }) => { const items = data[key]; if (!items || items.length === 0) return ""; return `
${ICON_MAP[icon]} ${title}
`; }) .join(""); }; const generateMediaLinks = (data, type, count = 10) => { if (!data || !type) return ""; const dataSlice = data.slice(0, count); if (dataSlice.length === 0) return null; const buildLink = (item) => { switch (type) { case "genre": return `${item["genre_name"]}`; case "artist": return `${item["name"]}`; case "book": return `${item["title"]}`; default: return ""; } }; if (dataSlice.length === 1) return buildLink(dataSlice[0]); const links = dataSlice.map(buildLink); const allButLast = links.slice(0, -1).join(", "); const last = links[links.length - 1]; return `${allButLast} and ${last}`; }; export const generateArtistHTML = (artist, globals) => { const playLabel = artist?.["total_plays"] === 1 ? "play" : "plays"; const concertsList = artist["concerts"]?.length ? `
${ICON_MAP["deviceSpeaker"]} I've seen this artist live! ` : ""; const albumsTable = artist["albums"]?.length ? ` ${artist["albums"] .map( (album) => ` ` ) .join("")}
AlbumPlaysYear
${album["name"]} ${album["total_plays"] || 0} ${album["release_year"]}

These are the albums by this artist that are in my collection, not necessarily a comprehensive discography.

` : ""; return ` ${ICON_MAP.arrowLeft} Back to music
${artist[
${artist["name"]} ${ICON_MAP["mapPin"]} ${parseCountryField( artist["country"] )} ${ artist["favorite"] ? `${ICON_MAP["heart"]} This is one of my favorite artists!` : "" } ${ artist["tattoo"] ? `${ICON_MAP["needle"]} I have a tattoo inspired by this artist!` : "" } ${ artist["total_plays"] ? `${artist["total_plays"]} ${playLabel}` : "" } ${ artist["genre"] ? `${artist["genre"]["name"]}` : "" }
${generateAssociatedMediaHTML(artist)} ${ artist["description"] ? `

Overview

${md.render( artist["description"] )}
` : "" } ${concertsList} ${albumsTable}
`; }; export const generateBookHTML = (book, globals) => { const alt = `${book["title"]}${ book["author"] ? ` by ${book["author"]}` : "" }`; const percentage = book["progress"] ? `${book["progress"]}%` : ""; const status = book["status"] === "finished" ? `Finished on ${formatDate( book["date_finished"] )}` : percentage ? `
` : ""; return ` ${ ICON_MAP["arrowLeft"] } Back to books
${alt}
${book["title"]} ${book["rating"] ? `${book["rating"]}` : ""} ${ book["author"] ? `By ${book["author"]}` : "" } ${ book["favorite"] ? `${ICON_MAP["heart"]} This is one of my favorite books!` : "" } ${ book["tattoo"] ? `${ICON_MAP["needle"]} I have a tattoo inspired by this book!` : "" } ${status ? status : ""}
${ book["review"] ? `${warningBanner}

My thoughts

${md.render(book["review"])}

` : "" } ${generateAssociatedMediaHTML(book)}

Overview

${md.render(book["description"])}

`; }; export const generateGenreHTML = (genre) => { const artistCount = genre["artists"]?.length || 0; const connectingWords = artistCount > 1 ? "artists are" : "artist is"; const mediaLinks = generateMediaLinks(genre["artists"], "artist", 5); return ` ${ ICON_MAP["arrowLeft"] } Back to music

${genre["name"]}

${ mediaLinks ? `

My top ${genre["name"]} ${connectingWords} ${mediaLinks}. I've listened to ${genre["total_plays"]} tracks from this genre.


` : "" } ${generateAssociatedMediaHTML(genre, true)} ${ genre["description"] ? `

Overview

${md.render(genre["description"])}

Continue reading at Wikipedia.

Wikipedia content provided under the terms of the Creative Commons BY-SA license.

` : "" }
`; }; export const generateWatchingHTML = (media, globals, type) => { const isShow = type === "show"; const label = isShow ? "show" : "movie"; const lastWatched = media["last_watched"] || (isShow && media["episode"]?.["last_watched_at"]); return ` ${ ICON_MAP.arrowLeft } Back to watching
${media[
${media["title"]} (${ media["year"] }) ${media["rating"] ? `${media["rating"]}` : ""} ${ media["favorite"] ? `${ICON_MAP["heart"]} This is one of my favorite ${label}s!` : "" } ${ media["tattoo"] ? `${ICON_MAP["needle"]} I have a tattoo inspired by this ${label}!` : "" } ${ media["collected"] ? `${ICON_MAP["circleCheck"]} This ${label} is in my collection!` : "" } ${ lastWatched ? `Last watched on ${formatDate( lastWatched )}.` : "" }
${ media["review"] ? `${warningBanner}

My thoughts

${md.render( media["review"] )}

` : "" } ${generateAssociatedMediaHTML(media)} ${ media["description"] ? `

Overview

${md.render(media["description"])}

` : "" }
`; }; export const generateConcertModal = (concert) => { const venue = concert["venue_name"] ? concert["venue_latitude"] && concert["venue_longitude"] ? `${concert["venue_name_short"]}` : concert["venue_name_short"] : ""; const notesModal = concert["notes"] ? ` ` : ""; return `
  • ${formatDate( concert["date"] )} at ${venue} ${notesModal}
  • `; };