ArchitSharma
commited on
Commit
•
1f6053d
1
Parent(s):
d2bd828
Upload 12 files
Browse files- main.py +137 -0
- nlp_model.pkl +3 -0
- requirements.txt +17 -0
- static/autocomplete.js +36 -0
- static/default.jpg +0 -0
- static/image.jpg +0 -0
- static/loader.gif +0 -0
- static/recommend.js +351 -0
- static/style.css +310 -0
- templates/home.html +112 -0
- templates/recommend.html +203 -0
- tranform.pkl +3 -0
main.py
ADDED
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import pandas as pd
|
3 |
+
from flask import Flask, render_template, request
|
4 |
+
from sklearn.feature_extraction.text import CountVectorizer
|
5 |
+
from sklearn.metrics.pairwise import cosine_similarity
|
6 |
+
import json
|
7 |
+
import bs4 as bs
|
8 |
+
import urllib.request
|
9 |
+
import pickle
|
10 |
+
import requests
|
11 |
+
from datetime import date, datetime
|
12 |
+
|
13 |
+
# load the nlp model and tfidf vectorizer from disk
|
14 |
+
filename = 'nlp_model.pkl'
|
15 |
+
clf = pickle.load(open(filename, 'rb'))
|
16 |
+
vectorizer = pickle.load(open('tranform.pkl','rb'))
|
17 |
+
|
18 |
+
# converting list of string to list (eg. "["abc","def"]" to ["abc","def"])
|
19 |
+
def convert_to_list(my_list):
|
20 |
+
my_list = my_list.split('","')
|
21 |
+
my_list[0] = my_list[0].replace('["','')
|
22 |
+
my_list[-1] = my_list[-1].replace('"]','')
|
23 |
+
return my_list
|
24 |
+
|
25 |
+
# convert list of numbers to list (eg. "[1,2,3]" to [1,2,3])
|
26 |
+
def convert_to_list_num(my_list):
|
27 |
+
my_list = my_list.split(',')
|
28 |
+
my_list[0] = my_list[0].replace("[","")
|
29 |
+
my_list[-1] = my_list[-1].replace("]","")
|
30 |
+
return my_list
|
31 |
+
|
32 |
+
def get_suggestions():
|
33 |
+
data = pd.read_csv('main_data.csv')
|
34 |
+
return list(data['movie_title'].str.capitalize())
|
35 |
+
|
36 |
+
app = Flask(__name__)
|
37 |
+
|
38 |
+
@app.route("/")
|
39 |
+
@app.route("/home")
|
40 |
+
def home():
|
41 |
+
suggestions = get_suggestions()
|
42 |
+
return render_template('home.html',suggestions=suggestions)
|
43 |
+
|
44 |
+
|
45 |
+
@app.route("/recommend",methods=["POST"])
|
46 |
+
def recommend():
|
47 |
+
# getting data from AJAX request
|
48 |
+
title = request.form['title']
|
49 |
+
cast_ids = request.form['cast_ids']
|
50 |
+
cast_names = request.form['cast_names']
|
51 |
+
cast_chars = request.form['cast_chars']
|
52 |
+
cast_bdays = request.form['cast_bdays']
|
53 |
+
cast_bios = request.form['cast_bios']
|
54 |
+
cast_places = request.form['cast_places']
|
55 |
+
cast_profiles = request.form['cast_profiles']
|
56 |
+
imdb_id = request.form['imdb_id']
|
57 |
+
poster = request.form['poster']
|
58 |
+
genres = request.form['genres']
|
59 |
+
overview = request.form['overview']
|
60 |
+
vote_average = request.form['rating']
|
61 |
+
vote_count = request.form['vote_count']
|
62 |
+
rel_date = request.form['rel_date']
|
63 |
+
release_date = request.form['release_date']
|
64 |
+
runtime = request.form['runtime']
|
65 |
+
status = request.form['status']
|
66 |
+
rec_movies = request.form['rec_movies']
|
67 |
+
rec_posters = request.form['rec_posters']
|
68 |
+
rec_movies_org = request.form['rec_movies_org']
|
69 |
+
rec_year = request.form['rec_year']
|
70 |
+
rec_vote = request.form['rec_vote']
|
71 |
+
|
72 |
+
# get movie suggestions for auto complete
|
73 |
+
suggestions = get_suggestions()
|
74 |
+
|
75 |
+
# call the convert_to_list function for every string that needs to be converted to list
|
76 |
+
rec_movies_org = convert_to_list(rec_movies_org)
|
77 |
+
rec_movies = convert_to_list(rec_movies)
|
78 |
+
rec_posters = convert_to_list(rec_posters)
|
79 |
+
cast_names = convert_to_list(cast_names)
|
80 |
+
cast_chars = convert_to_list(cast_chars)
|
81 |
+
cast_profiles = convert_to_list(cast_profiles)
|
82 |
+
cast_bdays = convert_to_list(cast_bdays)
|
83 |
+
cast_bios = convert_to_list(cast_bios)
|
84 |
+
cast_places = convert_to_list(cast_places)
|
85 |
+
|
86 |
+
# convert string to list (eg. "[1,2,3]" to [1,2,3])
|
87 |
+
cast_ids = convert_to_list_num(cast_ids)
|
88 |
+
rec_vote = convert_to_list_num(rec_vote)
|
89 |
+
rec_year = convert_to_list_num(rec_year)
|
90 |
+
|
91 |
+
# rendering the string to python string
|
92 |
+
for i in range(len(cast_bios)):
|
93 |
+
cast_bios[i] = cast_bios[i].replace(r'\n', '\n').replace(r'\"','\"')
|
94 |
+
|
95 |
+
for i in range(len(cast_chars)):
|
96 |
+
cast_chars[i] = cast_chars[i].replace(r'\n', '\n').replace(r'\"','\"')
|
97 |
+
|
98 |
+
# combining multiple lists as a dictionary which can be passed to the html file so that it can be processed easily and the order of information will be preserved
|
99 |
+
movie_cards = {rec_posters[i]: [rec_movies[i],rec_movies_org[i],rec_vote[i],rec_year[i]] for i in range(len(rec_posters))}
|
100 |
+
|
101 |
+
casts = {cast_names[i]:[cast_ids[i], cast_chars[i], cast_profiles[i]] for i in range(len(cast_profiles))}
|
102 |
+
|
103 |
+
cast_details = {cast_names[i]:[cast_ids[i], cast_profiles[i], cast_bdays[i], cast_places[i], cast_bios[i]] for i in range(len(cast_places))}
|
104 |
+
|
105 |
+
# web scraping to get user reviews from IMDB site
|
106 |
+
sauce = urllib.request.urlopen('https://www.imdb.com/title/{}/reviews?ref_=tt_ov_rt'.format(imdb_id)).read()
|
107 |
+
soup = bs.BeautifulSoup(sauce,'lxml')
|
108 |
+
soup_result = soup.find_all("div",{"class":"text show-more__control"})
|
109 |
+
|
110 |
+
reviews_list = [] # list of reviews
|
111 |
+
reviews_status = [] # list of comments (good or bad)
|
112 |
+
for reviews in soup_result:
|
113 |
+
if reviews.string:
|
114 |
+
reviews_list.append(reviews.string)
|
115 |
+
# passing the review to our model
|
116 |
+
movie_review_list = np.array([reviews.string])
|
117 |
+
movie_vector = vectorizer.transform(movie_review_list)
|
118 |
+
pred = clf.predict(movie_vector)
|
119 |
+
reviews_status.append('Positive' if pred else 'Negative')
|
120 |
+
|
121 |
+
# getting current date
|
122 |
+
movie_rel_date = ""
|
123 |
+
curr_date = ""
|
124 |
+
if(rel_date):
|
125 |
+
today = str(date.today())
|
126 |
+
curr_date = datetime.strptime(today,'%Y-%m-%d')
|
127 |
+
movie_rel_date = datetime.strptime(rel_date, '%Y-%m-%d')
|
128 |
+
|
129 |
+
# combining reviews and comments into a dictionary
|
130 |
+
movie_reviews = {reviews_list[i]: reviews_status[i] for i in range(len(reviews_list))}
|
131 |
+
|
132 |
+
# passing all the data to the html file
|
133 |
+
return render_template('recommend.html',title=title,poster=poster,overview=overview,vote_average=vote_average,
|
134 |
+
vote_count=vote_count,release_date=release_date,movie_rel_date=movie_rel_date,curr_date=curr_date,runtime=runtime,status=status,genres=genres,movie_cards=movie_cards,reviews=movie_reviews,casts=casts,cast_details=cast_details)
|
135 |
+
|
136 |
+
if __name__ == '__main__':
|
137 |
+
app.run(debug=True)
|
nlp_model.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d7f74f92a7c9fc61fc323aa41fc8485b5c76d5c6e6b5d6382eab91d3e2fa7629
|
3 |
+
size 64953
|
requirements.txt
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Flask==1.1.1
|
2 |
+
gunicorn==19.9.0
|
3 |
+
Jinja2==2.10.1
|
4 |
+
MarkupSafe==1.1.1
|
5 |
+
Werkzeug==0.15.5
|
6 |
+
numpy>=1.9.2
|
7 |
+
scipy>=0.15.1
|
8 |
+
nltk==3.5
|
9 |
+
scikit-learn>=0.18
|
10 |
+
pandas>=0.19
|
11 |
+
beautifulsoup4==4.9.1
|
12 |
+
jsonschema==3.2.0
|
13 |
+
tmdbv3api==1.6.1
|
14 |
+
lxml==4.6.2
|
15 |
+
urllib3==1.25.9
|
16 |
+
requests==2.23.0
|
17 |
+
pickleshare==0.7.5
|
static/autocomplete.js
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
new autoComplete({
|
2 |
+
data: { // Data src [Array, Function, Async] | (REQUIRED)
|
3 |
+
src: films,
|
4 |
+
},
|
5 |
+
selector: "#autoComplete", // Input field selector | (Optional)
|
6 |
+
threshold: 2, // Min. Chars length to start Engine | (Optional)
|
7 |
+
debounce: 100, // Post duration for engine to start | (Optional)
|
8 |
+
searchEngine: "strict", // Search Engine type/mode | (Optional)
|
9 |
+
resultsList: { // Rendered results list object | (Optional)
|
10 |
+
render: true,
|
11 |
+
container: source => {
|
12 |
+
source.setAttribute("id", "food_list");
|
13 |
+
},
|
14 |
+
destination: document.querySelector("#autoComplete"),
|
15 |
+
position: "afterend",
|
16 |
+
element: "ul"
|
17 |
+
},
|
18 |
+
maxResults: 5, // Max. number of rendered results | (Optional)
|
19 |
+
highlight: true, // Highlight matching results | (Optional)
|
20 |
+
resultItem: { // Rendered result item | (Optional)
|
21 |
+
content: (data, source) => {
|
22 |
+
source.innerHTML = data.match;
|
23 |
+
},
|
24 |
+
element: "li"
|
25 |
+
},
|
26 |
+
noResults: () => { // Action script on noResults | (Optional)
|
27 |
+
const result = document.createElement("li");
|
28 |
+
result.setAttribute("class", "no_result");
|
29 |
+
result.setAttribute("tabindex", "1");
|
30 |
+
result.innerHTML = "No Results";
|
31 |
+
document.querySelector("#autoComplete_list").appendChild(result);
|
32 |
+
},
|
33 |
+
onSelection: feedback => { // Action script onSelection event | (Optional)
|
34 |
+
document.getElementById('autoComplete').value = feedback.selection.value;
|
35 |
+
}
|
36 |
+
});
|
static/default.jpg
ADDED
static/image.jpg
ADDED
static/loader.gif
ADDED
static/recommend.js
ADDED
@@ -0,0 +1,351 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$(function() {
|
2 |
+
// Button will be disabled until we type something inside the input field
|
3 |
+
const source = document.getElementById('autoComplete');
|
4 |
+
const inputHandler = function(e) {
|
5 |
+
if(e.target.value==""){
|
6 |
+
$('.movie-button').attr('disabled', true);
|
7 |
+
}
|
8 |
+
else{
|
9 |
+
$('.movie-button').attr('disabled', false);
|
10 |
+
}
|
11 |
+
}
|
12 |
+
source.addEventListener('input', inputHandler);
|
13 |
+
|
14 |
+
$('.fa-arrow-up').click(function(){
|
15 |
+
$('html, body').animate({scrollTop:0}, 'slow');
|
16 |
+
});
|
17 |
+
|
18 |
+
$('.app-title').click(function(){
|
19 |
+
window.location.href = '/';
|
20 |
+
})
|
21 |
+
|
22 |
+
$('.movie-button').on('click',function(){
|
23 |
+
var my_api_key = '5492165c61b1a21c06eb3a3b578a6339';
|
24 |
+
var title = $('.movie').val();
|
25 |
+
if (title=="") {
|
26 |
+
$('.results').css('display','none');
|
27 |
+
$('.fail').css('display','block');
|
28 |
+
}
|
29 |
+
|
30 |
+
if (($('.fail').text() && ($('.footer').css('position') == 'absolute'))) {
|
31 |
+
$('.footer').css('position', 'fixed');
|
32 |
+
}
|
33 |
+
|
34 |
+
else{
|
35 |
+
load_details(my_api_key,title);
|
36 |
+
}
|
37 |
+
});
|
38 |
+
});
|
39 |
+
|
40 |
+
// will be invoked when clicking on the recommended movie cards
|
41 |
+
function recommendcard(e){
|
42 |
+
$("#loader").fadeIn();
|
43 |
+
var my_api_key = '5492165c61b1a21c06eb3a3b578a6339';
|
44 |
+
var title = e.getAttribute('title');
|
45 |
+
load_details(my_api_key,title);
|
46 |
+
}
|
47 |
+
|
48 |
+
|
49 |
+
// get the details of the movie from the API (based on the name of the movie)
|
50 |
+
function load_details(my_api_key,title){
|
51 |
+
$.ajax({
|
52 |
+
type: 'GET',
|
53 |
+
url:'https://api.themoviedb.org/3/search/movie?api_key='+my_api_key+'&query='+title,
|
54 |
+
async: false,
|
55 |
+
success: function(movie){
|
56 |
+
if(movie.results.length<1){
|
57 |
+
$('.fail').css('display','block');
|
58 |
+
$('.results').css('display','none');
|
59 |
+
$("#loader").delay(500).fadeOut();
|
60 |
+
}
|
61 |
+
else if(movie.results.length==1) {
|
62 |
+
$("#loader").fadeIn();
|
63 |
+
$('.fail').css('display','none');
|
64 |
+
$('.results').delay(1000).css('display','block');
|
65 |
+
var movie_id = movie.results[0].id;
|
66 |
+
var movie_title = movie.results[0].title;
|
67 |
+
var movie_title_org = movie.results[0].original_title;
|
68 |
+
get_movie_details(movie_id,my_api_key,movie_title,movie_title_org);
|
69 |
+
}
|
70 |
+
else{
|
71 |
+
var close_match = {};
|
72 |
+
var flag=0;
|
73 |
+
var movie_id="";
|
74 |
+
var movie_title="";
|
75 |
+
var movie_title_org="";
|
76 |
+
$("#loader").fadeIn();
|
77 |
+
$('.fail').css('display','none');
|
78 |
+
$('.results').delay(1000).css('display','block');
|
79 |
+
for(var count in movie.results){
|
80 |
+
if(title==movie.results[count].original_title){
|
81 |
+
flag = 1;
|
82 |
+
movie_id = movie.results[count].id;
|
83 |
+
movie_title = movie.results[count].title;
|
84 |
+
movie_title_org = movie.results[count].original_title;
|
85 |
+
break;
|
86 |
+
}
|
87 |
+
else{
|
88 |
+
close_match[movie.results[count].title] = similarity(title, movie.results[count].title);
|
89 |
+
}
|
90 |
+
}
|
91 |
+
if(flag==0){
|
92 |
+
movie_title = Object.keys(close_match).reduce(function(a, b){ return close_match[a] > close_match[b] ? a : b });
|
93 |
+
var index = Object.keys(close_match).indexOf(movie_title)
|
94 |
+
movie_id = movie.results[index].id;
|
95 |
+
movie_title_org = movie.results[index].original_title;
|
96 |
+
}
|
97 |
+
get_movie_details(movie_id,my_api_key,movie_title,movie_title_org);
|
98 |
+
}
|
99 |
+
},
|
100 |
+
error: function(error){
|
101 |
+
alert('Invalid Request - '+error);
|
102 |
+
$("#loader").delay(500).fadeOut();
|
103 |
+
},
|
104 |
+
});
|
105 |
+
}
|
106 |
+
|
107 |
+
// getting closest match to the requested movie name using Levenshtein distance
|
108 |
+
function similarity(s1, s2) {
|
109 |
+
var longer = s1;
|
110 |
+
var shorter = s2;
|
111 |
+
if (s1.length < s2.length) {
|
112 |
+
longer = s2;
|
113 |
+
shorter = s1;
|
114 |
+
}
|
115 |
+
var longerLength = longer.length;
|
116 |
+
if (longerLength == 0) {
|
117 |
+
return 1.0;
|
118 |
+
}
|
119 |
+
return (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength);
|
120 |
+
}
|
121 |
+
|
122 |
+
function editDistance(s1, s2) {
|
123 |
+
s1 = s1.toLowerCase();
|
124 |
+
s2 = s2.toLowerCase();
|
125 |
+
|
126 |
+
var costs = new Array();
|
127 |
+
for (var i = 0; i <= s1.length; i++) {
|
128 |
+
var lastValue = i;
|
129 |
+
for (var j = 0; j <= s2.length; j++) {
|
130 |
+
if (i == 0)
|
131 |
+
costs[j] = j;
|
132 |
+
else {
|
133 |
+
if (j > 0) {
|
134 |
+
var newValue = costs[j - 1];
|
135 |
+
if (s1.charAt(i - 1) != s2.charAt(j - 1))
|
136 |
+
newValue = Math.min(Math.min(newValue, lastValue),
|
137 |
+
costs[j]) + 1;
|
138 |
+
costs[j - 1] = lastValue;
|
139 |
+
lastValue = newValue;
|
140 |
+
}
|
141 |
+
}
|
142 |
+
}
|
143 |
+
if (i > 0)
|
144 |
+
costs[s2.length] = lastValue;
|
145 |
+
}
|
146 |
+
return costs[s2.length];
|
147 |
+
}
|
148 |
+
|
149 |
+
// get all the details of the movie using the movie id.
|
150 |
+
function get_movie_details(movie_id,my_api_key,movie_title,movie_title_org) {
|
151 |
+
$.ajax({
|
152 |
+
type:'GET',
|
153 |
+
url:'https://api.themoviedb.org/3/movie/'+movie_id+'?api_key='+my_api_key,
|
154 |
+
success: function(movie_details){
|
155 |
+
show_details(movie_details,movie_title,my_api_key,movie_id,movie_title_org);
|
156 |
+
},
|
157 |
+
error: function(error){
|
158 |
+
alert("API Error! - "+error);
|
159 |
+
$("#loader").delay(500).fadeOut();
|
160 |
+
},
|
161 |
+
});
|
162 |
+
}
|
163 |
+
|
164 |
+
// passing all the details to python's flask for displaying and scraping the movie reviews using imdb id
|
165 |
+
function show_details(movie_details,movie_title,my_api_key,movie_id,movie_title_org){
|
166 |
+
var imdb_id = movie_details.imdb_id;
|
167 |
+
var poster;
|
168 |
+
if(movie_details.poster_path){
|
169 |
+
poster = 'https://image.tmdb.org/t/p/original'+movie_details.poster_path;
|
170 |
+
}
|
171 |
+
else {
|
172 |
+
poster = 'static/default.jpg';
|
173 |
+
}
|
174 |
+
var overview = movie_details.overview;
|
175 |
+
var genres = movie_details.genres;
|
176 |
+
var rating = movie_details.vote_average;
|
177 |
+
var vote_count = movie_details.vote_count;
|
178 |
+
var release_date = movie_details.release_date;
|
179 |
+
var runtime = parseInt(movie_details.runtime);
|
180 |
+
var status = movie_details.status;
|
181 |
+
var genre_list = []
|
182 |
+
for (var genre in genres){
|
183 |
+
genre_list.push(genres[genre].name);
|
184 |
+
}
|
185 |
+
var my_genre = genre_list.join(", ");
|
186 |
+
if(runtime%60==0){
|
187 |
+
runtime = Math.floor(runtime/60)+" hour(s)"
|
188 |
+
}
|
189 |
+
else {
|
190 |
+
runtime = Math.floor(runtime/60)+" hour(s) "+(runtime%60)+" min(s)"
|
191 |
+
}
|
192 |
+
|
193 |
+
// calling `get_movie_cast` to get the top cast for the queried movie
|
194 |
+
movie_cast = get_movie_cast(movie_id,my_api_key);
|
195 |
+
|
196 |
+
// calling `get_individual_cast` to get the individual cast details
|
197 |
+
ind_cast = get_individual_cast(movie_cast,my_api_key);
|
198 |
+
|
199 |
+
// calling `get_recommendations` to get the recommended movies for the given movie id from the TMDB API
|
200 |
+
recommendations = get_recommendations(movie_id, my_api_key);
|
201 |
+
|
202 |
+
details = {
|
203 |
+
'title':movie_title,
|
204 |
+
'cast_ids':JSON.stringify(movie_cast.cast_ids),
|
205 |
+
'cast_names':JSON.stringify(movie_cast.cast_names),
|
206 |
+
'cast_chars':JSON.stringify(movie_cast.cast_chars),
|
207 |
+
'cast_profiles':JSON.stringify(movie_cast.cast_profiles),
|
208 |
+
'cast_bdays':JSON.stringify(ind_cast.cast_bdays),
|
209 |
+
'cast_bios':JSON.stringify(ind_cast.cast_bios),
|
210 |
+
'cast_places':JSON.stringify(ind_cast.cast_places),
|
211 |
+
'imdb_id':imdb_id,
|
212 |
+
'poster':poster,
|
213 |
+
'genres':my_genre,
|
214 |
+
'overview':overview,
|
215 |
+
'rating':rating,
|
216 |
+
'vote_count':vote_count.toLocaleString(),
|
217 |
+
'rel_date':release_date,
|
218 |
+
'release_date':new Date(release_date).toDateString().split(' ').slice(1).join(' '),
|
219 |
+
'runtime':runtime,
|
220 |
+
'status':status,
|
221 |
+
'rec_movies':JSON.stringify(recommendations.rec_movies),
|
222 |
+
'rec_posters':JSON.stringify(recommendations.rec_posters),
|
223 |
+
'rec_movies_org':JSON.stringify(recommendations.rec_movies_org),
|
224 |
+
'rec_year':JSON.stringify(recommendations.rec_year),
|
225 |
+
'rec_vote':JSON.stringify(recommendations.rec_vote)
|
226 |
+
}
|
227 |
+
|
228 |
+
$.ajax({
|
229 |
+
type:'POST',
|
230 |
+
data:details,
|
231 |
+
url:"/recommend",
|
232 |
+
dataType: 'html',
|
233 |
+
complete: function(){
|
234 |
+
$("#loader").delay(500).fadeOut();
|
235 |
+
},
|
236 |
+
success: function(response) {
|
237 |
+
$('.results').html(response);
|
238 |
+
$('#autoComplete').val('');
|
239 |
+
$('.footer').css('position','absolute');
|
240 |
+
if ($('.movie-content')) {
|
241 |
+
$('.movie-content').after('<div class="gototop"><i title="Go to Top" class="fa fa-arrow-up"></i></div>');
|
242 |
+
}
|
243 |
+
$(window).scrollTop(0);
|
244 |
+
}
|
245 |
+
});
|
246 |
+
}
|
247 |
+
|
248 |
+
// getting the details of individual cast
|
249 |
+
function get_individual_cast(movie_cast,my_api_key) {
|
250 |
+
cast_bdays = [];
|
251 |
+
cast_bios = [];
|
252 |
+
cast_places = [];
|
253 |
+
for(var cast_id in movie_cast.cast_ids){
|
254 |
+
$.ajax({
|
255 |
+
type:'GET',
|
256 |
+
url:'https://api.themoviedb.org/3/person/'+movie_cast.cast_ids[cast_id]+'?api_key='+my_api_key,
|
257 |
+
async:false,
|
258 |
+
success: function(cast_details){
|
259 |
+
cast_bdays.push((new Date(cast_details.birthday)).toDateString().split(' ').slice(1).join(' '));
|
260 |
+
if(cast_details.biography){
|
261 |
+
cast_bios.push(cast_details.biography);
|
262 |
+
}
|
263 |
+
else {
|
264 |
+
cast_bios.push("Not Available");
|
265 |
+
}
|
266 |
+
if(cast_details.place_of_birth){
|
267 |
+
cast_places.push(cast_details.place_of_birth);
|
268 |
+
}
|
269 |
+
else {
|
270 |
+
cast_places.push("Not Available");
|
271 |
+
}
|
272 |
+
}
|
273 |
+
});
|
274 |
+
}
|
275 |
+
return {cast_bdays:cast_bdays,cast_bios:cast_bios,cast_places:cast_places};
|
276 |
+
}
|
277 |
+
|
278 |
+
// getting the details of the cast for the requested movie
|
279 |
+
function get_movie_cast(movie_id,my_api_key){
|
280 |
+
cast_ids= [];
|
281 |
+
cast_names = [];
|
282 |
+
cast_chars = [];
|
283 |
+
cast_profiles = [];
|
284 |
+
top_10 = [0,1,2,3,4,5,6,7,8,9];
|
285 |
+
$.ajax({
|
286 |
+
type:'GET',
|
287 |
+
url:"https://api.themoviedb.org/3/movie/"+movie_id+"/credits?api_key="+my_api_key,
|
288 |
+
async:false,
|
289 |
+
success: function(my_movie){
|
290 |
+
if(my_movie.cast.length>0){
|
291 |
+
if(my_movie.cast.length>=10){
|
292 |
+
top_cast = [0,1,2,3,4,5,6,7,8,9];
|
293 |
+
}
|
294 |
+
else {
|
295 |
+
top_cast = [0,1,2,3,4];
|
296 |
+
}
|
297 |
+
for(var my_cast in top_cast){
|
298 |
+
cast_ids.push(my_movie.cast[my_cast].id)
|
299 |
+
cast_names.push(my_movie.cast[my_cast].name);
|
300 |
+
cast_chars.push(my_movie.cast[my_cast].character);
|
301 |
+
if(my_movie.cast[my_cast].profile_path){
|
302 |
+
cast_profiles.push("https://image.tmdb.org/t/p/original"+my_movie.cast[my_cast].profile_path);
|
303 |
+
}
|
304 |
+
else {
|
305 |
+
cast_profiles.push("static/default.jpg");
|
306 |
+
}
|
307 |
+
}
|
308 |
+
}
|
309 |
+
},
|
310 |
+
error: function(error){
|
311 |
+
alert("Invalid Request! - "+error);
|
312 |
+
$("#loader").delay(500).fadeOut();
|
313 |
+
}
|
314 |
+
});
|
315 |
+
|
316 |
+
return {cast_ids:cast_ids,cast_names:cast_names,cast_chars:cast_chars,cast_profiles:cast_profiles};
|
317 |
+
}
|
318 |
+
|
319 |
+
// getting recommendations
|
320 |
+
function get_recommendations(movie_id, my_api_key) {
|
321 |
+
rec_movies = [];
|
322 |
+
rec_posters = [];
|
323 |
+
rec_movies_org = [];
|
324 |
+
rec_year = [];
|
325 |
+
rec_vote = [];
|
326 |
+
|
327 |
+
$.ajax({
|
328 |
+
type: 'GET',
|
329 |
+
url: "https://api.themoviedb.org/3/movie/"+movie_id+"/recommendations?api_key="+my_api_key,
|
330 |
+
async: false,
|
331 |
+
success: function(recommend) {
|
332 |
+
for(var recs in recommend.results) {
|
333 |
+
rec_movies.push(recommend.results[recs].title);
|
334 |
+
rec_movies_org.push(recommend.results[recs].original_title);
|
335 |
+
rec_year.push(new Date(recommend.results[recs].release_date).getFullYear());
|
336 |
+
rec_vote.push(recommend.results[recs].vote_average);
|
337 |
+
if(recommend.results[recs].poster_path){
|
338 |
+
rec_posters.push("https://image.tmdb.org/t/p/original"+recommend.results[recs].poster_path);
|
339 |
+
}
|
340 |
+
else {
|
341 |
+
rec_posters.push("static/default.jpg");
|
342 |
+
}
|
343 |
+
}
|
344 |
+
},
|
345 |
+
error: function(error) {
|
346 |
+
alert("Invalid Request! - "+error);
|
347 |
+
$("#loader").delay(500).fadeOut();
|
348 |
+
}
|
349 |
+
});
|
350 |
+
return {rec_movies:rec_movies,rec_movies_org:rec_movies_org,rec_posters:rec_posters,rec_year:rec_year,rec_vote:rec_vote};
|
351 |
+
}
|
static/style.css
ADDED
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* width */
|
2 |
+
::-webkit-scrollbar {
|
3 |
+
width: 15px;
|
4 |
+
}
|
5 |
+
|
6 |
+
/* Track */
|
7 |
+
::-webkit-scrollbar-track {
|
8 |
+
border-radius: 10px;
|
9 |
+
}
|
10 |
+
|
11 |
+
/* Handle */
|
12 |
+
::-webkit-scrollbar-thumb {
|
13 |
+
background: #e50914;
|
14 |
+
border-radius: 10px;
|
15 |
+
}
|
16 |
+
|
17 |
+
/* Handle on hover */
|
18 |
+
::-webkit-scrollbar-thumb:hover {
|
19 |
+
background: #b30000;
|
20 |
+
}
|
21 |
+
|
22 |
+
.movie {
|
23 |
+
color: #fff;
|
24 |
+
margin-left: auto;
|
25 |
+
margin-right: auto;
|
26 |
+
resize: none;
|
27 |
+
}
|
28 |
+
|
29 |
+
.movie-content {
|
30 |
+
display: flex;
|
31 |
+
flex-wrap: wrap;
|
32 |
+
justify-content:center;
|
33 |
+
}
|
34 |
+
|
35 |
+
.movie-content > div {
|
36 |
+
margin:20px;
|
37 |
+
}
|
38 |
+
|
39 |
+
.btn-block{
|
40 |
+
width: 15%;
|
41 |
+
text-align: center;
|
42 |
+
margin-left: auto;
|
43 |
+
margin-right: auto;
|
44 |
+
color: #e4e0e0;
|
45 |
+
}
|
46 |
+
|
47 |
+
#content {
|
48 |
+
background-image: url("../static/image.jpg");
|
49 |
+
background-color: #181818;
|
50 |
+
font-family: 'Noto Sans JP', sans-serif;
|
51 |
+
}
|
52 |
+
|
53 |
+
#details {
|
54 |
+
margin-left: 50px;
|
55 |
+
}
|
56 |
+
|
57 |
+
.body-content {
|
58 |
+
position: relative;
|
59 |
+
min-height: 100%;
|
60 |
+
}
|
61 |
+
|
62 |
+
.footer {
|
63 |
+
color: #e4e0e0;
|
64 |
+
background-color: #e50914d1;
|
65 |
+
text-align:center;
|
66 |
+
position: fixed;
|
67 |
+
bottom: 0;
|
68 |
+
width: 100%;
|
69 |
+
}
|
70 |
+
|
71 |
+
.social-icons {
|
72 |
+
margin-left: 15px;
|
73 |
+
}
|
74 |
+
|
75 |
+
h1 {
|
76 |
+
font-family: 'Netflix Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
77 |
+
color: #e50914;
|
78 |
+
font-weight: bold;
|
79 |
+
margin-top: 30px;
|
80 |
+
text-shadow: #000000 0px 0px 13px;
|
81 |
+
}
|
82 |
+
|
83 |
+
.github-corner:hover .octo-arm {
|
84 |
+
animation: octocat-wave 560ms ease-in-out;
|
85 |
+
}
|
86 |
+
|
87 |
+
.fa-arrow-up {
|
88 |
+
font-size: 1.5em;
|
89 |
+
color: #ffffff;
|
90 |
+
background: #e50914;
|
91 |
+
padding: 10px;
|
92 |
+
border-radius: 50%;
|
93 |
+
float: right;
|
94 |
+
bottom: 25px;
|
95 |
+
position: relative;
|
96 |
+
}
|
97 |
+
|
98 |
+
.fa-arrow-up:hover {
|
99 |
+
cursor: pointer;
|
100 |
+
}
|
101 |
+
|
102 |
+
.app-title:hover {
|
103 |
+
cursor: pointer;
|
104 |
+
}
|
105 |
+
|
106 |
+
@keyframes octocat-wave {
|
107 |
+
0%,
|
108 |
+
100% {
|
109 |
+
transform: rotate(0)
|
110 |
+
}
|
111 |
+
|
112 |
+
20%,
|
113 |
+
60% {
|
114 |
+
transform: rotate(-25deg)
|
115 |
+
}
|
116 |
+
|
117 |
+
40%,
|
118 |
+
80% {
|
119 |
+
transform: rotate(10deg)
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
#autoComplete {
|
124 |
+
background-position: 98% ;
|
125 |
+
}
|
126 |
+
|
127 |
+
#name {
|
128 |
+
color: white;
|
129 |
+
padding: 1px;
|
130 |
+
}
|
131 |
+
|
132 |
+
h6 {
|
133 |
+
margin-bottom: 20px;
|
134 |
+
}
|
135 |
+
|
136 |
+
@media only screen and (max-width: 650px) {
|
137 |
+
#mcontent {
|
138 |
+
display: block;
|
139 |
+
}
|
140 |
+
.poster-lg {
|
141 |
+
display: none;
|
142 |
+
}
|
143 |
+
#details {
|
144 |
+
margin-left: 30px;
|
145 |
+
}
|
146 |
+
#loader {
|
147 |
+
display: none;
|
148 |
+
position: fixed;
|
149 |
+
z-index: 100;
|
150 |
+
left: 0;
|
151 |
+
top:0;
|
152 |
+
width: 100%;
|
153 |
+
height: 100%;
|
154 |
+
background-image: url("../static/loader.gif");
|
155 |
+
background-size: 40%;
|
156 |
+
background-position: 50% 50%;
|
157 |
+
background-color: rgba(255, 255, 255, 1);
|
158 |
+
background-repeat: no-repeat;
|
159 |
+
-webkit-transition: background-image 0.2s ease-in-out;
|
160 |
+
transition: background-image 0.2s ease-in-out;
|
161 |
+
}
|
162 |
+
|
163 |
+
#loader-text {
|
164 |
+
vertical-align: middle;
|
165 |
+
color:white;
|
166 |
+
}
|
167 |
+
|
168 |
+
#autoComplete {
|
169 |
+
background-position: 97% ;
|
170 |
+
}
|
171 |
+
|
172 |
+
svg[data-toggle=tooltip] {
|
173 |
+
width: 50px;
|
174 |
+
height: 50px;
|
175 |
+
}
|
176 |
+
|
177 |
+
.fa-arrow-up{
|
178 |
+
font-size: 1em;
|
179 |
+
color: #ffffff;
|
180 |
+
background: #e50914;
|
181 |
+
padding: 10px;
|
182 |
+
border-radius: 50%;
|
183 |
+
float: right;
|
184 |
+
bottom: 10px;
|
185 |
+
position: relative;
|
186 |
+
}
|
187 |
+
}
|
188 |
+
|
189 |
+
@media only screen and (max-width: 991px) {
|
190 |
+
.modal-body{
|
191 |
+
display: block;
|
192 |
+
}
|
193 |
+
.profile-pic {
|
194 |
+
margin-left: auto;
|
195 |
+
margin-right: auto;
|
196 |
+
display: block;
|
197 |
+
margin-bottom: 20px;
|
198 |
+
}
|
199 |
+
}
|
200 |
+
@media only screen and (min-width: 992px) {
|
201 |
+
.modal-body {
|
202 |
+
display: flex;
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
@media only screen and (min-width: 651px) {
|
207 |
+
.poster-sm {
|
208 |
+
display: none;
|
209 |
+
}
|
210 |
+
|
211 |
+
#mcontent {
|
212 |
+
display: flex;
|
213 |
+
flex-wrap: nowrap;
|
214 |
+
}
|
215 |
+
|
216 |
+
#loader {
|
217 |
+
display: none;
|
218 |
+
position: fixed;
|
219 |
+
z-index: 100;
|
220 |
+
left: 0;
|
221 |
+
top:0;
|
222 |
+
width: 100%;
|
223 |
+
height: 100%;
|
224 |
+
background-image: url("../static/loader.gif");
|
225 |
+
background-size: 20%;
|
226 |
+
background-position: 50% 50%;
|
227 |
+
background-color: rgba(255, 255, 255, 1);
|
228 |
+
background-repeat: no-repeat;
|
229 |
+
-webkit-transition: background-image 0.2s ease-in-out;
|
230 |
+
transition: background-image 0.2s ease-in-out;
|
231 |
+
}
|
232 |
+
|
233 |
+
#loader-text {
|
234 |
+
vertical-align: middle;
|
235 |
+
color:white;
|
236 |
+
}
|
237 |
+
|
238 |
+
}
|
239 |
+
|
240 |
+
.poster{
|
241 |
+
-webkit-box-shadow: 0px 1px 15px 4px rgba(250,250,250,1);
|
242 |
+
-moz-box-shadow: 0px 1px 15px 4px rgba(250,250,250,1);
|
243 |
+
box-shadow: 0px 1px 15px 4px rgba(250,250,250,1);
|
244 |
+
}
|
245 |
+
|
246 |
+
.card:hover {
|
247 |
+
cursor: pointer;
|
248 |
+
}
|
249 |
+
|
250 |
+
.castcard:hover {
|
251 |
+
cursor: pointer;
|
252 |
+
}
|
253 |
+
|
254 |
+
.cast-img {
|
255 |
+
filter: brightness(100%);
|
256 |
+
-moz-transition: all 0.75s ease;
|
257 |
+
-webkit-transition: all 0.75s ease;
|
258 |
+
transition: all 0.75s ease;
|
259 |
+
}
|
260 |
+
|
261 |
+
|
262 |
+
.cast-img:hover {
|
263 |
+
filter: brightness(50%);
|
264 |
+
-moz-transition: all 0.75s ease;
|
265 |
+
-webkit-transition: all 0.75s ease;
|
266 |
+
transition: all 0.75s ease;
|
267 |
+
}
|
268 |
+
|
269 |
+
.fig {
|
270 |
+
display: flex;
|
271 |
+
align-items: center;
|
272 |
+
justify-content: center;
|
273 |
+
backdrop-filter: brightness(50%);
|
274 |
+
position: absolute;
|
275 |
+
bottom: 0px;
|
276 |
+
top: 0px;
|
277 |
+
right: 0px;
|
278 |
+
left: 0px;
|
279 |
+
opacity: 0;
|
280 |
+
-moz-transition: all 0.75s ease;
|
281 |
+
-webkit-transition: all 0.75s ease;
|
282 |
+
transition: all 0.75s ease;
|
283 |
+
}
|
284 |
+
|
285 |
+
.fig:hover {
|
286 |
+
opacity: 1;
|
287 |
+
backdrop-filter:br;
|
288 |
+
-moz-transition: all 0.75s ease;
|
289 |
+
-webkit-transition: all 0.75s ease;
|
290 |
+
transition: all 0.75s ease;
|
291 |
+
}
|
292 |
+
|
293 |
+
.card-btn {
|
294 |
+
border-radius: 20px;
|
295 |
+
}
|
296 |
+
|
297 |
+
.imghvr {
|
298 |
+
position: relative;
|
299 |
+
}
|
300 |
+
|
301 |
+
.table td {
|
302 |
+
border-color: white;
|
303 |
+
border-style:solid;
|
304 |
+
border-width:1px;
|
305 |
+
}
|
306 |
+
|
307 |
+
.fail {
|
308 |
+
display: none;
|
309 |
+
color: white;
|
310 |
+
}
|
templates/home.html
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>Movie Recommendation System</title>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
7 |
+
|
8 |
+
<!-- Google Fonts -->
|
9 |
+
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap" rel="stylesheet">
|
10 |
+
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
|
11 |
+
|
12 |
+
<!-- Font Awesome -->
|
13 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
14 |
+
|
15 |
+
<!-- Bootstrap -->
|
16 |
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
17 |
+
|
18 |
+
<!-- jQuery -->
|
19 |
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
20 |
+
|
21 |
+
<!-- Auto Complete -->
|
22 |
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@7.2.0/dist/css/autoComplete.min.css">
|
23 |
+
|
24 |
+
<!-- External CSS -->
|
25 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='style.css') }}">
|
26 |
+
|
27 |
+
<script type="text/javascript">
|
28 |
+
var films = {{suggestions|tojson}};
|
29 |
+
$(document).ready(function(){
|
30 |
+
$("#myModal").modal('show');
|
31 |
+
});
|
32 |
+
</script>
|
33 |
+
|
34 |
+
</head>
|
35 |
+
|
36 |
+
<body id="content" style="font-family: 'Noto Sans JP', sans-serif;">
|
37 |
+
<div class="body-content">
|
38 |
+
<div class="ml-container" style="display: block;">
|
39 |
+
<a href="https://github.com/ArchitSharma21/Movie-Recommendation-System-With-Deployment-Using-Heroku" target="_blank" class="github-corner" title="View source on GitHub">
|
40 |
+
<svg data-toggle="tooltip"
|
41 |
+
data-placement="left" width="80" height="80" viewBox="0 0 250 250"
|
42 |
+
style="fill:#e50914; color:#fff; position: fixed;z-index:100; top: 0; border: 0; right: 0;" aria-hidden="true">
|
43 |
+
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
44 |
+
<path
|
45 |
+
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
46 |
+
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
47 |
+
<path
|
48 |
+
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
49 |
+
fill="currentColor" class="octo-body"></path>
|
50 |
+
</svg>
|
51 |
+
</a>
|
52 |
+
<center><h1 class="app-title">Movie Recommendation System</h1></center>
|
53 |
+
<div class="form-group shadow-textarea" style="margin-top: 30px;text-align: center;color: white;">
|
54 |
+
<input type="text" name="movie" class="movie form-control" id="autoComplete" autocomplete="off" placeholder="Enter the Movie Name" style="background-color: #ffffff;border-color:#ffffff;width: 60%;color: #181818" required="required" />
|
55 |
+
<br>
|
56 |
+
</div>
|
57 |
+
|
58 |
+
<div class="form-group" style="text-align: center;">
|
59 |
+
<button class="btn btn-primary btn-block movie-button" style="background-color: #e50914;text-align: center;border-color: #e50914;width:120px;" disabled="true" >Enter</button><br><br>
|
60 |
+
</div>
|
61 |
+
</div>
|
62 |
+
|
63 |
+
<div id="loader" class="text-center">
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<div class="fail">
|
67 |
+
<center><h3>Sorry! The movie you requested is not in our database.
|
68 |
+
Please check the spelling or try with other movies!</h3></center>
|
69 |
+
</div>
|
70 |
+
|
71 |
+
<div class="results">
|
72 |
+
<center>
|
73 |
+
<h2 id="name" class="text-uppercase"></h2>
|
74 |
+
</center>
|
75 |
+
</div>
|
76 |
+
|
77 |
+
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel3" aria-hidden="true">
|
78 |
+
<div class="modal-dialog modal-md" role="document">
|
79 |
+
<div class="modal-content">
|
80 |
+
<div class="modal-header" style="background-color: #e50914;color: white;">
|
81 |
+
<h5 class="modal-title" id="exampleModalLabel3">Hey there!</h5>
|
82 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
83 |
+
<span aria-hidden="true" style="color: white">×</span>
|
84 |
+
</button>
|
85 |
+
</div>
|
86 |
+
<div class="modal-body">
|
87 |
+
<p>Don't worry if the movie that you are looking for is not auto-suggested while typing. Just type the movie name and click on "enter". You will be good to go even though if you made some typo errors.</p>
|
88 |
+
</div>
|
89 |
+
<div class="modal-footer" style="text-align: center;">
|
90 |
+
<button type="button" class="btn btn-secondary" data-dismiss="modal">Let's go!</button>
|
91 |
+
</div>
|
92 |
+
</div>
|
93 |
+
</div>
|
94 |
+
</div>
|
95 |
+
|
96 |
+
<footer class="footer">
|
97 |
+
<br/>
|
98 |
+
<div class="social" style="margin-bottom: 8px">
|
99 |
+
<a class="social-icons" href="https://github.com/ArchitSharma21" target="_blank"><i class="fa fa-github-alt" style="font-size:24px;color: #e4e0e0"></i></a>
|
100 |
+
</div>
|
101 |
+
</footer>
|
102 |
+
</div>
|
103 |
+
|
104 |
+
<script src="https://cdn.jsdelivr.net/npm/@tarekraafat/autocomplete.js@7.2.0/dist/js/autoComplete.min.js"></script>
|
105 |
+
<script type="text/javascript" src="{{url_for('static', filename='autocomplete.js')}}"></script>
|
106 |
+
|
107 |
+
<script type="text/javascript" src="{{url_for('static', filename='recommend.js')}}"></script>
|
108 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
109 |
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
110 |
+
|
111 |
+
</body>
|
112 |
+
</html>
|
templates/recommend.html
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html>
|
3 |
+
<head>
|
4 |
+
<title>NEW</title>
|
5 |
+
|
6 |
+
<meta charset="UTF-8">
|
7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
8 |
+
|
9 |
+
<!-- Google Fonts -->
|
10 |
+
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap" rel="stylesheet">
|
11 |
+
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
|
12 |
+
<link href="https://fonts.googleapis.com/css2?family=Rowdies:wght@300&display=swap" rel="stylesheet">
|
13 |
+
|
14 |
+
<!-- Font Awesome -->
|
15 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
16 |
+
|
17 |
+
<!-- Bootstrap -->
|
18 |
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
19 |
+
|
20 |
+
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='style.css') }}">
|
21 |
+
|
22 |
+
</head>
|
23 |
+
|
24 |
+
<body id="content">
|
25 |
+
|
26 |
+
<div class="results">
|
27 |
+
<center>
|
28 |
+
<h2 id="name" class="text-uppercase" style="font-family: 'Rowdies', cursive;">{{title}}</h2>
|
29 |
+
</center>
|
30 |
+
</div>
|
31 |
+
<br/>
|
32 |
+
|
33 |
+
<div id="mycontent">
|
34 |
+
<div id="mcontent">
|
35 |
+
<div class="poster-lg">
|
36 |
+
<img class="poster" style="border-radius: 40px;margin-left: 90px;" height="400" width="250" src={{poster}}>
|
37 |
+
</div>
|
38 |
+
<div class="poster-sm text-center">
|
39 |
+
<img class="poster" style="border-radius: 40px;margin-bottom: 5%;" height="400" width="250" src={{poster}}>
|
40 |
+
</div>
|
41 |
+
<div id="details">
|
42 |
+
<br/>
|
43 |
+
<h6 id="title" style="color:white;">TITLE: {{title}}</h6>
|
44 |
+
<h6 id="overview" style="color:white;max-width: 85%">OVERVIEW: <br/><br/> {{overview}}</h6>
|
45 |
+
<h6 id="vote_average" style="color:white;">RATING: {{vote_average}}/10 ({{vote_count}} votes)</h6>
|
46 |
+
<h6 id="genres" style="color:white;">GENRE: {{genres}}</h6>
|
47 |
+
<h6 id="date" style="color:white;">RELEASE DATE: {{release_date}}</h6>
|
48 |
+
<h6 id="runtime" style="color:white;">RUNTIME: {{runtime}}</h6>
|
49 |
+
<h6 id="status" style="color:white;">STATUS: {{status}}</h6>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
<br/>
|
54 |
+
|
55 |
+
{% for name, details in cast_details.items() if not cast_details.hidden %}
|
56 |
+
<div class="modal fade" id="{{details[0]}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel3" aria-hidden="true">
|
57 |
+
<div class="modal-dialog modal-lg" role="document">
|
58 |
+
<div class="modal-content">
|
59 |
+
<div class="modal-header" style="background-color: #e50914;color: white;">
|
60 |
+
<h5 class="modal-title" id="exampleModalLabel3">{{name}}</h5>
|
61 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
62 |
+
<span aria-hidden="true" style="color: white">×</span>
|
63 |
+
</button>
|
64 |
+
</div>
|
65 |
+
|
66 |
+
<div class="modal-body">
|
67 |
+
<img class="profile-pic" src="{{details[1]}}" alt="{{name}} - profile" style="width: 250px;height:400px;border-radius: 10px;" />
|
68 |
+
<div style="margin-left: 20px">
|
69 |
+
<p><strong>Birthday:</strong> {{details[2]}} </p>
|
70 |
+
<p><strong>Place of Birth:</strong> {{details[3]}} </p>
|
71 |
+
<p>
|
72 |
+
<p><strong>Biography:</strong><p>
|
73 |
+
{{details[4]}}
|
74 |
+
</p>
|
75 |
+
</div>
|
76 |
+
</div>
|
77 |
+
<div class="modal-footer">
|
78 |
+
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
79 |
+
</div>
|
80 |
+
</div>
|
81 |
+
</div>
|
82 |
+
</div>
|
83 |
+
{% endfor %}
|
84 |
+
|
85 |
+
<div class="container">
|
86 |
+
|
87 |
+
{% if casts|length > 1 %}
|
88 |
+
<div class="movie" style="color: #E8E8E8;">
|
89 |
+
<center>
|
90 |
+
<h2 style="font-family: 'Rowdies', cursive;">TOP CAST</h2>
|
91 |
+
<h5>(Click on the cast to know more)</h5>
|
92 |
+
</center>
|
93 |
+
</div>
|
94 |
+
|
95 |
+
|
96 |
+
<div class="movie-content">
|
97 |
+
{% for name, details in casts.items() if not casts.hidden %}
|
98 |
+
<div class="castcard card" style="width: 14rem;" title="Click to know more about {{name}}" data-toggle="modal" data-target="#{{details[0]}}">
|
99 |
+
<div class="imghvr">
|
100 |
+
<img class="card-img-top cast-img" id="{{details[0]}}" height="360" width="240" alt="{{name}} - profile" src="{{details[2]}}">
|
101 |
+
<figcaption class="fig">
|
102 |
+
<button class="card-btn btn btn-danger"> Know More </button>
|
103 |
+
</figcaption>
|
104 |
+
</div>
|
105 |
+
<div class="card-body" style="font-family: 'Rowdies', cursive;font-size: 18px;">
|
106 |
+
<h5 class="card-title">{{name|upper}}</h5>
|
107 |
+
<h5 class="card-title" style="font-size: 18px"><span style="color:#756969;font-size: 18px;">AS {{details[1]|upper}}</span></h5>
|
108 |
+
</div>
|
109 |
+
</div>
|
110 |
+
{% endfor %}
|
111 |
+
</div>
|
112 |
+
{% endif %}
|
113 |
+
<br/>
|
114 |
+
|
115 |
+
<center>
|
116 |
+
{% if reviews %}
|
117 |
+
<h2 style="font-family: 'Rowdies', cursive;color:white">USER REVIEWS</h2>
|
118 |
+
<div class="col-md-12" style="margin: 0 auto; margin-top:25px;">
|
119 |
+
<table class="table table-bordered" bordercolor="white" style="color:white">
|
120 |
+
<thead>
|
121 |
+
<tr>
|
122 |
+
<th class="text-center" scope="col" style="width: 75%">Comments</th>
|
123 |
+
<th class="text-center" scope="col">Sentiments</th>
|
124 |
+
</tr>
|
125 |
+
</thead>
|
126 |
+
|
127 |
+
<tbody>
|
128 |
+
{% for review, status in reviews.items() if not reviews.hidden %}
|
129 |
+
<tr style="background-color:#e5091485;">
|
130 |
+
<td>{{review}}</td>
|
131 |
+
<td>
|
132 |
+
<center>
|
133 |
+
{{status}} :
|
134 |
+
{% if status =='Positive' %}
|
135 |
+
😃
|
136 |
+
{% else %}
|
137 |
+
😖
|
138 |
+
{% endif %}
|
139 |
+
</center>
|
140 |
+
</td>
|
141 |
+
</tr>
|
142 |
+
{% endfor %}
|
143 |
+
</tbody>
|
144 |
+
</table>
|
145 |
+
</div>
|
146 |
+
|
147 |
+
{% if (curr_date) and (movie_rel_date) %}
|
148 |
+
{% elif curr_date < movie_rel_date %}
|
149 |
+
<div style="color:white;">
|
150 |
+
<h1 style="color:white"> This movie is not released yet. Stay tuned! </h1>
|
151 |
+
</div>
|
152 |
+
{% else %}
|
153 |
+
<div style="color:white;">
|
154 |
+
<h1 style="color:white"> Sorry, the reviews for this movie are not available! :( </h1>
|
155 |
+
</div>
|
156 |
+
{% endif %}
|
157 |
+
{% else %}
|
158 |
+
<div style="color:white;">
|
159 |
+
<h1 style="color:white"> Sorry, the reviews for this movie are not available! :( </h1>
|
160 |
+
</div>
|
161 |
+
{% endif %}
|
162 |
+
</center>
|
163 |
+
<br/>
|
164 |
+
|
165 |
+
|
166 |
+
{% if movie_cards|length > 1 %}
|
167 |
+
|
168 |
+
<div class="movie" style="color: #E8E8E8;">
|
169 |
+
<center><h2 style="font-family: 'Rowdies', cursive;">RECOMMENDED MOVIES FOR YOU</h2><h5>(Click any of the movies to get recommendation)</h5></center>
|
170 |
+
</div>
|
171 |
+
|
172 |
+
<div class="movie-content">
|
173 |
+
{% for poster, details in movie_cards.items() if not movie_cards.hidden %}
|
174 |
+
<div class="card" style="width: 14rem;" title="{{details[1]}}" onclick="recommendcard(this)">
|
175 |
+
<div class="imghvr">
|
176 |
+
<img class="card-img-top" height="360" width="240" alt="{{details[0]}} - poster" src={{poster}}>
|
177 |
+
<div class="card-img-overlay" >
|
178 |
+
<span class="card-text" style="font-size:15px;background: #000000b8;color:white;padding:2px 5px;border-radius: 10px;"><span class="fa fa-star checked"> {{details[2]}}/10</span></span>
|
179 |
+
</div>
|
180 |
+
<div class=".card-img-overlay" style="position: relative;">
|
181 |
+
<span class="card-text" style="font-size:15px;position:absolute;bottom:20px;left:15px;background: #000000b8;color:white;padding: 5px;border-radius: 10px;">{{details[3]}}</span>
|
182 |
+
</div>
|
183 |
+
<figcaption class="fig">
|
184 |
+
<button class="card-btn btn btn-danger"> Click Me </button>
|
185 |
+
</figcaption>
|
186 |
+
</div>
|
187 |
+
<div class="card-body">
|
188 |
+
<h5 class="card-title" style="font-family: 'Rowdies', cursive;font-size: 17px;">{{details[0]|upper}}</h5>
|
189 |
+
</div>
|
190 |
+
</div>
|
191 |
+
{% endfor %}
|
192 |
+
</div>
|
193 |
+
{% endif %}
|
194 |
+
<br/><br/><br/><br/>
|
195 |
+
</div>
|
196 |
+
|
197 |
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
198 |
+
<script type="text/javascript" src="{{url_for('static', filename='recommend.js')}}"></script>
|
199 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
200 |
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
201 |
+
|
202 |
+
</body>
|
203 |
+
</html>
|
tranform.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:60040ccfcec04b8e9f6b763fad7166f688ae4451a9e13e058ee40193c1e3b628
|
3 |
+
size 58563
|