Initial commit

This commit is contained in:
Aleksi Lassila
2023-06-13 13:08:28 +03:00
commit e04a054999
71 changed files with 11716 additions and 0 deletions

13
.eslintignore Normal file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

30
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,30 @@
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
.vercel
.output
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/reiverr.iml" filepath="$PROJECT_DIR$/.idea/reiverr.iml" />
</modules>
</component>
</project>

8
.idea/prettier.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
<option name="myRunOnSave" value="true" />
<option name="myFilesPattern" value="{**/*,*}.{js,ts,jsx,tsx,vue,astro,svelte}" />
</component>
</project>

12
.idea/reiverr.iml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

2
.npmrc Normal file
View File

@@ -0,0 +1,2 @@
engine-strict=true
resolution-mode=highest

13
.prettierignore Normal file
View File

@@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

9
.prettierrc Normal file
View File

@@ -0,0 +1,9 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

38
README.md Normal file
View File

@@ -0,0 +1,38 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

15
codegen.yml Normal file
View File

@@ -0,0 +1,15 @@
overwrite: true
schema: "http://localhost:4000/graphql"
documents: "src/lib/{queries,mutations}/**/*.graphql"
generates:
src/lib/graphql.ts:
config:
clientPath: "$lib/apollo-client"
plugins:
- add:
content: '/* eslint-disable */'
- add:
content: '/* this is a generated file, do not edit */'
- typescript
- typescript-operations
- graphql-codegen-svelte-apollo

8462
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

54
package.json Normal file
View File

@@ -0,0 +1,54 @@
{
"name": "reiverr",
"version": "0.0.1",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"test": "playwright test",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@fontsource/fira-mono": "^4.5.10",
"@graphql-codegen/cli": "^4.0.1",
"@graphql-codegen/client-preset": "^4.0.0",
"@graphql-codegen/typescript": "^4.0.0",
"@graphql-codegen/typescript-operations": "^4.0.0",
"@neoconfetti/svelte": "^1.0.0",
"@playwright/test": "^1.28.1",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.5.0",
"@types/axios": "^0.14.0",
"@types/cookie": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"autoprefixer": "^10.4.14",
"classnames": "^2.3.2",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.26.0",
"graphql-codegen-svelte-apollo": "^1.1.0",
"postcss": "^8.4.24",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.8.1",
"svelte": "^3.54.0",
"svelte-check": "^3.0.1",
"tailwindcss": "^3.3.2",
"tslib": "^2.4.1",
"typescript": "^5.1.3",
"vite": "^4.3.0",
"vitest": "^0.25.3"
},
"type": "module",
"dependencies": {
"@apollo/client": "^3.7.15",
"axios": "^1.4.0",
"graphql": "^16.6.0",
"radix-icons-svelte": "^1.2.1",
"svelte-apollo": "^0.5.0"
}
}

12
playwright.config.ts Normal file
View File

@@ -0,0 +1,12 @@
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173
},
testDir: 'tests',
testMatch: /(.+\.)?(test|spec)\.[jt]s/
};
export default config;

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

3
src/app.css Normal file
View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

12
src/app.d.ts vendored Normal file
View File

@@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

15
src/app.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=Inter:wght@100;200;300;400;500;600;700;800;900&family=Nunito+Sans:wght@200;300;400;500;600;700;800;900;1000&display=swap" rel="stylesheet">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" class="bg-zinc-950 min-h-screen text-white">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

12
src/lib/apollo-client.ts Normal file
View File

@@ -0,0 +1,12 @@
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core';
const client = new ApolloClient({
link: new HttpLink({
uri: 'http://localhost:4000/graphql'
}),
cache: new InMemoryCache({
addTypename: false
})
});
export default client;

2113
src/lib/graphql.ts Normal file

File diff suppressed because it is too large Load Diff

16
src/lib/images/github.svg Normal file
View File

