pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add top sources page (/flix/sources)

+125 -1
+8 -1
src/pages/TopFlix.tsx
··· 1 1 import classNames from "classnames"; 2 2 import { ReactNode, useEffect, useState } from "react"; 3 - import { useNavigate } from "react-router-dom"; // Import Link from react-router-dom 3 + import { Link, useNavigate } from "react-router-dom"; // Import Link from react-router-dom 4 4 5 5 import { ThiccContainer } from "@/components/layout/ThinContainer"; 6 6 import { Divider } from "@/components/utils/Divider"; ··· 183 183 const [timeSinceProcessStart, setTimeSinceProcessStart] = useState< 184 184 string | null 185 185 >(null); 186 + const navigate = useNavigate(); 186 187 187 188 useEffect(() => { 188 189 getRecentPlayedItems() ··· 252 253 </p> 253 254 </div> 254 255 </div> 256 + {/* <Button 257 + className="py-px w-60 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center" 258 + onClick={() => navigate("/sources")} 259 + > 260 + Most used providers 261 + </Button> */} 255 262 </div> 256 263 257 264 <div className="pl-6 pr-6">
+115
src/pages/TopSources.tsx
··· 1 + import { ReactNode, useEffect, useState } from "react"; 2 + import { useNavigate } from "react-router-dom"; // Import Link from react-router-dom 3 + 4 + import { ThiccContainer } from "@/components/layout/ThinContainer"; 5 + import { Divider } from "@/components/utils/Divider"; 6 + import { Heading1, Paragraph } from "@/components/utils/Text"; 7 + 8 + import { SubPageLayout } from "./layouts/SubPageLayout"; 9 + // import { MediaGrid } from "@/components/media/MediaGrid" 10 + // import { TopFlixCard } from "@/components/media/FlixCard"; 11 + 12 + function ConfigValue(props: { name: string; children?: ReactNode }) { 13 + return ( 14 + <> 15 + <div className="flex"> 16 + <p className="flex-1 font-bold text-white pr-5 pl-3"> 17 + <p>{props.name}</p> 18 + </p> 19 + <p className="pr-3 cursor-default">{props.children}</p> 20 + </div> 21 + <p className="pr-5 pl-3 cursor-default"> 22 + {/* props.type.charAt(0).toUpperCase() + props.type.slice(1) */} 23 + </p> 24 + <Divider marginClass="my-3" /> 25 + </> 26 + ); 27 + } 28 + 29 + async function getRecentPlayedItems() { 30 + const response = await fetch("https://backend.sudo-flix.lol/metrics"); 31 + const text = await response.text(); 32 + 33 + const regex = 34 + /mw_provider_status_count{provider_id="([^"]+)",status="([^"]+)"} (\d+)/g; 35 + let match = regex.exec(text); 36 + const items: { [key: string]: any } = {}; 37 + 38 + while (match !== null) { 39 + const [_, providerId, status, count] = match; 40 + if (items[providerId]) { 41 + items[providerId].count += parseInt(count, 10); 42 + } else { 43 + items[providerId] = { 44 + providerId, 45 + status, 46 + count: parseInt(count, 10), 47 + }; 48 + } 49 + match = regex.exec(text); 50 + } 51 + 52 + if (Object.keys(items).length > 0) { 53 + return Object.values(items); 54 + } 55 + throw new Error("RECENT_PLAYED_ITEMS not found"); 56 + } 57 + 58 + export function TopSources() { 59 + const [recentPlayedItems, setRecentPlayedItems] = useState<any[]>([]); 60 + 61 + useEffect(() => { 62 + getRecentPlayedItems() 63 + .then((items) => { 64 + const limitedItems = items.filter( 65 + (item, index, self) => 66 + index === self.findIndex((t2) => t2.providerId === item.providerId), 67 + ); 68 + setRecentPlayedItems(limitedItems); 69 + }) 70 + .catch((error) => { 71 + console.error("Error fetching recent played items:", error); 72 + }); 73 + }, []); 74 + 75 + function getItemsForCurrentPage() { 76 + const sortedItems = recentPlayedItems.sort((a, b) => b.count - a.count); 77 + 78 + return sortedItems.map((item, index) => ({ 79 + ...item, 80 + rank: index + 1, 81 + })); 82 + } 83 + 84 + return ( 85 + <SubPageLayout> 86 + <ThiccContainer> 87 + <div className="mt-8 w-full px-8"> 88 + <Heading1>Top sources</Heading1> 89 + <Paragraph className="mb-6"> 90 + The most used providers on sudo-flix.lol, this data is fetched from 91 + the current backend deployment too. 92 + </Paragraph> 93 + </div> 94 + 95 + <div className="pl-6 pr-6"> 96 + <Divider marginClass="my-3" /> 97 + {getItemsForCurrentPage().map((item) => { 98 + return ( 99 + <ConfigValue 100 + key={item.tmdbFullId} 101 + name={`${ 102 + item.providerId.charAt(0).toUpperCase() + 103 + item.providerId.slice(1) 104 + }`} 105 + > 106 + {`Requests: `} 107 + <strong>{item.count}</strong> 108 + </ConfigValue> 109 + ); 110 + })} 111 + </div> 112 + </ThiccContainer> 113 + </SubPageLayout> 114 + ); 115 + }
+2
src/setup/App.tsx
··· 26 26 import { RegisterPage } from "@/pages/Register"; 27 27 import { SupportPage } from "@/pages/Support"; 28 28 import { TopFlix } from "@/pages/TopFlix"; 29 + import { TopSources } from "@/pages/TopSources"; 29 30 import { Layout } from "@/setup/Layout"; 30 31 import { useHistoryListener } from "@/stores/history"; 31 32 import { LanguageProvider } from "@/stores/language"; ··· 152 153 <Route path="/support" element={<SupportPage />} /> 153 154 {/* Top flix page */} 154 155 <Route path="/flix" element={<TopFlix />} /> 156 + <Route path="/flix/sources" element={<TopSources />} /> 155 157 {/* Settings page */} 156 158 <Route 157 159 path="/settings"