mastoradio-fork/src/util.js

135 lines
3.6 KiB
JavaScript
Raw Normal View History

2020-01-09 20:31:12 +01:00
import getUrls from 'get-urls'
2020-01-11 16:57:59 +01:00
import { execPipe, asyncFilter, asyncMap } from 'iter-tools'
2020-01-10 03:02:46 +01:00
2020-01-11 16:57:59 +01:00
const millisecond = 1
const second = 1000 * millisecond
const minute = 60 * second
2020-01-13 14:39:00 +01:00
const hour = 60 * minute
export const secondsToElapsedTime = (seconds) => {
const parts = [
Math.floor(seconds / 3600),
Math.floor(seconds / 60) % 60,
Math.floor(seconds) % 60
]
return parts
.filter((value, index) => value > 0 || index > 0)
.map(value => value < 10 ? '0' + value : value)
.join(':')
}
2020-01-10 14:30:17 +01:00
2020-01-11 16:57:59 +01:00
export async function* mkStatusesIterator(initialLink) {
let buffer = []
let { previousLink, nextLink } = initialLink
2020-01-10 03:02:46 +01:00
2020-01-11 16:57:59 +01:00
console.log('fetch initial')
const initial = await fetchTimeline(initialLink)
let latestPreviousFetch = Date.now()
2020-01-10 03:02:46 +01:00
2020-01-11 16:57:59 +01:00
if (initial.statuses.length > 0) {
buffer = [...initial.statuses]
previousLink = initial.links.prev
nextLink = initial.links.next
}
yield buffer.shift()
while (true) {
const now = Date.now()
2020-01-14 20:22:09 +01:00
2020-01-11 16:57:59 +01:00
if (latestPreviousFetch + 5 * minute < now) {
console.log('fetch newer')
const previous = await fetchTimeline(previousLink)
console.log(`${previous.length} newers`)
buffer.unshift(...previous.statuses)
previousLink = previous.links.prev
latestPreviousFetch = now
}
if (buffer.length === 0) {
console.log('fetch older')
const next = await fetchTimeline(nextLink)
buffer.push(...next.statuses)
nextLink = next.links.next
}
2020-01-10 03:02:46 +01:00
2020-01-11 16:57:59 +01:00
yield buffer.shift()
2020-01-10 03:02:46 +01:00
}
}
2020-01-11 16:57:59 +01:00
export async function* mkTracksIterator(domain, hashtags) {
2020-01-11 17:36:57 +01:00
// const known = new Set()
const known = {}
2020-01-11 16:57:59 +01:00
const [hashtag] = hashtags
2020-01-10 03:02:46 +01:00
2020-01-11 16:57:59 +01:00
const statuses = mkStatusesIterator(`https://${domain}/api/v1/timelines/tag/${hashtag}?limit=40`)
const tracks = execPipe(
statuses,
asyncMap(status => ({ status, data: mkData(status) })),
asyncFilter(({ data }) => {
if (data) {
2020-01-11 17:36:57 +01:00
// const found = known.has(data.id)
// known.add(data.id)
const found = known.hasOwnProperty(data.id)
known[data.id] = true
2020-01-11 16:57:59 +01:00
return !found
}
return false
}),
asyncMap(async ({ status, data }) => ({ status, data, metadata: await mkMetadata(data) }))
)
yield* tracks
}
2020-01-09 20:31:12 +01:00
2020-01-11 16:57:59 +01:00
export async function fetchTimeline(url) {
2020-01-09 20:31:12 +01:00
const response = await fetch(url)
const statuses = await response.json()
2020-01-11 16:57:59 +01:00
const links = response.headers.has('link')
? parseLinkHeader(response.headers.get('link'))
: {}
return { statuses, links }
2020-01-09 20:31:12 +01:00
}
const LINK_RE = /<(.+?)>; rel="(\w+)"/gi
2020-01-10 14:30:17 +01:00
function parseLinkHeader(link) {
2020-01-09 20:31:12 +01:00
const links = {}
for (const [ , url, name ] of link.matchAll(LINK_RE)) {
links[name] = url
}
return links
2020-01-10 14:30:17 +01:00
}
2020-01-11 16:57:59 +01:00
function mkData(status)
{
2020-01-10 14:30:17 +01:00
const urls = getUrls(status.content)
2020-01-11 16:57:59 +01:00
for (const urlAsString of urls) {
const url = new URL(urlAsString)
2020-01-10 14:30:17 +01:00
2020-01-11 16:57:59 +01:00
if (['youtube.com', 'music.youtube.com'].includes(url.hostname) && url.searchParams.has('v')) {
return { url: urlAsString, id: url.searchParams.get('v') }
} else if (url.hostname === 'youtu.be') {
return { url: urlAsString, id: url.pathname.substring(1) }
2020-01-10 14:30:17 +01:00
}
}
2020-01-11 16:57:59 +01:00
return null
2020-01-10 14:30:17 +01:00
}
2020-01-11 16:57:59 +01:00
async function mkMetadata(entry) {
return fetch(`https://noembed.com/embed?url=https://www.youtube.com/watch?v=${entry.id}`)
2020-01-10 14:30:17 +01:00
.then(response => response.json())
}
export function intersection(xs, ys) {
return xs.filter(x => ys.includes(x))
2020-01-07 19:23:49 +01:00
}