diff --git a/config.js b/config.js
index 74f87e8..788bb68 100644
--- a/config.js
+++ b/config.js
@@ -5,7 +5,8 @@ import {
getMalojaScrobble,
getReadingBooklogr,
getRSSItemTitle,
- getTraktEpisodeAndMovie,
+ getTraktEpisode,
+ getTraktMovie,
htmlLinkRegex,
} from './utils.js'
@@ -28,9 +29,14 @@ export const items = [
getLatest: async () => getLetterboxdActivity('stfudonny', false),
},
{
- id: 'trakt',
+ id: 'trakt-episode',
regex: htmlLinkRegex('trakt.tv/users/crankle'),
getLatest: async () =>
- getTraktEpisodeAndMovie('crankle', '78e1e87b446901f7e4f0883dd4995cec', false),
+ getTraktEpisode('crankle', '78e1e87b446901f7e4f0883dd4995cec', false),
+ },
+ {
+ id: 'trakt-movie',
+ regex: htmlLinkRegex('trakt.tv/users/crankle'),
+ getLatest: async () => getTraktMovie('crankle', false),
},
]
diff --git a/index.js b/index.js
index 0b035a7..1a7fa34 100644
--- a/index.js
+++ b/index.js
@@ -34,9 +34,17 @@ export default async function now() {
items.map(async ({ id, regex, getLatest }) => {
try {
const latest = await getLatest()
- console.log(`${id}: ${latest}`)
- if (latest) {
- newNow = newNow.replace(regex, `$1${latest}$2`)
+ console.log(`${id}: ${latest.text}`)
+ console.log(`${id} URL: ${latest.url}`)
+ if (latest && latest.text && latest.url) {
+ newNow = newNow.replace(regex, (match, openTag, closeTag) => {
+ // Replace href in opening tag
+ const updatedOpenTag = openTag.replace(
+ /href=["'][^"']*["']/,
+ `href="${latest.url}"`,
+ )
+ return `${updatedOpenTag}${latest.text}${closeTag}`
+ })
}
} catch (e) {
console.warn(`⚠️ Failed to fetch ${id}`, e)
diff --git a/now-page-template.md b/now-page-template.md
index 0964b6e..4c01547 100644
--- a/now-page-template.md
+++ b/now-page-template.md
@@ -15,8 +15,11 @@
I rated this
{letterboxd} tktk
- I watched this
{trakt}
tktk
-
+ I watched this episode
+ tktk
+ and this movie
+ tktk
+
---
diff --git a/utils.js b/utils.js
index c9fd645..30a5c33 100644
--- a/utils.js
+++ b/utils.js
@@ -38,27 +38,31 @@ export function markdownCharEscape(text) {
export async function getRSSItemTitle(feedUrl) {
const res = await fetch(feedUrl)
const data = await res.text()
- const dom = new JSDOM(data)
+ const dom = new JSDOM(data, { contentType: 'text/xml' })
const item = dom.window.document.querySelector('item')
const title = item.querySelector('title').textContent
- return markdownCharEscape(
+ const link = item.querySelector('link').textContent.trim()
+ const cleanTitle = markdownCharEscape(
sanitizeHtml(title, {
allowedTags: [],
allowedAttributes: {},
})?.trim(),
)
+ return { text: cleanTitle, url: link }
}
export async function getJsonFeedItemTitle(feedUrl, showImage = true) {
const res = await fetch(feedUrl)
const data = await res.json()
const post = data.items[0]
- const { title, image } = post
+ const { title, image, url } = post
const cleanTitle = markdownCharEscape(title)
- if (!showImage) {
- return cleanTitle
- }
- return image ? `${cleanTitle} ` : cleanTitle
+ const text = !showImage
+ ? cleanTitle
+ : image
+ ? `${cleanTitle} `
+ : cleanTitle
+ return { text, url }
}
export async function getJsonFeedItemContent(feedUrl, showImage = true) {
@@ -93,15 +97,22 @@ export async function getMalojaScrobble(malojaUrl) {
export async function getLetterboxdActivity(username, showImage = true) {
const res = await fetch(`https://letterboxd.com/${username}/rss/`)
const data = await res.text()
- const dom = new JSDOM(data)
+ const dom = new JSDOM(data, { contentType: 'text/xml' })
const item = dom.window.document.querySelector('item')
const title = item.querySelector('title').textContent
+ const link = item.querySelector('link').textContent.trim()
const cleanTitle = markdownCharEscape(title)
- const image = item.querySelector('description > img').src
- if (!showImage) {
- return cleanTitle
+
+ let text = cleanTitle
+ if (showImage) {
+ // Parse CDATA content as HTML to extract image
+ const description = item.querySelector('description').textContent
+ const imgMatch = description.match(/
]+src="([^"]+)"/)
+ const image = imgMatch ? imgMatch[1] : null
+ text = image ? `${cleanTitle} ` : cleanTitle
}
- return `${cleanTitle} `
+
+ return { text, url: link }
}
export async function getTraktEpisode(username, id, showImage = true) {
@@ -112,11 +123,12 @@ export async function getTraktEpisode(username, id, showImage = true) {
const dom = new JSDOM(data)
const entry = dom.window.document.querySelector('entry')
const title = entry.querySelector('title').textContent
+ const link = entry.querySelector('link').getAttribute('href')
const cleanTitle = markdownCharEscape(title)
- if (!showImage) {
- return cleanTitle
- }
- return `${cleanTitle} `
+ const text = !showImage
+ ? cleanTitle
+ : `${cleanTitle} `
+ return { text, url: link }
}
export async function getTraktMovie(username, showImage = true) {
@@ -127,13 +139,17 @@ export async function getTraktMovie(username, showImage = true) {
const dom = new JSDOM(data)
const entry = dom.window.document.querySelector('entry')
const title = entry.querySelector('title').textContent
- return markdownCharEscape(title)
+ const link = entry.querySelector('link').getAttribute('href')
+ return { text: markdownCharEscape(title), url: link }
}
export async function getTraktEpisodeAndMovie(username, id, showImage = true) {
const episode = await getTraktEpisode(username, id, showImage)
const movie = await getTraktMovie(username, showImage)
- return `${episode}
and
${movie}`
+ return {
+ text: `${episode.text}
and
${movie.text}`,
+ url: `https://trakt.tv/users/${username}`,
+ }
}
export async function getReadingBooklogr(booklogrUrl, booklogrUser) {