@@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -3 30 30">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5229 6.47715 22 12 22C17.5229 22 22 17.5229 22 12C22 6.47715 17.5229 2 12 2ZM0 12C0 5.3726 5.3726 0 12 0C18.6274 0 24 5.3726 24 12C24 18.6274 18.6274 24 12 24C5.3726 24 0 18.6274 0 12Z"
fill="rgba(0,0,0,0.7)"
stroke="none"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M9.59162 22.7357C9.49492 22.6109 9.49492 21.4986 9.59162 19.399C8.55572 19.4347 7.90122 19.3628 7.62812 19.1833C7.21852 18.9139 6.80842 18.0833 6.44457 17.4979C6.08072 16.9125 5.27312 16.8199 4.94702 16.6891C4.62091 16.5582 4.53905 16.0247 5.84562 16.4282C7.15222 16.8316 7.21592 17.9303 7.62812 18.1872C8.04032 18.4441 9.02572 18.3317 9.47242 18.1259C9.91907 17.9201 9.88622 17.1538 9.96587 16.8503C10.0666 16.5669 9.71162 16.5041 9.70382 16.5018C9.26777 16.5018 6.97697 16.0036 6.34772 13.7852C5.71852 11.5669 6.52907 10.117 6.96147 9.49369C7.24972 9.07814 7.22422 8.19254 6.88497 6.83679C8.11677 6.67939 9.06732 7.06709 9.73672 7.99999C9.73737 8.00534 10.6143 7.47854 12.0001 7.47854C13.386 7.47854 13.8777 7.90764 14.2571 7.99999C14.6365 8.09234 14.94 6.36699 17.2834 6.83679C16.7942 7.79839 16.3844 8.99999 16.6972 9.49369C17.0099 9.98739 18.2372 11.5573 17.4833 13.7852C16.9807 15.2706 15.9927 16.1761 14.5192 16.5018C14.3502 16.5557 14.2658 16.6427 14.2658 16.7627C14.2658 16.9427 14.4942 16.9624 14.8233 17.8058C15.0426 18.368 15.0585 19.9739 14.8708 22.6234C14.3953 22.7445 14.0254 22.8257 13.7611 22.8673C13.2924 22.9409 12.7835 22.9822 12.2834 22.9982C11.7834 23.0141 11.6098 23.0123 10.9185 22.948C10.4577 22.9051 10.0154 22.8343 9.59162 22.7357Z"
fill="rgba(0,0,0,0.7)"
stroke="none"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -0,0 +1,6 @@
mutation clearCache {
result: clearRedisCache {
success
message
}
}

View File

@@ -0,0 +1,14 @@
mutation downloadOwnTorrent(
$mediaId: Int!
$mediaType: FileType!
$torrent: String!
) {
downloadOwnTorrent(
mediaId: $mediaId
mediaType: $mediaType
torrent: $torrent
) {
success
message
}
}

View File

@@ -0,0 +1,20 @@
mutation startScanLibrary {
result: startScanLibraryJob {
success
message
}
}
mutation startFindNewEpisodes {
result: startFindNewEpisodesJob {
success
message
}
}
mutation startDownloadMissing {
result: startDownloadMissingJob {
success
message
}
}

View File

@@ -0,0 +1,31 @@
mutation downloadMovie($movieId: Int!, $jackettResult: JackettInput!) {
result: downloadMovie(movieId: $movieId, jackettResult: $jackettResult) {
success
message
}
}
mutation downloadTVEpisode($episodeId: Int!, $jackettResult: JackettInput!) {
result: downloadTVEpisode(
episodeId: $episodeId
jackettResult: $jackettResult
) {
success
message
}
}
mutation downloadSeason(
$tvShowTMDBId: Int!
$seasonNumber: Int!
$jackettResult: JackettInput!
) {
result: downloadSeason(
tvShowTMDBId: $tvShowTMDBId
seasonNumber: $seasonNumber
jackettResult: $jackettResult
) {
success
message
}
}

View File

@@ -0,0 +1,6 @@
mutation removeMovie($tmdbId: Int!) {
result: removeMovie(tmdbId: $tmdbId) {
success
message
}
}

View File

@@ -0,0 +1,6 @@
mutation removeTVShow($tmdbId: Int!) {
result: removeTVShow(tmdbId: $tmdbId) {
success
message
}
}

View File

