Skip to content

Commit 4744809

Browse files
authored
Merge pull request #2 from mohitdmak/dev
final implementations
2 parents c0fd87b + e607d48 commit 4744809

File tree

13 files changed

+1309
-82
lines changed

13 files changed

+1309
-82
lines changed

.github/workflows/node.js.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ jobs:
2727
uses: actions/setup-node@v2
2828
with:
2929
node-version: ${{ matrix.node-version }}
30-
- run: npm ci
31-
- run: npm run build --if-present
32-
- run: npm test
30+
- name: Install Packages
31+
run: npm ci
32+
33+
- name: Run tests
34+
env:
35+
MONGO_GITHUB_URI: ${{ secrets.MONGO_GITHUB_URI }}
36+
API_KEY_GITHUB: ${{ secrets.API_KEY_GITHUB }}
37+
run: npm run test

app.js

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,13 @@ app.get('/', (req, res) => {
1111
res.json({ response: "Hello Welcome to Yt SearchApi Homepage !" });
1212
});
1313

14-
const API_KEY = require('./config/apiKey');
1514
const fetch = require('axios');
1615

17-
const Router = require('./routes/searchRoutes');
18-
app.use('/search', Router);
16+
const ytRouter = require('./routes/searchYtRoutes');
17+
app.use('/search', ytRouter);
18+
19+
const dbRouter = require('./routes/searchDbRoutes');
20+
app.use('/find', dbRouter)
1921

20-
// app.get('/:id', async (req, res) =>{
21-
// var search = req.params.id;
22-
// var url = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=3&q=${search}&type=video&key=${API_KEY}`;
23-
// var arr = [];
24-
// try{
25-
// var response = await fetch(url);
26-
// for(i in response.data.items){
27-
// var items = response.data.items[i];
28-
// arr.push(items.snippet);
29-
// console.log(i);
30-
// }
31-
// res.status(200).json(arr);
32-
// }
33-
// catch(err){
34-
// console.log(err);
35-
// res.status(500).json(err);
36-
// }
37-
// fetch.get(url)
38-
// .then((r) => {
39-
// for(i in r.data.items){
40-
// var items = r.data.items[i];
41-
// arr.push(items.snippet);
42-
// console.log(arr);
43-
// }
44-
// res.json(arr);
45-
// })
46-
// .catch((err) => {
47-
// console.log(err);
48-
// res.send(err);
49-
// });
50-
//JSON.parse(data);
51-
52-
// });
5322

5423
module.exports = app;

controllers/findControllers.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Importing axios module to make get requests
2+
var fetch = require('axios');
3+
4+
// Importing mongo model for storing search data and video details
5+
var searchData = require('../models/searchModel');
6+
var videoData = require('../models/videoModel').videoData;
7+
8+
// Search Function which regularly refreshes search results
9+
async function findByTitle(Query, title){
10+
11+
try{
12+
var searchDB = await searchData.findOne({ query: Query });
13+
14+
if(searchDB === null){
15+
16+
return null;
17+
}
18+
else{
19+
20+
var data;
21+
for(video in searchDB.videoDetails){
22+
if( searchDB.videoDetails[video].title == title ){
23+
data = searchDB.videoDetails[video];
24+
}
25+
}
26+
return data;
27+
}
28+
}
29+
catch(err){
30+
console.log(err);
31+
return err;
32+
}
33+
}
34+
35+
// Search Function which regularly refreshes search results
36+
async function findByDescription(Query, description){
37+
38+
try{
39+
var searchDB = await searchData.findOne({ query: Query });
40+
41+
if(searchDB === null){
42+
return null;
43+
}
44+
else{
45+
46+
var data;
47+
for(video in searchDB.videoDetails){
48+
if( searchDB.videoDetails[video].description == description ){
49+
data = searchDB.videoDetails[video];
50+
}
51+
}
52+
return data;
53+
}
54+
}
55+
catch(err){
56+
console.log(err);
57+
return err;
58+
}
59+
}
60+
61+
// Search Function which regularly refreshes search results
62+
async function findByTitleAndDescription(Query, title, description){
63+
64+
try{
65+
var searchDB = await searchData.findOne({ query: Query });
66+
67+
if(searchDB === null){
68+
return null;
69+
}
70+
else{
71+
72+
var data;
73+
for(video in searchDB.videoDetails){
74+
if( searchDB.videoDetails[video].title == title && searchDB.videoDetails[video].description == description){
75+
data = searchDB.videoDetails[video];
76+
}
77+
}
78+
return data;
79+
}
80+
}
81+
catch(err){
82+
console.log(err);
83+
return err;
84+
}
85+
}
86+
87+
88+
// Controller to provide results as per request
89+
const provideResults = async (req, res) => {
90+
var maxResults = 2;
91+
var Query = req.params.searchquery;
92+
93+
if(req.query.title && !req.query.description){
94+
95+
var title = req.query.title;
96+
var data = await findByTitle(Query, title);
97+
}
98+
else if(req.query.description && !req.query.title){
99+
100+
var description = req.query.description;
101+
var data = await findByDescription(Query, description);
102+
}
103+
else if(req.query.title && req.query.description){
104+
105+
var title = req.query.title;
106+
var description = req.query.description;
107+
var data = await findByTitleAndDescription(Query, title, description);
108+
}
109+
else{
110+
res.status(400).json({ response: "Please provide either title or description of the required video."});
111+
}
112+
113+
114+
if(data === null){
115+
res.status(400).json({ response: "Please search for a video only after obtaining results through Youtube Api, thus visit the /search/:query URL first."});
116+
}else{
117+
res.status(200).json(data);
118+
}
119+
}
120+
121+
module.exports = {
122+
provideResults
123+
}

