diff --git a/.env.example b/.env.example index f608ce6552e6301b5c554b97b9c9b0ecc9b72c78..2b7020ced8d65cb55091a7afb8d0e2517266bf1c 100644 --- a/.env.example +++ b/.env.example @@ -19,4 +19,4 @@ EMAIL_USER=example@gmail.com # 이메일 비밀번호(2단계 인증 사용, 앱 비밀번호) EMAIL_APP_PASSWORD= # 유튜브 API 키 -YOUTUBE_API_KEY=sampleapikey \ No newline at end of file +YOUTUBE_API_KEY='AIzaSyAtnFTu-E6GUePD2AYOXwa2YXQugbb08Jc' \ No newline at end of file diff --git a/back/src/controllers/habittrackerController.js b/back/src/controllers/habittrackerController.js index fb05d60d40e8f16fcc02d1e679a3435a300039a5..7ee920aab8315dec92f4ad8dfb09fe97e28a6b71 100644 --- a/back/src/controllers/habittrackerController.js +++ b/back/src/controllers/habittrackerController.js @@ -23,7 +23,10 @@ const habittrackerController = { res.status(201).json(newGoal); } catch (error) { console.error(error); - res.status(500).send('Failed to add habitTracker goal'); + res.status(500).json({ + message: 'failed to add goal', + error: error.message, + }); } }, getGoal: async (req, res) => { @@ -37,8 +40,16 @@ const habittrackerController = { { user_id: req.user.user_id, goal_weekly: null, - goal_daily: [null, null, null, null, null, null, null], - goal_daily_time: null, + goal_daily: [ + false, + false, + false, + false, + false, + false, + false, + ], + goal_daily_time: '00:00', goal_weight: null, }, ]; @@ -64,7 +75,7 @@ const habittrackerController = { } }, getEveryRecords: async (req, res) => { - const { period } = req.body; + const { period } = req.query.period; try { //정규식 const regex = new RegExp(`^${period}`); diff --git a/back/src/controllers/videoController.js b/back/src/controllers/videoController.js index a9808beb0d6a31b749d6a858442c97b3246c22bf..0c1c4b2d7da4cbcfdb51e81d75937a1247a037b6 100644 --- a/back/src/controllers/videoController.js +++ b/back/src/controllers/videoController.js @@ -6,24 +6,25 @@ const Video = require('../models/video'); const videoController = { getVideo: async (req, res) => { try { - const page = parseInt(req.query.page); - const video_per_page = parseInt(req.query.video_per_page); - const skipCount = (page - 1) * video_per_page; + const video_per_page = parseInt(req.query.video_per_page) || 10; + const last_id = req.query.last_id; //전체 데이터 수 const totalVideos = await Video.countDocuments(); - //해당 페이지 - const videos = await Video.find() - .skip(skipCount) + // last_id 기반 쿼리 조건 설정 + const query = last_id ? { _id: { $gt: last_id } } : {}; + + //해당 페이지(오름차순) + const videos = await Video.find(query) + .sort({ _id: 1 }) .limit(video_per_page); res.json({ - page, video_per_page, - totalVideos, - totalPages: Math.ceil(totalVideos / video_per_page), videos, + last_id: + videos.length > 0 ? videos[videos.length - 1]._id : null, //다음페이지 여부 }); } catch (error) { res.status(500).json({ @@ -36,9 +37,26 @@ const videoController = { try { const tags = req.query.video_tag; const video_tag = tags ? tags.split(' ') : []; //video_tag 배열처리 - const video_min_time = minutesToSeconds(req.query.video_time_from); - const video_max_time = minutesToSeconds(req.query.video_time_to); + + //시간 undefined 방지를 위한 기본값 설정 + let video_min_time = minutesToSeconds('00:00'); + let video_max_time = minutesToSeconds('1440:00'); + + //00:00 입력에 대한 기본값 설정 + if ( + req.query.video_time_from === '00:00' && + req.query.video_time_to === '00:00' + ) { + video_min_time = minutesToSeconds('00:00'); + video_max_time = minutesToSeconds('1440:00'); + } else if (req.query.video_time_from && req.query.video_time_to) { + video_min_time = minutesToSeconds(req.query.video_time_from); + video_max_time = minutesToSeconds(req.query.video_time_to); + } + const video_level = req.query.video_level; + const video_per_page = parseInt(req.query.video_per_page) || 10; + const last_id = req.query.last_id; //커서페이징 const filter = { video_length: { $gte: video_min_time, $lte: video_max_time }, @@ -46,21 +64,46 @@ const videoController = { // video_tag가 존재하면 필터에 추가 if (video_tag && Array.isArray(video_tag)) { - filter.video_tag = { - $regex: video_tag.map(tag => `(${tag})`).join('|'), // 모든 태그가 포함되는 정규식 생성 - $options: 'i', // 대소문자 구분 없음 - }; + const tagregex = video_tag.map(tag => `(${tag})`).join('|'); + + // video_tag가 존재하고 Advanced가 존재하는 경우 + if (video_level) { + filter.video_tag = { + $regex: `(?=.*advanced)(?=.*(${tagregex}))`, + $options: 'i', + }; + } else { + filter.video_tag = { + $regex: `(?=.*(${tagregex}))`, + $options: 'i', + }; + } + } else { + // advanced 만 존재할 때 + if (video_level) { + filter.video_tag = { + $regex: `(?=.*advanced)`, + $options: `i`, + }; + } } - // Advanced 수준 필터 추가 - if (video_level && video_level.toLowerCase() === 'advanced') { - filter.video_tag = filter.video_tag || []; - filter.video_tag.$regex = `Advanced|${ - filter.video_tag.$regex || '' - }`; + const totalVideos = await Video.find(filter).countDocuments(); //filter된 영상 수 + + if (last_id) { + //페이징 여부에 따른 조건 추가 + filter._id = { $gt: last_id }; } - const videos = await Video.find(filter); - res.json(videos); + + const videos = await Video.find(filter) + .sort({ _id: 1 }) + .limit(video_per_page); + + res.json({ + totalVideos, + videos, + last_id: videos.length ? videos[videos.length - 1]._id : null, + }); } catch (error) { console.error('tag error', error); res.status(500).json({ diff --git a/back/src/models/records.js b/back/src/models/records.js index da8d1b3d782c90ef173205d54dca6e8d8c61d60e..47013ec420e86143181a27c0fadaf39861060c71 100644 --- a/back/src/models/records.js +++ b/back/src/models/records.js @@ -29,4 +29,4 @@ const recordSchema = new mongoose.Schema({ const Record = mongoose.model('Record', recordSchema); -module.export = Record; +module.exports = { Record }; diff --git a/back/src/models/routine.js b/back/src/models/routine.js index 04131b4c0729009c4ebdc9e602fdd18d69509157..3d15812f707af6227feac91b486f5f351364100c 100644 --- a/back/src/models/routine.js +++ b/back/src/models/routine.js @@ -1,31 +1,34 @@ const mongoose = require('mongoose'); -const routineSchema = new mongoose.Schema({ - user_id: { - type: String, - required: true, - ref: 'User' +const routineSchema = new mongoose.Schema( + { + user_id: { + type: String, + required: true, + ref: 'User', + }, + routine_name: { + type: String, + required: true, + }, + routine_exercises: [ + { + video: { + type: mongoose.Schema.Types.ObjectId, + ref: 'Video', // Videon참조 + }, + }, + ], + routine_created_at: { + type: Date, + default: Date.now, + }, }, - routine_name: { - type: String, - required: true - }, - routine_exercises : [{ - video : { - type: mongoose.Schema.Types.ObjectId, - ref: 'Video', // Videon참조 - } - }] - , - routine_created_at: { - type: Date, - default: Date.now + { + timestamps: true, } -}, { - timestamps: true -}); - +); const Routine = mongoose.model('Routine', routineSchema); -module.exports = Routine; \ No newline at end of file +module.exports = { Routine }; diff --git a/back/src/models/video.js b/back/src/models/video.js index 246b84cef942403c4aa9ad3ea90542468e6572c3..5106c7bb02c9b880a7b6160e34e1b5fd28511d15 100644 --- a/back/src/models/video.js +++ b/back/src/models/video.js @@ -24,6 +24,9 @@ const videoSchema = new mongoose.Schema({ type: Number, default: 0, }, + channel_title: { + type: String, + }, }); -module.exports = mongoose.model('Video', videoSchema); \ No newline at end of file +module.exports = mongoose.model('Video', videoSchema); diff --git a/back/src/utils/fetchVideoLength.js b/back/src/utils/fetchVideoLength.js index 0795d33feabfd20e023a45eeaa8cda44ba5d29b5..0472cd735cb0ae59ce7689faea2cbbb6de67a2c5 100644 --- a/back/src/utils/fetchVideoLength.js +++ b/back/src/utils/fetchVideoLength.js @@ -1,15 +1,21 @@ -const timeConvert = require('./utils/timeconvert'); +require('dotenv').config(); +const ptToSeconds = require('./utils/timeconvert'); async function fetchVidLength(videoId) { try { const response = await fetch( - `https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics,snippet&id=${videoId}&key=${apiKey}` + `https://www.googleapis.com/youtube/v3/videos?part=contentDetails,statistics,snippet&id=${videoId}&key=${process.env.YOUTUBE_API_KEY}` ); const data = await response.json(); let video_length = data.items[0].contentDetails.duration; let video_likes = data.items[0].statistics.likeCount; - video_length = timeConvert.ptToSeconds(video_length); - return { videoLength: video_length, videoLikes: video_likes }; + video_length = ptToSeconds(video_length); + let channel_title = data.items[0].snippet.channelTitle; + return { + videoLength: video_length, + videoLikes: video_likes, + channelTitle: channel_title, + }; } catch (err) { console.error('Error during API details request;', err); return null; diff --git a/back/src/utils/fetchYoutube.js b/back/src/utils/fetchYoutube.js index 8cd1c714c105ff2954908b54068562eba155e731..d3dcc456b6a3de0b2cf49ee80a5bc07017119c56 100644 --- a/back/src/utils/fetchYoutube.js +++ b/back/src/utils/fetchYoutube.js @@ -1,3 +1,4 @@ +require('dotenv').config(); const fetchVidLength = require('./fetchVideoLength'); // API 키를 사용하여 유튜브 API에 검색 요청 보내기 @@ -5,7 +6,9 @@ async function fetchYoutube(query, iteration, videoObject, pageToken = '') { const fetchUrl = `https://www.googleapis.com/youtube/v3/search?part=snippet&q=${encodeURIComponent( query )} - &type=video&maxResults=10&videoType=any&key=${apiKey}&pageToken=${pageToken}`; + &type=video&maxResults=10&videoType=any&key=${ + process.env.YOUTUBE_API_KEY + }&pageToken=${pageToken}`; if (iteration >= maxiterations) { return; @@ -18,9 +21,8 @@ async function fetchYoutube(query, iteration, videoObject, pageToken = '') { const results = await Promise.all( data.items.map(async item => { - let { videoLength, videoLikes } = await fetchVidLength( - item.id.videoId - ); + let { videoLength, videoLikes, channelTitle } = + await fetchVidLength(item.id.videoId); return { video_id: item.id.videoId, video_title: item.snippet.title, @@ -28,6 +30,7 @@ async function fetchYoutube(query, iteration, videoObject, pageToken = '') { video_tag: query, // 검색 키워드가 들어가게 된다. video_length: videoLength, //videoLength 값 사용 video_likes: videoLikes, + channel_title: channelTitle, //채널명 추가 }; }) ); diff --git a/back/src/videoScrap.js b/back/src/videoScrap.js index a2d91b35f0ac0218a918196a9dd8f2022e39228d..70a6594901fd140b67abfd5e095d555871123636 100644 --- a/back/src/videoScrap.js +++ b/back/src/videoScrap.js @@ -12,12 +12,12 @@ async function connect() { connect(); const fetchYoutube = require('./utils/fetchYoutube'); -const addVideoInfo = require('./controllers/addVideoController'); +const addVideoInfo = require('./utils/addVideoDB'); /*const apiKey = 'AIzaSyAtnFTu-E6GUePD2AYOXwa2YXQugbb08Jc'; /*키1*/ /*const apiKey = 'AIzaSyBeiUVktH4Rtnw34NQP-z3BNo7X5uXX38Y'; //임시 키2*/ /*const apiKey = 'AIzaSyB5AzBrtWbFNlQxzIFMs_k6Fmel-7jmMUM'; /*임시 키3*/ -const apiKey = 'AIzaSyDZN4lyCTEDWZV9H9P3cq4xIIDSUmc-y-w'; /*임시 키 4*/ +/*const apiKey = 'AIzaSyDZN4lyCTEDWZV9H9P3cq4xIIDSUmc-y-w'; /*임시 키 4*/ const queries = [ '팔 홈트레이닝 | Arms Home Training', @@ -41,7 +41,7 @@ const queries = [ ]; let currentIteration = 0; -const maxiterations = 10; // 10개씩 10번 추출하므로 max=10 +const maxiterations = 2; //50개씩 2번 추출하므로 max=2 //query에 대해 반복하는 함수 async function processAllQueries() {