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

Merge pull request #525 from movie-web/coop-fixes

Tag team fixes!
This commit is contained in:
mrjvs 2023-12-13 21:45:10 +01:00 committed by GitHub
commit 6a9eb11884
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 42 additions and 19 deletions

View file

@ -17,7 +17,7 @@
"login": {
"title": "Login to your account",
"description": "Please enter your passphrase to login to your account",
"validationError": "Invalid or incomplete passphrase",
"validationError": "Incorrect or incomplete passphrase",
"deviceLengthError": "Please enter a device name",
"submit": "Login",
"passphraseLabel": "12-Word passphrase",

View file

@ -16,7 +16,7 @@
"passphrasePlaceholder": "Passphrase",
"submit": "Hoist Anchor",
"title": "Hoist the Jolly Roger",
"validationError": "Arr, invalid or incomplete passphrase"
"validationError": "Arr, incorrect or incomplete passphrase"
},
"register": {
"information": {

View file

@ -95,7 +95,7 @@ function MediaCardContent({
{percentage !== undefined ? (
<>
<div
className={`absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-mediaCard-shadow to-transparent transition-colors ${
className={`absolute inset-x-0 -bottom-px pb-1 h-12 bg-gradient-to-t from-mediaCard-shadow to-transparent transition-colors ${
canLink ? "group-hover:from-mediaCard-hoverShadow" : ""
}`}
/>

View file

@ -16,7 +16,7 @@ export function OverlayMobilePosition(props: MobilePositionProps) {
return (
<div
className={classNames([
"pointer-events-auto px-4 pb-6 z-10 bottom-0 origin-top-left inset-x-0 absolute overflow-hidden max-h-[calc(100vh-1.5rem)] grid grid-rows-[minmax(0,1fr),auto]",
"pointer-events-auto px-4 pb-6 z-10 ml-[env(safe-area-inset-left)] mr-[env(safe-area-inset-right)] bottom-0 origin-top-left inset-x-0 absolute overflow-hidden max-h-[calc(100vh-1.5rem)] grid grid-rows-[minmax(0,1fr),auto]",
props.className,
])}
>

View file

@ -62,9 +62,11 @@ export function NextEpisodeButton(props: {
if (isHidden || status !== "playing" || duration === 0) show = false;
const animation = showingState === "hover" ? "slide-up" : "fade";
let bottom = "bottom-24";
let bottom = "bottom-[calc(6rem+env(safe-area-inset-bottom))]";
if (showingState === "always")
bottom = props.controlsShowing ? "bottom-24" : "bottom-12";
bottom = props.controlsShowing
? bottom
: "bottom-[calc(3rem+env(safe-area-inset-bottom))]";
const nextEp = meta?.episodes?.find(
(v) => v.number === (meta?.episode?.number ?? 0) + 1
@ -86,7 +88,7 @@ export function NextEpisodeButton(props: {
<Transition
animation={animation}
show={show}
className="absolute right-12 bottom-0"
className="absolute right-[calc(3rem+env(safe-area-inset-right))] bottom-0"
>
<div
className={classNames([

View file

@ -5,6 +5,7 @@ import { playerStatus } from "@/stores/player/slices/source";
import { ThumbnailImage } from "@/stores/player/slices/thumbnails";
import { usePlayerStore } from "@/stores/player/store";
import { LoadableSource, selectQuality } from "@/stores/player/utils/qualities";
import { isSafari } from "@/utils/detectFeatures";
function makeQueue(layers: number): number[] {
const output = [0, 1];
@ -39,7 +40,9 @@ class ThumnbnailWorker {
}
start(source: LoadableSource) {
if (isSafari) return false;
const el = document.createElement("video");
el.setAttribute("muted", "true");
const canvas = document.createElement("canvas");
this.hls = new Hls();
if (source.type === "mp4") {
@ -76,9 +79,14 @@ class ThumnbnailWorker {
private async takeSnapshot(at: number) {
if (!this.videoEl || !this.canvasEl) return;
await this.videoEl.play(); // so that `seeked` actually triggers
this.videoEl.currentTime = at;
await new Promise((resolve) => {
this.videoEl?.addEventListener("seeked", resolve);
const onSeeked = () => {
this.videoEl?.removeEventListener("seeked", onSeeked);
resolve(null);
};
this.videoEl?.addEventListener("seeked", onSeeked);
});
if (!this.videoEl || !this.canvasEl) return;
const ctx = this.canvasEl.getContext("2d");
@ -91,6 +99,7 @@ class ThumnbnailWorker {
this.canvasEl.height
);
const imgUrl = this.canvasEl.toDataURL();
if (this.interrupted) return;
if (imgUrl === "data:," || !imgUrl) return; // failed image rendering
@ -142,6 +151,7 @@ export function ThumbnailScraper() {
workerRef.current = ins;
ins.start(inputStream.stream);
}, [source, addImage, resetImages, status]);
const startRef = useRef(start);
useEffect(() => {
startRef.current = start;

View file

@ -161,8 +161,8 @@ function ParticlesCanvas() {
export function Lightbar(props: { className?: string }) {
return (
<div className="absolute inset-0 w-full h-screen overflow-hidden pointer-events-none -mt-64">
<div className="max-w-screen w-full h-screen relative pt-64">
<div className="absolute inset-0 w-full h-[calc(100vh+16rem)] overflow-hidden pointer-events-none -mt-64">
<div className="max-w-screen w-full h-[calc(100vh+16rem)] relative pt-64">
<div className={props.className}>
<div className="lightbar">
<ParticlesCanvas />

View file

@ -19,7 +19,7 @@ const testMeta: PlayerMeta = {
const testStreams: Record<StreamType, string> = {
hls: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8",
mp4: "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4",
mp4: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4",
};
const streamTypes: Record<StreamType, string> = {

View file

@ -26,7 +26,7 @@ export function BlurEllipsis(props: { positionClass?: string }) {
export function SubPageLayout(props: { children: React.ReactNode }) {
return (
<div
className="from-[#0D0D1A] to-background-main"
className="bg-background-main"
style={{
backgroundImage:
"linear-gradient(to bottom, var(--tw-gradient-from), var(--tw-gradient-to) 800px)",

View file

@ -1,6 +1,7 @@
import { useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { useAsyncFn } from "react-use";
import type { AsyncReturnType } from "type-fest";
import { verifyValidMnemonic } from "@/backend/accounts/crypto";
import { Button } from "@/components/buttons/Button";
@ -37,12 +38,19 @@ export function LoginFormPart(props: LoginFormPartProps) {
if (validatedDevice.length === 0)
throw new Error(t("auth.login.deviceLengthError") ?? undefined);
const account = await login({
let account: AsyncReturnType<typeof login>;
try {
account = await login({
mnemonic: inputMnemonic,
userData: {
device: validatedDevice,
},
});
} catch (err) {
if ((err as any).status === 401)
throw new Error(t("auth.login.validationError") ?? undefined);
throw err;
}
await importData(account, progressItems, bookmarkItems);

View file

@ -1,6 +1,7 @@
import { ProviderControls, ScrapeMedia } from "@movie-web/providers";
import classNames from "classnames";
import { useEffect, useRef } from "react";
import { useMountedState } from "react-use";
import type { AsyncReturnType } from "type-fest";
import {
@ -30,6 +31,7 @@ export interface ScrapingProps {
export function ScrapingPart(props: ScrapingProps) {
const { report } = useReportProviders();
const { startScraping, sourceOrder, sources, currentSource } = useScrape();
const isMounted = useMountedState();
const containerRef = useRef<HTMLDivElement | null>(null);
const listRef = useRef<HTMLDivElement | null>(null);
@ -57,6 +59,7 @@ export function ScrapingPart(props: ScrapingProps) {
started.current = true;
(async () => {
const output = await startScraping(props.media);
if (!isMounted()) return;
props.onResult?.(
resultRef.current.sources,
resultRef.current.sourceOrder
@ -70,7 +73,7 @@ export function ScrapingPart(props: ScrapingProps) {
);
props.onGetStream?.(output);
})();
}, [startScraping, props, report]);
}, [startScraping, props, report, isMounted]);
const currentProvider = sourceOrder.find(
(s) => sources[s.id].status === "pending"