Merge pull request 'add direct linking to individual items from feeds' (#1) from update/direct-links into main
Reviewed-on: https://source.tube/dylan/now-updater/pulls/1
This commit is contained in:
commit
a3ed5a4a82
4 changed files with 59 additions and 26 deletions
12
config.js
12
config.js
|
|
@ -5,7 +5,8 @@ import {
|
||||||
getMalojaScrobble,
|
getMalojaScrobble,
|
||||||
getReadingBooklogr,
|
getReadingBooklogr,
|
||||||
getRSSItemTitle,
|
getRSSItemTitle,
|
||||||
getTraktEpisodeAndMovie,
|
getTraktEpisode,
|
||||||
|
getTraktMovie,
|
||||||
htmlLinkRegex,
|
htmlLinkRegex,
|
||||||
} from './utils.js'
|
} from './utils.js'
|
||||||
|
|
||||||
|
|
@ -28,9 +29,14 @@ export const items = [
|
||||||
getLatest: async () => getLetterboxdActivity('stfudonny', false),
|
getLatest: async () => getLetterboxdActivity('stfudonny', false),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'trakt',
|
id: 'trakt-episode',
|
||||||
regex: htmlLinkRegex('trakt.tv/users/crankle'),
|
regex: htmlLinkRegex('trakt.tv/users/crankle'),
|
||||||
getLatest: async () =>
|
getLatest: async () =>
|
||||||
getTraktEpisodeAndMovie('crankle', '78e1e87b446901f7e4f0883dd4995cec', false),
|
getTraktEpisode('crankle', '78e1e87b446901f7e4f0883dd4995cec', false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'trakt-movie',
|
||||||
|
regex: htmlLinkRegex('trakt.tv/users/crankle'),
|
||||||
|
getLatest: async () => getTraktMovie('crankle', false),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
14
index.js
14
index.js
|
|
@ -34,9 +34,17 @@ export default async function now() {
|
||||||
items.map(async ({ id, regex, getLatest }) => {
|
items.map(async ({ id, regex, getLatest }) => {
|
||||||
try {
|
try {
|
||||||
const latest = await getLatest()
|
const latest = await getLatest()
|
||||||
console.log(`${id}: ${latest}`)
|
console.log(`${id}: ${latest.text}`)
|
||||||
if (latest) {
|
console.log(`${id} URL: ${latest.url}`)
|
||||||
newNow = newNow.replace(regex, `$1${latest}$2`)
|
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) {
|
} catch (e) {
|
||||||
console.warn(`⚠️ Failed to fetch ${id}`, e)
|
console.warn(`⚠️ Failed to fetch ${id}`, e)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,10 @@
|
||||||
I rated this<br>{letterboxd} <a href="https://letterboxd.com/STFUDonny" style="color: #f38ba8 !important;">tktk</a>
|
I rated this<br>{letterboxd} <a href="https://letterboxd.com/STFUDonny" style="color: #f38ba8 !important;">tktk</a>
|
||||||
</div>
|
</div>
|
||||||
<div style="background: rgba(250, 179, 135, 0.15); padding: 1rem; border: 4px solid #fab387; color:white;">
|
<div style="background: rgba(250, 179, 135, 0.15); padding: 1rem; border: 4px solid #fab387; color:white;">
|
||||||
I watched this<br>{trakt} <a href="https://trakt.tv/users/crankle" style="color: #fab387 !important;">tktk</a>
|
I watched this episode<br>
|
||||||
|
<i class="fa-solid fa-tv"></i> <a href="https://trakt.tv/users/crankle" style="color: #fab387 !important;">tktk</a><br>
|
||||||
|
and this movie<br>
|
||||||
|
<i class="fa-solid fa-film"></i> <a href="https://trakt.tv/users/crankle" style="color: #fab387 !important;">tktk</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
52
utils.js
52
utils.js
|
|
@ -38,27 +38,31 @@ export function markdownCharEscape(text) {
|
||||||
export async function getRSSItemTitle(feedUrl) {
|
export async function getRSSItemTitle(feedUrl) {
|
||||||
const res = await fetch(feedUrl)
|
const res = await fetch(feedUrl)
|
||||||
const data = await res.text()
|
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 item = dom.window.document.querySelector('item')
|
||||||
const title = item.querySelector('title').textContent
|
const title = item.querySelector('title').textContent
|
||||||
return markdownCharEscape(
|
const link = item.querySelector('link').textContent.trim()
|
||||||
|
const cleanTitle = markdownCharEscape(
|
||||||
sanitizeHtml(title, {
|
sanitizeHtml(title, {
|
||||||
allowedTags: [],
|
allowedTags: [],
|
||||||
allowedAttributes: {},
|
allowedAttributes: {},
|
||||||
})?.trim(),
|
})?.trim(),
|
||||||
)
|
)
|
||||||
|
return { text: cleanTitle, url: link }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getJsonFeedItemTitle(feedUrl, showImage = true) {
|
export async function getJsonFeedItemTitle(feedUrl, showImage = true) {
|
||||||
const res = await fetch(feedUrl)
|
const res = await fetch(feedUrl)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
const post = data.items[0]
|
const post = data.items[0]
|
||||||
const { title, image } = post
|
const { title, image, url } = post
|
||||||
const cleanTitle = markdownCharEscape(title)
|
const cleanTitle = markdownCharEscape(title)
|
||||||
if (!showImage) {
|
const text = !showImage
|
||||||
return cleanTitle
|
? cleanTitle
|
||||||
}
|
: image
|
||||||
return image ? `${cleanTitle} ` : cleanTitle
|
? `${cleanTitle} `
|
||||||
|
: cleanTitle
|
||||||
|
return { text, url }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getJsonFeedItemContent(feedUrl, showImage = true) {
|
export async function getJsonFeedItemContent(feedUrl, showImage = true) {
|
||||||
|
|
@ -93,15 +97,22 @@ export async function getMalojaScrobble(malojaUrl) {
|
||||||
export async function getLetterboxdActivity(username, showImage = true) {
|
export async function getLetterboxdActivity(username, showImage = true) {
|
||||||
const res = await fetch(`https://letterboxd.com/${username}/rss/`)
|
const res = await fetch(`https://letterboxd.com/${username}/rss/`)
|
||||||
const data = await res.text()
|
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 item = dom.window.document.querySelector('item')
|
||||||
const title = item.querySelector('title').textContent
|
const title = item.querySelector('title').textContent
|
||||||
|
const link = item.querySelector('link').textContent.trim()
|
||||||
const cleanTitle = markdownCharEscape(title)
|
const cleanTitle = markdownCharEscape(title)
|
||||||
const image = item.querySelector('description > img').src
|
|
||||||
if (!showImage) {
|
let text = cleanTitle
|
||||||
return cleanTitle
|
if (showImage) {
|
||||||
|
// Parse CDATA content as HTML to extract image
|
||||||
|
const description = item.querySelector('description').textContent
|
||||||
|
const imgMatch = description.match(/<img[^>]+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) {
|
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 dom = new JSDOM(data)
|
||||||
const entry = dom.window.document.querySelector('entry')
|
const entry = dom.window.document.querySelector('entry')
|
||||||
const title = entry.querySelector('title').textContent
|
const title = entry.querySelector('title').textContent
|
||||||
|
const link = entry.querySelector('link').getAttribute('href')
|
||||||
const cleanTitle = markdownCharEscape(title)
|
const cleanTitle = markdownCharEscape(title)
|
||||||
if (!showImage) {
|
const text = !showImage
|
||||||
return cleanTitle
|
? cleanTitle
|
||||||
}
|
: `${cleanTitle} `
|
||||||
return `${cleanTitle} `
|
return { text, url: link }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTraktMovie(username, showImage = true) {
|
export async function getTraktMovie(username, showImage = true) {
|
||||||
|
|
@ -127,13 +139,17 @@ export async function getTraktMovie(username, showImage = true) {
|
||||||
const dom = new JSDOM(data)
|
const dom = new JSDOM(data)
|
||||||
const entry = dom.window.document.querySelector('entry')
|
const entry = dom.window.document.querySelector('entry')
|
||||||
const title = entry.querySelector('title').textContent
|
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) {
|
export async function getTraktEpisodeAndMovie(username, id, showImage = true) {
|
||||||
const episode = await getTraktEpisode(username, id, showImage)
|
const episode = await getTraktEpisode(username, id, showImage)
|
||||||
const movie = await getTraktMovie(username, showImage)
|
const movie = await getTraktMovie(username, showImage)
|
||||||
return `${episode} <br>and<br> ${movie}`
|
return {
|
||||||
|
text: `${episode.text} <br>and<br> ${movie.text}`,
|
||||||
|
url: `https://trakt.tv/users/${username}`,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getReadingBooklogr(booklogrUrl, booklogrUser) {
|
export async function getReadingBooklogr(booklogrUrl, booklogrUser) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue