mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
Merge branch 'dev' into fix-referer-maybe
This commit is contained in:
commit
dfc3d9e50f
140 changed files with 891 additions and 296 deletions
53
.eslintrc.js
53
.eslintrc.js
|
@ -8,27 +8,28 @@ const a11yOff = Object.keys(require("eslint-plugin-jsx-a11y").rules).reduce(
|
|||
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true
|
||||
browser: true,
|
||||
},
|
||||
extends: [
|
||||
"airbnb",
|
||||
"airbnb/hooks",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"plugin:prettier/recommended"
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
ignorePatterns: ["public/*", "dist/*", "/*.js", "/*.ts"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
tsconfigRootDir: "./"
|
||||
tsconfigRootDir: "./",
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: {}
|
||||
}
|
||||
typescript: {
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
plugins: ["@typescript-eslint", "import"],
|
||||
},
|
||||
},
|
||||
plugins: ["@typescript-eslint", "import", "prettier"],
|
||||
rules: {
|
||||
"react/jsx-uses-react": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
|
@ -54,16 +55,44 @@ module.exports = {
|
|||
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
|
||||
"react/jsx-filename-extension": [
|
||||
"error",
|
||||
{ extensions: [".js", ".tsx", ".jsx"] }
|
||||
{ extensions: [".js", ".tsx", ".jsx"] },
|
||||
],
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
ts: "never",
|
||||
tsx: "never"
|
||||
}
|
||||
tsx: "never",
|
||||
},
|
||||
],
|
||||
...a11yOff
|
||||
}
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
groups: [
|
||||
"builtin",
|
||||
"external",
|
||||
"internal",
|
||||
["sibling", "parent"],
|
||||
"index",
|
||||
"unknown",
|
||||
],
|
||||
"newlines-between": "always",
|
||||
alphabetize: {
|
||||
order: "asc",
|
||||
caseInsensitive: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"sort-imports": [
|
||||
"error",
|
||||
{
|
||||
ignoreCase: false,
|
||||
ignoreDeclarationSort: true,
|
||||
ignoreMemberSort: false,
|
||||
memberSyntaxSortOrder: ["none", "all", "multiple", "single"],
|
||||
allowSeparatedGroups: true,
|
||||
},
|
||||
],
|
||||
...a11yOff,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<a href="https://discord.gg/vXsRvye8BS"><img src="https://discordapp.com/api/guilds/871713465100816424/widget.png?style=banner2" alt="Discord Server"></a>
|
||||
</p>
|
||||
|
||||
movie-web is a web app for watching movies easily. Check it out at **[movie.squeezebox.dev](https://movie.squeezebox.dev)**.
|
||||
movie-web is a web app for watching movies easily. Check it out at **[movie-web.app](https://movie-web.app)**.
|
||||
|
||||
This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface.
|
||||
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
"name": "movie-web",
|
||||
"version": "3.0.14",
|
||||
"private": true,
|
||||
"homepage": "https://movie.squeezebox.dev",
|
||||
"homepage": "https://movie-web.app",
|
||||
"dependencies": {
|
||||
"@formkit/auto-animate": "^1.0.0-beta.5",
|
||||
"@headlessui/react": "^1.5.0",
|
||||
"@react-spring/web": "^9.7.1",
|
||||
"@sentry/integrations": "^7.49.0",
|
||||
"@sentry/react": "^7.49.0",
|
||||
"@use-gesture/react": "^10.2.24",
|
||||
"core-js": "^3.29.1",
|
||||
"crypto-js": "^4.1.1",
|
||||
|
@ -80,7 +82,7 @@
|
|||
"eslint-config-airbnb": "19.0.4",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "7.29.4",
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { describe, it } from "vitest";
|
||||
|
||||
import "@/backend";
|
||||
import { getProviders } from "@/backend/helpers/register";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { runProvider } from "@/backend/helpers/run";
|
||||
import { testData } from "@/__tests__/providers/testdata";
|
||||
import { getProviders } from "@/backend/helpers/register";
|
||||
import { runProvider } from "@/backend/helpers/run";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
|
||||
describe("providers", () => {
|
||||
const providers = getProviders();
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { MWEmbedType } from "@/backend/helpers/embed";
|
||||
import { proxiedFetch } from "@/backend/helpers/fetch";
|
||||
import { registerEmbedScraper } from "@/backend/helpers/register";
|
||||
import {
|
||||
MWEmbedStream,
|
||||
MWStreamQuality,
|
||||
MWStreamType,
|
||||
MWEmbedStream,
|
||||
} from "@/backend/helpers/streams";
|
||||
import { proxiedFetch } from "@/backend/helpers/fetch";
|
||||
|
||||
const HOST = "streamm4u.club";
|
||||
const URL_BASE = `https://${HOST}`;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import DOMPurify from "dompurify";
|
||||
import { detect, list, parse } from "subsrt-ts";
|
||||
import { ContentCaption } from "subsrt-ts/dist/types/handler";
|
||||
|
||||
import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch";
|
||||
import { MWCaption } from "@/backend/helpers/streams";
|
||||
import DOMPurify from "dompurify";
|
||||
import { parse, detect, list } from "subsrt-ts";
|
||||
import { ContentCaption } from "subsrt-ts/dist/types/handler";
|
||||
|
||||
export const customCaption = "external-custom";
|
||||
export function makeCaptionId(caption: MWCaption, isLinked: boolean): string {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { conf } from "@/setup/config";
|
||||
import { ofetch } from "ofetch";
|
||||
|
||||
import { conf } from "@/setup/config";
|
||||
|
||||
let proxyUrlIndex = Math.floor(Math.random() * conf().PROXY_URLS.length);
|
||||
|
||||
// round robins all proxy urls
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { DetailedMeta } from "../metadata/getmeta";
|
||||
import { MWMediaType } from "../metadata/types";
|
||||
import { MWEmbed } from "./embed";
|
||||
import { MWStream } from "./streams";
|
||||
import { DetailedMeta } from "../metadata/getmeta";
|
||||
import { MWMediaType } from "../metadata/types";
|
||||
|
||||
export type MWProviderScrapeResult = {
|
||||
stream?: MWStream;
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { FetchError } from "ofetch";
|
||||
import { makeUrl, proxiedFetch } from "../helpers/fetch";
|
||||
|
||||
import {
|
||||
formatJWMeta,
|
||||
JWMediaResult,
|
||||
JWSeasonMetaResult,
|
||||
JW_API_BASE,
|
||||
formatJWMeta,
|
||||
mediaTypeToJW,
|
||||
} from "./justwatch";
|
||||
import { MWMediaMeta, MWMediaType } from "./types";
|
||||
import { makeUrl, proxiedFetch } from "../helpers/fetch";
|
||||
|
||||
type JWExternalIdType =
|
||||
| "eidr"
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { SimpleCache } from "@/utils/cache";
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
|
||||
import {
|
||||
formatJWMeta,
|
||||
JWContentTypes,
|
||||
JWMediaResult,
|
||||
JW_API_BASE,
|
||||
formatJWMeta,
|
||||
mediaTypeToJW,
|
||||
} from "./justwatch";
|
||||
import { MWMediaMeta, MWQuery } from "./types";
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
|
||||
const cache = new SimpleCache<MWQuery, MWMediaMeta[]>();
|
||||
cache.setCompare((a, b) => {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { compareTitle } from "@/utils/titleMatch";
|
||||
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
import { registerProvider } from "../helpers/register";
|
||||
import {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { unpack } from "unpacker";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { unpack } from "unpacker";
|
||||
|
||||
import { registerProvider } from "@/backend/helpers/register";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { MWStreamQuality } from "@/backend/helpers/streams";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
|
||||
const format = {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { MWEmbed, MWEmbedType } from "@/backend/helpers/embed";
|
||||
|
||||
import { proxiedFetch } from "../helpers/fetch";
|
||||
import { registerProvider } from "../helpers/register";
|
||||
import { MWMediaType } from "../metadata/types";
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { registerProvider } from "@/backend/helpers/register";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
|
||||
import { customAlphabet } from "nanoid";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { customAlphabet } from "nanoid";
|
||||
|
||||
import { proxiedFetch } from "@/backend/helpers/fetch";
|
||||
import { registerProvider } from "@/backend/helpers/register";
|
||||
import {
|
||||
MWCaption,
|
||||
MWCaptionType,
|
||||
MWStreamQuality,
|
||||
MWStreamType,
|
||||
} from "@/backend/helpers/streams";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { compareTitle } from "@/utils/titleMatch";
|
||||
|
||||
const nanoid = customAlphabet("0123456789abcdef", 32);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
interface Props {
|
||||
icon?: Icons;
|
||||
onClick?: () => void;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useSettings } from "@/state/settings";
|
||||
|
||||
import { Icon, Icons } from "./Icon";
|
||||
|
||||
export const colors = ["#ffffff", "#00ffff", "#ffff00"];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import React, { Fragment } from "react";
|
||||
|
||||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
export interface OptionItem {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Transition } from "@/components/Transition";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import { Transition } from "@/components/Transition";
|
||||
|
||||
export function Overlay(props: { children: React.ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { MWMediaType, MWQuery } from "@/backend/metadata/types";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { MWMediaType, MWQuery } from "@/backend/metadata/types";
|
||||
|
||||
import { DropdownButton } from "./buttons/DropdownButton";
|
||||
import { Icon, Icons } from "./Icon";
|
||||
import { TextInputControl } from "./text-inputs/TextInputControl";
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Fragment, ReactNode } from "react";
|
||||
import {
|
||||
Transition as HeadlessTransition,
|
||||
TransitionClasses,
|
||||
} from "@headlessui/react";
|
||||
import { Fragment, ReactNode } from "react";
|
||||
|
||||
type TransitionAnimations =
|
||||
| "slide-down"
|
||||
|
|
|
@ -4,10 +4,11 @@ import React, {
|
|||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { BackdropContainer, useBackdrop } from "@/components/layout/Backdrop";
|
||||
import { ButtonControlProps, ButtonControl } from "./ButtonControl";
|
||||
|
||||
import { ButtonControl, ButtonControlProps } from "./ButtonControl";
|
||||
|
||||
export interface OptionItem {
|
||||
id: string;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
import { ButtonControl } from "./ButtonControl";
|
||||
|
||||
export interface EditButtonProps {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { ButtonControlProps, ButtonControl } from "./ButtonControl";
|
||||
|
||||
import { ButtonControl, ButtonControlProps } from "./ButtonControl";
|
||||
|
||||
export interface IconButtonProps extends ButtonControlProps {
|
||||
icon: Icons;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { createRef, useEffect, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import { useFade } from "@/hooks/useFade";
|
||||
|
||||
interface BackdropProps {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
export function BrandPill(props: {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { Component } from "react";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch";
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { Link } from "@/components/text/Link";
|
||||
import { Title } from "@/components/text/Title";
|
||||
import { conf } from "@/setup/config";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
interface ErrorShowcaseProps {
|
||||
error: {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Overlay } from "@/components/Overlay";
|
||||
import { Transition } from "@/components/Transition";
|
||||
import { ReactNode } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import { Overlay } from "@/components/Overlay";
|
||||
import { Transition } from "@/components/Transition";
|
||||
|
||||
interface Props {
|
||||
show: boolean;
|
||||
children?: ReactNode;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { ReactNode, useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch";
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { conf } from "@/setup/config";
|
||||
import { useBannerSize } from "@/hooks/useBanner";
|
||||
import { conf } from "@/setup/config";
|
||||
import SettingsModal from "@/views/SettingsModal";
|
||||
|
||||
import { BrandPill } from "./BrandPill";
|
||||
|
||||
export interface NavigationProps {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ReactNode } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
interface SectionHeadingProps {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { DotList } from "@/components/text/DotList";
|
||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { JWMediaToId } from "@/backend/metadata/justwatch";
|
||||
import { Icons } from "../Icon";
|
||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||
import { DotList } from "@/components/text/DotList";
|
||||
|
||||
import { IconPatch } from "../buttons/IconPatch";
|
||||
import { Icons } from "../Icon";
|
||||
|
||||
export interface MediaCardProps {
|
||||
media: MWMediaMeta;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { useMemo } from "react";
|
||||
|
||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||
import { useWatchedContext } from "@/state/watched";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { MediaCard } from "./MediaCard";
|
||||
|
||||
export interface WatchedMediaCardProps {
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { animated, easings, useSpringValue } from "@react-spring/web";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { FloatingCardAnchorPosition } from "@/components/popout/positions/FloatingCardAnchorPosition";
|
||||
import { FloatingCardMobilePosition } from "@/components/popout/positions/FloatingCardMobilePosition";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { PopoutSection } from "@/video/components/popouts/PopoutUtils";
|
||||
import { useSpringValue, animated, easings } from "@react-spring/web";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { Icon, Icons } from "../Icon";
|
||||
|
||||
import { FloatingDragHandle, MobilePopoutSpacer } from "./FloatingDragHandle";
|
||||
import { Icon, Icons } from "../Icon";
|
||||
|
||||
interface FloatingCardProps {
|
||||
children?: ReactNode;
|
||||
|
@ -133,13 +136,15 @@ export const FloatingCardView = {
|
|||
action?: React.ReactNode;
|
||||
backText?: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
let left = (
|
||||
<div
|
||||
onClick={props.goBack}
|
||||
className="flex cursor-pointer items-center space-x-2 transition-colors duration-200 hover:text-white"
|
||||
>
|
||||
<Icon icon={Icons.ARROW_LEFT} />
|
||||
<span>{props.backText || "Go back"}</span>
|
||||
<span>{props.backText || t("videoPlayer.popouts.back")}</span>
|
||||
</div>
|
||||
);
|
||||
if (props.close)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Transition } from "@/components/Transition";
|
||||
import React, {
|
||||
ReactNode,
|
||||
useCallback,
|
||||
|
@ -8,6 +7,8 @@ import React, {
|
|||
} from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import { Transition } from "@/components/Transition";
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
onClose?: () => void;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { ReactNode } from "react";
|
||||
|
||||
import { Transition } from "@/components/Transition";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createFloatingAnchorEvent } from "@/components/popout/FloatingAnchor";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { createFloatingAnchorEvent } from "@/components/popout/FloatingAnchor";
|
||||
|
||||
interface AnchorPositionProps {
|
||||
children?: ReactNode;
|
||||
id: string;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useSpring, animated, config } from "@react-spring/web";
|
||||
import { animated, config, useSpring } from "@react-spring/web";
|
||||
import { useDrag } from "@use-gesture/react";
|
||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Link as LinkRouter } from "react-router-dom";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
|
||||
interface IArrowLinkPropsBase {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import {
|
||||
ReactNode,
|
||||
createContext,
|
||||
useState,
|
||||
useMemo,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useEffect,
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useMeasure } from "react-use";
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/// <reference types="chromecast-caf-sender"/>
|
||||
|
||||
import { isChromecastAvailable } from "@/setup/chromecast";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { isChromecastAvailable } from "@/setup/chromecast";
|
||||
|
||||
export function useChromecastAvailable() {
|
||||
const [available, setAvailable] = useState<boolean | null>(null);
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
import { findBestStream } from "@/backend/helpers/scrape";
|
||||
import { MWStream } from "@/backend/helpers/streams";
|
||||
import { DetailedMeta } from "@/backend/metadata/getmeta";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export interface ScrapeEventLog {
|
||||
type: "provider" | "embed";
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { MWMediaType, MWQuery } from "@/backend/metadata/types";
|
||||
import { useState } from "react";
|
||||
import { generatePath, useHistory, useRouteMatch } from "react-router-dom";
|
||||
|
||||
import { MWMediaType, MWQuery } from "@/backend/metadata/types";
|
||||
|
||||
function getInitialValue(params: { type: string; query: string }) {
|
||||
const type =
|
||||
Object.values(MWMediaType).find((v) => params.type === v) ||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
import { useState } from "react";
|
||||
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useState } from "react";
|
||||
|
||||
export function useVolumeControl(descriptor: string) {
|
||||
const [storedVolume, setStoredVolume] = useState(1);
|
||||
const controls = useControls(descriptor);
|
||||
const mediaPlaying = useMediaPlaying(descriptor);
|
||||
|
||||
const toggleVolume = () => {
|
||||
const toggleVolume = (isKeyboardEvent = false) => {
|
||||
if (mediaPlaying.volume > 0) {
|
||||
setStoredVolume(mediaPlaying.volume);
|
||||
controls.setVolume(0);
|
||||
controls.setVolume(0, isKeyboardEvent);
|
||||
} else {
|
||||
controls.setVolume(storedVolume > 0 ? storedVolume : 1);
|
||||
controls.setVolume(storedVolume > 0 ? storedVolume : 1, isKeyboardEvent);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -3,12 +3,14 @@ import React, { Suspense } from "react";
|
|||
import ReactDOM from "react-dom";
|
||||
import { BrowserRouter, HashRouter } from "react-router-dom";
|
||||
import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
|
||||
import { ErrorBoundary } from "@/components/layout/ErrorBoundary";
|
||||
import { conf } from "@/setup/config";
|
||||
import { registerSW } from "virtual:pwa-register";
|
||||
|
||||
import { ErrorBoundary } from "@/components/layout/ErrorBoundary";
|
||||
import App from "@/setup/App";
|
||||
import { conf } from "@/setup/config";
|
||||
|
||||
import "@/setup/ga";
|
||||
import "@/setup/sentry";
|
||||
import "@/setup/i18n";
|
||||
import "@/setup/index.css";
|
||||
import "@/backend";
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { lazy } from "react";
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import { BookmarkContextProvider } from "@/state/bookmark";
|
||||
import { WatchedContextProvider } from "@/state/watched";
|
||||
import { SettingsProvider } from "@/state/settings";
|
||||
|
||||
import { NotFoundPage } from "@/views/notfound/NotFoundView";
|
||||
import { MediaView } from "@/views/media/MediaView";
|
||||
import { SearchView } from "@/views/search/SearchView";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { V2MigrationView } from "@/views/other/v2Migration";
|
||||
import { BannerContextProvider } from "@/hooks/useBanner";
|
||||
import { Layout } from "@/setup/Layout";
|
||||
import { BookmarkContextProvider } from "@/state/bookmark";
|
||||
import { SettingsProvider } from "@/state/settings";
|
||||
import { WatchedContextProvider } from "@/state/watched";
|
||||
import { MediaView } from "@/views/media/MediaView";
|
||||
import { NotFoundPage } from "@/views/notfound/NotFoundView";
|
||||
import { V2MigrationView } from "@/views/other/v2Migration";
|
||||
import { SearchView } from "@/views/search/SearchView";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Banner } from "@/components/Banner";
|
||||
import { useBannerSize } from "@/hooks/useBanner";
|
||||
import { useIsOnline } from "@/hooks/usePing";
|
||||
import { ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export function Layout(props: { children: ReactNode }) {
|
||||
const { t } = useTranslation();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { APP_VERSION, GITHUB_LINK, DISCORD_LINK } from "./constants";
|
||||
import { APP_VERSION, DISCORD_LINK, GITHUB_LINK } from "./constants";
|
||||
|
||||
interface Config {
|
||||
APP_VERSION: string;
|
||||
|
|
|
@ -2,3 +2,5 @@ export const APP_VERSION = import.meta.env.PACKAGE_VERSION;
|
|||
export const DISCORD_LINK = "https://discord.gg/Jhqt4Xzpfb";
|
||||
export const GITHUB_LINK = "https://github.com/movie-web/movie-web";
|
||||
export const GA_ID = "G-44YVXRL61C";
|
||||
export const SENTRY_DSN =
|
||||
"https://b267ab7d52674c23af4e4e6cf2956251@o4505053491167232.ingest.sentry.io/4505053495296000";
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import ReactGA from "react-ga4";
|
||||
|
||||
import { GA_ID } from "@/setup/constants";
|
||||
|
||||
ReactGA.initialize([
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import LanguageDetector from "i18next-browser-languagedetector";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
|
||||
// Languages
|
||||
import en from "./locales/en/translation.json";
|
||||
import { captionLanguages } from "./iso6391";
|
||||
import en from "./locales/en/translation.json";
|
||||
import fr from "./locales/fr/translation.json";
|
||||
import nl from "./locales/nl/translation.json";
|
||||
import tr from "./locales/tr/translation.json";
|
||||
|
||||
const locales = {
|
||||
en: {
|
||||
translation: en,
|
||||
},
|
||||
nl: {
|
||||
translation: nl,
|
||||
},
|
||||
tr: {
|
||||
translation: tr,
|
||||
},
|
||||
fr: {
|
||||
translation: fr,
|
||||
},
|
||||
};
|
||||
i18n
|
||||
// detect user language
|
||||
|
|
|
@ -217,7 +217,7 @@ export const captionLanguages: CaptionLanguageOption[] = [
|
|||
id: "none",
|
||||
englishName: "None",
|
||||
name: "None",
|
||||
nativeName: "No caption language selected",
|
||||
nativeName: "Lorem ipsum",
|
||||
},
|
||||
{
|
||||
id: "aa",
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
"playbackSpeed": "Playback speed"
|
||||
},
|
||||
"popouts": {
|
||||
"back": "Go back",
|
||||
"sources": "Sources",
|
||||
"seasons": "Seasons",
|
||||
"captions": "Captions",
|
||||
|
|
|
@ -15,17 +15,35 @@
|
|||
"title": "Que voulez-vous voir?",
|
||||
"placeholder": "Que voulez-vous voir?"
|
||||
},
|
||||
"media": {
|
||||
"movie": "Films",
|
||||
"series": "Séries",
|
||||
"stopEditing": "Arrêter l'édition",
|
||||
"errors": {
|
||||
"genericTitle": "Oups, c'est coupé !",
|
||||
"failedMeta": "Impossible de charger les métadonnées",
|
||||
"mediaFailed": "Nous n'avons pas réussi à récupérer le média que vous avez demandé. Veuillez vérifier votre connexion Internet et réessayer.",
|
||||
"videoFailed": "Nous avons rencontré une erreur lors de la lecture de la vidéo que vous avez demandée. Si cela se reproduit, veuillez signaler le problème au serveur <0>Discord</0> ou sur <1>GitHub</1>."
|
||||
}
|
||||
},
|
||||
"seasons": {
|
||||
"seasonAndEpisode": "S{{saison}} E{{épisode}}"
|
||||
},
|
||||
"notFound": {
|
||||
"genericTitle": "Introuvable",
|
||||
"backArrow": "Retour à l'accueil",
|
||||
"media": {
|
||||
"title": "Impossible de trouver ce média",
|
||||
"description": "Nous n'avons pas pu trouver le média que vous avez demandé. Soit il a été supprimé, soit vous avez altéré l'URL."
|
||||
"description": "Nous n'avons pas trouvé le média que vous avez demandé. Soit il a été supprimé, soit vous avez modifié l'URL."
|
||||
},
|
||||
"provider": {
|
||||
"title": "Ce fournisseur a été désactivé",
|
||||
"description": "Nous avons eu des problèmes avec le fournisseur ou bien il était trop instable pour être utilisé, donc nous avons dû le désactiver."
|
||||
"description": "Nous avons eu des problèmes avec le fournisseur ou il était trop instable pour être utilisé, nous avons donc dû le désactiver."
|
||||
},
|
||||
"page": {
|
||||
"title": "Impossible de trouver cette page",
|
||||
"description": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas pu trouver la page que vous recherchez."
|
||||
"description": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez."
|
||||
}
|
||||
},
|
||||
"searchBar": {
|
||||
"movie": "Film",
|
||||
|
@ -51,9 +69,12 @@
|
|||
"playbackSpeed": "Vitesse"
|
||||
},
|
||||
"popouts": {
|
||||
"back": "Retourner",
|
||||
"sources": "Sources",
|
||||
"seasons": "Saisons",
|
||||
"captions": "Sous-titres",
|
||||
"playbackSpeed": "Vitesse de lecture",
|
||||
"customPlaybackSpeed": "Vitesse de lecture personnalisée",
|
||||
"captionPreferences": {
|
||||
"title": "Personnaliser",
|
||||
"delay": "Délai",
|
||||
|
@ -77,13 +98,19 @@
|
|||
"seasons": "Choisissez la saison que vous voulez regarder",
|
||||
"episode": "Sélectionnez un épisode",
|
||||
"captions": "Choisissez une langue de sous-titres",
|
||||
"captionPreferences": "Personnalisez l'apparence des sous-titres"
|
||||
"captionPreferences": "Personnalisez l'apparence des sous-titres",
|
||||
"playbackSpeed": "Changer la vitesse de lecture"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"fatalError": "Le lecteur vidéo a rencontré une erreur fatale, veuillez la signaler au serveur <0>Discord</0> ou sur <1>GitHub</1>."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Paramètres",
|
||||
"language": "Language",
|
||||
"captionLanguage": "Langue des sous-titres"
|
||||
},
|
||||
"v3": {
|
||||
"newSiteTitle": "Nouvelle version disponible!",
|
||||
"newDomain": "https://movie-web.app",
|
||||
|
|
128
src/setup/locales/nl/translation.json
Normal file
128
src/setup/locales/nl/translation.json
Normal file
|
@ -0,0 +1,128 @@
|
|||
{
|
||||
"global": {
|
||||
"name": "movie-web"
|
||||
},
|
||||
"search": {
|
||||
"loading_series": "We zoeken je favoriete series...",
|
||||
"loading_movie": "We zoeken je favoriete films...",
|
||||
"loading": "Aan het zoeken...",
|
||||
"allResults": "Dat is het!",
|
||||
"noResults": "We konden helaas niets vinden.",
|
||||
"allFailed": "Het is niet gelukt de media te laden, probeer het nog eens.",
|
||||
"headingTitle": "Zoekresultaten",
|
||||
"bookmarks": "Opgeslagen",
|
||||
"continueWatching": "Kijk verder",
|
||||
"title": "Wat wil je graag kijken?",
|
||||
"placeholder": "Wat wil je graag kijken?"
|
||||
},
|
||||
"media": {
|
||||
"movie": "Film",
|
||||
"series": "Serie",
|
||||
"stopEditing": "Stop met bewerken",
|
||||
"errors": {
|
||||
"genericTitle": "Oeps, hier ging iets mis!",
|
||||
"failedMeta": "Het is niet gelukt de meta-informatie op te halen/",
|
||||
"mediaFailed": "Het is niet gelukt deze media op te halen. Controleer of je een internetverbinding hebt en probeer het nog een keer.",
|
||||
"videoFailed": "Er ging iets mis tijdens het spelen van deze video. Als dit blijft gebeuren, deel het dan in de <0>Discord server</0> of maak een <1>GitHub issue</1>."
|
||||
}
|
||||
},
|
||||
"seasons": {
|
||||
"seasonAndEpisode": "S{{season}} A{{episode}}"
|
||||
},
|
||||
"notFound": {
|
||||
"genericTitle": "Pagina niet gevonden",
|
||||
"backArrow": "Naar de home-pagina",
|
||||
"media": {
|
||||
"title": "We konden deze media niet vinden.",
|
||||
"description": "We konden dit stukje media niet vinden. Het is mogelijk verwijderd, of jij hebt zelf de URL aangepast."
|
||||
},
|
||||
"provider": {
|
||||
"title": "Deze bron is niet langer beschikbaar",
|
||||
"description": "Deze bron was helaas te instabiel, we hebben hem jammer genoeg uit moeten zetten."
|
||||
},
|
||||
"page": {
|
||||
"title": "Pagina niet gevonden",
|
||||
"description": "We hebben echt alles geprobeerd, zelfs tijdrijzen; echter hebben we deze pagina helaas niet kunnen vinden."
|
||||
}
|
||||
},
|
||||
"searchBar": {
|
||||
"movie": "Films",
|
||||
"series": "Series",
|
||||
"Search": "Zoeken"
|
||||
},
|
||||
"videoPlayer": {
|
||||
"findingBestVideo": "De beste video voor jou aan het zoeken...",
|
||||
"noVideos": "Helaas konden we dat filmpje niet vinden",
|
||||
"loading": "Aan het laden...",
|
||||
"backToHome": "Naar de home-pagina",
|
||||
"backToHomeShort": "Terug",
|
||||
"seasonAndEpisode": "S{{season}} A{{episode}}",
|
||||
"timeLeft": "Nog {{timeLeft}}",
|
||||
"finishAt": "Afgelopen om {{timeFinished}}",
|
||||
"buttons": {
|
||||
"episodes": "Afleveringen",
|
||||
"source": "Bron",
|
||||
"captions": "Ondertiteling",
|
||||
"download": "Download",
|
||||
"settings": "Instellingen",
|
||||
"pictureInPicture": "Beeld-in-beeld",
|
||||
"playbackSpeed": "Afspeelsnelheid"
|
||||
},
|
||||
"popouts": {
|
||||
"back": "Terug",
|
||||
"sources": "Bronnen",
|
||||
"seasons": "Seizoenen",
|
||||
"captions": "Ondertiteling",
|
||||
"playbackSpeed": "Afspeelsnelheid",
|
||||
"customPlaybackSpeed": "Andere snelheden",
|
||||
"captionPreferences": {
|
||||
"title": "Instellingen",
|
||||
"delay": "Vertraging",
|
||||
"fontSize": "Lettergrootte",
|
||||
"opacity": "Doorzichtbaarheid",
|
||||
"color": "Kleur"
|
||||
},
|
||||
"episode": "A{{index}} - {{title}}",
|
||||
"noCaptions": "Geen ondertiteling",
|
||||
"linkedCaptions": "Gelinkte ondertiteling",
|
||||
"customCaption": "Eigen ondertiteling",
|
||||
"uploadCustomCaption": "Ondertiteling uploaden",
|
||||
"noEmbeds": "We hebben geen filmpjes kunnen vinden voor deze bron.",
|
||||
|
||||
"errors": {
|
||||
"loadingWentWong": "Er ging iets mis tijdens het laden van de afleveringen voor {{seasonTitle}}",
|
||||
"embedsError": "Er ging iets mis tijdens het laden van de embeds voor dit dingetje dat je waarschijnlijk leuk vindt"
|
||||
},
|
||||
"descriptions": {
|
||||
"sources": "Welke bron wil je graag gebruiken",
|
||||
"embeds": "Welk filmpje wil je gebruiken?",
|
||||
"seasons": "Welk seizoen wil je kijken?",
|
||||
"episode": "Kies een aflevering",
|
||||
"captions": "Kies een taal voor de ondertiteling",
|
||||
"captionPreferences": "Pas de ondertiteling aan aan je voorkeuren",
|
||||
"playbackSpeed": "Pas de afspeelsnelhijd aan"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"fatalError": "De videospeler is helaas ontploft, rapporteer deze fout op de <0>Discord server</0> of op <1>GitHub</1>."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Instellingen",
|
||||
"language": "Taal",
|
||||
"captionLanguage": "Taal voor de Ondertiteling"
|
||||
},
|
||||
"v3": {
|
||||
"newSiteTitle": "De nieuwe versie is uit!",
|
||||
"newDomain": "https://movie-web.app",
|
||||
"newDomainText": "We gaan binnenkort verhuizen naar een nieuw domein: <0>https://movie-web.app</0>. Pas je bladwijzers aan naar het nieuwe domein, want </b>het oude domein gaat stoppen met werken op {{date}}.</b>",
|
||||
"tireless": "We hebben mega hard gewerkt aan deze nieuwe versie, dus we hopen dat je er van gaat genieten.",
|
||||
"leaveAnnouncement": "Let's go!"
|
||||
},
|
||||
"casting": {
|
||||
"casting": "Aan het casten..."
|
||||
},
|
||||
"errors": {
|
||||
"offline": "Controleer je internetverbinding"
|
||||
}
|
||||
}
|
128
src/setup/locales/tr/translation.json
Normal file
128
src/setup/locales/tr/translation.json
Normal file
|
@ -0,0 +1,128 @@
|
|||
{
|
||||
"global": {
|
||||
"name": "movie-web"
|
||||
},
|
||||
"search": {
|
||||
"loading_series": "Favori dizileriniz aranıyor...",
|
||||
"loading_movie": "Favori filmleriniz aranıyor...",
|
||||
"loading": "Yükleniyor...",
|
||||
"allResults": "Bu kadarını bulabildik!",
|
||||
"noResults": "Hiçbir şey bulamadık!",
|
||||
"allFailed": "Medya bulunamadı, tekrar deneyin!",
|
||||
"headingTitle": "Arama sonuçları",
|
||||
"bookmarks": "Yerimleri",
|
||||
"continueWatching": "İzlemeye devam edin",
|
||||
"title": "Ne izlemek istersiniz?",
|
||||
"placeholder": "Ne izlemek istersiniz?"
|
||||
},
|
||||
"media": {
|
||||
"movie": "Film",
|
||||
"series": "Dizi",
|
||||
"stopEditing": "Düzenlemeyi durdur",
|
||||
"errors": {
|
||||
"genericTitle": "Hay aksi, bozuldu!",
|
||||
"failedMeta": "Önbilgi yüklenemedi",
|
||||
"mediaFailed": "İstediğiniz medyaya istek atarken hata oluştu, internet bağlantınızı kontrol edin ve tekrar deneyin.",
|
||||
"videoFailed": "İstediğiniz videoyu oynatırken bir sorunla karşılaştık. Bu durum devam ederse lütfen bunu <0>Discord sunucumuza</0> veya <1>GitHub</1> üzerinden bildiriniz."
|
||||
}
|
||||
},
|
||||
"seasons": {
|
||||
"seasonAndEpisode": "S{{season}} B{{episode}}"
|
||||
},
|
||||
"notFound": {
|
||||
"genericTitle": "Bulunamadı",
|
||||
"backArrow": "Geri",
|
||||
"media": {
|
||||
"title": "Medya bulunamadı",
|
||||
"description": "İstediğiniz medyayı bulamadık. URL'i yanlış girdiniz ya da medya kaldırıldı."
|
||||
},
|
||||
"provider": {
|
||||
"title": "Bu sağlayıcı devre dışı bırakıldı",
|
||||
"description": "Sağlayıcı ile ilgili bir sorun oluştu ya da kullanılacak kadar stabil değildi bu yüzden devre dışı bırakmak zorunda kaldık."
|
||||
},
|
||||
"page": {
|
||||
"title": "Sayfa bulunamadı",
|
||||
"description": "Her yere baktık: bazanın altına, dolabın içine hatta ara sunucuya ama maalesef aradığınız sayfayı bulamadık."
|
||||
}
|
||||
},
|
||||
"searchBar": {
|
||||
"movie": "Film",
|
||||
"series": "Dizi",
|
||||
"Search": "Ara"
|
||||
},
|
||||
"videoPlayer": {
|
||||
"findingBestVideo": "Sizin için en iyi videoyu buluyoruz...",
|
||||
"noVideos": "Hay aksi, hiçbir video bulamadık",
|
||||
"loading": "Yükleniyor...",
|
||||
"backToHome": "Ana sayfaya dön",
|
||||
"backToHomeShort": "Geri",
|
||||
"seasonAndEpisode": "S{{season}} B{{episode}}",
|
||||
"timeLeft": "{{timeLeft}} kaldı",
|
||||
"finishAt": "{{timeFinished, datetime}}'de/da bitiyor",
|
||||
"buttons": {
|
||||
"episodes": "Bölümler",
|
||||
"source": "Kaynak",
|
||||
"captions": "Altyazılar",
|
||||
"download": "İndir",
|
||||
"settings": "Ayarlar",
|
||||
"pictureInPicture": "Resim içinde Resim",
|
||||
"playbackSpeed": "Oynatma Hızı"
|
||||
},
|
||||
"popouts": {
|
||||
"back": "Geri git",
|
||||
"sources": "Kaynaklar",
|
||||
"seasons": "Sezonlar",
|
||||
"captions": "Altyazılar",
|
||||
"playbackSpeed": "Oynatma hızı",
|
||||
"customPlaybackSpeed": "Özel oynatma hızı",
|
||||
"captionPreferences": {
|
||||
"title": "Kişiselleştirme",
|
||||
"delay": "Gecikme",
|
||||
"fontSize": "Boyut",
|
||||
"opacity": "Opaklık",
|
||||
"color": "Renk"
|
||||
},
|
||||
"episode": "B{{index}} - {{title}}",
|
||||
"noCaptions": "Altyazı yok",
|
||||
"linkedCaptions": "Kaynak Altyazıları",
|
||||
"customCaption": "Özel altyazı",
|
||||
"uploadCustomCaption": "Altyazı yükle",
|
||||
"noEmbeds": "Bu kaynak için gömülü video bulunamadı",
|
||||
|
||||
"errors": {
|
||||
"loadingWentWong": "{{seasonTitle}} için bölümler yüklenirken bir hata oluştu",
|
||||
"embedsError": "İstediğiniz şey için gömülü video bulunurken bir hata oluştu"
|
||||
},
|
||||
"descriptions": {
|
||||
"sources": "Hangi sağlayıcıyı kullanmak istersiniz?",
|
||||
"embeds": "Görüntülemek istediğiniz videoyu seçiniz",
|
||||
"seasons": "İzlemek istediğiniz sezonu seçiniz",
|
||||
"episode": "Bir bölüm seçiniz",
|
||||
"captions": "Altyazı dili seçiniz",
|
||||
"captionPreferences": "Altyazıları istediğiniz gibi ayarlayın",
|
||||
"playbackSpeed": "Oynatma hızınızı değiştirin"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"fatalError": "Video oynatıcıda bir hata oluştu, lütfen bunu <0>Discord sunucumuzda</0> ya da <1>GitHub</1> üzeriden bildiriniz."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Ayarlar",
|
||||
"language": "Dil",
|
||||
"captionLanguage": "Altyazı Dili"
|
||||
},
|
||||
"v3": {
|
||||
"newSiteTitle": "Yeni sürüm yayınlandı!",
|
||||
"newDomain": "https://movie-web.app",
|
||||
"newDomainText": "movie-web yakında yeni bir alan adına taşınacak: <0>https://movie-web.app</0>. <1>{{date}} tarihinde eski site çalışmayacağı için</1> yerimlerinizi güncellemeyi unutmayın.",
|
||||
"tireless": "Bu yeni güncelleme için gece gündüz çalıştık, umarız aylardan beri hazırladığımız bu güncellemeyi beğenirsiniz.",
|
||||
"leaveAnnouncement": "Götür beni!"
|
||||
},
|
||||
"casting": {
|
||||
"casting": "Cihaza aktarılıyor..."
|
||||
},
|
||||
"errors": {
|
||||
"offline": "İnternet bağlantınızı kontrol ediniz"
|
||||
}
|
||||
}
|
16
src/setup/sentry.tsx
Normal file
16
src/setup/sentry.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { CaptureConsole, HttpClient } from "@sentry/integrations";
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
import { conf } from "@/setup/config";
|
||||
import { SENTRY_DSN } from "@/setup/constants";
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
release: `movie-web@${conf().APP_VERSION}`,
|
||||
sampleRate: 0.5,
|
||||
integrations: [
|
||||
new Sentry.BrowserTracing(),
|
||||
new CaptureConsole(),
|
||||
new HttpClient(),
|
||||
],
|
||||
});
|
|
@ -1,6 +1,8 @@
|
|||
import { ReactNode, createContext, useContext, useMemo } from "react";
|
||||
|
||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||
import { useStore } from "@/utils/storage";
|
||||
import { createContext, ReactNode, useContext, useMemo } from "react";
|
||||
|
||||
import { BookmarkStore } from "./store";
|
||||
import { BookmarkStoreData } from "./types";
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { createVersionedStore } from "@/utils/storage";
|
||||
import { migrateV1Bookmarks, OldBookmarks } from "../watched/migrations/v2";
|
||||
|
||||
import { BookmarkStoreData } from "./types";
|
||||
import { OldBookmarks, migrateV1Bookmarks } from "../watched/migrations/v2";
|
||||
|
||||
export const BookmarkStore = createVersionedStore<BookmarkStoreData>()
|
||||
.setKey("mw-bookmarks")
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { useStore } from "@/utils/storage";
|
||||
import { createContext, ReactNode, useContext, useMemo } from "react";
|
||||
import { ReactNode, createContext, useContext, useMemo } from "react";
|
||||
|
||||
import { LangCode } from "@/setup/iso6391";
|
||||
import { useStore } from "@/utils/storage";
|
||||
|
||||
import { SettingsStore } from "./store";
|
||||
import { MWSettingsData } from "./types";
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { createVersionedStore } from "@/utils/storage";
|
||||
|
||||
import { MWSettingsData, MWSettingsDataV1 } from "./types";
|
||||
|
||||
export const SettingsStore = createVersionedStore<MWSettingsData>()
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { DetailedMeta } from "@/backend/metadata/getmeta";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useStore } from "@/utils/storage";
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from "react";
|
||||
|
||||
import { DetailedMeta } from "@/backend/metadata/getmeta";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useStore } from "@/utils/storage";
|
||||
|
||||
import { VideoProgressStore } from "./store";
|
||||
import { StoreMediaItem, WatchedStoreItem, WatchedStoreData } from "./types";
|
||||
import { StoreMediaItem, WatchedStoreData, WatchedStoreItem } from "./types";
|
||||
|
||||
const FIVETEEN_MINUTES = 15 * 60;
|
||||
const FIVE_MINUTES = 5 * 60;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
|||
import { searchForMedia } from "@/backend/metadata/search";
|
||||
import { MWMediaMeta, MWMediaType } from "@/backend/metadata/types";
|
||||
import { compareTitle } from "@/utils/titleMatch";
|
||||
|
||||
import { WatchedStoreData, WatchedStoreItem } from "../types";
|
||||
|
||||
interface OldMediaBase {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { createVersionedStore } from "@/utils/storage";
|
||||
import { migrateV2Videos, OldData } from "./migrations/v2";
|
||||
|
||||
import { OldData, migrateV2Videos } from "./migrations/v2";
|
||||
import { WatchedStoreData } from "./types";
|
||||
|
||||
export const VideoProgressStore = createVersionedStore<WatchedStoreData>()
|
||||
|
|
|
@ -1,36 +1,38 @@
|
|||
import { ReactNode, useCallback, useState } from "react";
|
||||
|
||||
import { Transition } from "@/components/Transition";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { AirplayAction } from "@/video/components/actions/AirplayAction";
|
||||
import { BackdropAction } from "@/video/components/actions/BackdropAction";
|
||||
import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
|
||||
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
|
||||
import { FullscreenAction } from "@/video/components/actions/FullscreenAction";
|
||||
import { HeaderAction } from "@/video/components/actions/HeaderAction";
|
||||
import { KeyboardShortcutsAction } from "@/video/components/actions/KeyboardShortcutsAction";
|
||||
import { LoadingAction } from "@/video/components/actions/LoadingAction";
|
||||
import { MiddlePauseAction } from "@/video/components/actions/MiddlePauseAction";
|
||||
import { MobileCenterAction } from "@/video/components/actions/MobileCenterAction";
|
||||
import { PageTitleAction } from "@/video/components/actions/PageTitleAction";
|
||||
import { PauseAction } from "@/video/components/actions/PauseAction";
|
||||
import { PictureInPictureAction } from "@/video/components/actions/PictureInPictureAction";
|
||||
import { ProgressAction } from "@/video/components/actions/ProgressAction";
|
||||
import { SeriesSelectionAction } from "@/video/components/actions/SeriesSelectionAction";
|
||||
import { ShowTitleAction } from "@/video/components/actions/ShowTitleAction";
|
||||
import { KeyboardShortcutsAction } from "@/video/components/actions/KeyboardShortcutsAction";
|
||||
import { SkipTimeAction } from "@/video/components/actions/SkipTimeAction";
|
||||
import { TimeAction } from "@/video/components/actions/TimeAction";
|
||||
import { VolumeAction } from "@/video/components/actions/VolumeAction";
|
||||
import { VideoPlayerError } from "@/video/components/parts/VideoPlayerError";
|
||||
import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderAction";
|
||||
import {
|
||||
VideoPlayerBase,
|
||||
VideoPlayerBaseProps,
|
||||
} from "@/video/components/VideoPlayerBase";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { ReactNode, useCallback, useState } from "react";
|
||||
import { PopoutProviderAction } from "@/video/components/popouts/PopoutProviderAction";
|
||||
import { ChromecastAction } from "@/video/components/actions/ChromecastAction";
|
||||
import { CastingTextAction } from "@/video/components/actions/CastingTextAction";
|
||||
import { PictureInPictureAction } from "@/video/components/actions/PictureInPictureAction";
|
||||
|
||||
import { CaptionRendererAction } from "./actions/CaptionRendererAction";
|
||||
import { SettingsAction } from "./actions/SettingsAction";
|
||||
import { DividerAction } from "./actions/DividerAction";
|
||||
import { SettingsAction } from "./actions/SettingsAction";
|
||||
import { VolumeAdjustedAction } from "./actions/VolumeAdjustedAction";
|
||||
|
||||
type Props = VideoPlayerBaseProps;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { useRef } from "react";
|
||||
|
||||
import { CastingInternal } from "@/video/components/internal/CastingInternal";
|
||||
import { WrapperRegisterInternal } from "@/video/components/internal/WrapperRegisterInternal";
|
||||
import { VideoErrorBoundary } from "@/video/components/parts/VideoErrorBoundary";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { useRef } from "react";
|
||||
import {
|
||||
useVideoPlayerDescriptor,
|
||||
VideoPlayerContextProvider,
|
||||
} from "../state/hooks";
|
||||
|
||||
import { MetaAction } from "./actions/MetaAction";
|
||||
import { VideoElementInternal } from "./internal/VideoElementInternal";
|
||||
import {
|
||||
VideoPlayerContextProvider,
|
||||
useVideoPlayerDescriptor,
|
||||
} from "../state/hooks";
|
||||
|
||||
export interface VideoPlayerBaseProps {
|
||||
children?:
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useMisc } from "@/video/state/logic/misc";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
interface BackdropActionProps {
|
||||
children?: React.ReactNode;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { Transition } from "@/components/Transition";
|
||||
import { useSettings } from "@/state/settings";
|
||||
import { sanitize, parseSubtitles } from "@/backend/helpers/captions";
|
||||
import { ContentCaption } from "subsrt-ts/dist/types/handler";
|
||||
import { useRef } from "react";
|
||||
import { useAsync } from "react-use";
|
||||
import { ContentCaption } from "subsrt-ts/dist/types/handler";
|
||||
|
||||
import { parseSubtitles, sanitize } from "@/backend/helpers/captions";
|
||||
import { Transition } from "@/components/Transition";
|
||||
import { useSettings } from "@/state/settings";
|
||||
|
||||
import { useVideoPlayerDescriptor } from "../../state/hooks";
|
||||
import { useProgress } from "../../state/logic/progress";
|
||||
import { useSource } from "../../state/logic/source";
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMisc } from "@/video/state/logic/misc";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export function CastingTextAction() {
|
||||
const { t } = useTranslation();
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMisc } from "@/video/state/logic/misc";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
|
||||
export function DividerAction() {
|
||||
const descriptor = useVideoPlayerDescriptor();
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { canFullscreen } from "@/utils/detectFeatures";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { useVolumeControl } from "@/hooks/useVolumeToggle";
|
||||
import { getPlayerState } from "@/video/state/cache";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { getPlayerState } from "@/video/state/cache";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
import { useVolumeControl } from "@/hooks/useVolumeToggle";
|
||||
|
||||
export function KeyboardShortcutsAction() {
|
||||
const descriptor = useVideoPlayerDescriptor();
|
||||
|
@ -60,7 +61,7 @@ export function KeyboardShortcutsAction() {
|
|||
|
||||
// Mute
|
||||
case "m":
|
||||
toggleVolume();
|
||||
toggleVolume(true);
|
||||
break;
|
||||
|
||||
// Decrease volume
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
import { MWCaption } from "@/backend/helpers/streams";
|
||||
import { DetailedMeta } from "@/backend/metadata/getmeta";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export type WindowMeta = {
|
||||
meta: DetailedMeta;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useCallback } from "react";
|
||||
|
||||
export function MiddlePauseAction() {
|
||||
const descriptor = useVideoPlayerDescriptor();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
|
||||
import { useCurrentSeriesEpisodeInfo } from "../hooks/useCurrentSeriesEpisodeInfo";
|
||||
|
||||
export function PageTitleAction() {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { useCallback } from "react";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useCallback } from "react";
|
||||
import {
|
||||
canPictureInPicture,
|
||||
canWebkitPictureInPicture,
|
||||
} from "@/utils/detectFeatures";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
|
||||
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
import {
|
||||
makePercentage,
|
||||
makePercentageString,
|
||||
|
@ -7,7 +9,6 @@ import { getPlayerState } from "@/video/state/cache";
|
|||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
|
||||
export function ProgressAction() {
|
||||
const descriptor = useVideoPlayerDescriptor();
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { Icons } from "@/components/Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
|
||||
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FloatingAnchor } from "@/components/popout/FloatingAnchor";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
|
||||
import { useCurrentSeriesEpisodeInfo } from "../hooks/useCurrentSeriesEpisodeInfo";
|
||||
|
||||
export function ShowTitleAction() {
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Icons } from "@/components/Icon";
|
|||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
|
||||
import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { VideoPlayerTimeFormat } from "@/video/state/types";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
|
||||
function durationExceedsHour(secs: number): boolean {
|
||||
return secs > 60 * 60;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import {
|
||||
makePercentage,
|
||||
|
@ -10,7 +12,6 @@ import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
|||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { Icons } from "@/components/Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
|
||||
import { PopoutListAction } from "../../popouts/PopoutUtils";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { Icons } from "@/components/Icon";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useSource } from "@/video/state/logic/source";
|
||||
import { MWStreamType } from "@/backend/helpers/streams";
|
||||
import { normalizeTitle } from "@/utils/normalizeTitle";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { MWStreamType } from "@/backend/helpers/streams";
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { normalizeTitle } from "@/utils/normalizeTitle";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { useSource } from "@/video/state/logic/source";
|
||||
|
||||
import { PopoutListAction } from "../../popouts/PopoutUtils";
|
||||
|
||||
export function DownloadAction() {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { Icons } from "@/components/Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
|
||||
import { PopoutListAction } from "../../popouts/PopoutUtils";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { Icons } from "@/components/Icon";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PopoutListAction } from "../../popouts/PopoutUtils";
|
||||
|
||||
import { Icons } from "@/components/Icon";
|
||||
|
||||
import { QualityDisplayAction } from "./QualityDisplayAction";
|
||||
import { PopoutListAction } from "../../popouts/PopoutUtils";
|
||||
|
||||
interface Props {
|
||||
onClick?: () => any;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
import { MWCaption } from "@/backend/helpers/streams";
|
||||
import { MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { VideoPlayerMeta } from "@/video/state/types";
|
||||
import { useEffect } from "react";
|
||||
|
||||
interface MetaControllerProps {
|
||||
data?: VideoPlayerMeta;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useEffect, useMemo, useRef } from "react";
|
||||
import throttle from "lodash.throttle";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useMisc } from "@/video/state/logic/misc";
|
||||
import { useProgress } from "@/video/state/logic/progress";
|
||||
|
||||
interface Props {
|
||||
startAt?: number;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
|
||||
interface SeriesControllerProps {
|
||||
onSelect?: (state: { episodeId?: string; seasonId?: string }) => void;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { getCaptionUrl, makeCaptionId } from "@/backend/helpers/captions";
|
||||
import {
|
||||
MWCaption,
|
||||
|
@ -9,7 +11,6 @@ import { useSettings } from "@/state/settings";
|
|||
import { useInitialized } from "@/video/components/hooks/useInitialized";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useControls } from "@/video/state/logic/controls";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
interface SourceControllerProps {
|
||||
source: string;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { MWMediaType } from "@/backend/metadata/types";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
|
||||
export function useCurrentSeriesEpisodeInfo(descriptor: string) {
|
||||
const meta = useMeta(descriptor);
|
||||
const { t } = useTranslation();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useMisc } from "@/video/state/logic/misc";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { useMisc } from "@/video/state/logic/misc";
|
||||
|
||||
export function useInitialized(descriptor: string): { initialized: boolean } {
|
||||
const misc = useMisc(descriptor);
|
||||
const initialized = useMemo(() => !!misc.initalized, [misc]);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { ControlMethods, useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
|
||||
import { ControlMethods, useControls } from "@/video/state/logic/controls";
|
||||
import { useInterface } from "@/video/state/logic/interface";
|
||||
|
||||
function syncRouteToPopout(
|
||||
location: ReturnType<typeof useLocation>,
|
||||
controls: ControlMethods
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
import { useChromecastAvailable } from "@/hooks/useChromecastAvailable";
|
||||
import { getPlayerState } from "@/video/state/cache";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { updateMisc, useMisc } from "@/video/state/logic/misc";
|
||||
import { createCastingStateProvider } from "@/video/state/providers/castingStateProvider";
|
||||
import { setProvider, unsetStateProvider } from "@/video/state/providers/utils";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
export function CastingInternal() {
|
||||
const descriptor = useVideoPlayerDescriptor();
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||
import { useMisc } from "@/video/state/logic/misc";
|
||||
import { setProvider, unsetStateProvider } from "@/video/state/providers/utils";
|
||||
import { createVideoStateProvider } from "@/video/state/providers/videoStateProvider";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
interface Props {
|
||||
autoPlay?: boolean;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
import { getPlayerState } from "@/video/state/cache";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { updateMisc } from "@/video/state/logic/misc";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export function WrapperRegisterInternal(props: {
|
||||
wrapper: HTMLDivElement | null;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { Component } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
|
||||
|
||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
|
||||
import { Link } from "@/components/text/Link";
|
||||
import { conf } from "@/setup/config";
|
||||
import { Component } from "react";
|
||||
import { Trans } from "react-i18next";
|
||||
import type { ReactNode } from "react-router-dom/node_modules/@types/react/index";
|
||||
|
||||
import { VideoPlayerHeader } from "./VideoPlayerHeader";
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { ReactNode } from "react";
|
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch";
|
||||
import { Icons } from "@/components/Icon";
|
||||
import { Title } from "@/components/text/Title";
|
||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||
import { useError } from "@/video/state/logic/error";
|
||||
import { useMeta } from "@/video/state/logic/meta";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import { VideoPlayerHeader } from "./VideoPlayerHeader";
|
||||
|
||||
interface VideoPlayerErrorProps {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue