mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-18 13:22:07 +00:00
Merge branch 'p-stream:production' into chromecast-rework
This commit is contained in:
commit
575521cc88
11 changed files with 126 additions and 50 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"packageManager": "pnpm@9.14.4",
|
||||
"name": "P-Stream",
|
||||
"version": "5.1.0",
|
||||
"version": "5.1.1",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/p-stream/p-stream",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,28 @@
|
|||
<lastBuildDate>Mon, 28 Jul 2025 21:53:00 GMT</lastBuildDate>
|
||||
<atom:link href="https://pstream.mov/notifications.xml" rel="self" type="application/rss+xml" />
|
||||
|
||||
<item>
|
||||
<guid>notification-037</guid>
|
||||
<title>P-Stream v5.1.1 released!</title>
|
||||
<description>Minor updates to fix bugs and add some QoL features.
|
||||
|
||||
- Now anyone can watch trailers on the details modal! A custom proxy or the extension no longer required. (Other IMDb info still requires the extension or proxy)
|
||||
- Added a resume watching page.
|
||||
- Added checks and label to see what embed a source is currently on. (e.x. fox for xprime)
|
||||
- Updated curated movie lists and editor picks!
|
||||
- Added an onboarding reminder for content that is not found but there are other sources that may have it.
|
||||
- Using a custom subtitle source with no baked in ads.
|
||||
- Fixed a broken URL issue caused by the Dood source.
|
||||
- Fixed 2x boost overlay sizing.
|
||||
- Fixed speed boost not working on mobile.
|
||||
- Other bug fixes!
|
||||
|
||||
P.S. I wanted to highlight TrinityHades, a community member, who is working on adding Safari support to the P-Stream extension! It's not quire ready yet but soon mac users will be able to use the extension to watch P-Stream on Safari!
|
||||
</description>
|
||||
<pubDate>Sun, 24 Aug 2025 22:37:00 MST</pubDate>
|
||||
<category>update</category>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<guid>notification-036</guid>
|
||||
<title>Some sources are currently down</title>
|
||||
|
|
|
|||
|
|
@ -106,16 +106,18 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
|
|||
undefined,
|
||||
undefined,
|
||||
formattedLanguage,
|
||||
data.type,
|
||||
);
|
||||
// Transform the data to match the expected format
|
||||
if (
|
||||
typeof imdbMetadata.imdb_rating === "number" &&
|
||||
typeof imdbMetadata.votes === "number"
|
||||
(typeof imdbMetadata.imdb_rating === "number" &&
|
||||
typeof imdbMetadata.votes === "number") ||
|
||||
imdbMetadata.trailer_url
|
||||
) {
|
||||
setImdbData({
|
||||
rating: imdbMetadata.imdb_rating,
|
||||
votes: imdbMetadata.votes,
|
||||
trailer_url: imdbMetadata.trailer_url || null,
|
||||
trailer_url: imdbMetadata.trailer_url,
|
||||
});
|
||||
} else {
|
||||
setImdbData(null);
|
||||
|
|
|
|||
|
|
@ -12,14 +12,23 @@ export function TrailerOverlay({ trailerUrl, onClose }: TrailerOverlayProps) {
|
|||
className="relative w-[90%] max-w-6xl aspect-video"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<video
|
||||
className="w-full h-full object-contain"
|
||||
autoPlay
|
||||
controls
|
||||
playsInline
|
||||
>
|
||||
<source src={trailerUrl} type="video/mp4" />
|
||||
</video>
|
||||
{trailerUrl.includes("youtube.com/embed") ? (
|
||||
<iframe
|
||||
src={trailerUrl}
|
||||
className="w-full h-full"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
/>
|
||||
) : (
|
||||
<video
|
||||
className="w-full h-full object-contain"
|
||||
autoPlay
|
||||
controls
|
||||
playsInline
|
||||
>
|
||||
<source src={trailerUrl} type="video/mp4" />
|
||||
</video>
|
||||
)}
|
||||
|
||||
{/* Close Button */}
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export function SpeedChangedPopout() {
|
|||
show={showSpeedIndicator && currentOverlay === "speed"}
|
||||
className="absolute inset-x-0 top-4 flex justify-center pointer-events-none"
|
||||
>
|
||||
<Flare.Base className="hover:flare-enabled pointer-events-auto bg-video-context-background pl-4 pr-6 py-3 group w-72 h-full rounded-lg transition-colors text-video-context-type-main">
|
||||
<Flare.Base className="hover:flare-enabled pointer-events-auto bg-video-context-background pl-4 pr-6 py-3 group w-20 h-full rounded-lg transition-colors text-video-context-type-main">
|
||||
<Flare.Light
|
||||
enabled
|
||||
flareSize={200}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export function EmbedOption(props: {
|
|||
routerId: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const currentEmbedId = usePlayerStore((s) => s.embedId);
|
||||
const unknownEmbedName = t("player.menus.sources.unknownOption");
|
||||
|
||||
const embedName = useMemo(() => {
|
||||
|
|
@ -45,7 +46,12 @@ export function EmbedOption(props: {
|
|||
);
|
||||
|
||||
return (
|
||||
<SelectableLink loading={loading} error={errored} onClick={run}>
|
||||
<SelectableLink
|
||||
loading={loading}
|
||||
error={errored}
|
||||
onClick={run}
|
||||
selected={props.embedId === currentEmbedId}
|
||||
>
|
||||
<span className="flex flex-col">
|
||||
<span>{embedName}</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ export function LegalPage() {
|
|||
<br />
|
||||
<br />
|
||||
We strongly recommend using VPN services for enhanced privacy
|
||||
and security while browsing.
|
||||
and security while browsing. Downloading is not advised.
|
||||
<br />
|
||||
<br />
|
||||
Please respect intellectual property rights and be mindful of
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ import { SourceSliceSource, StreamType } from "@/stores/player/utils/qualities";
|
|||
const testMeta: PlayerMeta = {
|
||||
releaseYear: 2010,
|
||||
title: "Sintel",
|
||||
tmdbId: "",
|
||||
tmdbId: "45745",
|
||||
type: "movie",
|
||||
poster: "https://image.tmdb.org/t/p/w342//4BMG9hk9NvSBeQvC82sVmVRK140.jpg",
|
||||
};
|
||||
|
||||
const testStreams: Record<StreamType, string> = {
|
||||
|
|
|
|||
|
|
@ -171,7 +171,13 @@ export function FeaturedCarousel({
|
|||
if (!hasExtension.current || !currentMedia?.external_ids?.imdb_id) return;
|
||||
|
||||
try {
|
||||
const imdbData = await scrapeIMDb(currentMedia.external_ids.imdb_id);
|
||||
const imdbData = await scrapeIMDb(
|
||||
currentMedia.external_ids.imdb_id,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
currentMedia.type,
|
||||
);
|
||||
// Only update if we have both rating and votes as non-null numbers
|
||||
if (
|
||||
typeof imdbData.imdb_rating === "number" &&
|
||||
|
|
|
|||
|
|
@ -67,6 +67,14 @@ export const EDITOR_PICKS_MOVIES = shuffleArray([
|
|||
{ id: 18971, type: "movie" }, // Rosencrantz and Guildenstern Are Dead
|
||||
{ id: 26388, type: "movie" }, // Buried
|
||||
{ id: 152601, type: "movie" }, // Her
|
||||
{ id: 11886, type: "movie" }, // Robin Hood
|
||||
{ id: 1362, type: "movie" }, // The Hobbit 1977
|
||||
{ id: 578, type: "movie" }, // Jaws
|
||||
{ id: 78, type: "movie" }, // Blade Runner
|
||||
{ id: 348, type: "movie" }, // Alien
|
||||
{ id: 198184, type: "movie" }, // Chappie
|
||||
{ id: 405774, type: "movie" }, // Bird Box
|
||||
{ id: 333339, type: "movie" }, // Ready Player One
|
||||
]);
|
||||
|
||||
export const EDITOR_PICKS_TV_SHOWS = shuffleArray([
|
||||
|
|
@ -86,6 +94,17 @@ export const EDITOR_PICKS_TV_SHOWS = shuffleArray([
|
|||
{ id: 93405, type: "show" }, // Squid Game
|
||||
{ id: 87108, type: "show" }, // Chernobyl
|
||||
{ id: 105248, type: "show" }, // Cyberpunk: Edgerunners
|
||||
{ id: 82738, type: "show" }, // IRODUKU: The World in Colors
|
||||
{ id: 615, type: "show" }, // Futurama
|
||||
{ id: 4625, type: "show" }, // The New Batman Adventures
|
||||
{ id: 513, type: "show" }, // Batman Beyond
|
||||
{ id: 110948, type: "show" }, // The Snoopy Show
|
||||
{ id: 110492, type: "show" }, // Peacemaker
|
||||
{ id: 125988, type: "show" }, // Silo
|
||||
{ id: 87917, type: "show" }, // For All Mankind
|
||||
{ id: 42009, type: "show" }, // Black Mirror
|
||||
{ id: 86831, type: "show" }, // Love, Death & Robots
|
||||
{ id: 261579, type: "show" }, // Secret Level
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -40,30 +40,30 @@ function getImdbLanguageCode(language: string): string {
|
|||
}
|
||||
|
||||
interface IMDbMetadata {
|
||||
title: string;
|
||||
original_title: string;
|
||||
title_type: string;
|
||||
year: number | null;
|
||||
end_year: number | null;
|
||||
day: number | null;
|
||||
month: number | null;
|
||||
date: string;
|
||||
runtime: number | null;
|
||||
age_rating: string;
|
||||
imdb_rating: number | null;
|
||||
votes: number | null;
|
||||
plot: string;
|
||||
poster_url: string;
|
||||
trailer_url: string;
|
||||
url: string;
|
||||
genre: string[];
|
||||
cast: string[];
|
||||
directors: string[];
|
||||
writers: string[];
|
||||
keywords: string[];
|
||||
countries: string[];
|
||||
languages: string[];
|
||||
locations: string[];
|
||||
title?: string;
|
||||
original_title?: string;
|
||||
title_type?: string;
|
||||
year?: number | null;
|
||||
end_year?: number | null;
|
||||
day?: number | null;
|
||||
month?: number | null;
|
||||
date?: string;
|
||||
runtime?: number | null;
|
||||
age_rating?: string;
|
||||
imdb_rating?: number | null;
|
||||
votes?: number | null;
|
||||
plot?: string;
|
||||
poster_url?: string;
|
||||
trailer_url?: string;
|
||||
url?: string;
|
||||
genre?: string[];
|
||||
cast?: string[];
|
||||
directors?: string[];
|
||||
writers?: string[];
|
||||
keywords?: string[];
|
||||
countries?: string[];
|
||||
languages?: string[];
|
||||
locations?: string[];
|
||||
season?: number;
|
||||
episode?: number;
|
||||
episode_title?: string;
|
||||
|
|
@ -115,12 +115,23 @@ export async function scrapeIMDb(
|
|||
season?: number,
|
||||
episode?: number,
|
||||
language?: string,
|
||||
type?: "movie" | "show",
|
||||
): Promise<IMDbMetadata> {
|
||||
// Check if we have a proxy or extension
|
||||
const hasExtension = await isExtensionActive();
|
||||
const hasProxy = Boolean(useAuthStore.getState().proxySet);
|
||||
|
||||
if (!hasExtension && !hasProxy) {
|
||||
// Custom API for trailers:
|
||||
const trailerResponse = await fetch(
|
||||
`https://fed-trailers.pstream.mov/${type === "movie" ? "movie" : "tv"}/${imdbId}`,
|
||||
).then((res) => res.json());
|
||||
if (trailerResponse.trailer?.embed_url) {
|
||||
return {
|
||||
trailer_url: trailerResponse.trailer.embed_url,
|
||||
};
|
||||
}
|
||||
// END CUSTOM API
|
||||
throw new Error(
|
||||
"IMDb scraping requires either the browser extension or a custom proxy to be set up. " +
|
||||
"Please install the extension or set up a proxy in the settings.",
|
||||
|
|
@ -300,17 +311,17 @@ export function printIMDbMetadata(metadata: IMDbMetadata): void {
|
|||
}
|
||||
console.log("Type:", metadata.title_type);
|
||||
console.log("Year:", metadata.year);
|
||||
console.log("Runtime:", formatRuntime(metadata.runtime));
|
||||
console.log("Runtime:", formatRuntime(metadata.runtime || null));
|
||||
console.log("Date:", metadata.date);
|
||||
console.log("Age Rating:", metadata.age_rating);
|
||||
console.log("Genre:", arrayToString(metadata.genre));
|
||||
console.log("Cast:", arrayToString(metadata.cast));
|
||||
console.log("Directed by:", arrayToString(metadata.directors));
|
||||
console.log("Writers:", arrayToString(metadata.writers));
|
||||
console.log("Countries:", arrayToString(metadata.countries));
|
||||
console.log("Filming Locations:", arrayToString(metadata.locations));
|
||||
console.log("Languages:", arrayToString(metadata.languages));
|
||||
console.log("Keywords:", arrayToString(metadata.keywords));
|
||||
console.log("Genre:", arrayToString(metadata.genre || []));
|
||||
console.log("Cast:", arrayToString(metadata.cast || []));
|
||||
console.log("Directed by:", arrayToString(metadata.directors || []));
|
||||
console.log("Writers:", arrayToString(metadata.writers || []));
|
||||
console.log("Countries:", arrayToString(metadata.countries || []));
|
||||
console.log("Filming Locations:", arrayToString(metadata.locations || []));
|
||||
console.log("Languages:", arrayToString(metadata.languages || []));
|
||||
console.log("Keywords:", arrayToString(metadata.keywords || []));
|
||||
|
||||
if (metadata.season && metadata.episode) {
|
||||
console.log("\nEpisode Details:");
|
||||
|
|
|
|||
Loading…
Reference in a new issue