Showing
30 changed files
with
886 additions
and
0 deletions
.gitignore
0 → 100644
README.md
0 → 100644
1 | +## 이 프로젝트는 박광훈 교수님의 주관하시는 주제인 유튜브 동영상 분류를 위한 자동 태깅 방법에 대한 연구에 관한 서비스를 개발하는 것입니다. | ||
2 | +### 프로젝트 참가 인원은 다음과 같습니다. | ||
3 | +*윤영빈(컴퓨터공학과, 2015104192) | ||
4 | +*윤준현(컴퓨터공학과, 2015104193) | ||
5 | +*이현규(컴퓨터공학과, 2015104209) | ||
6 | +*이태현(컴퓨터공학과, 2015104208) | ||
7 | +### 진행할 연구는 다음과 같습니다 | ||
8 | +*Video Classification | ||
9 | +*Auto Tagging | ||
10 | + | ||
11 | +### 만들고자하는 서비스 : 영상 분류를 통하여 자동으로 사용자에게 태그 추천해주는 서비스 개발 | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/backend/app.js
0 → 100644
1 | +var createError = require('http-errors'); | ||
2 | +var express = require('express'); | ||
3 | +var path = require('path'); | ||
4 | +var cookieParser = require('cookie-parser'); | ||
5 | +var logger = require('morgan'); | ||
6 | +var history = require('connect-history-api-fallback'); | ||
7 | +var cors = require('cors') | ||
8 | +var app = express(); | ||
9 | +var mongoDB = require('./lib/db_info') | ||
10 | + | ||
11 | +app.use(logger('dev')); | ||
12 | +app.use(express.json()); | ||
13 | +app.use(express.urlencoded({ extended: false })); | ||
14 | +app.use(cookieParser()); | ||
15 | + | ||
16 | +if (process.env.NODE_ENV !== 'production') | ||
17 | + app.use(cors()); | ||
18 | +app.use('/api', require('./routes/api')); | ||
19 | +app.use(history()); | ||
20 | +app.use(express.static(path.join(__dirname, '../frontend', 'dist'))); | ||
21 | +// catch 404 and forward to error handler | ||
22 | +app.use(function (req, res, next) { | ||
23 | + next(createError(404)); | ||
24 | +}); | ||
25 | + | ||
26 | +// error handler | ||
27 | +app.use(function (err, req, res, next) { | ||
28 | + // set locals, only providing error in development | ||
29 | + res.locals.message = err.message; | ||
30 | + res.locals.error = req.app.get('env') === 'development' ? err : {}; | ||
31 | + | ||
32 | + // render the error page | ||
33 | + res.status(err.status || 500); | ||
34 | + res.send({ msg: err.message }); | ||
35 | + console.error(err.message); | ||
36 | +}); | ||
37 | + | ||
38 | +module.exports = app; | ||
39 | + | ||
40 | +const mongoose = require('mongoose') | ||
41 | + | ||
42 | +mongoose.connect(mongoDB.db, { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: true }, (err) => { | ||
43 | + if (err) return console.error(err) | ||
44 | + console.log('mongoose connected') | ||
45 | +}) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/backend/bin/www
0 → 100755
1 | +#!/usr/bin/env node | ||
2 | + | ||
3 | +/** | ||
4 | + * Module dependencies. | ||
5 | + */ | ||
6 | + | ||
7 | +var app = require('../app'); | ||
8 | +var debug = require('debug')('backend:server'); | ||
9 | +var http = require('http'); | ||
10 | + | ||
11 | +/** | ||
12 | + * Get port from environment and store in Express. | ||
13 | + */ | ||
14 | + | ||
15 | +var port = normalizePort(process.env.PORT || '3000'); | ||
16 | +app.set('port', port); | ||
17 | + | ||
18 | +/** | ||
19 | + * Create HTTP server. | ||
20 | + */ | ||
21 | + | ||
22 | +var server = http.createServer(app); | ||
23 | + | ||
24 | +/** | ||
25 | + * Listen on provided port, on all network interfaces. | ||
26 | + */ | ||
27 | + | ||
28 | +server.listen(port); | ||
29 | +server.on('error', onError); | ||
30 | +server.on('listening', onListening); | ||
31 | + | ||
32 | +/** | ||
33 | + * Normalize a port into a number, string, or false. | ||
34 | + */ | ||
35 | + | ||
36 | +function normalizePort(val) { | ||
37 | + var port = parseInt(val, 10); | ||
38 | + | ||
39 | + if (isNaN(port)) { | ||
40 | + // named pipe | ||
41 | + return val; | ||
42 | + } | ||
43 | + | ||
44 | + if (port >= 0) { | ||
45 | + // port number | ||
46 | + return port; | ||
47 | + } | ||
48 | + | ||
49 | + return false; | ||
50 | +} | ||
51 | + | ||
52 | +/** | ||
53 | + * Event listener for HTTP server "error" event. | ||
54 | + */ | ||
55 | + | ||
56 | +function onError(error) { | ||
57 | + if (error.syscall !== 'listen') { | ||
58 | + throw error; | ||
59 | + } | ||
60 | + | ||
61 | + var bind = typeof port === 'string' | ||
62 | + ? 'Pipe ' + port | ||
63 | + : 'Port ' + port; | ||
64 | + | ||
65 | + // handle specific listen errors with friendly messages | ||
66 | + switch (error.code) { | ||
67 | + case 'EACCES': | ||
68 | + console.error(bind + ' requires elevated privileges'); | ||
69 | + process.exit(1); | ||
70 | + break; | ||
71 | + case 'EADDRINUSE': | ||
72 | + console.error(bind + ' is already in use'); | ||
73 | + process.exit(1); | ||
74 | + break; | ||
75 | + default: | ||
76 | + throw error; | ||
77 | + } | ||
78 | +} | ||
79 | + | ||
80 | +/** | ||
81 | + * Event listener for HTTP server "listening" event. | ||
82 | + */ | ||
83 | + | ||
84 | +function onListening() { | ||
85 | + var addr = server.address(); | ||
86 | + var bind = typeof addr === 'string' | ||
87 | + ? 'pipe ' + addr | ||
88 | + : 'port ' + addr.port; | ||
89 | + debug('Listening on ' + bind); | ||
90 | +} |
web/backend/lib/db_info.js
0 → 100644
web/backend/model/tagList.js
0 → 100644
web/backend/model/video.js
0 → 100644
1 | +const mongoose = require('mongoose') | ||
2 | +mongoose.set('useCreateIndex', true) | ||
3 | + | ||
4 | +const postSchema = new mongoose.Schema({ | ||
5 | + tag: [], | ||
6 | + videoUrl: { type: String, default: '',}, | ||
7 | + title: { type: String, default: '' }, | ||
8 | +}) | ||
9 | + | ||
10 | +const Post = mongoose.model('post', postSchema) | ||
11 | + | ||
12 | +module.exports = Post | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/backend/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "backend", | ||
3 | + "version": "0.0.0", | ||
4 | + "private": true, | ||
5 | + "scripts": { | ||
6 | + "start": "node ./bin/www" | ||
7 | + }, | ||
8 | + "dependencies": { | ||
9 | + "connect-history-api-fallback": "^1.6.0", | ||
10 | + "cookie-parser": "^1.4.5", | ||
11 | + "cors": "^2.8.5", | ||
12 | + "debug": "~2.6.9", | ||
13 | + "express": "~4.16.1", | ||
14 | + "http-errors": "~1.6.3", | ||
15 | + "moment": "^2.24.0", | ||
16 | + "mongoose": "^5.9.6", | ||
17 | + "morgan": "~1.9.1", | ||
18 | + "multer": "^1.4.2", | ||
19 | + "pug": "2.0.0-beta11" | ||
20 | + } | ||
21 | +} |
web/backend/routes/api/home/index.js
0 → 100644
1 | +var express = require('express'); | ||
2 | +var createError = require('http-errors'); | ||
3 | +var router = express.Router(); | ||
4 | +const post = require('../../../model/video') | ||
5 | + | ||
6 | +router.get('/list', (req, res, next) => { | ||
7 | + let { tag, skip } = req.query | ||
8 | + let joinedTag = tag.join("|") | ||
9 | + let regexsearch = { tag: { $regex: joinedTag, $options: 'si' } } | ||
10 | + skip = parseInt(skip) | ||
11 | + post.find() | ||
12 | + .sort({'_id': -1}) | ||
13 | + .limit(12) | ||
14 | + .skip(skip) | ||
15 | + .then(rs => { | ||
16 | + console.log(rs) | ||
17 | + res.send({ success: true, d: rs, }) | ||
18 | + }) | ||
19 | + .catch(e => { | ||
20 | + console.log(e) | ||
21 | + res.send({ success: false, msg: e.message }) | ||
22 | + }) | ||
23 | +}) | ||
24 | + | ||
25 | +router.delete('/:_id', (req, res, next) => { | ||
26 | + const _id = req.params._id | ||
27 | + post.findOne({ _id }) | ||
28 | + .then(r => { | ||
29 | + console.log(r) | ||
30 | + return post.deleteOne({ _id }) | ||
31 | + }) | ||
32 | + .then(r => { | ||
33 | + res.send({ success: true, d: r}) | ||
34 | + }) | ||
35 | + .catch(e => { | ||
36 | + console.log(e) | ||
37 | + res.send({ success: false, msg: e.message }) | ||
38 | + }) | ||
39 | +}) | ||
40 | + | ||
41 | +router.all('*', function (req, res, next) { | ||
42 | + next(new Error ('Wrong Url!')); | ||
43 | +}); | ||
44 | + | ||
45 | +module.exports = router; | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
web/backend/routes/api/index.js
0 → 100644
1 | +var createError = require('http-errors'); | ||
2 | +var express = require('express'); | ||
3 | +var router = express.Router(); | ||
4 | + | ||
5 | +router.use('/home', require('./home')) | ||
6 | +router.use('/upload', require('./upload')) | ||
7 | + | ||
8 | +router.all('*', function(req, res, next) { | ||
9 | + next(createError(404, 'This page is not exisit')) | ||
10 | +}) | ||
11 | + | ||
12 | +module.exports = router | ||
13 | + |
web/backend/routes/api/upload/index.js
0 → 100644
1 | +var express = require('express'); | ||
2 | +var createError = require('http-errors'); | ||
3 | +var router = express.Router(); | ||
4 | +const post = require('../../../model/video') | ||
5 | +const multer = require('multer') | ||
6 | +var moment = require('moment') | ||
7 | +var fs = require('fs') | ||
8 | +// event post | ||
9 | +router.post('/video', multer({dest: 'videos/'}).single('bin'), (req, res, next) => { | ||
10 | + console.log(req.file) | ||
11 | + | ||
12 | + fs.rename(`../../../videos/${req.file.filename}`, `../../../videos/${req.file.originalname}`, (r) => { | ||
13 | + console.log(r) | ||
14 | + }) | ||
15 | + var atc = { | ||
16 | + videoUrl : req.file.location, | ||
17 | + title : req.body.title, | ||
18 | + tag: req.body.tag | ||
19 | + } | ||
20 | + // post.create(atc) | ||
21 | + // .then( r => { | ||
22 | + // res.send({ success: true, d: r, token: req.token }) | ||
23 | + // }) | ||
24 | + // .catch((err) => { | ||
25 | + // console.log(err); | ||
26 | + // res.send({ success: false, msg: err.message }) | ||
27 | + // }); | ||
28 | +}) | ||
29 | +router.post('/post', (req,res,next) => { | ||
30 | + console.log(req.body) | ||
31 | + var atc = { | ||
32 | + title : req.body.title, | ||
33 | + tag: req.body.tag | ||
34 | + } | ||
35 | + post.create(atc) | ||
36 | + .then( r => { | ||
37 | + res.send({ success: true, d: r, token: req.token }) | ||
38 | + }) | ||
39 | + .catch((err) => { | ||
40 | + console.log(err); | ||
41 | + res.send({ success: false, msg: err.message }) | ||
42 | + }); | ||
43 | +}) | ||
44 | + | ||
45 | +router.all('*', function(req, res, next) { | ||
46 | + next(createError(404, 'This page is not exisit')) | ||
47 | +}) | ||
48 | + | ||
49 | +module.exports = router |
web/backend/yarn.lock
0 → 100644
This diff is collapsed. Click to expand it.
web/frontend/README.md
0 → 100644
1 | +# frontend | ||
2 | + | ||
3 | +## Project setup | ||
4 | +``` | ||
5 | +yarn install | ||
6 | +``` | ||
7 | + | ||
8 | +### Compiles and hot-reloads for development | ||
9 | +``` | ||
10 | +yarn serve | ||
11 | +``` | ||
12 | + | ||
13 | +### Compiles and minifies for production | ||
14 | +``` | ||
15 | +yarn build | ||
16 | +``` | ||
17 | + | ||
18 | +### Lints and fixes files | ||
19 | +``` | ||
20 | +yarn lint | ||
21 | +``` | ||
22 | + | ||
23 | +### Customize configuration | ||
24 | +See [Configuration Reference](https://cli.vuejs.org/config/). |
web/frontend/babel.config.js
0 → 100644
web/frontend/package.json
0 → 100644
1 | +{ | ||
2 | + "name": "frontend", | ||
3 | + "version": "0.1.0", | ||
4 | + "private": true, | ||
5 | + "scripts": { | ||
6 | + "serve": "vue-cli-service serve", | ||
7 | + "build": "vue-cli-service build", | ||
8 | + "lint": "vue-cli-service lint" | ||
9 | + }, | ||
10 | + "dependencies": { | ||
11 | + "@mdi/font": "^5.0.45", | ||
12 | + "axios": "^0.19.2", | ||
13 | + "core-js": "^3.6.4", | ||
14 | + "filepond": "^4.13.0", | ||
15 | + "filepond-plugin-file-validate-type": "^1.2.5", | ||
16 | + "filepond-plugin-image-preview": "^4.6.1", | ||
17 | + "moment": "^2.24.0", | ||
18 | + "roboto-fontface": "*", | ||
19 | + "vee-validate": "^3.2.5", | ||
20 | + "vue": "^2.6.11", | ||
21 | + "vue-filepond": "^6.0.2", | ||
22 | + "vue-infinite-scroll": "^2.0.2", | ||
23 | + "vue-router": "^3.1.5", | ||
24 | + "vue-video-player": "^5.0.2", | ||
25 | + "vuetify": "^2.2.11", | ||
26 | + "vuex": "^3.1.2" | ||
27 | + }, | ||
28 | + "devDependencies": { | ||
29 | + "@vue/cli-plugin-babel": "~4.2.0", | ||
30 | + "@vue/cli-plugin-eslint": "~4.2.0", | ||
31 | + "@vue/cli-plugin-router": "~4.2.0", | ||
32 | + "@vue/cli-plugin-vuex": "~4.2.0", | ||
33 | + "@vue/cli-service": "~4.2.0", | ||
34 | + "@vue/eslint-config-prettier": "^6.0.0", | ||
35 | + "babel-eslint": "^10.0.3", | ||
36 | + "eslint": "^6.7.2", | ||
37 | + "eslint-plugin-prettier": "^3.1.1", | ||
38 | + "eslint-plugin-vue": "^6.1.2", | ||
39 | + "node-sass": "^4.12.0", | ||
40 | + "prettier": "^1.19.1", | ||
41 | + "sass": "^1.19.0", | ||
42 | + "sass-loader": "^8.0.2", | ||
43 | + "vue-cli-plugin-vuetify": "~2.0.5", | ||
44 | + "vue-template-compiler": "^2.6.11", | ||
45 | + "vuetify-loader": "^1.3.0" | ||
46 | + }, | ||
47 | + "eslintConfig": { | ||
48 | + "root": true, | ||
49 | + "env": { | ||
50 | + "node": true | ||
51 | + }, | ||
52 | + "extends": [ | ||
53 | + "plugin:vue/essential", | ||
54 | + "eslint:recommended", | ||
55 | + "@vue/prettier" | ||
56 | + ], | ||
57 | + "parserOptions": { | ||
58 | + "parser": "babel-eslint" | ||
59 | + }, | ||
60 | + "rules": {} | ||
61 | + }, | ||
62 | + "browserslist": [ | ||
63 | + "> 1%", | ||
64 | + "last 2 versions" | ||
65 | + ] | ||
66 | +} |
web/frontend/public/favicon.ico
0 → 100644
No preview for this file type
web/frontend/public/index.html
0 → 100644
1 | +<!DOCTYPE html> | ||
2 | +<html lang="ko"> | ||
3 | + <head> | ||
4 | + <meta charset="utf-8"> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
6 | + <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||
7 | + <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||
8 | + <title><%= htmlWebpackPlugin.options.title %></title> | ||
9 | + </head> | ||
10 | + <body> | ||
11 | + <noscript> | ||
12 | + <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> | ||
13 | + </noscript> | ||
14 | + <div id="app"></div> | ||
15 | + <!-- built files will be auto injected --> | ||
16 | + </body> | ||
17 | +</html> |
web/frontend/src/App.vue
0 → 100644
1 | +<template> | ||
2 | + <v-app> | ||
3 | + <v-app-bar app color="#ffffff" elevation="1"> | ||
4 | + <v-tabs grow v-model="tab"> | ||
5 | + <v-tab @click="$router.push('/')">Home</v-tab> | ||
6 | + <v-tab @click="$router.push('/upload')">Upload</v-tab> | ||
7 | + </v-tabs> | ||
8 | + </v-app-bar> | ||
9 | + <v-content> | ||
10 | + <router-view /> | ||
11 | + </v-content> | ||
12 | + <v-footer> | ||
13 | + <v-row justify="center" @click="exDialog = true"> | ||
14 | + <v-avatar size="25" tile style="border-radius: 4px"> | ||
15 | + <v-img src="./assets/logo.png"></v-img> | ||
16 | + </v-avatar> | ||
17 | + <div> | ||
18 | + <span | ||
19 | + style="margin-left: 2px; font-size: 15px; color: #5a5a5a; font-weight: 400" | ||
20 | + > | ||
21 | + Profit-Hunter | ||
22 | + </span> | ||
23 | + <div | ||
24 | + style="margin-left: 4px; margin-top: -1px; font-size: 10px; color: #888; font-weight: 400" | ||
25 | + > | ||
26 | + Used OpenSource | ||
27 | + </div> | ||
28 | + </div> | ||
29 | + </v-row> | ||
30 | + </v-footer> | ||
31 | + </v-app> | ||
32 | +</template> | ||
33 | + | ||
34 | +<script> | ||
35 | +export default { | ||
36 | + name: "App", | ||
37 | + | ||
38 | + data: () => ({ | ||
39 | + tab: null, | ||
40 | + search: "", | ||
41 | + exDialog: false | ||
42 | + }), | ||
43 | + mounted() { | ||
44 | + console.log(window.location.href.substring(22)); | ||
45 | + if (window.location.href.substring(22) === "") { | ||
46 | + this.tab = 0; | ||
47 | + } else if (window.location.href.substring(22) === "upload") { | ||
48 | + this.tab = 1; | ||
49 | + } else { | ||
50 | + this.tab = null; | ||
51 | + } | ||
52 | + } | ||
53 | +}; | ||
54 | +</script> |
web/frontend/src/assets/logo.png
0 → 100644
87.3 KB
web/frontend/src/assets/logo.svg
0 → 100644
1 | +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg> |
web/frontend/src/components/uploadFile.vue
0 → 100644
1 | +<template> | ||
2 | + <div> | ||
3 | + <file-pond | ||
4 | + name="bin" | ||
5 | + ref="pond" | ||
6 | + allow-multiple="false" | ||
7 | + max-files="1" | ||
8 | + :server="server" | ||
9 | + v-bind:files="myFiles" | ||
10 | + v-on:init="handleFilePondInit" | ||
11 | + v-on:processfile="onload" | ||
12 | + /> | ||
13 | + </div> | ||
14 | +</template> | ||
15 | + | ||
16 | +<script> | ||
17 | +// Import Vue FilePond | ||
18 | +import vueFilePond from "vue-filepond"; | ||
19 | + | ||
20 | +// Import FilePond styles | ||
21 | +import "filepond/dist/filepond.min.css"; | ||
22 | + | ||
23 | +// Import FilePond plugins | ||
24 | +// Please note that you need to install these plugins separately | ||
25 | + | ||
26 | +// Import image preview plugin styles | ||
27 | +import "filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css"; | ||
28 | + | ||
29 | +// Import image preview and file type validation plugins | ||
30 | +import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type"; | ||
31 | +import FilePondPluginImagePreview from "filepond-plugin-image-preview"; | ||
32 | + | ||
33 | +// Create component | ||
34 | +const FilePond = vueFilePond( | ||
35 | + FilePondPluginFileValidateType, | ||
36 | + FilePondPluginImagePreview | ||
37 | +); | ||
38 | + | ||
39 | +export default { | ||
40 | + name: "app", | ||
41 | + data() { | ||
42 | + return { | ||
43 | + myFiles: [], | ||
44 | + server: { | ||
45 | + url: `${this.$apiRootPath}upload/video`, | ||
46 | + process: {} | ||
47 | + } | ||
48 | + }; | ||
49 | + }, | ||
50 | + methods: { | ||
51 | + handleFilePondInit() { | ||
52 | + console.log("FilePond has initialized"); | ||
53 | + // FilePond instance methods are available on `this.$refs.pond` | ||
54 | + }, | ||
55 | + onload(e, r) { | ||
56 | + console.log(r); | ||
57 | + // this.$store.dispatch(r); | ||
58 | + } | ||
59 | + }, | ||
60 | + components: { | ||
61 | + FilePond | ||
62 | + } | ||
63 | +}; | ||
64 | +</script> |
web/frontend/src/main.js
0 → 100644
1 | +import Vue from "vue"; | ||
2 | +import App from "./App.vue"; | ||
3 | +import router from "./router"; | ||
4 | +import store from "./store"; | ||
5 | +import vuetify from "./plugins/vuetify"; | ||
6 | +import "roboto-fontface/css/roboto/roboto-fontface.css"; | ||
7 | +import "@mdi/font/css/materialdesignicons.css"; | ||
8 | +import * as VeeValidate from "vee-validate"; | ||
9 | +import "./vee-validate"; | ||
10 | +import infiniteScroll from "vue-infinite-scroll"; | ||
11 | + | ||
12 | +Vue.config.productionTip = false; | ||
13 | +Vue.use(infiniteScroll); | ||
14 | +Vue.use(VeeValidate); | ||
15 | + | ||
16 | +new Vue({ | ||
17 | + router, | ||
18 | + store, | ||
19 | + vuetify, | ||
20 | + render: h => h(App) | ||
21 | +}).$mount("#app"); |
web/frontend/src/plugins/vuetify.js
0 → 100644
1 | +import Vue from "vue"; | ||
2 | +import Vuetify from "vuetify/lib"; | ||
3 | +import ko from "vuetify/es5/locale/ko"; | ||
4 | + | ||
5 | +Vue.use(Vuetify); | ||
6 | + | ||
7 | +export default new Vuetify({ | ||
8 | + theme: { | ||
9 | + options: { | ||
10 | + customProperties: true | ||
11 | + }, | ||
12 | + themes: { | ||
13 | + light: { | ||
14 | + primary: "#7DC1E8", | ||
15 | + secondary: "#EBC478", | ||
16 | + accent: "#82B1FF", | ||
17 | + error: "#FF5252", | ||
18 | + info: "#2196F3", | ||
19 | + success: "#4CAF50", | ||
20 | + warning: "#FFC107", | ||
21 | + grey300: "#eceeef", | ||
22 | + grey500: "#aaaaaa", | ||
23 | + grey700: "#5a5a5a", | ||
24 | + grey900: "#212529" | ||
25 | + } | ||
26 | + } | ||
27 | + }, | ||
28 | + lang: { | ||
29 | + locales: { ko }, | ||
30 | + current: "ko" | ||
31 | + } | ||
32 | +}); |
web/frontend/src/router/index.js
0 → 100644
1 | +import Vue from "vue"; | ||
2 | +import VueRouter from "vue-router"; | ||
3 | +import axios from "axios"; | ||
4 | +Vue.prototype.$axios = axios; | ||
5 | +const apiRootPath = | ||
6 | + process.env.NODE_ENV !== "production" | ||
7 | + ? "http://localhost:3000/api/" | ||
8 | + : "/api/"; | ||
9 | +Vue.prototype.$apiRootPath = apiRootPath; | ||
10 | +axios.defaults.baseURL = apiRootPath; | ||
11 | + | ||
12 | +Vue.use(VueRouter); | ||
13 | + | ||
14 | +const routes = [ | ||
15 | + { | ||
16 | + path: "/", | ||
17 | + name: "Home", | ||
18 | + component: () => import("../views/Home.vue") | ||
19 | + }, | ||
20 | + { | ||
21 | + path: "/upload", | ||
22 | + name: "upload", | ||
23 | + component: () => import("../views/Upload.vue") | ||
24 | + } | ||
25 | +]; | ||
26 | + | ||
27 | +const router = new VueRouter({ | ||
28 | + mode: "history", | ||
29 | + base: process.env.BASE_URL, | ||
30 | + routes | ||
31 | +}); | ||
32 | + | ||
33 | +export default router; |
web/frontend/src/store/index.js
0 → 100644
1 | +import Vue from "vue"; | ||
2 | +import Vuex from "vuex"; | ||
3 | + | ||
4 | +Vue.use(Vuex); | ||
5 | + | ||
6 | +export default new Vuex.Store({ | ||
7 | + state: { | ||
8 | + tags: [] | ||
9 | + }, | ||
10 | + mutations: { | ||
11 | + setTags(state, tags) { | ||
12 | + state.tags = tags; | ||
13 | + } | ||
14 | + }, | ||
15 | + getters: { | ||
16 | + getTags: state => { | ||
17 | + return state.tags; | ||
18 | + } | ||
19 | + }, | ||
20 | + actions: { | ||
21 | + commitSetTags: context => { | ||
22 | + return context.commit("setTags"); | ||
23 | + } | ||
24 | + }, | ||
25 | + modules: {} | ||
26 | +}); |
web/frontend/src/vee-validate.js
0 → 100644
1 | +import { required, max, min } from "vee-validate/dist/rules"; | ||
2 | +import { extend } from "vee-validate"; | ||
3 | + | ||
4 | +extend("required", { | ||
5 | + ...required, | ||
6 | + message: "This field is required" | ||
7 | +}); | ||
8 | + | ||
9 | +extend("max", { | ||
10 | + ...max, | ||
11 | + message: "This field must be {length} characters or less" | ||
12 | +}); | ||
13 | + | ||
14 | +extend("min", { | ||
15 | + ...min, | ||
16 | + message: "This field must have at least {length} characters" | ||
17 | +}); |
web/frontend/src/views/Home.vue
0 → 100644
1 | +<template> | ||
2 | + <v-sheet> | ||
3 | + <!-- autocomplete에 저장된 tag 넣어서(동영상 업로드 할 때마다 tag가 저장됨) 자동완성 되게끔.--> | ||
4 | + <v-text-field | ||
5 | + class="mx-10 mt-4 mb-5" | ||
6 | + prepend-inner-icon="mdi-shape" | ||
7 | + v-model="search" | ||
8 | + label="Tag" | ||
9 | + placeholder="Search Tag" | ||
10 | + type="text" | ||
11 | + > | ||
12 | + </v-text-field> | ||
13 | + <!-- 동영상 리스트 --> | ||
14 | + <v-layout justify-center> | ||
15 | + <v-row class="mx-5"> | ||
16 | + <v-flex | ||
17 | + xs12 | ||
18 | + sm6 | ||
19 | + v-for="(post, index) in postList" | ||
20 | + :key="index" | ||
21 | + class="mx-0" | ||
22 | + > | ||
23 | + <v-card class="mx-2 my-1"> | ||
24 | + <div>{{ post.title }}1</div> | ||
25 | + </v-card> | ||
26 | + </v-flex> | ||
27 | + </v-row> | ||
28 | + </v-layout> | ||
29 | + </v-sheet> | ||
30 | +</template> | ||
31 | +<script> | ||
32 | +// @ is an alias to /src | ||
33 | +export default { | ||
34 | + name: "Home", | ||
35 | + components: {}, | ||
36 | + data() { | ||
37 | + return { | ||
38 | + postList: [], | ||
39 | + search: "", | ||
40 | + params: { | ||
41 | + tag: ["", ""], | ||
42 | + skip: 0 | ||
43 | + } | ||
44 | + }; | ||
45 | + }, | ||
46 | + mounted() { | ||
47 | + this.getPost(); | ||
48 | + }, | ||
49 | + methods: { | ||
50 | + getPost() { | ||
51 | + this.$axios | ||
52 | + .get("/home/list", { params: this.params }) | ||
53 | + .then(r => { | ||
54 | + this.postList = r.data.d; | ||
55 | + console.log(this.postList); | ||
56 | + }) | ||
57 | + .catch(e => { | ||
58 | + console.log(e); | ||
59 | + }); | ||
60 | + } | ||
61 | + } | ||
62 | +}; | ||
63 | +</script> |
web/frontend/src/views/Upload.vue
0 → 100644
1 | +<template> | ||
2 | + <v-sheet> | ||
3 | + <v-layout justify-center> | ||
4 | + <v-flex xs12 sm8 md6> | ||
5 | + <v-row justify="center" class="mx-0 mt-10"> | ||
6 | + <v-icon color="grey500">mdi-power-on</v-icon> | ||
7 | + <div | ||
8 | + style="text-align: center; font-size: 22px; font-weight: 400; color: #343a40; " | ||
9 | + > | ||
10 | + Upload Video | ||
11 | + </div> | ||
12 | + <v-icon color="grey500">mdi-power-on</v-icon> | ||
13 | + </v-row> | ||
14 | + <v-card elevation="0"> | ||
15 | + <v-text-field | ||
16 | + class="mx-10 mt-8 mb-6" | ||
17 | + prepend-inner-icon="mdi-pen" | ||
18 | + v-model="form.title" | ||
19 | + :counter="40" | ||
20 | + label="Title" | ||
21 | + placeholder="Please input Title" | ||
22 | + type="text" | ||
23 | + > | ||
24 | + </v-text-field> | ||
25 | + <!-- file upload --> | ||
26 | + <div class="mx-10"> | ||
27 | + <video-upload /> | ||
28 | + </div> | ||
29 | + <v-card outlined class="pa-2 mx-10" elevation="0" min-height="67"> | ||
30 | + <v-chip-group column> | ||
31 | + <v-chip | ||
32 | + color="secondary" | ||
33 | + v-for="(tag, index) in form.tags" | ||
34 | + :key="index" | ||
35 | + @click="deleteTags(index)" | ||
36 | + > | ||
37 | + {{ tag }} | ||
38 | + </v-chip> | ||
39 | + </v-chip-group> | ||
40 | + </v-card> | ||
41 | + <v-text-field | ||
42 | + class="mx-10 mt-3 mb-5" | ||
43 | + prepend-inner-icon="mdi-shape" | ||
44 | + v-model="tag" | ||
45 | + :counter="20" | ||
46 | + label="Tag" | ||
47 | + placeholder="Type to add Tag" | ||
48 | + append-icon="mdi-arrow-up-bold" | ||
49 | + @click:append="addTags(tag)" | ||
50 | + @keydown.enter="addTags(tag)" | ||
51 | + type="text" | ||
52 | + > | ||
53 | + </v-text-field> | ||
54 | + </v-card> | ||
55 | + <v-row justify="center" style="margin-bottom: 30px"> | ||
56 | + <v-btn elevation="0" large color="primary" @click="submit()"> | ||
57 | + <span style="font-size: 24px; font-weight: 300; letter-spacing: 2px" | ||
58 | + >Upload</span | ||
59 | + > | ||
60 | + </v-btn> | ||
61 | + </v-row> | ||
62 | + </v-flex> | ||
63 | + </v-layout> | ||
64 | + </v-sheet> | ||
65 | +</template> | ||
66 | +<script> | ||
67 | +import videoUpload from "../components/uploadFile"; | ||
68 | +export default { | ||
69 | + name: "Upload", | ||
70 | + components: { | ||
71 | + videoUpload | ||
72 | + }, | ||
73 | + data() { | ||
74 | + return { | ||
75 | + myFiles: [], | ||
76 | + tag: "", | ||
77 | + form: { | ||
78 | + title: "", | ||
79 | + videoUrl: "", | ||
80 | + tags: [ | ||
81 | + "Work", | ||
82 | + "Home Improvement", | ||
83 | + "Vacation", | ||
84 | + "Food", | ||
85 | + "Drawers", | ||
86 | + "Shopping", | ||
87 | + "Art", | ||
88 | + "Tech", | ||
89 | + "Creative Writing" | ||
90 | + ] | ||
91 | + }, | ||
92 | + successDialog: false, | ||
93 | + errorDialog: false | ||
94 | + }; | ||
95 | + }, | ||
96 | + mounted() { | ||
97 | + this.form.tags = this.$store.getters.getTags; | ||
98 | + }, | ||
99 | + methods: { | ||
100 | + submit() { | ||
101 | + if (this.form.tags.length || this.form.title) { | ||
102 | + this.errorDialog = true; | ||
103 | + } else { | ||
104 | + this.$axios | ||
105 | + .post("/upload/post", this.form) | ||
106 | + .then(r => { | ||
107 | + console.log(r); | ||
108 | + this.successDialog = true; | ||
109 | + }) | ||
110 | + .catch(e => { | ||
111 | + console.log(e); | ||
112 | + this.errorDialog = true; | ||
113 | + }); | ||
114 | + } | ||
115 | + }, | ||
116 | + deleteTags(index) { | ||
117 | + for (let i = 0; i < this.form.tags.length; i++) { | ||
118 | + const element = this.form.tags[i]; | ||
119 | + if (this.form.tags[index] === element) { | ||
120 | + this.form.tags[i] = this.form.tags[this.form.tags.length - 1]; | ||
121 | + break; | ||
122 | + } | ||
123 | + } | ||
124 | + this.form.tags.pop(); | ||
125 | + }, | ||
126 | + addTags(tag) { | ||
127 | + let i; | ||
128 | + let check = true; | ||
129 | + for (i = 0; i < this.form.tags.length; i++) { | ||
130 | + const element = this.form.tags[i]; | ||
131 | + if (tag === element) { | ||
132 | + check = false; | ||
133 | + } | ||
134 | + } | ||
135 | + if (tag && check) this.form.tags.push(tag); | ||
136 | + this.tag = ""; | ||
137 | + } | ||
138 | + } | ||
139 | +}; | ||
140 | +</script> |
web/frontend/vue.config.js
0 → 100644
web/frontend/yarn.lock
0 → 100644
This diff could not be displayed because it is too large.
-
Please register or login to post a comment