@@ -0,0 +1,9 @@
mutation resetLibrary($deleteFiles: Boolean!, $resetSettings: Boolean!) {
result: resetLibrary(
deleteFiles: $deleteFiles
resetSettings: $resetSettings
) {
success
message
}
}

View File

@@ -0,0 +1,6 @@
mutation saveQuality($qualities: [QualityInput!]!) {
result: saveQualityParams(qualities: $qualities) {
success
message
}
}

View File

@@ -0,0 +1,6 @@
mutation saveTags($tags: [TagInput!]!) {
result: saveTags(tags: $tags) {
success
message
}
}

View File

@@ -0,0 +1,5 @@
mutation trackMovie($title: String!, $tmdbId: Int!) {
movie: trackMovie(title: $title, tmdbId: $tmdbId) {
id
}
}

View File

@@ -0,0 +1,5 @@
mutation trackTVShow($tmdbId: Int!, $seasonNumbers: [Int!]!) {
tvShow: trackTVShow(tmdbId: $tmdbId, seasonNumbers: $seasonNumbers) {
id
}
}

View File

@@ -0,0 +1,6 @@
mutation updateParams($params: [UpdateParamsInput!]!) {
result: updateParams(params: $params) {
success
message
}
}

View File

@@ -0,0 +1,22 @@
query getCalendar {
calendar: getCalendar {
movies {
id
title
state
releaseDate
}
tvEpisodes {
id
tvShow {
id
title
}
episodeNumber
seasonNumber
state
releaseDate
}
}
}

View File

@@ -0,0 +1,31 @@
query getDiscover(
$entertainment: Entertainment
$originLanguage: String
$primaryReleaseYear: String
$score: Float
$genres: [Float!]
$page: Float
) {
TMDBResults: discover(
entertainment: $entertainment
originLanguage: $originLanguage
primaryReleaseYear: $primaryReleaseYear
score: $score
genres: $genres
page: $page
) {
page
totalResults
totalPages
results {
id
tmdbId
title
posterPath
overview
runtime
voteAverage
releaseDate
}
}
}

View File

@@ -0,0 +1,18 @@
query getDownloading {
searching: getSearchingMedias {
id
title
resourceId
resourceType
}
downloading: getDownloadingMedias {
id
title
tag
quality
torrent
resourceId
resourceType
}
}

View File

@@ -0,0 +1,13 @@
query getGenres {
genres: getGenres {
movieGenres {
id,
name
}
tvShowGenres {
id,
name
}
}
}

View File

@@ -0,0 +1,6 @@
query getLanguages {
languages: getLanguages {
code,
language
}
}

View File

@@ -0,0 +1,16 @@
query getLibraryMovies {
movies: getMovies {
id
tmdbId
title
originalTitle
state
posterPath
overview
runtime
voteAverage
releaseDate
createdAt
updatedAt
}
}

View File

@@ -0,0 +1,15 @@
query getLibraryTVShows {
tvShows: getTVShows {
id
tmdbId
title
originalTitle
posterPath
runtime
overview
voteAverage
releaseDate
createdAt
updatedAt
}
}

View File

@@ -0,0 +1,26 @@
fragment MissingTVEpisodes on EnrichedTVEpisode {
id
seasonNumber
episodeNumber
releaseDate
tvShow {
id
title
}
}
fragment MissingMovies on EnrichedMovie {
id
title
releaseDate
}
query getMissing {
tvEpisodes: getMissingTVEpisodes {
...MissingTVEpisodes
}
movies: getMissingMovies {
...MissingMovies
}
}

View File

@@ -0,0 +1,8 @@
query getMovieFileDetails($tmdbId: Int!) {
details: getMovieFileDetails(tmdbId: $tmdbId) {
id
libraryPath
libraryFileSize
torrentFileName
}
}

View File

@@ -0,0 +1,11 @@
query getParams {
params: getParams {
region
language
tmdb_api_key
jackett_api_key
max_movie_download_size
max_tvshow_episode_download_size
organize_library_strategy
}
}

View File

