Browse Source
Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com> Co-authored-by: William Oldham <github@binaryoverload.co.uk>pull/497/head
44 changed files with 10426 additions and 87 deletions
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
dist |
||||
node_modules |
||||
.output |
||||
.nuxt |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
module.exports = { |
||||
root: true, |
||||
extends: '@nuxt/eslint-config', |
||||
rules: { |
||||
'vue/max-attributes-per-line': 'off', |
||||
'vue/multi-word-component-names': 'off' |
||||
} |
||||
} |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
node_modules |
||||
*.iml |
||||
.idea |
||||
*.log* |
||||
.nuxt |
||||
.vscode |
||||
.DS_Store |
||||
coverage |
||||
dist |
||||
sw.* |
||||
.env |
||||
.output |
@ -0,0 +1,57 @@
@@ -0,0 +1,57 @@
|
||||
# Docus Starter |
||||
|
||||
Starter template for [Docus](https://docus.dev). |
||||
|
||||
## Clone |
||||
|
||||
Clone the repository (using `nuxi`): |
||||
|
||||
```bash |
||||
npx nuxi init -t themes/docus |
||||
``` |
||||
|
||||
## Setup |
||||
|
||||
Install dependencies: |
||||
|
||||
```bash |
||||
yarn install |
||||
``` |
||||
|
||||
## Development |
||||
|
||||
```bash |
||||
yarn dev |
||||
``` |
||||
|
||||
## Edge Side Rendering |
||||
|
||||
Can be deployed to Vercel Functions, Netlify Functions, AWS, and most Node-compatible environments. |
||||
|
||||
Look at all the available presets [here](https://v3.nuxtjs.org/guide/deploy/presets). |
||||
|
||||
```bash |
||||
yarn build |
||||
``` |
||||
|
||||
## Static Generation |
||||
|
||||
Use the `generate` command to build your application. |
||||
|
||||
The HTML files will be generated in the .output/public directory and ready to be deployed to any static compatible hosting. |
||||
|
||||
```bash |
||||
yarn generate |
||||
``` |
||||
|
||||
## Preview build |
||||
|
||||
You might want to preview the result of your build locally, to do so, run the following command: |
||||
|
||||
```bash |
||||
yarn preview |
||||
``` |
||||
|
||||
--- |
||||
|
||||
For a detailed explanation of how things work, check out [Docus](https://docus.dev). |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
export default defineAppConfig({ |
||||
docus: { |
||||
title: 'movie-web', |
||||
description: 'For all your media scraping needs', |
||||
socials: { |
||||
github: 'movie-web/providers', |
||||
}, |
||||
image: '', |
||||
aside: { |
||||
level: 0, |
||||
exclude: [], |
||||
}, |
||||
header: { |
||||
logo: false, |
||||
title: "movie-web" |
||||
}, |
||||
}, |
||||
}); |
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
--- |
||||
title: "movie-web | For all your movie and TV show needs" |
||||
navigation: false |
||||
layout: page |
||||
--- |
||||
|
||||
::block-hero |
||||
--- |
||||
cta: |
||||
- Get Started |
||||
- /guide/usage |
||||
secondary: |
||||
- Open on GitHub → |
||||
- https://github.com/movie-web/movie-web |
||||
--- |
||||
|
||||
#title |
||||
movie-web |
||||
|
||||
#description |
||||
A simple and no-BS app for watching movies and TV shows (Binary is a silly goose) |
||||
:: |
||||
|
||||
::card-grid |
||||
#title |
||||
What's all the fuss? |
||||
|
||||
#root |
||||
:ellipsis |
||||
|
||||
#default |
||||
::card{icon="mdi:server-network"} |
||||
#title |
||||
Easy to host |
||||
#description |
||||
movie-web can be easily hosted on any static website host. |
||||
:: |
||||
::card{icon="material-symbols:hangout-video-off"} |
||||
#title |
||||
No ADs |
||||
#description |
||||
movie-web will never show ADs, enjoy watching without interruptions. |
||||
:: |
||||
::card{icon="ic:baseline-ondemand-video"} |
||||
#title |
||||
Custom Player |
||||
#description |
||||
Enjoy a fully custom video player including streaming integration, subtitle customisation and easy TV season navigation. |
||||
:: |
||||
:: |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
# Getting Started |
||||
|
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
icon: ph:star-duotone |
||||
navigation.redirect: /introduction/getting-started |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
# How to self host |
||||
|
||||
::alert{type="info"} |
||||
We **do not** provide support on how to self-host. If you can't figure it out then tough luck. Please do not make GitHub issues or ask in our Discord server for support on how to self-host. |
||||
:: |
||||
|
||||
The movie-web application is made of two parts: the proxy and the client. Click the following links to find out more: |
||||
- [Setup the Proxy](2.proxy.md) |
||||
- [Setup the Client](3.client.md) |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
# Setting up the proxy |
||||
|
||||
Our proxy is used to bypass CORS-protected URLs on the client side, allowing users to make requests to protected endpoints without a backend server. |
||||
|
||||
The proxy is made using [Nitro by UnJS](https://nitro.unjs.io/) which supports building the proxy to work on multiple providers including Cloudflare Workers, AWS Lambda and [more...](https://nitro.unjs.io/deploy) |
||||
|
||||
Our recommended provider is Cloudflare due to its [generous free plan](https://www.cloudflare.com/en-gb/plans/developer-platform/). |
||||
|
||||
## Cloudflare Workers |
||||
|
||||
The proxy is made as a Cloudflare worker. Cloudflare has a generous free plan, so you don't need to pay anything unless you get hundreds of users. |
||||
|
||||
[](https://deploy.workers.cloudflare.com/?url=https://github.com/movie-web/simple-proxy) |
||||
|
||||
1. Create a GitHub account at https://github.com if you don't have one. |
||||
1. Click the "Deploy with workers" button above. |
||||
1. Click the "Authorize Workers" button to authorise Cloudflare to talk to GitHub. |
||||
1. Authorize Cloudflare Workers in the GitHub page that pops up. |
||||
1. Follow the instructions to configure your Cloudflare account. Select "I have an account" if you have a Cloudflare account already, otherwise follow the link to create one. |
||||
1. Click the link to "Workers Dashboard" to find your account ID. |
||||
1. If you have used workers in the past, there will be a place on the right hand side to copy your account ID. |
||||
1. If you haven't used workers before, you can copy your account ID from the URL e.g. https://dash.cloudflare.com/ab7cb454c93987b6343350d4e84c16c7/workers-and-pages/create where `ab7cb454c93987b6343350d4e84c16c7` is the account ID. |
||||
1. Paste the account ID into the text box on the original Cloudflare workers page. |
||||
1. Click the link to "My Profile" to create an API token. |
||||
1. Click "Create Token". |
||||
1. Select "Use template" next to "Edit Cloudflare Workers". |
||||
1. Under "Account Resources", select "Include" and your account under the dropdown. |
||||
1. Under "Zone Resources", select "All zones" (You can select a more specific zone if you have the zones available). |
||||
1. At the bottom of the page, click "Continue to summary". |
||||
1. On the next screen, click "Create token". |
||||
1. Copy the API token and **save it in a safe place, it won't be shown again**. |
||||
1. Paste the API token into the Cloudflare Workers API Token text box. |
||||
1. Click "Fork the Repository" and follow the instructions to enable workflows. |
||||
1. Click "Deploy" to deploy to Cloudflare Workers. |
||||
1. Congratulations! Your worker is now deploying. Please wait for the GitHub Action to build and publish your worker. |
||||
1. You can click the "Worker dash" and "GitHub repo" buttons to see the status of the deploy. |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
title: 'Self-Hosting' |
||||
icon: mdi:server-network |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
export default defineNuxtConfig({ |
||||
// https://github.com/nuxt-themes/docus
|
||||
extends: '@nuxt-themes/docus', |
||||
devtools: { enabled: true }, |
||||
|
||||
modules: [ |
||||
// Remove it if you don't use Plausible analytics
|
||||
// https://github.com/nuxt-modules/plausible
|
||||
'@nuxtjs/plausible' |
||||
] |
||||
}) |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
{ |
||||
"name": "docus-starter", |
||||
"version": "0.1.0", |
||||
"private": true, |
||||
"scripts": { |
||||
"dev": "nuxi dev", |
||||
"build": "nuxi build", |
||||
"generate": "nuxi generate", |
||||
"preview": "nuxi preview", |
||||
"lint": "eslint ." |
||||
}, |
||||
"devDependencies": { |
||||
"@nuxt-themes/docus": "latest", |
||||
"@nuxt/devtools": "^0.8.5", |
||||
"@nuxt/eslint-config": "^0.2.0", |
||||
"@nuxtjs/plausible": "^0.2.3", |
||||
"@types/node": "^20.8.2", |
||||
"eslint": "^8.50.0", |
||||
"nuxt": "^3.7.4" |
||||
} |
||||
} |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 24 KiB |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
{ |
||||
"extends": [ |
||||
"@nuxtjs" |
||||
], |
||||
"lockFileMaintenance": { |
||||
"enabled": true |
||||
} |
||||
} |
@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
|
||||
import { defineTheme } from 'pinceau' |
||||
|
||||
export default defineTheme({ |
||||
color: { |
||||
primary: { |
||||
50: "#F5E5FF", |
||||
100: "#E7CCFF", |
||||
200: "#D4A9FF", |
||||
300: "#BE85FF", |
||||
400: "#A861FF", |
||||
500: "#8E3DFF", |
||||
600: "#7F36D4", |
||||
700: "#662CA6", |
||||
800: "#552578", |
||||
900: "#441E49" |
||||
} |
||||
} |
||||
}) |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
{ |
||||
"extends": "./.nuxt/tsconfig.json" |
||||
} |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
name: Publish docs |
||||
|
||||
on: |
||||
push: |
||||
branches: |
||||
- master |
||||
|
||||
jobs: |
||||
build: |
||||
name: Build |
||||
runs-on: ubuntu-latest |
||||
|
||||
steps: |
||||
- name: Checkout code |
||||
uses: actions/checkout@v3 |
||||
|
||||
- name: Install Node.js |
||||
uses: actions/setup-node@v3 |
||||
with: |
||||
node-version: 18 |
||||
|
||||
- name: Install packages |
||||
working-directory: ./.docs |
||||
run: npm install |
||||
|
||||
- name: Build project |
||||
working-directory: ./.docs |
||||
run: npm run generate |
||||
|
||||
- name: Upload production-ready build files |
||||
uses: actions/upload-pages-artifact@v1 |
||||
with: |
||||
path: ./.docs/.output/public |
||||
|
||||
deploy: |
||||
name: Deploy |
||||
needs: build |
||||
permissions: |
||||
pages: write |
||||
id-token: write |
||||
environment: |
||||
name: docs |
||||
url: ${{ steps.deployment.outputs.page_url }} |
||||
runs-on: ubuntu-latest |
||||
steps: |
||||
- name: Deploy to GitHub Pages |
||||
id: deployment |
||||
uses: actions/deploy-pages@v2 |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
import { Icon, Icons } from "@/components/Icon"; |
||||
|
||||
export function IconPill(props: { icon: Icons; children?: React.ReactNode }) { |
||||
return ( |
||||
<div className="bg-pill-background bg-opacity-50 px-4 py-2 rounded-full text-white flex justify-center items-center"> |
||||
<Icon |
||||
icon={props.icon ?? Icons.WAND} |
||||
className="mr-3 text-xl text-bink-600" |
||||
/> |
||||
{props.children} |
||||
</div> |
||||
); |
||||
} |
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
export interface HeroTitleProps { |
||||
children?: React.ReactNode; |
||||
className?: string; |
||||
} |
||||
|
||||
export function HeroTitle(props: HeroTitleProps) { |
||||
return ( |
||||
<h1 |
||||
className={`text-2xl font-bold text-white sm:text-3xl md:text-4xl ${ |
||||
props.className ?? "" |
||||
}`}
|
||||
> |
||||
{props.children} |
||||
</h1> |
||||
); |
||||
} |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
export function Paragraph(props: { children: React.ReactNode }) { |
||||
return <p className="text-errors-type-secondary mt-6">{props.children}</p>; |
||||
} |
@ -1,16 +1,17 @@
@@ -1,16 +1,17 @@
|
||||
export interface TitleProps { |
||||
children?: React.ReactNode; |
||||
className?: string; |
||||
} |
||||
import classNames from "classnames"; |
||||
|
||||
export function Title(props: TitleProps) { |
||||
export function Title(props: { |
||||
children: React.ReactNode; |
||||
className?: string; |
||||
}) { |
||||
return ( |
||||
<h1 |
||||
className={`text-2xl font-bold text-white sm:text-3xl md:text-4xl ${ |
||||
props.className ?? "" |
||||
}`}
|
||||
<h2 |
||||
className={classNames( |
||||
"text-white text-3xl font-bold text-opacity-100 mt-6", |
||||
props.className |
||||
)} |
||||
> |
||||
{props.children} |
||||
</h1> |
||||
</h2> |
||||
); |
||||
} |
||||
|
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
import classNames from "classnames"; |
||||
import { ReactNode } from "react"; |
||||
|
||||
export function ErrorContainer(props: { |
||||
children: React.ReactNode; |
||||
maxWidth?: string; |
||||
}) { |
||||
return ( |
||||
<div |
||||
className={classNames( |
||||
"w-full p-6 text-center flex flex-col items-center", |
||||
props.maxWidth ?? "max-w-[28rem]" |
||||
)} |
||||
> |
||||
{props.children} |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
export function ErrorLayout(props: { children?: ReactNode }) { |
||||
return ( |
||||
<div className="w-full h-full flex justify-center items-center flex-col"> |
||||
{props.children} |
||||
</div> |
||||
); |
||||
} |
@ -1,22 +0,0 @@
@@ -1,22 +0,0 @@
|
||||
import { useTranslation } from "react-i18next"; |
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch"; |
||||
import { Icons } from "@/components/Icon"; |
||||
import { ArrowLink } from "@/components/text/ArrowLink"; |
||||
import { Title } from "@/components/text/Title"; |
||||
|
||||
export function MediaNotFoundPart() { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<div className="flex flex-1 flex-col items-center justify-center p-5 text-center"> |
||||
<IconPatch |
||||
icon={Icons.EYE_SLASH} |
||||
className="mb-6 text-xl text-bink-600" |
||||
/> |
||||
<Title>{t("notFound.media.title")}</Title> |
||||
<p className="mb-12 mt-5 max-w-sm">{t("notFound.media.description")}</p> |
||||
<ArrowLink to="/" linkText={t("notFound.backArrow")} /> |
||||
</div> |
||||
); |
||||
} |
@ -1,24 +0,0 @@
@@ -1,24 +0,0 @@
|
||||
import { useTranslation } from "react-i18next"; |
||||
|
||||
import { IconPatch } from "@/components/buttons/IconPatch"; |
||||
import { Icons } from "@/components/Icon"; |
||||
import { ArrowLink } from "@/components/text/ArrowLink"; |
||||
import { Title } from "@/components/text/Title"; |
||||
|
||||
export function ProviderNotFoundPart() { |
||||
const { t } = useTranslation(); |
||||
|
||||
return ( |
||||
<div className="flex flex-1 flex-col items-center justify-center p-5 text-center"> |
||||
<IconPatch |
||||
icon={Icons.EYE_SLASH} |
||||
className="mb-6 text-xl text-bink-600" |
||||
/> |
||||
<Title>{t("notFound.provider.title")}</Title> |
||||
<p className="mb-12 mt-5 max-w-sm"> |
||||
{t("notFound.provider.description")} |
||||
</p> |
||||
<ArrowLink to="/" linkText={t("notFound.backArrow")} /> |
||||
</div> |
||||
); |
||||
} |
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
import { useMemo } from "react"; |
||||
|
||||
import { Button } from "@/components/Button"; |
||||
import { Icon, Icons } from "@/components/Icon"; |
||||
import { IconPill } from "@/components/layout/IconPill"; |
||||
import { Paragraph } from "@/components/text/Paragraph"; |
||||
import { Title } from "@/components/text/Title"; |
||||
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape"; |
||||
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout"; |
||||
|
||||
export interface ScrapeErrorPartProps { |
||||
data: { |
||||
sources: Record<string, ScrapingSegment>; |
||||
sourceOrder: ScrapingItems[]; |
||||
}; |
||||
} |
||||
|
||||
export function ScrapeErrorPart(props: ScrapeErrorPartProps) { |
||||
const error = useMemo(() => { |
||||
const data = props.data; |
||||
const amountError = Object.values(data.sources).filter( |
||||
(v) => v.status === "failure" |
||||
); |
||||
if (amountError.length === 0) return null; |
||||
let str = ""; |
||||
Object.values(data.sources).forEach((v) => { |
||||
str += `${v.id}: ${v.status}\n`; |
||||
if (v.reason) str += `${v.reason}\n`; |
||||
if (v.error) str += `${v.error.toString()}\n`; |
||||
}); |
||||
return str; |
||||
}, [props]); |
||||
|
||||
return ( |
||||
<ErrorLayout> |
||||
<ErrorContainer> |
||||
<IconPill icon={Icons.WAND}>Not found</IconPill> |
||||
<Title>Goo goo gaa gaa</Title> |
||||
<Paragraph> |
||||
Oh, my apowogies, sweetie! The itty-bitty movie-web did its utmost |
||||
bestest, but alas, no wucky videos to be spotted anywhere (´⊙ω⊙`)
|
||||
Please don't be angwy, wittle movie-web ish twying so hard. Can |
||||
you find it in your heart to forgive? UwU 💖 |
||||
</Paragraph> |
||||
<Button |
||||
href="/" |
||||
theme="purple" |
||||
padding="md:px-12 p-2.5" |
||||
className="mt-6" |
||||
> |
||||
Go home |
||||
</Button> |
||||
</ErrorContainer> |
||||
<ErrorContainer maxWidth="max-w-[45rem]"> |
||||
{/* Error */} |
||||
{error ? ( |
||||
<div className="w-full bg-errors-card p-6 rounded-lg"> |
||||
<div className="flex justify-between items-center pb-2 border-b border-errors-border"> |
||||
<span className="text-white font-medium">Error details</span> |
||||
<div className="flex justify-center items-center gap-3"> |
||||
<Button theme="secondary" padding="p-2 md:px-4"> |
||||
<Icon icon={Icons.COPY} className="text-2xl mr-3" /> |
||||
Copy |
||||
</Button> |
||||
<Button theme="secondary" padding="p-2 md:px-2"> |
||||
<Icon icon={Icons.X} className="text-2xl" /> |
||||
</Button> |
||||
</div> |
||||
</div> |
||||
<div className="mt-4 h-60 overflow-y-auto text-left whitespace-pre pointer-events-auto"> |
||||
{error} |
||||
</div> |
||||
</div> |
||||
) : null} |
||||
</ErrorContainer> |
||||
</ErrorLayout> |
||||
); |
||||
} |
Loading…
Reference in new issue