pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
at main 107 lines 2.8 kB view raw
1import { 2 ProgressEpisodeItem, 3 ProgressItem, 4 ProgressMediaItem, 5 ProgressSeasonItem, 6} from "@/stores/progress"; 7 8export interface ShowProgressResult { 9 episode?: ProgressEpisodeItem; 10 season?: ProgressSeasonItem; 11 progress: ProgressItem; 12 show: boolean; 13} 14 15const defaultProgress = { 16 duration: 0, 17 watched: 0, 18}; 19 20function progressIsCompleted(duration: number, watched: number): boolean { 21 const timeFromEnd = duration - watched; 22 23 // too close to the end, is completed 24 if (timeFromEnd < 60 * 2) return true; 25 26 // satisfies all constraints, not completed 27 return false; 28} 29 30function progressIsNotStarted(duration: number, watched: number): boolean { 31 // too short watch time 32 if (watched < 20) return true; 33 34 // satisfies all constraints, not completed 35 return false; 36} 37 38function progressIsAcceptableRange(duration: number, watched: number): boolean { 39 // not started enough yet, not acceptable 40 if (progressIsNotStarted(duration, watched)) return false; 41 42 // is already at the end, not acceptable 43 if (progressIsCompleted(duration, watched)) return false; 44 45 // satisfied all constraints 46 return true; 47} 48 49function isFirstEpisodeOfShow( 50 item: ProgressMediaItem, 51 episode: ProgressEpisodeItem, 52): boolean { 53 const seasonId = episode.seasonId; 54 const season = item.seasons[seasonId]; 55 return season.number === 1 && episode.number === 1; 56} 57 58export function getProgressPercentage( 59 watched: number, 60 duration: number, 61): number { 62 // Handle edge cases to prevent infinity or invalid percentages 63 if (!duration || duration <= 0) return 0; 64 if (!watched || watched < 0) return 0; 65 66 // Cap percentage at 100% to prevent >100% values 67 const percentage = Math.min((watched / duration) * 100, 100); 68 return percentage; 69} 70 71export function shouldShowProgress( 72 item: ProgressMediaItem, 73): ShowProgressResult { 74 // non shows just hide or show depending on acceptable ranges 75 if (item.type !== "show") { 76 return { 77 show: progressIsAcceptableRange( 78 item.progress?.duration ?? 0, 79 item.progress?.watched ?? 0, 80 ), 81 progress: item.progress ?? defaultProgress, 82 }; 83 } 84 85 // shows only hide an item if its too early in episode, it still shows if its near the end. 86 // Otherwise you would lose episode progress 87 const ep = Object.values(item.episodes) 88 .sort((a, b) => b.updatedAt - a.updatedAt) 89 .filter( 90 (epi) => 91 !progressIsNotStarted(epi.progress.duration, epi.progress.watched) || 92 !isFirstEpisodeOfShow(item, epi), 93 )[0]; 94 95 const season = item.seasons[ep?.seasonId]; 96 if (!ep || !season) 97 return { 98 show: false, 99 progress: defaultProgress, 100 }; 101 return { 102 season, 103 episode: ep, 104 show: true, 105 progress: ep.progress, 106 }; 107}