init: Initial Commit
This commit is contained in:
13
services/frontend/Dockerfile
Normal file
13
services/frontend/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM node:lts-alpine as develop-stage
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN yarn install
|
||||
COPY . .
|
||||
|
||||
FROM develop-stage as build-stage
|
||||
RUN yarn build
|
||||
|
||||
FROM nginx:stable-alpine as production-stage
|
||||
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
12
services/frontend/constants/filenames.ts
Normal file
12
services/frontend/constants/filenames.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
const exampleFilenames = [
|
||||
'Solyaris.1972.1080p.BluRay.FLAC2.0.x264-SbR.mkv',
|
||||
'Dekalog.E01.REPACK.1989.720p.BluRay.AAC.1.0.x264-ZQ.mkv',
|
||||
'The.Mandalorian.S02E01.Chapter.9.The.Marshal.1080p.DSNP.WEB-DL.DDP5.1.Atmos.H.264-LAZY.mkv',
|
||||
'Planet.Earth.SE.E01.From.Pole.To.Pole.2006.1080i.BluRay.Remux.VC-1.DTS-HD.5.1-DA4LiFE.mkv',
|
||||
'Ugetsu monogatari 1953 720p BluRay AAC2.0 x264-npuer.mkv',
|
||||
'Tetsuwan Atom (1959) [TSHS] episode 01 [66C473BB].mp4',
|
||||
'The.Star.Wars.Holiday.Special.1978.EditDroid.Edition.DVDRip.x264.mkv',
|
||||
'Hitchcock.Presents.S01E01.1955.Revenge.DVDRip.DD2.x264-HANDJOB.mkv'
|
||||
]
|
||||
|
||||
export default exampleFilenames
|
||||
13
services/frontend/index.html
Normal file
13
services/frontend/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>guessit</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
2654
services/frontend/package-lock.json
generated
Normal file
2654
services/frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
27
services/frontend/package.json
Normal file
27
services/frontend/package.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "guessit",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@highlightjs/vue-plugin": "github:highlightjs/vue-plugin",
|
||||
"axios": "^1.6.5",
|
||||
"highlight.js": "^11.9.0",
|
||||
"vue": "^3.3.11",
|
||||
"vue3-simple-icons": "^11.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"postcss": "^8.4.33",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.8",
|
||||
"vue-tsc": "^1.8.25"
|
||||
}
|
||||
}
|
||||
6
services/frontend/postcss.config.js
Normal file
6
services/frontend/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
1
services/frontend/public/vite.svg
Normal file
1
services/frontend/public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
22
services/frontend/src/App.vue
Normal file
22
services/frontend/src/App.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import Header from './components/Header.vue'
|
||||
import Home from './components/Home.vue'
|
||||
import About from './components/About.vue'
|
||||
import Footer from './components/Footer.vue'
|
||||
|
||||
const isHome = ref(true)
|
||||
const setHome = (value: boolean) => {
|
||||
isHome.value = value
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen w-full bg-slate-100 flex flex-col justify-between items-center p-16 gap-5 text-neutral-700">
|
||||
<Header @setHome="setHome" />
|
||||
<Home v-if="isHome" />
|
||||
<About v-else />
|
||||
<Footer />
|
||||
</div>
|
||||
</template>
|
||||
1
services/frontend/src/assets/vue.svg
Normal file
1
services/frontend/src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
43
services/frontend/src/components/About.vue
Normal file
43
services/frontend/src/components/About.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import { VueDotjsIcon, TailwindCssIcon, FlaskIcon, DockerIcon, VercelIcon, GoogleCloudIcon } from 'vue3-simple-icons'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-2 flew-grow text-center md:w-[750px]">
|
||||
<div class="flex flex-col items-center justify-center bg-[#2e3440] rounded-lg shadow-lg text-white p-8 space-y-5">
|
||||
<h2 class="text-4xl font-semibold">
|
||||
About
|
||||
</h2>
|
||||
<p>
|
||||
A web app that guesses info about a video file based on its filename, this app was made with Vue 3 and Tailwind CSS, the backend its powered by Flask and the python library guessit, it was deployed with Vercel and GCP.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-sm flex-wrap justify-center">
|
||||
<div class="bg-[#2e3440] rounded shadow-lg text-white px-3 py-2 flex gap-2 items-center">
|
||||
<FlaskIcon class="w-4 h-4 fill-slate-50" />
|
||||
<p>Flask</p>
|
||||
</div>
|
||||
<div class="bg-[#2e3440] rounded shadow-lg text-white px-3 py-2 flex gap-2 items-center">
|
||||
<VueDotjsIcon class="w-4 h-4 fill-slate-50" />
|
||||
<p>VueJS</p>
|
||||
</div>
|
||||
<div class="bg-[#2e3440] rounded shadow-lg text-white px-3 py-2 flex gap-2 items-center">
|
||||
<TailwindCssIcon class="w-4 h-4 fill-slate-50" />
|
||||
<p>TailwindCSS</p>
|
||||
</div>
|
||||
<div class="bg-[#2e3440] rounded shadow-lg text-white px-3 py-2 flex gap-2 items-center">
|
||||
<DockerIcon class="w-4 h-4 fill-slate-50" />
|
||||
<p>Docker</p>
|
||||
</div>
|
||||
<div class="bg-[#2e3440] rounded shadow-lg text-white px-3 py-2 flex gap-2 items-center">
|
||||
<VercelIcon class="w-4 h-4 fill-slate-50" />
|
||||
<p>Vercel</p>
|
||||
</div>
|
||||
<div class="bg-[#2e3440] rounded shadow-lg text-white px-3 py-2 flex gap-2 items-center">
|
||||
<GoogleCloudIcon class="w-4 h-4 fill-slate-50" />
|
||||
<p>Google Cloud Platform</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
14
services/frontend/src/components/Footer.vue
Normal file
14
services/frontend/src/components/Footer.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ul class="flex justify-center bg-white rounded-lg px-2 py-1 text-sm shadow">
|
||||
<a href="https://www.xyvs.io/" target="_blank" >
|
||||
<li class="font-semibold rounded-lg px-3 py-2 hover:bg-neutral-50">
|
||||
Made by xyvs
|
||||
</li>
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
30
services/frontend/src/components/Header.vue
Normal file
30
services/frontend/src/components/Header.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
const emit = defineEmits(['setHome'])
|
||||
|
||||
const setHome = (value: boolean) => {
|
||||
emit('setHome', value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ul class="flex justify-center bg-white rounded-lg px-2 py-1 text-sm shadow">
|
||||
<a href="#/" @click="setHome(true)">
|
||||
<li class="font-semibold rounded-lg px-3 py-2 hover:bg-neutral-50">
|
||||
Home
|
||||
</li>
|
||||
</a>
|
||||
<a href="#/" @click="setHome(false)">
|
||||
<li class="font-semibold rounded-lg px-3 py-2 hover:bg-neutral-50">
|
||||
About
|
||||
</li>
|
||||
</a>
|
||||
<a href="https://github.com/xyvs/guessit_app" target="_blank">
|
||||
<li class="font-semibold rounded-lg px-3 py-2 hover:bg-neutral-50">
|
||||
Code
|
||||
</li>
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
68
services/frontend/src/components/Home.vue
Normal file
68
services/frontend/src/components/Home.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import axios from 'axios'
|
||||
import exampleFilenames from '../../constants/filenames.ts'
|
||||
import formatDictionaryAsString from '../../utils/formatting.ts'
|
||||
|
||||
const filename = ref('')
|
||||
const code = ref(`{
|
||||
result: 'Fill the input with the filename and click Guess'
|
||||
}`)
|
||||
|
||||
const url = `${import.meta.env.VITE_HOST_URL}`
|
||||
|
||||
const setRandomFilename = () => {
|
||||
filename.value = exampleFilenames[Math.floor(Math.random() * exampleFilenames.length)];
|
||||
getFilenameData()
|
||||
}
|
||||
|
||||
const getFilenameData = () => {
|
||||
const data = {
|
||||
filename: filename.value
|
||||
}
|
||||
|
||||
axios.get(url, { params: data })
|
||||
.then(response => {
|
||||
code.value = formatDictionaryAsString(response.data)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-12 flew-grow text-center md:w-[600px] bg-[#2e3440] rounded-lg text-neutral-100 shadow pt-12 pb-6">
|
||||
<div class="space-y-12 px-12">
|
||||
<div class="flex flex-col items-center justify-center gap-4">
|
||||
<h2 class="text-4xl font-semibold">
|
||||
guessit
|
||||
</h2>
|
||||
<p>
|
||||
A web app that guesses info about a video file based on its filename.
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-4 text-neutral-700">
|
||||
<div class="text-sm flex w-full">
|
||||
<div class="flex-grow">
|
||||
<input v-model="filename" type="text" class="bg-neutral-100 p-3 rounded-l appearance-none focus:outline-none w-full shadow" placeholder="Enter the filename">
|
||||
</div>
|
||||
<button class="bg-slate-200 px-4 py-3 rounded-r shadow font-semibold hover:bg-slate-300" @click="getFilenameData">
|
||||
Guess
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center text-sm gap-4">
|
||||
<a href="#/">
|
||||
<div class="bg-slate-200 rounded shadow px-4 py-2 font-semibold hover:bg-slate-300" @click="setRandomFilename">
|
||||
Try a random filename
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-left text-sm rounded px-4">
|
||||
<highlightjs :code="code" ref="codeRef" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
13
services/frontend/src/main.ts
Normal file
13
services/frontend/src/main.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import 'highlight.js/styles/nord.css'
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import hljsVuePlugin from "@highlightjs/vue-plugin";
|
||||
|
||||
hljs.registerLanguage('javascript', javascript);
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(hljsVuePlugin)
|
||||
app.mount('#app')
|
||||
3
services/frontend/src/style.css
Normal file
3
services/frontend/src/style.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
1
services/frontend/src/vite-env.d.ts
vendored
Normal file
1
services/frontend/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
11
services/frontend/tailwind.config.js
Normal file
11
services/frontend/tailwind.config.js
Normal file
@@ -0,0 +1,11 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
25
services/frontend/tsconfig.json
Normal file
25
services/frontend/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
10
services/frontend/tsconfig.node.json
Normal file
10
services/frontend/tsconfig.node.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
15
services/frontend/utils/formatting.ts
Normal file
15
services/frontend/utils/formatting.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
const formatDictionaryAsString = (obj: Object, indentation = 2) => {
|
||||
let result: string = "{\n";
|
||||
for (let key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
let value = obj[key];
|
||||
let line = ' '.repeat(indentation) + `${key}: '${value}',\n`;
|
||||
result += line;
|
||||
}
|
||||
}
|
||||
result = result.slice(0, -2);
|
||||
result += `\n}`;
|
||||
return result;
|
||||
}
|
||||
|
||||
export default formatDictionaryAsString;
|
||||
7
services/frontend/vite.config.ts
Normal file
7
services/frontend/vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|
||||
Reference in New Issue
Block a user