Merge pull request #938 from qtchaos/remove-domain

Remove references to official domain
This commit is contained in:
William Oldham 2024-02-25 22:19:14 +00:00 committed by GitHub
commit 9e4241e464
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 107 additions and 61 deletions

View file

@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
codeofconduct@movie-web.app. our [Discord](https://discord.gg/gQYB6fGArX).
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the

View file

@ -1,6 +1,6 @@
# Contributing Guidelines for movie-web # Contributing Guidelines for movie-web
Thank you for investing your time in contributing to our project! Your contribution will be reflected on [movie-web.app](https://movie-web.app). Thank you for investing your time in contributing to our project! Your contribution will be reflected on all of the community hosted instances that are on the latest version.
Please read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. Please read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
@ -33,7 +33,7 @@ There are two places where to request features or report bugs:
### Discord Server ### Discord Server
If you do not have a GitHub account or want to discuss a feature or bug with us before making an issue, you can join our Discord server. If you do not have a GitHub account or want to discuss a feature or bug with us before making an issue, you can join our Discord server.
<a href="https://discord.movie-web.app"><img src="https://discord.com/api/guilds/871713465100816424/widget.png?style=banner2" alt="Discord Server"></a> <a href="https://discord.gg/gQYB6fGArX"><img src="https://discord.com/api/guilds/871713465100816424/widget.png?style=banner2" alt="Discord Server"></a>
### GitHub Issues ### GitHub Issues
To make a GitHub issue for movie-web, please visit the [new issue page](https://github.com/movie-web/movie-web/issues/new/choose) where you can pick either the "Bug Report" or "Feature Request" template. To make a GitHub issue for movie-web, please visit the [new issue page](https://github.com/movie-web/movie-web/issues/new/choose) where you can pick either the "Bug Report" or "Feature Request" template.
@ -85,7 +85,7 @@ Here are some tips to make sure that your pull requests are :pinched_fingers: fi
### Language Contributions ### Language Contributions
Language contributions help movie-web massively, allowing people worldwide to use our app! Language contributions help movie-web massively, allowing people worldwide to use our app!
We use weblate for crowdsourcing our translations. [Click here to go to our translation tool.](https://weblate.movie-web.app/projects/movie-web/website/) We use weblate for crowdsourcing our translations.
1. First make sure you make an account. (click the link above) 1. First make sure you make an account. (click the link above)
2. Click the language you want to help translate, if it's not listed you can click the plus top left to add a new language. 2. Click the language you want to help translate, if it's not listed you can click the plus top left to add a new language.

9
.github/SECURITY.md vendored
View file

@ -2,12 +2,9 @@
## Supported Versions ## Supported Versions
The movie-web maintainers only support the latest version of movie-web published at https://movie-web.app. The latest version of movie-web is the only version that is supported, as it is the only version that is being actively developed.
Support is not provided for any forks or mirrors of movie-web.
## Reporting a Vulnerability ## Reporting a Vulnerability
There are two ways you can contact the movie-web maintainers to report a vulnerability: You can contact the movie-web maintainers to report a vulnerability:
- Email [security@movie-web.app](mailto:security@movie-web.app) - Report the vulnerability in the [movie-web Discord server](https://discord.gg/gQYB6fGArX)
- Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app)

View file

@ -4,13 +4,13 @@
<p align="center"> <p align="center">
<img src="https://skillicons.dev/icons?i=react,vite,ts" /> <img src="https://skillicons.dev/icons?i=react,vite,ts" />
<br/> <br/>
<a href="https://discord.movie-web.app"><kbd>🔵 discord</kbd></a> <a href="https://movie-web.app"><kbd>🟢 website</kbd></a> <a href="https://discord.gg/gQYB6fGArX"><kbd>🔵 discord</kbd></a> <a href="https://movie-web.github.io/docs"><kbd>🟢 docs</kbd></a>
</p> </p>
<br/><br/> <br/><br/>
# ⚡What is movie-web? # ⚡What is movie-web?
movie-web is a web app for watching movies easily. Check it out at <a href="https://movie-web.app"><kbd>movie-web.app</kbd></a>. movie-web is a web app for watching movies easily.
This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface. This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface.
@ -57,7 +57,7 @@ pnpm build
A simple guide has been written to assist in hosting your own instance of movie-web. Check it out below A simple guide has been written to assist in hosting your own instance of movie-web. Check it out below
|[Selfhosting guide](https://docs.movie-web.app)| |[Selfhosting guide](https://movie-web.github.io/docs)|
|---| |---|
## 🤝 Thanks to all Contributors ## 🤝 Thanks to all Contributors

View file

@ -2,7 +2,7 @@
"name": "movie-web", "name": "movie-web",
"version": "4.4.2", "version": "4.4.2",
"private": true, "private": true,
"homepage": "https://movie-web.app", "homepage": "https://github.com/movie-web/movie-web",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",

View file

@ -11,7 +11,7 @@ window.__CONFIG__ = {
// Whether to disable hash-based routing, leave this as false if you don't know what this is // Whether to disable hash-based routing, leave this as false if you don't know what this is
VITE_NORMAL_ROUTER: false, VITE_NORMAL_ROUTER: false,
// The backend URL to communicate with, defaults to the movie-web hosted one at backend.movie-web.app // The backend URL to communicate with
VITE_BACKEND_URL: null, VITE_BACKEND_URL: null,
// A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-<id>" and "movie-<id>" // A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-<id>" and "movie-<id>"

View file

@ -55,6 +55,8 @@
"text": "Did you configure it correctly?", "text": "Did you configure it correctly?",
"title": "Failed to reach server" "title": "Failed to reach server"
}, },
"noHostTitle": "Server not configured!",
"noHost": "The server has not been configured, therefore you cannot create an account",
"host": "You are connecting to <0>{{hostname}}</0> - please confirm you trust it before making an account", "host": "You are connecting to <0>{{hostname}}</0> - please confirm you trust it before making an account",
"no": "Go back", "no": "Go back",
"title": "Do you trust this server?", "title": "Do you trust this server?",

View file

@ -5,13 +5,14 @@ import { useCallback } from "react";
import { isExtensionActiveCached } from "@/backend/extension/messaging"; import { isExtensionActiveCached } from "@/backend/extension/messaging";
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape"; import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape";
import { BACKEND_URL } from "@/setup/constants";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
import { PlayerMeta } from "@/stores/player/slices/source"; import { PlayerMeta } from "@/stores/player/slices/source";
// for anybody who cares - these are anonymous metrics. // for anybody who cares - these are anonymous metrics.
// They are just used for figuring out if providers are broken or not // They are just used for figuring out if providers are broken or not
const metricsEndpoint = "https://backend.movie-web.app/metrics/providers"; const metricsEndpoint = `${BACKEND_URL}/metrics/providers`;
const captchaMetricsEndpoint = "https://backend.movie-web.app/metrics/captcha"; const captchaMetricsEndpoint = `${BACKEND_URL}/metrics/captcha`;
const batchId = () => nanoid(32); const batchId = () => nanoid(32);
export type ProviderMetric = { export type ProviderMetric = {
@ -44,6 +45,7 @@ function getStackTrace(error: Error, lines: number) {
} }
export async function reportProviders(items: ProviderMetric[]): Promise<void> { export async function reportProviders(items: ProviderMetric[]): Promise<void> {
if (!BACKEND_URL) return;
return ofetch(metricsEndpoint, { return ofetch(metricsEndpoint, {
method: "POST", method: "POST",
body: { body: {
@ -156,6 +158,7 @@ export function useReportProviders() {
} }
export function reportCaptchaSolve(success: boolean) { export function reportCaptchaSolve(success: boolean) {
if (!BACKEND_URL) return;
ofetch(captchaMetricsEndpoint, { ofetch(captchaMetricsEndpoint, {
method: "POST", method: "POST",
body: { body: {

View file

@ -63,6 +63,7 @@ export function useAuth() {
const login = useCallback( const login = useCallback(
async (loginData: LoginData) => { async (loginData: LoginData) => {
if (!backendUrl) return;
const keys = await keysFromMnemonic(loginData.mnemonic); const keys = await keysFromMnemonic(loginData.mnemonic);
const publicKeyBase64Url = bytesToBase64Url(keys.publicKey); const publicKeyBase64Url = bytesToBase64Url(keys.publicKey);
const { challenge } = await getLoginChallengeToken( const { challenge } = await getLoginChallengeToken(
@ -87,7 +88,7 @@ export function useAuth() {
); );
const logout = useCallback(async () => { const logout = useCallback(async () => {
if (!currentAccount) return; if (!currentAccount || !backendUrl) return;
try { try {
await removeSession( await removeSession(
backendUrl, backendUrl,
@ -102,6 +103,7 @@ export function useAuth() {
const register = useCallback( const register = useCallback(
async (registerData: RegistrationData) => { async (registerData: RegistrationData) => {
if (!backendUrl) return;
const { challenge } = await getRegisterChallengeToken( const { challenge } = await getRegisterChallengeToken(
backendUrl, backendUrl,
registerData.recaptchaToken, registerData.recaptchaToken,
@ -134,6 +136,7 @@ export function useAuth() {
progressItems: Record<string, ProgressMediaItem>, progressItems: Record<string, ProgressMediaItem>,
bookmarks: Record<string, BookmarkMediaItem>, bookmarks: Record<string, BookmarkMediaItem>,
) => { ) => {
if (!backendUrl) return;
if ( if (
Object.keys(progressItems).length === 0 && Object.keys(progressItems).length === 0 &&
Object.keys(bookmarks).length === 0 Object.keys(bookmarks).length === 0
@ -159,6 +162,7 @@ export function useAuth() {
const restore = useCallback( const restore = useCallback(
async (account: AccountWithToken) => { async (account: AccountWithToken) => {
if (!backendUrl) return;
let user: { user: UserResponse; session: SessionResponse }; let user: { user: UserResponse; session: SessionResponse };
try { try {
user = await getUser(backendUrl, account.token); user = await getUser(backendUrl, account.token);

View file

@ -1,7 +1,7 @@
import { conf } from "@/setup/config"; import { conf } from "@/setup/config";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
export function useBackendUrl() { export function useBackendUrl(): string | undefined {
const backendUrl = useAuthStore((s) => s.backendUrl); const backendUrl = useAuthStore((s) => s.backendUrl);
return backendUrl ?? conf().BACKEND_URL; return backendUrl ?? conf().BACKEND_URL;
} }

View file

@ -70,6 +70,7 @@ export function AccountSettings(props: {
const url = useBackendUrl(); const url = useBackendUrl();
const { account } = props; const { account } = props;
const [sessionsResult, execSessions] = useAsyncFn(() => { const [sessionsResult, execSessions] = useAsyncFn(() => {
if (!url) return Promise.resolve([]);
return getSessions(url, account); return getSessions(url, account);
}, [account, url]); }, [account, url]);
useEffect(() => { useEffect(() => {
@ -144,7 +145,7 @@ export function SettingsPage() {
); );
const saveChanges = useCallback(async () => { const saveChanges = useCallback(async () => {
if (account) { if (account && backendUrl) {
if ( if (
state.appLanguage.changed || state.appLanguage.changed ||
state.theme.changed || state.theme.changed ||

View file

@ -43,7 +43,7 @@ export function OnboardingProxyPage() {
throw new Error("onboarding.proxy.input.errorNotProxy"); throw new Error("onboarding.proxy.input.errorNotProxy");
setProxySet([url]); setProxySet([url]);
if (account) { if (account && backendUrl) {
await updateSettings(backendUrl, account, { await updateSettings(backendUrl, account, {
proxyUrls: [url], proxyUrls: [url],
}); });

View file

@ -32,13 +32,21 @@ export function BackendTestPart() {
value: null, value: null,
}); });
if (!backendUrl) {
return setStatus({
hasTested: true,
success: false,
errorText: "Backend URL is not set",
value: null,
});
}
try { try {
const backendData = await getBackendMeta(backendUrl); const backendData = await getBackendMeta(backendUrl);
return setStatus({ return setStatus({
hasTested: true, hasTested: true,
success: true, success: true,
errorText: errorText: "",
"Failed to call backend, double check the URL key and your internet connection",
value: backendData, value: backendData,
}); });
} catch (err) { } catch (err) {
@ -46,7 +54,7 @@ export function BackendTestPart() {
hasTested: true, hasTested: true,
success: false, success: false,
errorText: errorText:
"Failed to call backend, double check the URL key and your internet connection", "Failed to call backend, double check the URL, your internet connection, and ensure CORS is properly configured on your backend.",
value: null, value: null,
}); });
} }

View file

@ -52,6 +52,9 @@ export function LoginFormPart(props: LoginFormPartProps) {
throw err; throw err;
} }
if (!account)
throw new Error(t("auth.login.validationError") ?? undefined);
await importData(account, progressItems, bookmarkItems); await importData(account, progressItems, bookmarkItems);
await restore(account); await restore(account);

View file

@ -22,8 +22,12 @@ interface TrustBackendPartProps {
export function TrustBackendPart(props: TrustBackendPartProps) { export function TrustBackendPart(props: TrustBackendPartProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const backendUrl = useBackendUrl(); const backendUrl = useBackendUrl();
const hostname = useMemo(() => new URL(backendUrl).hostname, [backendUrl]); const hostname = useMemo(
() => (backendUrl ? new URL(backendUrl).hostname : undefined),
[backendUrl],
);
const result = useAsync(() => { const result = useAsync(() => {
if (!backendUrl) return Promise.resolve(null);
return getBackendMeta(backendUrl); return getBackendMeta(backendUrl);
}, [backendUrl]); }, [backendUrl]);
const { t } = useTranslation(); const { t } = useTranslation();
@ -50,38 +54,52 @@ export function TrustBackendPart(props: TrustBackendPartProps) {
return ( return (
<LargeCard> <LargeCard>
<LargeCardText <LargeCardText
title={t("auth.trust.title")} title={hostname ? t("auth.trust.title") : t("auth.trust.noHostTitle")}
icon={<Icon icon={Icons.CIRCLE_EXCLAMATION} />} icon={<Icon icon={Icons.CIRCLE_EXCLAMATION} />}
> >
<Trans {hostname ? (
i18nKey="auth.trust.host" <Trans
values={{ i18nKey="auth.trust.host"
hostname, values={{
}} hostname,
> }}
<span className="text-white" /> >
</Trans> <span className="text-white" />
</Trans>
) : (
<p>{t("auth.trust.noHost")}</p>
)}
</LargeCardText> </LargeCardText>
<div className="border border-authentication-border rounded-xl px-4 py-8 flex flex-col items-center space-y-2 my-8"> {hostname ? (
{cardContent} <>
</div> <div className="border border-authentication-border rounded-xl px-4 py-8 flex flex-col items-center space-y-2 my-8">
<LargeCardButtons> {cardContent}
<Button theme="secondary" onClick={() => navigate("/")}> </div>
{t("auth.trust.no")} <LargeCardButtons>
</Button> <Button theme="secondary" onClick={() => navigate("/")}>
<Button {t("auth.trust.no")}
theme="purple" </Button>
onClick={() => result.value && props.onNext?.(result.value)} <Button
> theme="purple"
{t("auth.trust.yes")} onClick={() => result.value && props.onNext?.(result.value)}
</Button> >
</LargeCardButtons> {t("auth.trust.yes")}
<p className="text-center mt-6"> </Button>
<Trans i18nKey="auth.hasAccount"> </LargeCardButtons>
<MwLink to="/login">.</MwLink> <p className="text-center mt-6">
</Trans> <Trans i18nKey="auth.hasAccount">
</p> <MwLink to="/login">.</MwLink>
</Trans>
</p>
</>
) : (
<LargeCardButtons>
<Button theme="purple" onClick={() => navigate("/")}>
{t("auth.trust.no")}
</Button>
</LargeCardButtons>
)}
</LargeCard> </LargeCard>
); );
} }

View file

@ -47,6 +47,8 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
const [result, execute] = useAsyncFn( const [result, execute] = useAsyncFn(
async (inputMnemonic: string) => { async (inputMnemonic: string) => {
if (!backendUrl)
throw new Error(t("auth.verify.noBackendUrl") ?? undefined);
if (!props.mnemonic || !props.userData) if (!props.mnemonic || !props.userData)
throw new Error(t("auth.verify.invalidData") ?? undefined); throw new Error(t("auth.verify.invalidData") ?? undefined);
@ -68,6 +70,9 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
recaptchaToken, recaptchaToken,
}); });
if (!account)
throw new Error(t("auth.verify.registrationFailed") ?? undefined);
await importData(account, progressItems, bookmarkItems); await importData(account, progressItems, bookmarkItems);
await updateSettings(backendUrl, account, { await updateSettings(backendUrl, account, {

View file

@ -18,7 +18,7 @@ export function AccountActionsPart() {
const deleteModal = useModal("account-delete"); const deleteModal = useModal("account-delete");
const [deleteResult, deleteExec] = useAsyncFn(async () => { const [deleteResult, deleteExec] = useAsyncFn(async () => {
if (!account) return; if (!account || !url) return;
await deleteUser(url, account); await deleteUser(url, account);
await logout(); await logout();
deleteModal.hide(); deleteModal.hide();

View file

@ -55,7 +55,7 @@ function ProxyEdit({ proxyUrls, setProxyUrls }: ProxyEditProps) {
</p> </p>
<p className="max-w-[20rem] font-medium"> <p className="max-w-[20rem] font-medium">
<Trans i18nKey="settings.connections.workers.description"> <Trans i18nKey="settings.connections.workers.description">
<MwLink to="https://docs.movie-web.app/proxy/deploy"> <MwLink to="https://movie-web.github.io/docs/proxy/deploy">
Proxy documentation Proxy documentation
</MwLink> </MwLink>
</Trans> </Trans>
@ -125,7 +125,7 @@ function BackendEdit({ backendUrl, setBackendUrl }: BackendEditProps) {
</p> </p>
<p className="max-w-[20rem] font-medium"> <p className="max-w-[20rem] font-medium">
<Trans i18nKey="settings.connections.server.description"> <Trans i18nKey="settings.connections.server.description">
<MwLink to="https://docs.movie-web.app/backend/deploy"> <MwLink to="https://movie-web.github.io/docs/backend/deploy">
Backend documentation Backend documentation
</MwLink> </MwLink>
</Trans> </Trans>

View file

@ -24,6 +24,7 @@ export function Device(props: {
const token = useAuthStore((s) => s.account?.token); const token = useAuthStore((s) => s.account?.token);
const [result, exec] = useAsyncFn(async () => { const [result, exec] = useAsyncFn(async () => {
if (!token) throw new Error("No token present"); if (!token) throw new Error("No token present");
if (!url) throw new Error("No backend set");
await removeSession(url, token, props.id); await removeSession(url, token, props.id);
props.onRemove?.(); props.onRemove?.();
}, [url, token, props.id]); }, [url, token, props.id]);

View file

@ -14,9 +14,9 @@ import { useAuthStore } from "@/stores/auth";
const rem = 16; const rem = 16;
function SecureBadge(props: { url: string }) { function SecureBadge(props: { url: string | undefined }) {
const { t } = useTranslation(); const { t } = useTranslation();
const secure = props.url.startsWith("https://"); const secure = props.url ? props.url.startsWith("https://") : false;
return ( return (
<div className="flex items-center gap-1 -mx-1 ml-3 px-1 rounded bg-largeCard-background font-bold"> <div className="flex items-center gap-1 -mx-1 ml-3 px-1 rounded bg-largeCard-background font-bold">
<Icon icon={secure ? Icons.LOCK : Icons.UNLOCK} /> <Icon icon={secure ? Icons.LOCK : Icons.UNLOCK} />
@ -68,6 +68,7 @@ export function SidebarPart() {
const backendUrl = useBackendUrl(); const backendUrl = useBackendUrl();
const backendMeta = useAsync(async () => { const backendMeta = useAsync(async () => {
if (!backendUrl) return;
return getBackendMeta(backendUrl); return getBackendMeta(backendUrl);
}, [backendUrl]); }, [backendUrl]);
@ -159,7 +160,7 @@ export function SidebarPart() {
<SecureBadge url={backendUrl} /> <SecureBadge url={backendUrl} />
</div> </div>
<p className="text-white"> <p className="text-white">
{backendUrl.replace(/https?:\/\//, "")} {backendUrl?.replace(/https?:\/\//, "") ?? "—"}
</p> </p>
</div> </div>

View file

@ -1,6 +1,6 @@
export const APP_VERSION = import.meta.env.PACKAGE_VERSION; export const APP_VERSION = import.meta.env.PACKAGE_VERSION;
export const DISCORD_LINK = "https://discord.movie-web.app"; export const DISCORD_LINK = "https://discord.gg/gQYB6fGArX";
export const GITHUB_LINK = "https://github.com/movie-web/movie-web"; export const GITHUB_LINK = "https://github.com/movie-web/movie-web";
export const DONATION_LINK = "https://ko-fi.com/movieweb"; export const DONATION_LINK = "https://ko-fi.com/movieweb";
export const GA_ID = "G-44YVXRL61C"; export const GA_ID = "G-44YVXRL61C";
export const BACKEND_URL = "https://backend.movie-web.app"; export const BACKEND_URL = import.meta.env.VITE_BACKEND_URL;

View file

@ -60,6 +60,7 @@ export function BookmarkSyncer() {
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
(async () => { (async () => {
if (!url) return;
const state = useBookmarkStore.getState(); const state = useBookmarkStore.getState();
const user = useAuthStore.getState(); const user = useAuthStore.getState();
await syncBookmarks( await syncBookmarks(

View file

@ -62,6 +62,7 @@ export function ProgressSyncer() {
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
(async () => { (async () => {
if (!url) return;
const state = useProgressStore.getState(); const state = useProgressStore.getState();
const user = useAuthStore.getState(); const user = useAuthStore.getState();
await syncProgress( await syncProgress(

View file

@ -16,6 +16,7 @@ export function SettingsSyncer() {
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
(async () => { (async () => {
if (!url) return;
const state = useSubtitleStore.getState(); const state = useSubtitleStore.getState();
const user = useAuthStore.getState(); const user = useAuthStore.getState();
if (state.lastSync.lastSelectedLanguage === state.lastSelectedLanguage) if (state.lastSync.lastSelectedLanguage === state.lastSelectedLanguage)