Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions apps/web/__tests__/allanime-aareq.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,31 @@ describe("allanime aaReq helpers", () => {
).toBe("351b496f677e5d86758b86ce0546bd64a9fabb7769adf6813d294f1756fb4d23");
});

it("builds mkissa colon-ordered boot tokens with live mask params", () => {
const maskParts = [
"Xe0sbWji894=",
"kDl8ZYLJjcY=",
"Zp9QHHse7BY=",
"UscVFCx+xrI=",
] as const;
const maskParams = {
saltMul: 241,
saltAdd: 209,
fragMul: 210,
fragAdd: 42,
};
const maskKey = deriveAllanimeMaskKey(maskParts, "168", maskParams);
expect(maskKey.toString("hex")).toBe(
"bd33f386638e49a623be5721d45584ff15bb5abcaacffd9c7c0baced29108a18",
);
expect(
buildAllanimeBootToken(maskKey, "168", 2955, ALLANIME_EPISODE_LANE, {
bootPrefix: "vmcFXS3Dmg:",
messageJoin: ":",
}),
).toBe("07bb64a548d57e794eb33543cc2cad32f149fa768b17f01166e67e3aadeadab6");
});

it("still discovers legacy buildId ternaries", () => {
expect(extractAllanimeBuildId(`Am=typeof x!=="string"?"119":""`)).toBe(
"119",
Expand Down
19 changes: 19 additions & 0 deletions apps/web/__tests__/kitsu-fallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,25 @@ describe("kitsu client", () => {
expect(path).not.toContain("filter%5Bseason%5D");
});

it("uses the 20-item Kitsu cap for hasNextPage when callers request more", async () => {
vi.stubGlobal(
"fetch",
vi.fn(
async () =>
new Response(
JSON.stringify({
data: [kitsuItem("46474")],
included: [],
meta: { count: 22 },
}),
),
),
);
const result = await fetchKitsuAnimePage({ page: 1, perPage: 24 });
expect(result?.hasNextPage).toBe(true);
expect(result?.totalPages).toBe(2);
});

it("parses anilist + mal mappings from included", async () => {
vi.stubGlobal(
"fetch",
Expand Down
4 changes: 2 additions & 2 deletions apps/web/__tests__/watchlist-button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ vi.mock("next-auth/react", () => ({
useSession: vi.fn(),
}));

vi.mock("@/app/watchlist/actions", () => ({
vi.mock("@/app/(library)/watchlist/actions", () => ({
getWatchlistItem: vi.fn(),
}));

Expand All @@ -23,7 +23,7 @@ vi.mock("sonner", () => ({
}));

const { WatchlistButton } = await import("@/components/watchlist/watchlist");
const watchlistActions = await import("@/app/watchlist/actions");
const watchlistActions = await import("@/app/(library)/watchlist/actions");

const mockUseSession = vi.mocked(useSession);
const mockGetWatchlistItem = vi.mocked(watchlistActions.getWatchlistItem);
Expand Down
File renamed without changes.
49 changes: 49 additions & 0 deletions apps/web/app/(catalog)/ad-free/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { IndexHeader } from "@/components/catalog/index-header";
import { IndexPage } from "@/components/catalog/index-page";
import { LegalPage } from "@/components/layout/legal-page";
import Link from "next/link";

export const metadata = {
title: "Big News",
};

export default function AdFreePage() {
return (
<IndexPage header={<IndexHeader title="Ad-Free, On Purpose" />}>
<LegalPage title="Ad-Free, On Purpose">
<div className="mx-auto max-w-2xl space-y-6 text-left">
<p className="text-lg leading-relaxed text-foreground sm:text-xl">
Despite being yet another streaming site that could make money from
ads on every single thing you can click on here, we don&apos;t care.
</p>
<p className="text-base leading-relaxed text-muted-foreground sm:text-lg">
That said, we&apos;re now ad-free.{" "}
<Link
href="https://nyuma.dev/b/downloader#nyumatflix"
target="_blank"
rel="noopener noreferrer"
className="text-foreground underline underline-offset-4 hover:text-primary"
>
I talk about "how" in more detail here
</Link>
. So, when you click play, you'll see the new UI. It's pretty
intuitive
</p>

<p className="text-base leading-relaxed text-muted-foreground sm:text-lg">
but remember fam, if it needs fixing, we're{" "}
<Link
href="https://github.com/nyumat/nyumatflix"
target="_blank"
rel="noopener noreferrer"
className="text-foreground underline underline-offset-4 hover:text-primary"
>
open source
</Link>
.
</p>
</div>
</LegalPage>
</IndexPage>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DetailPageLoading } from "@/components/layout/page-loading/detail-page-loading";
import { resolveAnilistTvDetailRedirects } from "@/lib/server/anime-tv-detail-route";
import { resolveKitsuAnimeDetailRedirects } from "@/lib/server/kitsu-anime-detail-route";
import { resolveMalAnimeDetailRedirects } from "@/lib/server/mal-anime-detail-route";
import { resolveTmdbAnimeDetailRedirects } from "@/lib/server/tmdb-anime-detail-route";
import { TvShowDetailLayoutContent } from "@/lib/server/tv-detail-layout-content";
Expand All @@ -17,6 +18,7 @@ export default async function AnimeDetailLayout({ children, params }: Props) {

await resolveMalAnimeDetailRedirects(id);
await resolveTmdbAnimeDetailRedirects(id);
await resolveKitsuAnimeDetailRedirects(id);
await resolveAnilistTvDetailRedirects(id);

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AnimeResultsGrid } from "@/components/anilist/anime-results-grid";
import { CatalogPageShell } from "@/components/catalog/catalog-page-shell";
import { CatalogGridFallback } from "@/components/catalog/catalog-suspense-fallbacks";
import { Button } from "@/components/ui/button";
import { parseAniListSearchParams } from "@/lib/anilist";
import { parseAniListSearchParams } from "@/lib/anilist-shared";
import { normalizeRouteSearchParams } from "@/lib/utils";
import type { Metadata } from "next";
import Link from "next/link";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
AnimeHubGenreRowsPart1,
AnimeHubGenreRowsPart2,
AnimeHubGenreRowsPart3,
getAnimeHubAmbientBackdrop,
AnimeHubHero,
AnimeHubHentaiCarousel,
AnimeHubMoviesCarousel,
Expand All @@ -15,6 +16,7 @@ import {
import { AnimeHeroFallback } from "@/components/anilist/anime-suspense-fallbacks";
import { AnimeResultsGrid } from "@/components/anilist/anime-results-grid";
import { CatalogPageShell } from "@/components/catalog/catalog-page-shell";
import { IndexPage } from "@/components/catalog/index-page";
import {
CatalogGridFallback,
CatalogRowFallback,
Expand All @@ -26,12 +28,12 @@ import {
ANIME_BROWSE_PATH,
isAniListResultsLayout,
parseAniListSearchParams,
} from "@/lib/anilist";
} from "@/lib/anilist-shared";
import { normalizeRouteSearchParams } from "@/lib/utils";
import type { Metadata } from "next";
import Link from "next/link";
import { redirect } from "next/navigation";
import { Suspense } from "react";
import { Suspense, type ReactNode } from "react";

export const revalidate = 3600;

Expand Down Expand Up @@ -89,53 +91,55 @@ const redirectLegacyAnimeModeParam = (sp: Record<string, string>) => {
redirect(query ? `/anime?${query}` : ANIME_BROWSE_PATH);
};

const AnimeHubHome = () => (
const AnimeHubHome = ({ chrome }: { chrome?: ReactNode }) => (
<>
<Suspense fallback={<AnimeHeroFallback />}>
<AnimeHubHero />
</Suspense>

<Suspense fallback={<RecentlyWatchedRowFallback />}>
<RecentlyWatchedRow scope="anime" />
{chrome}

<Suspense fallback={<RecentlyWatchedRowFallback bleed />}>
<RecentlyWatchedRow bleed scope="anime" />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubRankedRow />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubTrendingCarousel />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubPopularCarousel />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubSeasonCarousel />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubAiringCarousel />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubMoviesCarousel />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubGenreRowsPart1 />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubGenreRowsPart2 />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubGenreRowsPart3 />
</Suspense>

<Suspense fallback={<CatalogRowFallback />}>
<Suspense fallback={<CatalogRowFallback bleed />}>
<AnimeHubHentaiCarousel />
</Suspense>
</>
Expand All @@ -149,26 +153,39 @@ export default async function AnimePage(props: PageProps) {
const params = parseAniListSearchParams(sp);
const currentPage = toPageNumber(sp.page);
const isResultsLayout = isAniListResultsLayout(sp);
const backdrop = isResultsLayout ? null : await getAnimeHubAmbientBackdrop();

if (!isResultsLayout) {
return (
<IndexPage backdrop={backdrop}>
<AnimeHubHome
chrome={
<div className="flex flex-wrap items-center justify-between gap-2">
<AniListFiltersDynamic serverParams={sp} />
<Button asChild variant="outline">
<Link href={ANIME_BROWSE_PATH}>All anime</Link>
</Button>
</div>
}
/>
</IndexPage>
);
}

return (
<CatalogPageShell
title="Anime"
backdrop={null}
toolbar={<AniListFiltersDynamic serverParams={sp} />}
action={
<Button asChild variant="outline">
<Link href={isResultsLayout ? "/anime" : ANIME_BROWSE_PATH}>
{isResultsLayout ? "Anime home" : "All anime"}
</Link>
<Link href="/anime">Anime home</Link>
</Button>
}
>
{isResultsLayout ? (
<Suspense fallback={<CatalogGridFallback />}>
<AnimeResultsGrid params={params} page={currentPage} />
</Suspense>
) : (
<AnimeHubHome />
)}
<Suspense fallback={<CatalogGridFallback />}>
<AnimeResultsGrid params={params} page={currentPage} />
</Suspense>
</CatalogPageShell>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { ContentContainer } from "@/components/layout/content-container";
import { PageContainer } from "@/components/layout/page-container";
import { catalogCardsToMediaItems } from "@/lib/cards/catalog-dto";
import { fetchCountryBrowsePage } from "@/lib/server/browse-catalog";
import { tmdbImage } from "@/tmdb/utils";
import { getFriendlyCountryName } from "@/utils/country-helpers";
import { countries } from "country-data-list";
import BrowseCountryClient from "./browse-client";
Expand Down Expand Up @@ -34,21 +33,16 @@ export default async function BrowseCountryPage(props: PageProps) {
);
const initialItems = catalogCardsToMediaItems(data.results);

const backdropImage =
initialItems.length > 0 && initialItems[0].backdrop_path
? tmdbImage.backdrop(initialItems[0].backdrop_path, "w1280")
: "/movie-banner.webp";

return (
<PageContainer className="pb-16">
<StaticHero
imageUrl={backdropImage}
imageUrl="/movie-banner.webp"
title={countryName}
route=""
hideTitle
/>
<ContentContainer className="relative z-10" topSpacing={false}>
<div className="mx-auto w-full max-w-7xl space-y-8 px-2 pb-12 sm:px-4">
<div className="index-container space-y-8 pb-12">
<div className="flex flex-col gap-4 pb-2 pt-20 md:pt-28">
<div className="text-center">
<h1 className="mb-4 flex flex-wrap items-center justify-center gap-2 text-3xl font-bold tracking-tight text-foreground sm:mb-6 sm:gap-4 sm:text-5xl md:text-6xl">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ContentContainer } from "@/components/layout/content-container";
import { PageContainer } from "@/components/layout/page-container";
import { catalogCardsToMediaItems } from "@/lib/cards/catalog-dto";
import { fetchGenreBrowsePage } from "@/lib/server/browse-catalog";
import { tmdbImage } from "@/tmdb/utils";
import BrowseGenreClient from "./browse-client";

interface PageProps {
Expand All @@ -30,21 +29,16 @@ export default async function BrowseGenrePage(props: PageProps) {
const genreName = matchedGenre ? matchedGenre.name : "Unknown Genre";
const initialItems = catalogCardsToMediaItems(data.results);

const backdropImage =
initialItems.length > 0 && initialItems[0].backdrop_path
? tmdbImage.backdrop(initialItems[0].backdrop_path, "w1280")
: "/movie-banner.webp";

return (
<PageContainer className="pb-16">
<StaticHero
imageUrl={backdropImage}
imageUrl="/movie-banner.webp"
title={genreName}
route=""
hideTitle
/>
<ContentContainer className="relative z-10" topSpacing={false}>
<div className="mx-auto w-full max-w-7xl space-y-8 px-2 pb-12 sm:px-4">
<div className="index-container space-y-8 pb-12">
<div className="flex flex-col gap-4 pb-2 pt-20 md:pt-28">
<div className="text-center">
<h1 className="mb-4 text-3xl font-bold tracking-tight text-foreground sm:mb-6 sm:text-5xl md:text-6xl">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default async function CollectionPage(props: Props) {
hideTitle
/>
<ContentContainer className="relative z-10" topSpacing={false}>
<div className="mx-auto w-full max-w-7xl space-y-6 px-2 pb-12 sm:px-4">
<div className="index-container space-y-8 pb-12">
<div className="flex flex-col gap-4 pb-8 pt-20 md:pt-28">
<BackButton fallbackUrl="/movies" />
<div>
Expand Down
File renamed without changes.
Loading
Loading