mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
Sexy page :)
This commit is contained in:
parent
0889d7ff27
commit
a3a06b168a
8 changed files with 76 additions and 211 deletions
20
public/config.js
Normal file
20
public/config.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
window.__CONFIG__ = {
|
||||||
|
// The URL for the CORS proxy, the URL must NOT end with a slash!
|
||||||
|
// If not specified, the onboarding will not allow a "default setup". The user will have to use the extension or set up a proxy themselves
|
||||||
|
VITE_CORS_PROXY_URL: "https://sudo-proxy.up.railway.app",
|
||||||
|
|
||||||
|
// The READ API key to access TMDB
|
||||||
|
VITE_TMDB_READ_API_KEY: "eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJhZTljNGE2ZDE1ZDFiODZiNzdlMWQyYmI5ZGY0MzdmYyIsInN1YiI6IjY1YjNmMWI0NTk0Yzk0MDE2MzNkZDBjNSIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.kAX7TkbKuJkNty6IsjcCLnoENFicVZn6d6DkLQsy3p8",
|
||||||
|
|
||||||
|
// The DMCA email displayed in the footer, null to hide the DMCA link
|
||||||
|
VITE_DMCA_EMAIL: null,
|
||||||
|
|
||||||
|
// Whether to disable hash-based routing, leave this as false if you don't know what this is
|
||||||
|
VITE_NORMAL_ROUTER: true,
|
||||||
|
|
||||||
|
// The backend URL to communicate with
|
||||||
|
VITE_BACKEND_URL: "https://backend.sudo-flix.lol",
|
||||||
|
|
||||||
|
// A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-<id>" and "movie-<id>"
|
||||||
|
VITE_DISALLOWED_IDS: "",
|
||||||
|
};
|
1
public/lightbar-images/dice.svg
Normal file
1
public/lightbar-images/dice.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24" fill="#fff" viewBox="0 -960 960 960" width="24"><path d="M300-240q25 0 42.5-17.5T360-300q0-25-17.5-42.5T300-360q-25 0-42.5 17.5T240-300q0 25 17.5 42.5T300-240Zm0-360q25 0 42.5-17.5T360-660q0-25-17.5-42.5T300-720q-25 0-42.5 17.5T240-660q0 25 17.5 42.5T300-600Zm180 180q25 0 42.5-17.5T540-480q0-25-17.5-42.5T480-540q-25 0-42.5 17.5T420-480q0 25 17.5 42.5T480-420Zm180 180q25 0 42.5-17.5T720-300q0-25-17.5-42.5T660-360q-25 0-42.5 17.5T600-300q0 25 17.5 42.5T660-240Zm0-360q25 0 42.5-17.5T720-660q0-25-17.5-42.5T660-720q-25 0-42.5 17.5T600-660q0 25 17.5 42.5T660-600ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Z"/></svg>
|
After Width: | Height: | Size: 744 B |
|
@ -109,8 +109,6 @@
|
||||||
"pages": {
|
"pages": {
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"dmca": "DMCA",
|
"dmca": "DMCA",
|
||||||
"topSources": "Top Sources",
|
|
||||||
"topFlix": "Top Flix",
|
|
||||||
"discover": "Discover",
|
"discover": "Discover",
|
||||||
"support": "Support",
|
"support": "Support",
|
||||||
"login": "Login",
|
"login": "Login",
|
||||||
|
|
|
@ -80,7 +80,7 @@ export function AboutPage() {
|
||||||
className="py-px mt-8 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center"
|
className="py-px mt-8 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center"
|
||||||
onClick={() => navigate("/flix")}
|
onClick={() => navigate("/flix")}
|
||||||
>
|
>
|
||||||
Top Flix
|
Discover
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className="py-px mt-8 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center"
|
className="py-px mt-8 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { LazyLoadImage } from "react-lazy-load-image-component";
|
import { LazyLoadImage } from "react-lazy-load-image-component";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
@ -11,6 +10,7 @@ import { WideContainer } from "@/components/layout/WideContainer";
|
||||||
import { HomeLayout } from "@/pages/layouts/HomeLayout";
|
import { HomeLayout } from "@/pages/layouts/HomeLayout";
|
||||||
import { conf } from "@/setup/config";
|
import { conf } from "@/setup/config";
|
||||||
|
|
||||||
|
import { PageTitle } from "./parts/util/PageTitle";
|
||||||
import { get } from "../backend/metadata/tmdb";
|
import { get } from "../backend/metadata/tmdb";
|
||||||
import { Icon, Icons } from "../components/Icon";
|
import { Icon, Icons } from "../components/Icon";
|
||||||
|
|
||||||
|
@ -142,7 +142,14 @@ export function TopFlix() {
|
||||||
language: "en-US",
|
language: "en-US",
|
||||||
});
|
});
|
||||||
|
|
||||||
setTVGenres(data.genres);
|
// Shuffle the array of genres
|
||||||
|
for (let i = data.genres.length - 1; i > 0; i -= 1) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[data.genres[i], data.genres[j]] = [data.genres[j], data.genres[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch only the first 5 TV show genres
|
||||||
|
setTVGenres(data.genres.slice(0, 5));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching TV show genres:", error);
|
console.error("Error fetching TV show genres:", error);
|
||||||
}
|
}
|
||||||
|
@ -156,7 +163,8 @@ export function TopFlix() {
|
||||||
const fetchTVShowsForGenre = async (genreId: number) => {
|
const fetchTVShowsForGenre = async (genreId: number) => {
|
||||||
try {
|
try {
|
||||||
const tvShows: any[] = [];
|
const tvShows: any[] = [];
|
||||||
for (let page = 1; page <= pagesToFetch; page += 1) {
|
for (let page = 1; page <= 5; page += 1) {
|
||||||
|
// Fetch only 5 pages
|
||||||
const data = await get<any>("/discover/tv", {
|
const data = await get<any>("/discover/tv", {
|
||||||
api_key: conf().TMDB_READ_API_KEY,
|
api_key: conf().TMDB_READ_API_KEY,
|
||||||
with_genres: genreId.toString(),
|
with_genres: genreId.toString(),
|
||||||
|
@ -236,73 +244,59 @@ export function TopFlix() {
|
||||||
? `${category} Programmes`
|
? `${category} Programmes`
|
||||||
: `${category} Movies`;
|
: `${category} Movies`;
|
||||||
return (
|
return (
|
||||||
<div className="relative overflow-hidden mt-4">
|
<div className="relative overflow-hidden mt-4 rounded-xl">
|
||||||
<h2 className="text-2xl font-bold text-white sm:text-3xl md:text-2xl mx-auto pl-10">
|
<h2 className="text-2xl font-bold text-white sm:text-3xl md:text-2xl mx-auto pl-2">
|
||||||
{displayCategory}
|
{displayCategory}
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
id={`carousel-${categorySlug}`}
|
id={`carousel-${categorySlug}`}
|
||||||
className="flex whitespace-nowrap overflow-auto scroll-snap-x-mandatory pb-4 mt-4 pl-10"
|
className="flex whitespace-nowrap overflow-auto scroll-snap-x-mandatory mb-4 mt-4"
|
||||||
ref={(el) => {
|
ref={(el) => {
|
||||||
carouselRefs.current[categorySlug] = el;
|
carouselRefs.current[categorySlug] = el;
|
||||||
}}
|
}}
|
||||||
|
style={{ overflowX: "hidden" }}
|
||||||
>
|
>
|
||||||
{medias.slice(0, 5).map((media) => (
|
{medias.slice(0, 20).map((media) => (
|
||||||
<a
|
<a
|
||||||
key={media.id}
|
key={media.id}
|
||||||
href={`media/tmdb-${isTVShow ? "tv" : "movie"}-${media.id}-${
|
href={`media/tmdb-${isTVShow ? "tv" : "movie"}-${media.id}-${
|
||||||
isTVShow ? media.name : media.title
|
isTVShow ? media.name : media.title
|
||||||
}`}
|
}`}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="block text-center relative overflow-hidden transition-transform transform hover:scale-105 mr-4"
|
className="block text-center relative overflow-hidden transition-transform transform hover:scale-95 mr-4"
|
||||||
style={{ flex: "0 0 auto", width: movieWidth }} // Set a fixed width for each movie
|
style={{ flex: `0 0 ${movieWidth}` }} // Set a fixed width for each movie
|
||||||
>
|
>
|
||||||
<LazyLoadImage
|
<LazyLoadImage
|
||||||
src={`https://image.tmdb.org/t/p/w500${media.poster_path}`}
|
src={`https://image.tmdb.org/t/p/w500${media.poster_path}`}
|
||||||
alt={isTVShow ? media.name : media.title}
|
alt={isTVShow ? media.name : media.title}
|
||||||
className="rounded-xl mb-2"
|
className="rounded-xl"
|
||||||
effect="blur"
|
effect="blur"
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "auto",
|
height: "100%",
|
||||||
transform: "scale(1)",
|
|
||||||
transition: "opacity 0.3s, transform 0.3s",
|
transition: "opacity 0.3s, transform 0.3s",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div
|
|
||||||
className="absolute inset-0 rounded-xl flex items-center justify-center text-white font-bold opacity-0 hover:opacity-100 transition-opacity"
|
|
||||||
style={{
|
|
||||||
backdropFilter: "blur(0px)",
|
|
||||||
transition: "opacity 0.5s",
|
|
||||||
backgroundColor: "rgba(0, 0, 0, 0.5)", // Darkening effect
|
|
||||||
whiteSpace: "normal", // Allow the text to wrap to the next line
|
|
||||||
wordWrap: "break-word", // Break words to prevent overflow
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<p className="text-sm m-4">
|
|
||||||
{isTVShow ? media.name : media.title}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button" // Added type attribute with value "button"
|
type="button" // Added type attribute with value "button"
|
||||||
title="Back"
|
title="Back"
|
||||||
className="absolute top-1/2 transform -translate-y-1/2 z-10"
|
className="absolute top-1/2 transform -translate-y-1/2 z-10 left-2"
|
||||||
onClick={() => scrollCarousel(categorySlug, "left")}
|
onClick={() => scrollCarousel(categorySlug, "left")}
|
||||||
>
|
>
|
||||||
<div className="cursor-pointer hover:text-white flex justify-center items-center h-10 w-10 rounded-full hover:bg-search-hoverBackground active:scale-110 transition-[transform,background-color] duration-200">
|
<div className="cursor-pointer text-white flex justify-center items-center h-10 w-10 rounded-full bg-search-hoverBackground active:scale-110 transition-[transform,background-color] duration-200">
|
||||||
<Icon icon={Icons.ARROW_LEFT} />
|
<Icon icon={Icons.ARROW_LEFT} />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button" // Added type attribute with value "button"
|
type="button" // Added type attribute with value "button"
|
||||||
title="Next"
|
title="Next"
|
||||||
className="absolute top-1/2 right-4 transform -translate-y-1/2 z-10"
|
className="absolute top-1/2 right-2 transform -translate-y-1/2 z-10"
|
||||||
onClick={() => scrollCarousel(categorySlug, "right")}
|
onClick={() => scrollCarousel(categorySlug, "right")}
|
||||||
>
|
>
|
||||||
<div className="cursor-pointer hover:text-white flex justify-center items-center h-10 w-10 rounded-full hover:bg-search-hoverBackground active:scale-110 transition-[transform,background-color] duration-200">
|
<div className="cursor-pointer text-white flex justify-center items-center h-10 w-10 rounded-full bg-search-hoverBackground active:scale-110 transition-[transform,background-color] duration-200">
|
||||||
<Icon icon={Icons.ARROW_RIGHT} />
|
<Icon icon={Icons.ARROW_RIGHT} />
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
@ -343,7 +337,14 @@ export function TopFlix() {
|
||||||
language: "en-US",
|
language: "en-US",
|
||||||
});
|
});
|
||||||
|
|
||||||
setGenres(data.genres);
|
// Shuffle the array of genres
|
||||||
|
for (let i = data.genres.length - 1; i > 0; i -= 1) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[data.genres[i], data.genres[j]] = [data.genres[j], data.genres[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch only the first 5 genres
|
||||||
|
setGenres(data.genres.slice(0, 5));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching genres:", error);
|
console.error("Error fetching genres:", error);
|
||||||
}
|
}
|
||||||
|
@ -357,7 +358,8 @@ export function TopFlix() {
|
||||||
const fetchMoviesForGenre = async (genreId: number) => {
|
const fetchMoviesForGenre = async (genreId: number) => {
|
||||||
try {
|
try {
|
||||||
const movies: any[] = [];
|
const movies: any[] = [];
|
||||||
for (let page = 1; page <= pagesToFetch; page += 1) {
|
for (let page = 1; page <= 5; page += 1) {
|
||||||
|
// Fetch only 5 pages
|
||||||
const data = await get<any>("/discover/movie", {
|
const data = await get<any>("/discover/movie", {
|
||||||
api_key: conf().TMDB_READ_API_KEY,
|
api_key: conf().TMDB_READ_API_KEY,
|
||||||
with_genres: genreId.toString(),
|
with_genres: genreId.toString(),
|
||||||
|
@ -378,6 +380,7 @@ export function TopFlix() {
|
||||||
|
|
||||||
genres.forEach((genre) => fetchMoviesForGenre(genre.id));
|
genres.forEach((genre) => fetchMoviesForGenre(genre.id));
|
||||||
}, [genres]);
|
}, [genres]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let countdownInterval: NodeJS.Timeout;
|
let countdownInterval: NodeJS.Timeout;
|
||||||
if (countdown !== null && countdown > 0) {
|
if (countdown !== null && countdown > 0) {
|
||||||
|
@ -395,10 +398,8 @@ export function TopFlix() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HomeLayout showBg={showBg}>
|
<HomeLayout showBg={showBg}>
|
||||||
<div className="mb-16 sm:mb-24">
|
<div className="mb-16 sm:mb-2">
|
||||||
<Helmet>
|
<PageTitle subpage k="global.pages.discover" />
|
||||||
<title>{t("global.name")}</title>
|
|
||||||
</Helmet>
|
|
||||||
<ThinContainer>
|
<ThinContainer>
|
||||||
<div className="mt-44 space-y-16 text-center">
|
<div className="mt-44 space-y-16 text-center">
|
||||||
<div className="relative z-10 mb-16">
|
<div className="relative z-10 mb-16">
|
||||||
|
@ -411,25 +412,27 @@ export function TopFlix() {
|
||||||
</div>
|
</div>
|
||||||
<WideContainer>
|
<WideContainer>
|
||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-center mt-6 mb-6">
|
<div className="flex items-center justify-center mb-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex items-center space-x-2 rounded-full px-4 text-white py-2 bg-pill-background bg-opacity-50 hover:bg-pill-backgroundHover transition-[background,transform] duration-100 hover:scale-105"
|
className="flex items-center space-x-2 rounded-full px-4 text-white py-2 bg-pill-background bg-opacity-50 hover:bg-pill-backgroundHover transition-[background,transform] duration-100 hover:scale-105"
|
||||||
onClick={handleRandomMovieClick}
|
onClick={handleRandomMovieClick}
|
||||||
disabled={countdown !== null && countdown > 0} // Disable the button during the countdown
|
disabled={countdown !== null && countdown > 0} // Disable the button during the countdown
|
||||||
>
|
>
|
||||||
<img
|
<span className="flex items-center space-x-2">
|
||||||
src="https://cdn-icons-png.flaticon.com/512/4058/4058790.png"
|
{countdown !== null && countdown > 0
|
||||||
alt="Small Image"
|
? `Playing in ${countdown} seconds`
|
||||||
style={{
|
: "Watch Something New"}
|
||||||
width: "20px", // Adjust the width as needed
|
<img
|
||||||
height: "20px", // Adjust the height as needed
|
src="/lightbar-images/dice.svg"
|
||||||
marginRight: "10px", // Add margin-right
|
alt="Small Image"
|
||||||
}}
|
style={{
|
||||||
/>
|
width: "20px", // Adjust the width as needed
|
||||||
{countdown !== null && countdown > 0
|
height: "20px", // Adjust the height as needed
|
||||||
? `Playing in ${countdown} seconds`
|
marginLeft: "10px", // Add margin-right
|
||||||
: "Watch a Random Movie"}
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{randomMovie && (
|
{randomMovie && (
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
|
||||||
import { useNavigate } from "react-router-dom";
|
|
||||||
|
|
||||||
import { ThiccContainer } from "@/components/layout/ThinContainer";
|
|
||||||
import { Divider } from "@/components/utils/Divider";
|
|
||||||
import { Heading1, Paragraph } from "@/components/utils/Text";
|
|
||||||
|
|
||||||
import { SubPageLayout } from "./layouts/SubPageLayout";
|
|
||||||
import { PageTitle } from "./parts/util/PageTitle";
|
|
||||||
import { Button } from "./TopFlix";
|
|
||||||
|
|
||||||
function ConfigValue(props: { name: string; children?: ReactNode }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="flex">
|
|
||||||
<p className="flex-1 font-bold text-white pr-5 pl-3">
|
|
||||||
<p className="cursor-default">{props.name}</p>
|
|
||||||
</p>
|
|
||||||
<p className="pr-3 cursor-default">{props.children}</p>
|
|
||||||
</div>
|
|
||||||
<Divider marginClass="my-3" />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTopSources() {
|
|
||||||
const response = await fetch("https://backend.sudo-flix.lol/metrics");
|
|
||||||
const text = await response.text();
|
|
||||||
|
|
||||||
const regex =
|
|
||||||
/mw_provider_status_count{provider_id="([^"]+)",status="([^"]+)"} (\d+)/g;
|
|
||||||
let match = regex.exec(text);
|
|
||||||
const items: { [key: string]: any } = {};
|
|
||||||
|
|
||||||
while (match !== null) {
|
|
||||||
const [_, providerId, status, count] = match;
|
|
||||||
if (items[providerId]) {
|
|
||||||
items[providerId].count += parseInt(count, 10);
|
|
||||||
} else {
|
|
||||||
items[providerId] = {
|
|
||||||
providerId,
|
|
||||||
status,
|
|
||||||
count: parseInt(count, 10),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
match = regex.exec(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(items).length > 0) {
|
|
||||||
return Object.values(items);
|
|
||||||
}
|
|
||||||
throw new Error("RECENT_PLAYED_ITEMS not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TopSources() {
|
|
||||||
const [recentPlayedItems, setRecentPlayedItems] = useState<any[]>([]);
|
|
||||||
const [failStatusCount, setFailStatusCount] = useState<number>(0);
|
|
||||||
const [successStatusCount, setSuccessStatusCount] = useState<number>(0);
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getTopSources()
|
|
||||||
.then((items) => {
|
|
||||||
const limitedItems = items.filter(
|
|
||||||
(item, index, self) =>
|
|
||||||
index === self.findIndex((t2) => t2.providerId === item.providerId),
|
|
||||||
);
|
|
||||||
setRecentPlayedItems(limitedItems);
|
|
||||||
|
|
||||||
// Calculate fail and success status counts
|
|
||||||
const failCount = limitedItems.reduce(
|
|
||||||
(acc, item) =>
|
|
||||||
item.status === "failed" || item.status === "notfound"
|
|
||||||
? acc + parseInt(item.count, 10)
|
|
||||||
: acc,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const successCount = limitedItems.reduce(
|
|
||||||
(acc, item) =>
|
|
||||||
item.status === "success" ? acc + parseInt(item.count, 10) : acc,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
setFailStatusCount(failCount.toLocaleString());
|
|
||||||
setSuccessStatusCount(successCount.toLocaleString());
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error fetching recent played items:", error);
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function getItemsForCurrentPage() {
|
|
||||||
const sortedItems = recentPlayedItems.sort((a, b) => b.count - a.count);
|
|
||||||
|
|
||||||
return sortedItems.map((item, index) => ({
|
|
||||||
...item,
|
|
||||||
rank: index + 1,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SubPageLayout>
|
|
||||||
<ThiccContainer>
|
|
||||||
<PageTitle subpage k="global.pages.topSources" />
|
|
||||||
<div className="mt-8 w-full px-8 cursor-default">
|
|
||||||
<Heading1>Top sources</Heading1>
|
|
||||||
<Paragraph className="mb-6">
|
|
||||||
The most used providers on sudo-flix.lol, this data is fetched from
|
|
||||||
the current backend deployment too.
|
|
||||||
</Paragraph>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<div className="bg-buttons-secondary rounded-xl scale-95 py-3 px-5 mb-2">
|
|
||||||
<p className="font-bold text-buttons-secondaryText">
|
|
||||||
Fail Count: {failStatusCount}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="bg-buttons-secondary rounded-xl scale-95 py-3 px-5 mb-2">
|
|
||||||
<p className="font-bold text-buttons-secondaryText">
|
|
||||||
Success Count: {successStatusCount}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<Button
|
|
||||||
className="py-px w-40 box-content bg-buttons-secondary hover:bg-buttons-secondaryHover bg-opacity-90 text-buttons-secondaryText justify-center items-center inline-block"
|
|
||||||
onClick={() => navigate("/flix")}
|
|
||||||
>
|
|
||||||
Go back
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pl-6 pr-6">
|
|
||||||
<Divider marginClass="my-3" />
|
|
||||||
{getItemsForCurrentPage().map((item) => {
|
|
||||||
return (
|
|
||||||
<ConfigValue
|
|
||||||
key={item.tmdbFullId}
|
|
||||||
name={`${
|
|
||||||
item.providerId.charAt(0).toUpperCase() +
|
|
||||||
item.providerId.slice(1)
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{`Requests: `}
|
|
||||||
<strong>{parseInt(item.count, 10).toLocaleString()}</strong>
|
|
||||||
</ConfigValue>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</ThiccContainer>
|
|
||||||
</SubPageLayout>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -26,7 +26,6 @@ import { OnboardingProxyPage } from "@/pages/onboarding/OnboardingProxy";
|
||||||
import { RegisterPage } from "@/pages/Register";
|
import { RegisterPage } from "@/pages/Register";
|
||||||
import { SupportPage } from "@/pages/Support";
|
import { SupportPage } from "@/pages/Support";
|
||||||
import { TopFlix } from "@/pages/TopFlix";
|
import { TopFlix } from "@/pages/TopFlix";
|
||||||
import { TopSources } from "@/pages/TopSources";
|
|
||||||
import { Layout } from "@/setup/Layout";
|
import { Layout } from "@/setup/Layout";
|
||||||
import { useHistoryListener } from "@/stores/history";
|
import { useHistoryListener } from "@/stores/history";
|
||||||
import { LanguageProvider } from "@/stores/language";
|
import { LanguageProvider } from "@/stores/language";
|
||||||
|
@ -153,7 +152,6 @@ function App() {
|
||||||
<Route path="/support" element={<SupportPage />} />
|
<Route path="/support" element={<SupportPage />} />
|
||||||
{/* Top flix page */}
|
{/* Top flix page */}
|
||||||
<Route path="/flix" element={<TopFlix />} />
|
<Route path="/flix" element={<TopFlix />} />
|
||||||
<Route path="/flix/sources" element={<TopSources />} />
|
|
||||||
{/* Settings page */}
|
{/* Settings page */}
|
||||||
<Route
|
<Route
|
||||||
path="/settings"
|
path="/settings"
|
||||||
|
|
|
@ -26,7 +26,6 @@ import { OnboardingProxyPage } from "@/pages/onboarding/OnboardingProxy";
|
||||||
import { RegisterPage } from "@/pages/Register";
|
import { RegisterPage } from "@/pages/Register";
|
||||||
import { SupportPage } from "@/pages/Support";
|
import { SupportPage } from "@/pages/Support";
|
||||||
import { TopFlix } from "@/pages/TopFlix";
|
import { TopFlix } from "@/pages/TopFlix";
|
||||||
import { TopSources } from "@/pages/TopSources";
|
|
||||||
import { Layout } from "@/setup/Layout";
|
import { Layout } from "@/setup/Layout";
|
||||||
import { useHistoryListener } from "@/stores/history";
|
import { useHistoryListener } from "@/stores/history";
|
||||||
import { LanguageProvider } from "@/stores/language";
|
import { LanguageProvider } from "@/stores/language";
|
||||||
|
@ -87,7 +86,6 @@ function QueryView() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duplicate of @/src/setup/App.tsx
|
|
||||||
function App() {
|
function App() {
|
||||||
useHistoryListener();
|
useHistoryListener();
|
||||||
useOnlineListener();
|
useOnlineListener();
|
||||||
|
@ -154,7 +152,6 @@ function App() {
|
||||||
<Route path="/support" element={<SupportPage />} />
|
<Route path="/support" element={<SupportPage />} />
|
||||||
{/* Top flix page */}
|
{/* Top flix page */}
|
||||||
<Route path="/flix" element={<TopFlix />} />
|
<Route path="/flix" element={<TopFlix />} />
|
||||||
<Route path="/flix/sources" element={<TopSources />} />
|
|
||||||
{/* Settings page */}
|
{/* Settings page */}
|
||||||
<Route
|
<Route
|
||||||
path="/settings"
|
path="/settings"
|
||||||
|
@ -182,3 +179,5 @@ function App() {
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
Loading…
Reference in a new issue