@@ -0,0 +1,25 @@
query getPopular {
results: getPopular {
movies {
id
tmdbId
title
releaseDate
posterPath
overview
runtime
voteAverage
}
tvShows {
id
tmdbId
title
releaseDate
posterPath
overview
runtime
voteAverage
}
}
}

View File

@@ -0,0 +1,11 @@
query getQuality($type: Entertainment!) {
qualities: getQualityParams(type: $type) {
id
name
match
score
updatedAt
createdAt
type
}
}

View File

@@ -0,0 +1,23 @@
query getRecommended {
tvShows: getRecommendedTVShows {
id
tmdbId
title
releaseDate
posterPath
overview
runtime
voteAverage
}
movies: getRecommendedMovies {
id
tmdbId
title
releaseDate
posterPath
overview
runtime
voteAverage
}
}

View File

@@ -0,0 +1,9 @@
query getTags {
tags: getTags {
id
name
score
createdAt
updatedAt
}
}

View File

@@ -0,0 +1,14 @@
query getTorrentStatus($torrents: [GetTorrentStatusInput!]!) {
torrents: getTorrentStatus(torrents: $torrents) {
id
resourceId
resourceType
percentDone
rateDownload
rateUpload
uploadRatio
uploadedEver
totalSize
status
}
}

View File

@@ -0,0 +1,22 @@
query getTVSeasonDetails($tvShowTMDBId: Int!, $seasonNumber: Int!) {
episodes: getTVSeasonDetails(
tvShowTMDBId: $tvShowTMDBId
seasonNumber: $seasonNumber
) {
id
episodeNumber
seasonNumber
state
updatedAt
voteAverage
releaseDate
createdAt
tvShow {
id
title
tmdbId
updatedAt
createdAt
}
}
}

View File

@@ -0,0 +1,12 @@
query getTVShowSeasons($tvShowTMDBId: Int!) {
seasons: getTVShowSeasons(tvShowTMDBId: $tvShowTMDBId) {
id
name
seasonNumber
episodeCount
overview
posterPath
airDate
inLibrary
}
}

View File

@@ -0,0 +1,13 @@
query omdbSearch(
$title: String!
) {
result: omdbSearch(
title: $title
) {
ratings {
IMDB
rottenTomatoes
metaCritic
}
}
}

View File

@@ -0,0 +1,18 @@
query searchTorrent($query: String!) {
results: searchJackett(query: $query) {
id
title
quality
qualityScore
seeders
peers
link
downloadLink
tag
tagScore
normalizedTitle
normalizedTitleParts
size
publishDate
}
}

View File

@@ -0,0 +1,25 @@
query search($query: String!) {
results: search(query: $query) {
movies {
id
tmdbId
title
releaseDate
posterPath
overview
runtime
voteAverage
}
tvShows {
id
tmdbId
title
releaseDate
posterPath
overview
runtime
voteAverage
}
}
}

9
src/lib/tmdb-api.ts Normal file
View File

@@ -0,0 +1,9 @@
import axios from 'axios';
import { PUBLIC_TMDB_API_KEY } from '$env/static/public';
export const TmdbApi = axios.create({
baseURL: 'https://api.themoviedb.org/3',
headers: {
Authorization: `Bearer ${PUBLIC_TMDB_API_KEY}`
}
});

19
src/routes/+layout.svelte Normal file
View File

@@ -0,0 +1,19 @@
<script>
import '../app.css';
import { setClient } from 'svelte-apollo';
import client from '$lib/apollo-client';
import Navbar from './Navbar.svelte';
setClient(client);
</script>
<div class="app">
<Navbar />
<main>
<slot />
</main>
<!-- <footer>-->
<!-- <p>visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit</p>-->
<!-- </footer>-->
</div>

158
src/routes/+page.svelte Normal file
View File

