From fdb39d2159f112c6454184a4b9b164f3b3c06117 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:30:27 +0000 Subject: [PATCH 1/9] updated getRSSItemTitle, getJsonFeedItemTitle, getLetterboxdActivity, getTraktEpisode, and getTraktMovie to return structured objects with text and a direct link to the content --- utils.js | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/utils.js b/utils.js index c9fd645..2934128 100644 --- a/utils.js +++ b/utils.js @@ -41,24 +41,28 @@ export async function getRSSItemTitle(feedUrl) { const dom = new JSDOM(data) const item = dom.window.document.querySelector('item') const title = item.querySelector('title').textContent - return markdownCharEscape( + const link = item.querySelector('link').textContent + 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}](${image})` : cleanTitle + const text = !showImage + ? cleanTitle + : image + ? `${cleanTitle} ![${cleanTitle}](${image})` + : cleanTitle + return { text, url } } export async function getJsonFeedItemContent(feedUrl, showImage = true) { @@ -96,12 +100,13 @@ export async function getLetterboxdActivity(username, showImage = true) { const dom = new JSDOM(data) const item = dom.window.document.querySelector('item') const title = item.querySelector('title').textContent + const link = item.querySelector('link').textContent const cleanTitle = markdownCharEscape(title) const image = item.querySelector('description > img').src - if (!showImage) { - return cleanTitle - } - return `${cleanTitle} ![${cleanTitle}](${image})` + const text = !showImage + ? cleanTitle + : `${cleanTitle} ![${cleanTitle}](${image})` + return { text, url: link } } export async function getTraktEpisode(username, id, showImage = true) { @@ -112,11 +117,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} ![${cleanTitle}](https://widgets.trakt.tv/users/${id}/watched/thumb@2x.jpg?type=episode&image_only=1)` + const text = !showImage + ? cleanTitle + : `${cleanTitle} ![${cleanTitle}](https://widgets.trakt.tv/users/${id}/watched/thumb@2x.jpg?type=episode&image_only=1)` + return { text, url: link } } export async function getTraktMovie(username, showImage = true) { @@ -127,13 +133,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) { From 7d8c4940585dca552a448422d2b1975046e5b7c9 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:30:40 +0000 Subject: [PATCH 2/9] updated now function to replace hrefs with latest URLs and display structured text --- index.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 0b035a7..2150d0a 100644 --- a/index.js +++ b/index.js @@ -34,9 +34,16 @@ 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}`) + 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) From 0a91a3235913baf4c5de2333926f4b155b2de03a Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:41:07 +0000 Subject: [PATCH 3/9] updated now function to log the latest URL for each item --- index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/index.js b/index.js index 2150d0a..1a7fa34 100644 --- a/index.js +++ b/index.js @@ -35,6 +35,7 @@ export default async function now() { try { const latest = await getLatest() 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 From 1b5c73365329638369e237a38cee8a1ae3f80fff Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:45:43 +0000 Subject: [PATCH 4/9] updated getRSSItemTitle and getLetterboxdActivity to log link element presence and text so we can see what's happening --- utils.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/utils.js b/utils.js index 2934128..6df273b 100644 --- a/utils.js +++ b/utils.js @@ -41,7 +41,10 @@ export async function getRSSItemTitle(feedUrl) { const dom = new JSDOM(data) const item = dom.window.document.querySelector('item') const title = item.querySelector('title').textContent - const link = item.querySelector('link').textContent + const linkEl = item.querySelector('link') + console.log(`RSS link element found: ${!!linkEl}`) + if (linkEl) console.log(`RSS link text: "${linkEl.textContent}"`) + const link = linkEl ? linkEl.textContent.trim() : '' const cleanTitle = markdownCharEscape( sanitizeHtml(title, { allowedTags: [], @@ -100,7 +103,10 @@ export async function getLetterboxdActivity(username, showImage = true) { const dom = new JSDOM(data) const item = dom.window.document.querySelector('item') const title = item.querySelector('title').textContent - const link = item.querySelector('link').textContent + const linkEl = item.querySelector('link') + console.log(`Letterboxd link element found: ${!!linkEl}`) + if (linkEl) console.log(`Letterboxd link text: "${linkEl.textContent}"`) + const link = linkEl ? linkEl.textContent.trim() : '' const cleanTitle = markdownCharEscape(title) const image = item.querySelector('description > img').src const text = !showImage From 5328d0fd25a358d848168ab57716766f02cf31e1 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:52:25 +0000 Subject: [PATCH 5/9] updated getRSSItemTitle and getLetterboxdActivity to set content type for JSDOM and removed unnecessary link element logging --- utils.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/utils.js b/utils.js index 6df273b..e948db7 100644 --- a/utils.js +++ b/utils.js @@ -38,13 +38,10 @@ 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 - const linkEl = item.querySelector('link') - console.log(`RSS link element found: ${!!linkEl}`) - if (linkEl) console.log(`RSS link text: "${linkEl.textContent}"`) - const link = linkEl ? linkEl.textContent.trim() : '' + const link = item.querySelector('link').textContent.trim() const cleanTitle = markdownCharEscape( sanitizeHtml(title, { allowedTags: [], @@ -100,13 +97,10 @@ 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 linkEl = item.querySelector('link') - console.log(`Letterboxd link element found: ${!!linkEl}`) - if (linkEl) console.log(`Letterboxd link text: "${linkEl.textContent}"`) - const link = linkEl ? linkEl.textContent.trim() : '' + const link = item.querySelector('link').textContent.trim() const cleanTitle = markdownCharEscape(title) const image = item.querySelector('description > img').src const text = !showImage From a64bce8020e1c3b10f32db49336542fda21a657e Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:52:29 +0000 Subject: [PATCH 6/9] updated config to separate trakt episode and movie into distinct items --- config.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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), }, ] From 101d99d22a945b36923d29dab5a9a130e19a91b3 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 18:52:35 +0000 Subject: [PATCH 7/9] updated now-page-template to separate trakt episode and movie links --- now-page-template.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/now-page-template.md b/now-page-template.md index 0964b6e..9e5f2d2 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 + --- From c7717e6d29c73c718f122ed5f1c948ef11d95ea5 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 19:16:11 +0000 Subject: [PATCH 8/9] updated now-page-template to include icons for episode and movie links --- now-page-template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/now-page-template.md b/now-page-template.md index 9e5f2d2..4c01547 100644 --- a/now-page-template.md +++ b/now-page-template.md @@ -16,9 +16,9 @@
I watched this episode
- tktk
+ tktk
and this movie
- tktk + tktk
From 360465395a42ffef8a95eedb40d3ed8060317a58 Mon Sep 17 00:00:00 2001 From: dylan Date: Thu, 25 Dec 2025 19:19:09 +0000 Subject: [PATCH 9/9] updated getLetterboxdActivity to conditionally include image in returned text --- utils.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/utils.js b/utils.js index e948db7..30a5c33 100644 --- a/utils.js +++ b/utils.js @@ -102,10 +102,16 @@ export async function getLetterboxdActivity(username, showImage = true) { const title = item.querySelector('title').textContent const link = item.querySelector('link').textContent.trim() const cleanTitle = markdownCharEscape(title) - const image = item.querySelector('description > img').src - const text = !showImage - ? cleanTitle - : `${cleanTitle} ![${cleanTitle}](${image})` + + 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}](${image})` : cleanTitle + } + return { text, url: link } }