controllers/searchControllers.js

Lines changed: 136 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,170 @@
11
// Importing axios module to make get requests
22
var fetch = require('axios');
33

4-
// Importing mongo model for storing search data
4+
// Importing mongo model for storing search data and video details
55
var searchData = require('../models/searchModel');
6+
var videoData = require('../models/videoModel').videoData;
67

78
// Importing API_KEY
8-
const API_KEY = require('../config/apiKey');
9+
if(process.env.API_KEY_GITHUB){
10+
const keys = process.env.API_KEY_GITHUB;
11+
}else{
12+
const keys = require('../config/apiKey');
13+
}
14+
15+
// Stores queries for which server is already updating results in background every 10 seconds.
16+
var initializedPinging = [];
17+
18+
// Setting globally accessible currently used API_KEY variable
19+
var KEY;
20+
21+
// Adding a prototype function to array structure to cycle through elements
22+
// This switches API_KEY upon exhausting quota of 1 ( we have 3 api keys reserved )
23+
function switchKeys(API_KEY){
24+
25+
// getting current index
26+
const i = keys.indexOf(API_KEY);
27+
28+
// getting element in next index
29+
return keys[(i + 1)%keys.length];
30+
}
31+
32+
33+
// Updating search results
34+
async function updateResults(maxResults, Query, KEY){
35+
var url = `https://www.googleapis.com/youtube/v3/search?order=date&part=snippet&maxResults=${maxResults}&q=${Query}&type=video&key=${KEY}`;
936

10-
// Search Function which regularly refreshes search results
11-
async function SearchResults(maxResults, Query, API_KEY, first){
12-
var url = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=${maxResults}&q=${Query}&type=video&key=${API_KEY}`;
13-
1437
try{
1538
var response = await fetch(url);
39+
// finding appropriate searchData instance to update
40+
var search = await searchData.findOne({ query: Query });
41+
1642
for(i in response.data.items){
1743
var items = response.data.items[i];
18-
var search = new searchData({
44+
var video = new videoData({
1945
title: items.snippet.title,
2046
description: items.snippet.description,
2147
thumbnail: items.snippet.thumbnails.default.url,
2248
publishTime: items.snippet.publishedAt
2349
});
24-
var data = await search.save();
25-
console.log(data);
50+
// updating model's videoDetails documents
51+
search.videoDetails[i] = video;
2652
}
53+
await search.save();
54+
console.log(`Updated Videos for query searched by ${Query}`);
2755
}
2856
catch(err){
2957
console.log(err);
30-
return err;
3158
}
3259

33-
// setTimeout(async () => {
34-
// try{
35-
// await SearchResults(maxResults, Query, API_KEY, false);
36-
// }catch(err){
37-
// console.log(err);
38-
// }
39-
40-
// }, 10000);
60+
console.log(` Again Querying for following : ${initializedPinging}`);
61+
// updating search results every 10 seconds.
62+
setTimeout(async () => {
63+
try{
64+
await updateResults(maxResults, Query, KEY);
65+
}catch(err){
66+
console.log(err);
67+
}
68+
}, 10000);
69+
}
70+
71+
// Search Function which regularly refreshes search results
72+
async function SearchResults(maxResults, Query, API_KEY){
73+
74+
var url = `https://www.googleapis.com/youtube/v3/search?order=date&part=snippet&maxResults=${maxResults}&q=${Query}&type=video&key=${API_KEY}`;
75+
76+
try{
77+
//finding appropriate searchData model, according to search query
78+
var searchDB = await searchData.findOne({ query: Query });
79+
80+
// if user has searched for a new query, we need to fetch via yt api and wont be able to server from db
81+
if(searchDB === null){
82+
var response = await fetch(url);
83+
84+
// preparing new searchData model for new query
85+
var search = new searchData({
86+
query: Query,
87+
maxResults: maxResults
88+
});
89+
90+
for(i in response.data.items){
91+
var items = response.data.items[i];
92+
var video = new videoData({
93+
title: items.snippet.title,
94+
description: items.snippet.description,
95+
thumbnail: items.snippet.thumbnails.default.url,
96+
publishTime: items.snippet.publishedAt
97+
});
98+
search.videoDetails.push(video);
99+
console.log(video);
100+
}
101+
var data = await search.save();
102+
console.log(`Initializing Pinging for query ${Query}`);
103+
104+
// storing this query in this array so that when user requests on this same query again, the updateResults is not called again
105+
// ( otherwise it will keep requesting duplicate queries to yt api, and exhaust our key quota )
106+
initializedPinging.push(Query);
107+
await updateResults(maxResults, Query, KEY);
108+
return data;
109+
}
110+
// If query is already searched before, we directly server from our db, as db itself is updated in background.
111+
else{
112+
console.log(`Sending results from Database for query ${Query}`);
41113

42-
if(first){
43-
return data;
114+
if(initializedPinging.includes(Query)){
115+
console.log(`Already pinging ${Query}`);
116+
return searchDB;
117+
}
118+
// If query is searched before, but its updation is not started ( could be due to server restart ).
119+
else{
120+
console.log(`Initializing Pinging for query ${Query}`);
121+
initializedPinging.push(Query);
122+
await updateResults(maxResults, Query, KEY);
123+
return searchDB;
124+
}
125+
}
126+
}
127+
catch(err){
128+
// If error is due to exhaustion of api key quota, we get a 403 response code error
129+
// Then we switch to a new key
130+
if( err.message == 'Request failed with status code 403'){
131+
console.log(`API KEY Quota has been exhausted, switching api keys . . .`);
132+
KEY = switchKeys(API_KEY);
133+
}
134+
// notifying main controller to recall search function
135+
return "Switched API_KEYS, reinitiate request";
136+
}
137+
}
138+
139+
140+
// Handler to set which API_KEY to use
141+
function setKey(){
142+
// setting for the first time and then serving currently used key.
143+
if(KEY){
144+
return KEY;
145+
}
146+
else{
147+
KEY = keys[1];
148+
return KEY;
44149
}
45150
}
46151

47-
var initializedPinging = 0;
48152

49153
// Controller to provide results as per request
50154
const provideResults = async (req, res) => {
51-
if(initializedPinging == 0){
52-
var maxResults = 2;
53-
var Query = req.params.id;
155+
var maxResults = 2;
156+
var Query = req.params.searchquery;
157+
158+
// setting initial key for every new request
159+
var key = setKey();
160+
var data = await SearchResults(maxResults, Query, key);
54161

55-
var data = await SearchResults(maxResults, Query, API_KEY, true);
56-
res.status(200).json(data);
57-
initializedPinging = 1
162+
// if key was switched, we need to recall search function, before sending the response to user.
163+
if( data == "Switched API_KEYS, reinitiate request" ){
164+
var data = await SearchResults(maxResults, Query, KEY);
58165
}
166+
167+
res.status(200).json(data);
59168
}
60169

61170
module.exports = {

0 commit comments

Comments
 (0)