FreeTube/src/renderer/helpers/subscriptions.js

163 lines
4.6 KiB
JavaScript

import store from '../store/index'
import { calculatePublishedDate } from './utils'
/**
* Filtering and sort based on user preferences
* @param {any[]} videos
*/
export function updateVideoListAfterProcessing(videos) {
let videoList = videos
if (store.getters.getHideLiveStreams) {
videoList = videoList.filter(item => {
return (!item.liveNow && !item.isUpcoming)
})
}
if (store.getters.getHideUpcomingPremieres) {
videoList = videoList.filter(item => {
if (item.isRSS) {
// viewCount is our only method of detecting premieres in RSS
// data without sending an additional request.
// If we ever get a better flag, use it here instead.
return item.viewCount !== '0'
}
// Observed for premieres in Local API Subscriptions.
return (item.premiereDate == null ||
// Invidious API
// `premiereTimestamp` only available on premiered videos
// https://docs.invidious.io/api/common_types/#videoobject
item.premiereTimestamp == null
)
})
}
if (store.getters.getHideWatchedSubs) {
const historyCacheById = store.getters.getHistoryCacheById
videoList = videoList.filter((video) => {
return !Object.hasOwn(historyCacheById, video.videoId)
})
}
// ordered last to show first eligible video from channel
// if the first one incidentally failed one of the above checks
if (store.getters.getOnlyShowLatestFromChannel) {
const authors = new Set()
videoList = videoList.filter((video) => {
if (!video.authorId) {
return true
} else if (!authors.has(video.authorId)) {
authors.add(video.authorId)
return true
}
return false
})
}
videoList.sort((a, b) => {
return b.publishedDate - a.publishedDate
})
return videoList
}
/**
* @param {string} rssString
* @param {string} channelId
*/
export async function parseYouTubeRSSFeed(rssString, channelId) {
// doesn't need to be asynchronous, but doing it allows us to do the relatively slow DOM querying in parallel
try {
const xmlDom = new DOMParser().parseFromString(rssString, 'application/xml')
const channelName = xmlDom.querySelector('author > name').textContent
const entries = xmlDom.querySelectorAll('entry')
const promises = []
for (const entry of entries) {
promises.push(parseRSSEntry(entry, channelId, channelName))
}
return {
name: channelName,
videos: await Promise.all(promises)
}
} catch (e) {
return {
videos: []
}
}
}
/**
* @param {Element} entry
* @param {string} channelId
* @param {string} channelName
*/
async function parseRSSEntry(entry, channelId, channelName) {
// doesn't need to be asynchronous, but doing it allows us to do the relatively slow DOM querying in parallel
const published = new Date(entry.querySelector('published').textContent)
return {
authorId: channelId,
author: channelName,
// querySelector doesn't support xml namespaces so we have to use getElementsByTagName here
videoId: entry.getElementsByTagName('yt:videoId')[0].textContent,
title: entry.querySelector('title').textContent,
publishedDate: published,
publishedText: published.toLocaleString(),
viewCount: entry.getElementsByTagName('media:statistics')[0]?.getAttribute('views') || null,
type: 'video',
lengthSeconds: '0:00',
isRSS: true
}
}
/**
* @param {{
* liveNow: boolean,
* isUpcoming: boolean,
* premiereDate: Date,
* publishedText: string,
* publishedDate: number
* }[]} videos publishedDate is added by this function,
* but adding it to the type definition stops vscode warning that the property doesn't exist
*/
export function addPublishedDatesLocal(videos) {
videos.forEach(video => {
if (video.liveNow) {
video.publishedDate = new Date().getTime()
} else if (video.isUpcoming) {
video.publishedDate = video.premiereDate
} else {
video.publishedDate = calculatePublishedDate(video.publishedText)
}
return video
})
}
/**
* @param {{
* liveNow: boolean,
* isUpcoming: boolean,
* premiereTimestamp: number,
* published: number,
* publishedDate: number
* }[]} videos publishedDate is added by this function,
* but adding it to the type definition stops vscode warning that the property doesn't exist
*/
export function addPublishedDatesInvidious(videos) {
videos.forEach(video => {
if (video.liveNow) {
video.publishedDate = new Date().getTime()
} else if (video.isUpcoming) {
video.publishedDate = new Date(video.premiereTimestamp * 1000)
} else {
video.publishedDate = new Date(video.published * 1000)
}
return video
})
}