1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-20 14:37:43 +01:00

fix multi origin and add airplay support

This commit is contained in:
Jelle van Snik 2023-01-22 20:51:58 +01:00
parent f472f04735
commit e7a6484094
9 changed files with 63 additions and 7 deletions

View file

@ -1,5 +1,5 @@
import { FetchError } from "ofetch";
import { makeUrl, mwFetch } from "../helpers/fetch";
import { makeUrl, proxiedFetch } from "../helpers/fetch";
import {
formatJWMeta,
JWMediaResult,
@ -45,7 +45,7 @@ export async function getMetaFromId(
type: queryType,
id,
});
data = await mwFetch<JWDetailedMeta>(url, { baseURL: JW_API_BASE });
data = await proxiedFetch<JWDetailedMeta>(url, { baseURL: JW_API_BASE });
} catch (err) {
if (err instanceof FetchError) {
// 400 and 404 are treated as not found
@ -69,7 +69,7 @@ export async function getMetaFromId(
const url = makeUrl("/content/titles/show_season/{id}/locale/en_US", {
id: seasonToScrape,
});
seasonData = await mwFetch<any>(url, { baseURL: JW_API_BASE });
seasonData = await proxiedFetch<any>(url, { baseURL: JW_API_BASE });
}
return {

View file

@ -1,5 +1,5 @@
import { SimpleCache } from "@/utils/cache";
import { mwFetch } from "../helpers/fetch";
import { proxiedFetch } from "../helpers/fetch";
import {
formatJWMeta,
JWContentTypes,
@ -42,7 +42,7 @@ export async function searchForMedia(query: MWQuery): Promise<MWMediaMeta[]> {
page_size: 40,
};
const data = await mwFetch<JWPage<JWMediaResult>>(
const data = await proxiedFetch<JWPage<JWMediaResult>>(
"/content/titles/en_US/popular",
{
baseURL: JW_API_BASE,

View file

@ -25,6 +25,7 @@ export enum Icons {
VOLUME_X = "volume_x",
X = "x",
EDIT = "edit",
AIRPLAY = "airplay",
}
export interface IconProps {
@ -57,6 +58,7 @@ const iconList: Record<Icons, string> = {
x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"/></svg>`,
edit: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>`,
bookmark_outline: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M336 0h-288C21.49 0 0 21.49 0 48v431.9c0 24.7 26.79 40.08 48.12 27.64L192 423.6l143.9 83.93C357.2 519.1 384 504.6 384 479.9V48C384 21.49 362.5 0 336 0zM336 452L192 368l-144 84V54C48 50.63 50.63 48 53.1 48h276C333.4 48 336 50.63 336 54V452z"/></svg>`,
airplay: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-airplay"><path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-1"></path><polygon fill="currentColor" points="12 15 17 21 7 21 12 15"></polygon></svg>`,
};
export const Icon = memo((props: IconProps) => {

View file

@ -1,6 +1,7 @@
import { MWMediaMeta } from "@/backend/metadata/types";
import { useCallback, useRef, useState } from "react";
import { CSSTransition } from "react-transition-group";
import { AirplayControl } from "./controls/AirplayControl";
import { BackdropControl } from "./controls/BackdropControl";
import { FullscreenControl } from "./controls/FullscreenControl";
import { LoadingControl } from "./controls/LoadingControl";
@ -91,6 +92,7 @@ export function DecoratedVideoPlayer(
<div className="flex items-center">
<LeftSideControls />
<div className="flex-1" />
<AirplayControl />
<FullscreenControl />
</div>
</div>

View file

@ -0,0 +1,26 @@
import { Icons } from "@/components/Icon";
import { useCallback } from "react";
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
import { useVideoPlayerState } from "../VideoContext";
interface Props {
className?: string;
}
export function AirplayControl(props: Props) {
const { videoState } = useVideoPlayerState();
const handleClick = useCallback(() => {
videoState.startAirplay();
}, [videoState]);
if (!videoState.canAirplay) return null;
return (
<VideoPlayerIconButton
className={props.className}
onClick={handleClick}
icon={Icons.AIRPLAY}
/>
);
}

View file

@ -30,6 +30,7 @@ export interface PlayerControls {
setLeftControlsHover(hovering: boolean): void;
initPlayer(sourceUrl: string, sourceType: MWStreamType): void;
setShowData(data: ShowData): void;
startAirplay(): void;
}
export const initialControls: PlayerControls = {
@ -43,6 +44,7 @@ export const initialControls: PlayerControls = {
setLeftControlsHover: () => null,
initPlayer: () => null,
setShowData: () => null,
startAirplay: () => null,
};
export function populateControls(
@ -118,6 +120,11 @@ export function populateControls(
setShowData(data) {
update((s) => ({ ...s, seasonData: data }));
},
startAirplay() {
const videoPlayer = player as any;
if (videoPlayer.webkitShowPlaybackTargetPicker)
videoPlayer.webkitShowPlaybackTargetPicker();
},
initPlayer(sourceUrl: string, sourceType: MWStreamType) {
this.setVolume(getStoredVolume());

View file

@ -34,6 +34,7 @@ export type PlayerState = {
name: string;
description: string;
};
canAirplay: boolean;
};
export type PlayerContext = PlayerState & PlayerControls;
@ -57,6 +58,7 @@ export const initialPlayerState: PlayerContext = {
seasonData: {
isSeries: false,
},
canAirplay: false,
...initialControls,
};
@ -159,6 +161,14 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
: null,
}));
};
const canAirplay = (e: any) => {
if (e.availability === "available") {
update((s) => ({
...s,
canAirplay: true,
}));
}
};
player.addEventListener("pause", pause);
player.addEventListener("playing", playing);
@ -172,6 +182,10 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
player.addEventListener("waiting", waiting);
player.addEventListener("canplay", canplay);
player.addEventListener("error", error);
player.addEventListener(
"webkitplaybacktargetavailabilitychanged",
canAirplay
);
return () => {
player.removeEventListener("pause", pause);
@ -186,6 +200,10 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
player.removeEventListener("waiting", waiting);
player.removeEventListener("canplay", canplay);
player.removeEventListener("error", error);
player.removeEventListener(
"webkitplaybacktargetavailabilitychanged",
canAirplay
);
};
}

View file

@ -25,6 +25,7 @@ if (key) {
// - source selection
// - safari fullscreen will make video overlap player controls
// - safari progress bar is fucked (video doesnt change time but video.currentTime does change)
// - safari progress bar cannot be dragged
// TODO stuff to test:
// - browser: firefox, chrome, edge, safari desktop
@ -41,7 +42,8 @@ if (key) {
// - AFTER all that: rank providers/embedscrapers
// TODO general todos:
// - localize everything
// - localize everything (fix loading screen text (series vs movies))
// - make mobile friendly
ReactDOM.render(
<React.StrictMode>

View file

@ -155,7 +155,6 @@ export function MediaView() {
const [stream, setStream] = useState<MWStream | null>(null);
useEffect(() => {
console.log("I am being ran");
exec(params.media, params.season).then((v) => {
setMeta(v ?? null);
if (v) {