feat: host tv + movies

This commit is contained in:
Cory Dransfeldt 2024-05-14 16:51:01 -07:00
parent 9b1528dda1
commit f0041e0525
No known key found for this signature in database
23 changed files with 1282 additions and 166 deletions

View file

@ -5,27 +5,49 @@ const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_KEY = process.env.SUPABASE_KEY
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
const PAGE_SIZE = 1000
const fetchAllMovies = async () => {
let movies = []
let rangeStart = 0
while (true) {
const { data, error } = await supabase
.from('movies')
.select(`
tmdb_id,
slug,
last_watched,
title,
year,
collected,
plays,
favorite
`)
.order('last_watched', { ascending: false })
.range(rangeStart, rangeStart + PAGE_SIZE - 1)
if (error) {
console.error(error)
break
}
movies = movies.concat(data)
if (data.length < PAGE_SIZE) break
rangeStart += PAGE_SIZE
}
return movies
}
export default async function () {
const { data: movies, error } = await supabase
.from('movies')
.select(`
tmdb_id,
slug,
last_watched,
title,
year,
collected,
plays,
favorite
`)
.order('last_watched', { ascending: false })
if (error) return []
const formatMovieData = (movies) => movies.map((item) => {
const movies = await fetchAllMovies()
const formatMovieData = (movies, watched = true) => movies.map((item) => {
const movie = {
title: item['title'],
dateAdded: item['last_watched'],
lastWatched: item['last_watched'],
year: item['year'],
url: `https://www.themoviedb.org/movie/${item['tmdb_id']}`,
description: `<p>${item['title']} (${item['year']})</p><p>Watched at: ${DateTime.fromISO(item['last_watched'], { zone: 'utc' }).setZone('America/Los_Angeles').toFormat('MMMM d, yyyy, h:mma')}</p>`,
type: 'movie',
@ -35,17 +57,18 @@ export default async function () {
collected: item['collected'],
favorite: item['favorite'],
}
return movie;
})
return movie
}).filter(movie => watched ? movie['lastWatched'] : !movie['lastWatched'])
const favoriteMovies = movies.filter(movie => movie['favorite'])
const collectedMovies = movies.filter(movie => movie['collected'])
const recentlyWatchedMovies = movies.filter(movie => movie['last_watched']).sort((a, b) => new Date(b['last_watched']) - new Date(a['last_watched'])).slice(0, 6)
return {
movies,
watchHistory: formatMovieData(movies),
recentlyWatched: formatMovieData(movies.slice(0, 6)),
favorites: formatMovieData(favoriteMovies),
recentlyWatched: formatMovieData(recentlyWatchedMovies),
favorites: formatMovieData(favoriteMovies).sort((a, b) => a['title'].localeCompare(b['title'])),
collection: formatMovieData(collectedMovies),
toWatch: formatMovieData(movies, false).sort((a, b) => a['title'].localeCompare(b['title'])),
}
}

View file

@ -32,6 +32,32 @@ const fetchDataForPeriod = async (startPeriod, fields, table) => {
return rows
}
const fetchAllTimeData = async (fields, table) => {
const PAGE_SIZE = 1000
let rows = []
let rangeStart = 0
while (true) {
const { data, error } = await supabase
.from(table)
.select(fields)
.order('listened_at', { ascending: false })
.range(rangeStart, rangeStart + PAGE_SIZE - 1)
if (error) {
console.error(error)
break
}
rows = rows.concat(data)
if (data.length < PAGE_SIZE) break
rangeStart += PAGE_SIZE
}
return rows
}
const aggregateData = (data, groupByField, groupByType, sort = true) => {
const aggregation = {}
data.forEach(item => {
@ -65,16 +91,14 @@ const aggregateData = (data, groupByField, groupByType, sort = true) => {
aggregation[key].plays++
})
const aggregatedData = sort ? Object.values(aggregation).sort((a, b) => b.plays - a.plays) : Object.values(aggregation)
return aggregatedData
return aggregatedData.filter(item => item.plays > 0)
}
export default async function() {
const periods = {
week: DateTime.now().minus({ days: 7 }).startOf('day'), // Last week
month: DateTime.now().minus({ days: 30 }).startOf('day'), // Last 30 days
threeMonth: DateTime.now().minus({ months: 3 }).startOf('day'), // Last three months
year: DateTime.now().minus({ years: 1 }).startOf('day'), // Last 365 days
week: DateTime.now().minus({ days: 7 }).startOf('day'), // last week
month: DateTime.now().minus({ days: 30 }).startOf('day'), // last 30 days
threeMonth: DateTime.now().minus({ months: 3 }).startOf('day'), // last three months
}
const results = {}
@ -97,15 +121,23 @@ export default async function() {
}
}
// Fetch and aggregate all-time data
const allTimeData = await fetchAllTimeData(selectFields, 'listens')
results['allTime'] = {
artists: aggregateData(allTimeData, 'artist_name', 'artists'),
albums: aggregateData(allTimeData, 'album_name', 'albums'),
tracks: aggregateData(allTimeData, 'track_name', 'track')
}
const recentData = await fetchDataForPeriod(DateTime.now().minus({ days: 7 }), selectFields, 'listens')
results.recent = {
results['recent'] = {
artists: aggregateData(recentData, 'artist_name', 'artists'),
albums: aggregateData(recentData, 'album_name', 'albums'),
tracks: aggregateData(recentData, 'track_name', 'track'),
tracksChronological: aggregateData(recentData, 'track_name', 'track', false),
}
results.nowPlaying = results.recent.tracksChronological[0]
results['nowPlaying'] = results['recent']['tracksChronological'][0]
return results
}

View file

@ -21,7 +21,7 @@ export default async function () {
{ name: 'npm', url: 'https://www.npmjs.com/~cdransf', icon: 'brand-npm'},
{ name: 'Mastodon', url: 'https://social.lol/@cory', icon: 'brand-mastodon' },
{ name: 'ListenBrainz', url: 'https://listenbrainz.org/user/cdransf/', icon: 'brain' },
{ name: 'Trakt', url: 'https://trakt.tv/users/cdransf', icon: 'device-tv' },
{ name: 'Watching', url: '/watching', icon: 'device-tv' },
{ name: 'Instapaper', url: 'https://www.instapaper.com/p/coryd', icon: 'news' },
{ name: 'Books', url: '/books', icon: 'books' },
{ name: 'Webrings', url: '/webrings', icon: 'heart-handshake' },

View file

@ -4,22 +4,45 @@ const SUPABASE_URL = process.env.SUPABASE_URL
const SUPABASE_KEY = process.env.SUPABASE_KEY
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
export default async function () {
const { data: shows, error } = await supabase
.from('shows')
.select(`
title,
tmdb_id,
collected,
favorite,
episodes (
episode_number,
season_number,
last_watched_at
)
`)
const PAGE_SIZE = 1000
if (error) return []
const fetchAllShows = async () => {
let shows = []
let rangeStart = 0
while (true) {
const { data, error } = await supabase
.from('shows')
.select(`
title,
tmdb_id,
collected,
favorite,
year,
episodes (
episode_number,
season_number,
last_watched_at
)
`)
.range(rangeStart, rangeStart + PAGE_SIZE - 1)
if (error) {
console.error(error)
break
}
shows = shows.concat(data)
if (data.length < PAGE_SIZE) break
rangeStart += PAGE_SIZE
}
return shows
}
export default async function () {
const shows = await fetchAllShows()
let episodes = []
shows.forEach(show => {
@ -29,14 +52,15 @@ export default async function () {
show_title: show['title'],
show_tmdb_id: show['tmdb_id'],
collected: show['collected'],
favorite: show['favorite']
favorite: show['favorite'],
year: show['year']
})
})
})
episodes.sort((a, b) => new Date(b['last_watched_at']) - new Date(a['last_watched_at']))
const allEpisodes = episodes
episodes = episodes.slice(0, 75)
const recentlyWatchedEpisodes = episodes.slice(0, 75)
const formatEpisodeData = (episodes) => {
const episodeData = []
@ -55,8 +79,9 @@ export default async function () {
showEpisodesMap[showTmdbId] = {
title: showTitle,
tmdbId: showTmdbId,
collected: collected,
favorite: favorite,
collected,
favorite,
lastWatchedAt,
episodes: []
}
}
@ -71,7 +96,7 @@ export default async function () {
type: 'tv',
image: `https://coryd.dev/media/shows/poster-${showTmdbId}.jpg`,
backdrop: `https://coryd.dev/media/shows/backdrops/backdrop-${showTmdbId}.jpg`,
lastWatchedAt: lastWatchedAt
lastWatchedAt
})
})
@ -101,8 +126,8 @@ export default async function () {
})
} else {
const singleEpisode = show['episodes'][0]
singleEpisode.collected = show['collected']
singleEpisode.favorite = show['favorite']
singleEpisode['collected'] = show['collected']
singleEpisode['favorite'] = show['favorite']
episodeData.push(singleEpisode)
}
})
@ -112,24 +137,26 @@ export default async function () {
const favoriteShows = shows.filter(show => show['favorite'])
const collectedShows = shows.filter(show => show['collected'])
const toWatch = shows.filter(show => !show.episodes.some(episode => episode.last_watched_at)).sort((a, b) => a['title'].localeCompare(b['title']))
return {
shows,
watchHistory: formatEpisodeData(allEpisodes),
recentlyWatched: formatEpisodeData(episodes),
recentlyWatched: formatEpisodeData(recentlyWatchedEpisodes),
favorites: formatEpisodeData(favoriteShows.flatMap(show => show['episodes'].map(episode => ({
...episode,
show_title: show['title'],
show_tmdb_id: show['tmdb_id'],
collected: show['collected'],
favorite: show['favorite']
})))),
})))).sort((a, b) => a['name'].localeCompare(b['name'])),
collection: formatEpisodeData(collectedShows.flatMap(show => show['episodes'].map(episode => ({
...episode,
show_title: show['title'],
show_tmdb_id: show['tmdb_id'],
collected: show['collected'],
favorite: show['favorite']
}))))
})))),
toWatch
}
}
}