@@ -0,0 +1,158 @@
<script lang="ts">
import { getPopular } from '$lib/graphql';
import { ChevronDown, ChevronLeft, ChevronRight, Dot, DotFilled } from 'radix-icons-svelte';
import SmallPoster from './SmallPoster.svelte';
const popular = getPopular({});
// let bgUrl = '';
//
// $: {
// const popularMovie = $popular.data?.results?.movies[2];
//
// if (popularMovie) {
// TmdbApi.get('/movie/' + popularMovie.tmdbId + '/images')
// .then((res) => {
// console.log('GOt response');
// bgUrl = 'https://www.themoviedb.org/t/p/original/' + res.data.backdrops[0].file_path;
// })
// .catch(console.error);
// }
// }
const backgroundUrl = 'https://www.themoviedb.org/t/p/original/5AcP07WJl1VZbnloLZrMVgYjR2s.jpg';
$: if ($popular.data.results) console.log($popular.data.results);
</script>
<div
class="h-screen w-screen bg-center bg-cover"
style={"background-image: url('" + backgroundUrl + "')"}
>
<div
class="bg-[#070501bf] h-full w-full flex px-16 pb-12 pt-32 grid grid-cols-[1fr_max-content] grid-rows-[1fr_min-content] gap-x-16 gap-y-8 relative"
>
<div class="flex flex-col justify-self-start min-w-0">
<!-- <h2 class="tracking-wider font-display font-light text-amber-200">Popular Now</h2>-->
<!-- <h1 class="uppercase text-8xl font-bold font-display">Top Gun: Maveric</h1>-->
<!-- <h2 class="text-zinc-300 text-sm self-end">-->
<!-- <span class="font-bold uppercase tracking-wider">September</span> 2022-->
<!-- </h2>-->
<div class="relative">
<h2 class="text-zinc-300 text-sm self-end">
<span class="font-bold uppercase tracking-wider">September</span> 2022
</h2>
<h2
class="tracking-wider font-display font-extrabold text-amber-300 absolute opacity-10 text-8xl -ml-6 mt-8"
>
Popular Now
</h2>
<h1 class="uppercase text-8xl font-bold font-display z-[1] relative">Top Gun: Maveric</h1>
</div>
<div class="mt-auto max-w-3xl flex flex-col gap-4">
<div class="text-xl font-semibold tracking-wider">Feel the need... The need for speed.</div>
<div
class="tracking-wider text-zinc-200 font-light leading-6 pl-4 border-l-2 border-zinc-300"
>
After more than thirty years of service as one of the Navys top aviators, and dodging the
advancement in rank that would ground him, Pete “Maverick” Mitchell finds himself training
a detachment of TOP GUN graduates for a specialized mission the likes of which no living
pilot has ever seen.
</div>
</div>
<div class="flex gap-6 mt-10">
<div class="flex gap-1">
<button
class="bg-white border-2 border-white hover:bg-amber-400 hover:border-amber-400 transition-colors text-zinc-900 px-8 py-3.5 uppercase tracking-widest font-extrabold cursor-pointer text-xs"
>Stream</button
>
<div
class="hidden items-center justify-center border-2 border-white w-10 cursor-pointer hover:bg-white hover:text-zinc-900 transition-colors"
>
<ChevronDown size="20" />
</div>
</div>
<button
class="border-2 border-white cursor-pointer transition-colors px-8 py-3.5 uppercase tracking-widest font-semibold text-xs hover:bg-white hover:text-black"
>Watch Trailer</button
>
</div>
</div>
<div class="flex flex-col gap-6">
<h3 class="text-xs tracking-wide uppercase">Details</h3>
<div class="flex flex-col gap-1">
<div class="tracking-widest font-extralight text-sm">Action, Drama</div>
<div class="tracking-widest font-extralight text-sm">2h 13min</div>
<div class="tracking-widest font-extralight text-sm">Currently <b>Streaming</b></div>
<div class="tracking-widest font-extralight text-sm"><b>83%</b> IMDB</div>
<div class="flex mt-4">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="text-white w-4"
><g
><path d="M0 0h24v24H0z" fill="none" /><path
d="M11.29 3.814l2.02 5.707.395 1.116.007-4.81.01-4.818h4.27L18 11.871c.003 5.98-.003 10.89-.015 10.9-.012.009-.209 0-.436-.027-.989-.118-2.29-.236-3.34-.282a14.57 14.57 0 0 1-.636-.038c-.003-.004-.273-.762-.776-2.184v-.004l-2.144-6.061-.34-.954-.008 4.586c-.006 4.365-.01 4.61-.057 4.61-.163 0-1.57.09-2.04.136-.308.027-.926.09-1.37.145-.446.051-.816.085-.823.078C6.006 22.77 6 17.867 6 11.883V1.002h.005V1h4.288l.028.08c.007.016.065.176.157.437l.641 1.778.173.496-.001.023z"
fill-rule="evenodd"
fill="currentColor"
/></g
></svg
>
</div>
</div>
<h3 class="text-xs tracking-wide uppercase">Starring</h3>
<div class="flex flex-col gap-1">
<div class="tracking-widest font-extralight text-sm">Tom Cruise</div>
<div class="tracking-widest font-extralight text-sm">Miles Teller</div>
<div class="tracking-widest font-extralight text-sm">Jennifer Connelly</div>
<div class="tracking-widest font-extralight text-sm">Jon Hamm</div>
<div class="tracking-widest font-extralight text-sm">Glen Powell</div>
</div>
</div>
<div class="relative z-[1] flex gap-8 justify-between items-center col-span-2">
<div class="p-4 cursor-pointer text-zinc-200 hover:text-zinc-100">
<ChevronLeft size="24" />
</div>
<div class="flex gap-2">
<DotFilled size="15" />
<DotFilled size="15" class="opacity-20" />
<DotFilled size="15" class="opacity-20" />
<DotFilled size="15" class="opacity-20" />
<DotFilled size="15" class="opacity-20" />
</div>
<div class="p-4 cursor-pointer text-zinc-200 hover:text-zinc-100">
<ChevronRight size="24" />
</div>
</div>
<div class="absolute inset-x-0 bottom-6 flex justify-center mx-auto opacity-50">
<ChevronDown size="20" />
</div>
</div>
</div>
{#if $popular.loading}
<div>Loading</div>
{:else if $popular.error}
<div>Error: {$popular.error.message}</div>
{:else}
<div class="" style={"background-image: url('" + backgroundUrl + "')"}>
<div class="p-8 flex flex-col gap-6 backdrop-blur-xl bg-[#000000ee]">
<h1 class="uppercase tracking-widest font-bold">Continue Watching</h1>
<!-- <h1-->
<!-- class="text-black bg-white px-6 py-3 inline-block text-xs uppercase tracking-widest font-bold self-start"-->
<!-- >-->
<!-- Continue Watching-->
<!-- </h1>-->
<!-- <h1-->
<!-- class="text-white px-6 py-3 inline-block text-xs uppercase tracking-widest font-bold self-start border-2 border-white"-->
<!-- >-->
<!-- Continue Watching-->
<!-- </h1>-->
<div class="flex gap-4 overflow-x-scroll">
{#each $popular.data.results.movies.splice(0, 5) as item (item.id)}
<SmallPoster tmdbId={item.tmdbId} />
{/each}
{#each $popular.data.results.tvShows.splice(0, 2) as item (item.id)}
<SmallPoster type="tv" tmdbId={item.tmdbId} />
{/each}
</div>
</div>
</div>
{/if}

View File

@@ -0,0 +1 @@
<div>Large poster</div>

45
src/routes/Navbar.svelte Normal file
View File

@@ -0,0 +1,45 @@
<script>
import { MagnifyingGlass, Person } from 'radix-icons-svelte';
import classNames from 'classnames';
let y = 0;
let transparent = true;
let baseStyle = '';
$: {
transparent = y === 0;
baseStyle = classNames(
'fixed px-8 inset-x-0 grid grid-cols-[min-content_1fr_min-content] items-center z-10',
'transition-all',
{
'bg-zinc-900 bg-opacity-50 backdrop-blur-2xl h-16': !transparent,
'h-24': transparent
}
);
}
</script>
<svelte:window bind:scrollY={y} />
<div class={baseStyle}>
<div class="flex gap-2 items-center">
<div class="rounded-full bg-amber-300 h-4 w-4" />
<h1 class="font-display uppercase font-semibold tracking-wider text-xl">Reiverr</h1>
</div>
<div
class="flex items-center justify-center gap-8 font-normal text-sm tracking-wider text-zinc-200"
>
<div class="cursor-pointer text-amber-200">Home</div>
<div class="hover:text-zinc-50 cursor-pointer">Discover</div>
<div class="hover:text-zinc-50 cursor-pointer">Sources</div>
<div class="hover:text-zinc-50 cursor-pointer">Settings</div>
</div>
<div class="flex gap-2">
<div class="p-2 cursor-pointer text-zinc-200 hover:text-zinc-50">
<MagnifyingGlass size="20" />
</div>
<div class="p-2 cursor-pointer text-zinc-200 hover:text-zinc-50">
<Person size="20" />
</div>
</div>
</div>

View File

@@ -0,0 +1,80 @@
<script lang="ts">
import { TmdbApi } from '$lib/tmdb-api';
import { onMount } from 'svelte';
import { ChevronDown } from 'radix-icons-svelte';
export let tmdbId;
export let type: 'movie' | 'tv' = 'movie';
let bg = '';
let title = 'Loading...';
const progress = Math.random() > 0.5 ? Math.round(Math.random() * 100) : 100;
onMount(() => {
TmdbApi.get('/' + type + '/' + tmdbId)
.then((res) => res.data)
.then((data: any) => {
console.log('data', data);
bg = 'https://www.themoviedb.org/t/p/original/' + data.poster_path;
title = data.title;
});
});
</script>
<div class="group grid grid-cols-[3px_1fr_3px] grid-rows-[3px_1fr_3px]">
<div
style={'width: ' + progress + '%'}
class="h-full bg-white opacity-90 group-hover:opacity-100 transition-opacity col-span-3"
/>
<div
transition-opacity
style={'height: ' + progress + '%'}
class="w-full bg-white opacity-90 group-hover:opacity-100"
/>
<div
class="bg-center bg-cover aspect-[2/3] h-72 shadow-2xl m-1.5"
style={"background-image: url('" + bg + "')"}
>
<div
class="w-full h-full bg-gradient-to-b from-[#00000099] via-20% via-transparent hover:bg-[#00000099] transition-all flex"
>
<div
class="opacity-0 group-hover:opacity-100 transition-opacity p-2 flex flex-col justify-between flex-1 cursor-pointer"
>
<div>
<h1 class="font-bold text-lg tracking-wide">
{title}
</h1>
{#if type === 'movie'}
<h2 class="text-xs uppercase text-zinc-300"><b>December</b> 2022</h2>
{:else}
<h2 class="text-xs uppercase text-zinc-300">S1 <b>E2</b></h2>
{/if}
{#if progress}
<h2>30min left</h2>
{/if}
</div>
<div class="flex flex-col gap-2">
<button
class="bg-white border-2 border-white hover:bg-amber-400 hover:border-amber-400 transition-colors text-zinc-900 px-8 py-2.5 uppercase tracking-widest font-extrabold cursor-pointer text-xs"
>Stream</button
>
<button
class="border-2 border-white cursor-pointer transition-colors px-8 py-2.5 uppercase tracking-widest font-semibold text-xs hover:bg-amber-400 hover:text-black"
>Details</button
>
</div>
</div>
</div>
</div>
<div
style={'height: ' + progress + '%'}
class="w-full bg-white opacity-90 group-hover:opacity-100 transition-opacity self-end"
/>
<div
style={'width: ' + progress + '%'}
class="h-full bg-white opacity-90 group-hover:opacity-100 transition-opacity col-span-3 justify-self-end"
/>
</div>

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

3
static/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

18
svelte.config.js Normal file
View File

@@ -0,0 +1,18 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

13
tailwind.config.js Normal file
View File

@@ -0,0 +1,13 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
display: ['Inter', 'system', 'sans-serif']
}
}
},
plugins: []
};

6
tests/test.ts Normal file
View File

@@ -0,0 +1,6 @@
import { expect, test } from '@playwright/test';
test('about page has expected h1', async ({ page }) => {
await page.goto('/about');
await expect(page.getByRole('heading', { name: 'About this app' })).toBeVisible();
});

17
tsconfig.json Normal file
View File

@@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

9
vite.config.ts Normal file
View File

@@ -0,0 +1,9 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
});