mirror of
https://github.com/p-stream/p-stream.git
synced 2026-05-07 07:09:55 +00:00
Inject popup ad for xprime sources
Xprime's own site has ads, but people have found pstream (which doesnt have ads) and moves here since there are no ads. Xprime is losing money and is finding it difficult to support the proxies and servers.
This commit is contained in:
parent
1e00777c64
commit
9f5be225b5
10 changed files with 148 additions and 2 deletions
|
|
@ -1126,6 +1126,9 @@
|
|||
"doubleClickToSeek": "Double tap to seek",
|
||||
"doubleClickToSeekDescription": "Double tap on the left or right side of the player to seek 10 seconds forward or backward.",
|
||||
"doubleClickToSeekLabel": "Enable double tap to seek",
|
||||
"disableXPrimeAds": "XPrime ads",
|
||||
"disableXPrimeAdsDescription": "Disable popup ads and notifications when watching content from XPrime sources. XPrime uses ads to support their service and keep it free for you and us to use.",
|
||||
"disableXPrimeAdsLabel": "Disable XPrime ads",
|
||||
"sourceOrder": "Reordering sources",
|
||||
"sourceOrderDescription": "Drag and drop to reorder sources. This will determine the order in which sources are checked for the media you are trying to watch. If a source is greyed out, it means the <bold>extension</bold> is required for that source. <br><br> <strong>(The default order is best for most users)</strong>",
|
||||
"sourceOrderEnableLabel": "Custom source order",
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export interface SettingsInput {
|
|||
homeSectionOrder?: string[] | null;
|
||||
manualSourceSelection?: boolean;
|
||||
enableDoubleClickToSeek?: boolean;
|
||||
disableXPrimeAds?: boolean;
|
||||
}
|
||||
|
||||
export interface SettingsResponse {
|
||||
|
|
@ -69,6 +70,7 @@ export interface SettingsResponse {
|
|||
homeSectionOrder?: string[] | null;
|
||||
manualSourceSelection?: boolean;
|
||||
enableDoubleClickToSeek?: boolean;
|
||||
disableXPrimeAds?: boolean;
|
||||
}
|
||||
|
||||
export function updateSettings(
|
||||
|
|
|
|||
56
src/components/player/atoms/XPrimeAdOverlay.tsx
Normal file
56
src/components/player/atoms/XPrimeAdOverlay.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { Flare } from "@/components/utils/Flare";
|
||||
import { Transition } from "@/components/utils/Transition";
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
|
||||
export function XPrimeAdOverlay() {
|
||||
const sourceId = usePlayerStore((s) => s.sourceId);
|
||||
const status = usePlayerStore((s) => s.status);
|
||||
const disableXPrimeAds = usePreferencesStore((s) => s.disableXPrimeAds);
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (sourceId === "xprime" && status === "playing" && !disableXPrimeAds) {
|
||||
setShow(true);
|
||||
const timer = setTimeout(() => {
|
||||
setShow(false);
|
||||
}, 5000); // Hide after 5 seconds
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
setShow(false);
|
||||
}, [sourceId, status, disableXPrimeAds]);
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition
|
||||
animation="slide-down"
|
||||
show
|
||||
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-96 h-full rounded-lg transition-colors text-video-context-type-main">
|
||||
<Flare.Light
|
||||
enabled
|
||||
flareSize={200}
|
||||
cssColorVar="--colors-video-context-light"
|
||||
backgroundClass="bg-video-context-background duration-100"
|
||||
className="rounded-lg"
|
||||
/>
|
||||
<Flare.Child className="grid grid-cols-[auto,1fr] gap-3 pointer-events-auto relative transition-transform">
|
||||
<Icon className="text-xl" icon={Icons.CIRCLE_EXCLAMATION} />
|
||||
<div className="w-full flex items-center">
|
||||
<span className="text-sm text-center">
|
||||
XPrime uses ads, but they can be disabled from settings!
|
||||
</span>
|
||||
</div>
|
||||
</Flare.Child>
|
||||
</Flare.Base>
|
||||
</Transition>
|
||||
);
|
||||
}
|
||||
|
|
@ -18,11 +18,13 @@ import {
|
|||
isUrlAlreadyProxied,
|
||||
} from "@/components/player/utils/proxy";
|
||||
import { useLanguageStore } from "@/stores/language";
|
||||
import { usePlayerStore } from "@/stores/player/store";
|
||||
import {
|
||||
LoadableSource,
|
||||
SourceQuality,
|
||||
getPreferredQuality,
|
||||
} from "@/stores/player/utils/qualities";
|
||||
import { usePreferencesStore } from "@/stores/preferences";
|
||||
import { processCdnLink } from "@/utils/cdn";
|
||||
import {
|
||||
canChangeVolume,
|
||||
|
|
@ -381,6 +383,24 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Inject popup ad for xprime sources
|
||||
const sourceId = usePlayerStore.getState().sourceId;
|
||||
const disableXPrimeAds = usePreferencesStore.getState().disableXPrimeAds;
|
||||
if (
|
||||
sourceId === "xprime" &&
|
||||
!disableXPrimeAds &&
|
||||
!document.querySelector(
|
||||
'script[data-cfasync="false"][src*="jg.prisagedibbuk.com"]',
|
||||
)
|
||||
) {
|
||||
const script = document.createElement("script");
|
||||
script.setAttribute("data-cfasync", "false");
|
||||
script.async = true;
|
||||
script.type = "text/javascript";
|
||||
script.src = "//jg.prisagedibbuk.com/r47OViiCQMeGnyQ/131974";
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
});
|
||||
videoElement.addEventListener("waiting", () => emit("loading", true));
|
||||
videoElement.addEventListener("volumechange", () =>
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ export function useSettingsState(
|
|||
homeSectionOrder: string[],
|
||||
manualSourceSelection: boolean,
|
||||
enableDoubleClickToSeek: boolean,
|
||||
disableXPrimeAds: boolean,
|
||||
) {
|
||||
const [proxyUrlsState, setProxyUrls, resetProxyUrls, proxyUrlsChanged] =
|
||||
useDerived(proxyUrls);
|
||||
|
|
@ -262,6 +263,12 @@ export function useSettingsState(
|
|||
resetEnableDoubleClickToSeek,
|
||||
enableDoubleClickToSeekChanged,
|
||||
] = useDerived(enableDoubleClickToSeek);
|
||||
const [
|
||||
disableXPrimeAdsState,
|
||||
setDisableXPrimeAdsState,
|
||||
resetDisableXPrimeAds,
|
||||
disableXPrimeAdsChanged,
|
||||
] = useDerived(disableXPrimeAds);
|
||||
|
||||
function reset() {
|
||||
resetTheme();
|
||||
|
|
@ -299,6 +306,7 @@ export function useSettingsState(
|
|||
resetHomeSectionOrder();
|
||||
resetManualSourceSelection();
|
||||
resetEnableDoubleClickToSeek();
|
||||
resetDisableXPrimeAds();
|
||||
}
|
||||
|
||||
const changed =
|
||||
|
|
@ -336,7 +344,8 @@ export function useSettingsState(
|
|||
enableHoldToBoostChanged ||
|
||||
homeSectionOrderChanged ||
|
||||
manualSourceSelectionChanged ||
|
||||
enableDoubleClickToSeekChanged;
|
||||
enableDoubleClickToSeekChanged ||
|
||||
disableXPrimeAdsChanged;
|
||||
|
||||
return {
|
||||
reset,
|
||||
|
|
@ -516,5 +525,10 @@ export function useSettingsState(
|
|||
set: setEnableDoubleClickToSeekState,
|
||||
changed: enableDoubleClickToSeekChanged,
|
||||
},
|
||||
disableXPrimeAds: {
|
||||
state: disableXPrimeAdsState,
|
||||
set: setDisableXPrimeAdsState,
|
||||
changed: disableXPrimeAdsChanged,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -486,6 +486,9 @@ export function SettingsPage() {
|
|||
(s) => s.setEnableDoubleClickToSeek,
|
||||
);
|
||||
|
||||
const disableXPrimeAds = usePreferencesStore((s) => s.disableXPrimeAds);
|
||||
const setDisableXPrimeAds = usePreferencesStore((s) => s.setDisableXPrimeAds);
|
||||
|
||||
const account = useAuthStore((s) => s.account);
|
||||
const updateProfile = useAuthStore((s) => s.setAccountProfile);
|
||||
const updateDeviceName = useAuthStore((s) => s.updateDeviceName);
|
||||
|
|
@ -557,6 +560,7 @@ export function SettingsPage() {
|
|||
homeSectionOrder,
|
||||
manualSourceSelection,
|
||||
enableDoubleClickToSeek,
|
||||
disableXPrimeAds,
|
||||
);
|
||||
|
||||
const availableSources = useMemo(() => {
|
||||
|
|
@ -622,7 +626,8 @@ export function SettingsPage() {
|
|||
state.enableHoldToBoost.changed ||
|
||||
state.homeSectionOrder.changed ||
|
||||
state.manualSourceSelection.changed ||
|
||||
state.enableDoubleClickToSeek
|
||||
state.enableDoubleClickToSeek ||
|
||||
state.disableXPrimeAds.changed
|
||||
) {
|
||||
await updateSettings(backendUrl, account, {
|
||||
applicationLanguage: state.appLanguage.state,
|
||||
|
|
@ -651,6 +656,7 @@ export function SettingsPage() {
|
|||
homeSectionOrder: state.homeSectionOrder.state,
|
||||
manualSourceSelection: state.manualSourceSelection.state,
|
||||
enableDoubleClickToSeek: state.enableDoubleClickToSeek.state,
|
||||
disableXPrimeAds: state.disableXPrimeAds.state,
|
||||
});
|
||||
}
|
||||
if (state.deviceName.changed) {
|
||||
|
|
@ -705,6 +711,7 @@ export function SettingsPage() {
|
|||
setHomeSectionOrder(state.homeSectionOrder.state);
|
||||
setManualSourceSelection(state.manualSourceSelection.state);
|
||||
setEnableDoubleClickToSeek(state.enableDoubleClickToSeek.state);
|
||||
setDisableXPrimeAds(state.disableXPrimeAds.state);
|
||||
|
||||
if (state.profile.state) {
|
||||
updateProfile(state.profile.state);
|
||||
|
|
@ -757,6 +764,7 @@ export function SettingsPage() {
|
|||
setHomeSectionOrder,
|
||||
setManualSourceSelection,
|
||||
setEnableDoubleClickToSeek,
|
||||
setDisableXPrimeAds,
|
||||
]);
|
||||
return (
|
||||
<SubPageLayout>
|
||||
|
|
@ -838,6 +846,8 @@ export function SettingsPage() {
|
|||
setManualSourceSelection={state.manualSourceSelection.set}
|
||||
enableDoubleClickToSeek={state.enableDoubleClickToSeek.state}
|
||||
setEnableDoubleClickToSeek={state.enableDoubleClickToSeek.set}
|
||||
disableXPrimeAds={state.disableXPrimeAds.state}
|
||||
setDisableXPrimeAds={state.disableXPrimeAds.set}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { Player } from "@/components/player";
|
|||
import { SkipIntroButton } from "@/components/player/atoms/SkipIntroButton";
|
||||
import { UnreleasedEpisodeOverlay } from "@/components/player/atoms/UnreleasedEpisodeOverlay";
|
||||
import { WatchPartyStatus } from "@/components/player/atoms/WatchPartyStatus";
|
||||
import { XPrimeAdOverlay } from "@/components/player/atoms/XPrimeAdOverlay";
|
||||
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
|
||||
import { useSkipTime } from "@/components/player/hooks/useSkipTime";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
|
|
@ -227,6 +228,7 @@ export function PlayerPart(props: PlayerPartProps) {
|
|||
<Player.SubtitleDelayPopout />
|
||||
<Player.SpeedChangedPopout />
|
||||
<UnreleasedEpisodeOverlay />
|
||||
<XPrimeAdOverlay />
|
||||
|
||||
<Player.NextEpisodeButton
|
||||
controlsShowing={showTargets}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { FlagIcon } from "@/components/FlagIcon";
|
|||
import { Dropdown } from "@/components/form/Dropdown";
|
||||
import { SortableListWithToggles } from "@/components/form/SortableListWithToggles";
|
||||
import { Heading1 } from "@/components/utils/Text";
|
||||
import { conf } from "@/setup/config";
|
||||
import { appLanguageOptions } from "@/setup/i18n";
|
||||
import { isAutoplayAllowed } from "@/utils/autoplay";
|
||||
import { getLocaleInfo, sortLangCodes } from "@/utils/language";
|
||||
|
|
@ -39,6 +40,8 @@ export function PreferencesPart(props: {
|
|||
setManualSourceSelection: (v: boolean) => void;
|
||||
enableDoubleClickToSeek: boolean;
|
||||
setEnableDoubleClickToSeek: (v: boolean) => void;
|
||||
disableXPrimeAds: boolean;
|
||||
setDisableXPrimeAds: (v: boolean) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const sorted = sortLangCodes(appLanguageOptions.map((item) => item.code));
|
||||
|
|
@ -184,6 +187,7 @@ export function PreferencesPart(props: {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Low Performance Mode */}
|
||||
<div>
|
||||
<p className="text-white font-bold mb-3">
|
||||
|
|
@ -244,6 +248,29 @@ export function PreferencesPart(props: {
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Disable XPrime Ads */}
|
||||
{conf().XPRIME_ADS && (
|
||||
<div>
|
||||
<p className="text-white font-bold mb-3">
|
||||
{t("settings.preferences.disableXPrimeAds")}
|
||||
</p>
|
||||
<p className="max-w-[25rem] font-medium">
|
||||
{t("settings.preferences.disableXPrimeAdsDescription")}
|
||||
</p>
|
||||
<div
|
||||
onClick={() =>
|
||||
props.setDisableXPrimeAds(!props.disableXPrimeAds)
|
||||
}
|
||||
className="bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg"
|
||||
>
|
||||
<Toggle enabled={props.disableXPrimeAds} />
|
||||
<p className="flex-1 text-white font-bold">
|
||||
{t("settings.preferences.disableXPrimeAdsLabel")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Column */}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ interface Config {
|
|||
BANNER_MESSAGE: string;
|
||||
BANNER_ID: string;
|
||||
USE_TRAKT: boolean;
|
||||
XPRIME_ADS: boolean;
|
||||
}
|
||||
|
||||
export interface RuntimeConfig {
|
||||
|
|
@ -62,6 +63,7 @@ export interface RuntimeConfig {
|
|||
BANNER_MESSAGE: string | null;
|
||||
BANNER_ID: string | null;
|
||||
USE_TRAKT: boolean;
|
||||
XPRIME_ADS: boolean;
|
||||
}
|
||||
|
||||
const env: Record<keyof Config, undefined | string> = {
|
||||
|
|
@ -94,6 +96,7 @@ const env: Record<keyof Config, undefined | string> = {
|
|||
BANNER_MESSAGE: import.meta.env.VITE_BANNER_MESSAGE,
|
||||
BANNER_ID: import.meta.env.VITE_BANNER_ID,
|
||||
USE_TRAKT: import.meta.env.VITE_USE_TRAKT,
|
||||
XPRIME_ADS: import.meta.env.VITE_XPRIME_ADS,
|
||||
};
|
||||
|
||||
function coerceUndefined(value: string | null | undefined): string | undefined {
|
||||
|
|
@ -169,5 +172,6 @@ export function conf(): RuntimeConfig {
|
|||
BANNER_MESSAGE: getKey("BANNER_MESSAGE"),
|
||||
BANNER_ID: getKey("BANNER_ID"),
|
||||
USE_TRAKT: getKey("USE_TRAKT", "false") === "true",
|
||||
XPRIME_ADS: getKey("XPRIME_ADS", "false") === "true",
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export interface PreferencesStore {
|
|||
homeSectionOrder: string[];
|
||||
manualSourceSelection: boolean;
|
||||
enableDoubleClickToSeek: boolean;
|
||||
disableXPrimeAds: boolean;
|
||||
|
||||
setEnableThumbnails(v: boolean): void;
|
||||
setEnableAutoplay(v: boolean): void;
|
||||
|
|
@ -58,6 +59,7 @@ export interface PreferencesStore {
|
|||
setHomeSectionOrder(v: string[]): void;
|
||||
setManualSourceSelection(v: boolean): void;
|
||||
setEnableDoubleClickToSeek(v: boolean): void;
|
||||
setDisableXPrimeAds(v: boolean): void;
|
||||
}
|
||||
|
||||
export const usePreferencesStore = create(
|
||||
|
|
@ -90,6 +92,7 @@ export const usePreferencesStore = create(
|
|||
homeSectionOrder: ["watching", "bookmarks"],
|
||||
manualSourceSelection: false,
|
||||
enableDoubleClickToSeek: false,
|
||||
disableXPrimeAds: false,
|
||||
setEnableThumbnails(v) {
|
||||
set((s) => {
|
||||
s.enableThumbnails = v;
|
||||
|
|
@ -230,6 +233,11 @@ export const usePreferencesStore = create(
|
|||
s.enableDoubleClickToSeek = v;
|
||||
});
|
||||
},
|
||||
setDisableXPrimeAds(v) {
|
||||
set((s) => {
|
||||
s.disableXPrimeAds = v;
|
||||
});
|
||||
},
|
||||
})),
|
||||
{
|
||||
name: "__MW::preferences",
|
||||
|
|
|
|||
Loading…
Reference in a new issue