diff --git a/controller/models/models.py b/controller/models/models.py index 55612d0..87c95b1 100644 --- a/controller/models/models.py +++ b/controller/models/models.py @@ -1,6 +1,6 @@ from werkzeug.security import generate_password_hash, check_password_hash import db -from sqlalchemy import Column, Integer, String, Float, DateTime, Text +from sqlalchemy import Column, Integer, String, Float, DateTime, Text, Date from datetime import datetime class Wanderpi(db.Base): @@ -11,7 +11,7 @@ class Wanderpi(db.Base): long = Column(String(256), nullable=False) thumbnail_url = Column(String(256), nullable=False) created_date = Column(DateTime, default=datetime.utcnow) - + travel_id = Column(String(256), nullable=False) def __repr__(self): return f'' @@ -39,4 +39,55 @@ def get_by_id(id): @staticmethod def get_all(): - return db.session.query(Wanderpi).all() \ No newline at end of file + return db.session.query(Wanderpi).all() + + +class Travel(db.Base): + __tablename__ = 'travel' + id = Column(String(256), primary_key=True) + name = Column(String(256), nullable=False) + lat = Column(String(256), nullable=False) + long = Column(String(256), nullable=False) + destination = Column(String(256), nullable=False) + created_date = Column(DateTime, default=datetime.utcnow) + start_date = Column(Date) + end_date = Column(Date) + + + def __repr__(self): + return f'' + + def set_id(self, id): + self.id = id + + def set_name(self, id): + self.id = id + + def save(self): + print(self.start_date) + db.session.add(self) + db.session.commit() + + def delete_all_wanderpis(self): + video = db.session.query(Wanderpi).filter(Wanderpi.travel_id == self.id).all() + for v in video: + db.session.delete(v) + db.session.commit() + return True + + def get_all_wanderpis(self): + return db.session.query(Wanderpi).filter(Wanderpi.travel_id == self.id).all() + + @staticmethod + def delete(id): + db.session.query(Travel).filter(Travel.id == id).delete() + db.session.commit() + return True + + @staticmethod + def get_by_id(id): + return db.session.query(Travel).get(id) + + @staticmethod + def get_all(): + return db.session.query(Travel).all() \ No newline at end of file diff --git a/controller/modules/home/geocode_utils.py b/controller/modules/home/geocode_utils.py new file mode 100644 index 0000000..44643fe --- /dev/null +++ b/controller/modules/home/geocode_utils.py @@ -0,0 +1,27 @@ +import json +import urllib + +import geopy +from geopy.geocoders import Nominatim +from geopy.extra.rate_limiter import RateLimiter + +class GeoCodeUtils: + + @staticmethod + def reverse_latlong(lat,long): + """Function that having a lat and long translates it to a posiblea ddress" + """ + locator = Nominatim(user_agent="openmapquest") + coordinates = "{0}, {1}".format(lat, long) + location = locator.reverse(coordinates) + + return location.address + + @staticmethod + def reverse_adress(adress): + """Function that having an adress translates it to a lat and long values" + """ + locator = Nominatim(user_agent="openmapquest") + location = locator.geocode(adress) + print(location) + return location.latitude, location.longitude \ No newline at end of file diff --git a/controller/modules/home/views.py b/controller/modules/home/views.py index b483b03..dd59d0e 100644 --- a/controller/modules/home/views.py +++ b/controller/modules/home/views.py @@ -3,8 +3,14 @@ from flask import json, session, render_template, redirect, url_for, Response,request, jsonify,send_from_directory from controller.modules.home import home_blu from controller.utils.camera import VideoCamera -from controller.models.models import Wanderpi +from controller.models.models import Wanderpi, Travel +from controller.modules.home.geocode_utils import GeoCodeUtils +from controller.utils.video_editor import VideoEditor + +from datetime import * +import os +import uuid video_camera_ids = [{"deviceId" : 0, "deviceLabel" : "Webcam"}, {"deviceId" : 1, "deviceLabel" : "Webcam1"}] @@ -19,8 +25,43 @@ def index(): session["initialized"] = False return redirect(url_for("user.login")) - wanderpis = Wanderpi.get_all() - return render_template("index.html", wanderpis=wanderpis) + travels = Travel.get_all() + + return render_template("index.html", travels=travels) + +@home_blu.route('/travel/') +def travel(travel_id): + print(travel_id) + username = session.get("username") + if not username: + session["initialized"] = False + return redirect(url_for("user.login")) + + travel = Travel.get_by_id(travel_id) + wanderpis = travel.get_all_wanderpis() + + return render_template("travel_view.html", wanderpis=wanderpis, travel=travel) + +@home_blu.route('/global_map/') +def global_map(travel_id): + # 模板渲染 + username = session.get("username") + if not username: + session["initialized"] = False + return redirect(url_for("user.login")) + + print(travel_id) + travel = Travel.get_by_id(travel_id) + wanderpis = travel.get_all_wanderpis() + return render_template("global_map.html", wanderpis=wanderpis, travel=travel) + +@home_blu.route('/delete_travel/') +def delete_travel(id): + print(id) + travel = Travel.get_by_id(id) + travel.delete_all_wanderpis() + Travel.delete(id) + return redirect("/", code=302) @home_blu.route('/delete_video/') def delete_video(id): @@ -28,10 +69,8 @@ def delete_video(id): Wanderpi.delete(id) return redirect("/", code=302) - -@home_blu.route('/') +@home_blu.route('/video/') def single_video(id): - # 模板渲染 username = session.get("username") if not username: session["initialized"] = False @@ -40,15 +79,16 @@ def single_video(id): wanderpi = Wanderpi.get_by_id(id) return render_template("single-video-view.html", video=wanderpi) -# 主页 -@home_blu.route('/record') -def record(): +@home_blu.route('/record/') +def record(travel_id): # 模板渲染 username = session.get("username") if not username: session["initialized"] = False return redirect(url_for("user.login")) - return render_template("record.html") + + travel = Travel.get_by_id(travel_id) + return render_template("record.html", travel=travel) def init_camera(camera_id): global video_camera @@ -68,9 +108,7 @@ def init_camera(camera_id): # video_camera.stop_record() return video_camera - -# 获取视频流 def video_stream(camera_id): global video_camera global global_frame @@ -88,18 +126,14 @@ def video_stream(camera_id): yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + global_frame + b'\r\n\r\n') - -# 视频流 @home_blu.route('/video_feed//') def video_feed(camera_id): - # 模板渲染 username = session.get("username") if not username: return redirect(url_for("user.login")) return Response(video_stream(camera_id), mimetype='multipart/x-mixed-replace; boundary=frame') -# 录制状态 @home_blu.route('/record_status', methods=['POST']) def record_status(): global video_camera @@ -109,22 +143,39 @@ def record_status(): json = request.get_json() + print(json) + status = json['status'] if status == "true": print("Start recording...") + video_id_1 = request.args.get('video_id', default=None) + + if video_id_1: + print("Deleting video that was not saved on database with id: {0}".format(video_id_1)) + p = VIDEOS_FOLDER + "/" + video_id_1 + ".mp4" + if os.path.exists(p): + os.remove(p) + video_id = video_camera.start_record() return jsonify(result="started", video_id=video_id) else: print("Stop recording...") + lat = json['lat'] + lng = json['long'] + video_id = video_camera.stop_record() - return jsonify(result="stopped", video_id=video_id) + + video_name = str(datetime.now()) + " at " + GeoCodeUtils.reverse_latlong(lat, lng) + + VideoEditor.AddTitleToVideo(video_camera.recordingThread.path_rel, video_name) + + return jsonify(result="stopped", video_id=video_id, video_name=video_name) @home_blu.route('/uploads/', methods=['GET', 'POST']) def download_file(filename): return send_from_directory(VIDEOS_FOLDER, filename, as_attachment=True) - @home_blu.route('/get_available_video_sources', methods=['GET', 'POST']) def get_available_video_sources(): #todo get available video sources from database return jsonify(devices = video_camera_ids) @@ -138,13 +189,51 @@ def save_video(video_id): #todo get available video sources from database name = video_id lat_coord = request.args.get('lat') long_coord = request.args.get('long') + travel_id = request.args.get('travel_id') thumbnail_url = "thumbnail-%s.jpg" % str(video_id) print(name, lat_coord, long_coord, thumbnail_url) - wanderpi = Wanderpi(id=video_id, name=name, lat=lat_coord, long=long_coord, thumbnail_url=thumbnail_url) + wanderpi = Wanderpi(id=video_id, name=name, lat=lat_coord, long=long_coord, thumbnail_url=thumbnail_url, travel_id=travel_id) wanderpi.save() - return jsonify(devices = video_camera_ids) + return jsonify(status_code = 200, message = "OK") +def toDate(dateString): + print(dateString) + return datetime.strptime(dateString, "%Y-%m-%dT%H:%M") #example datetime input 2021-07-01T13:45 +@home_blu.route('/save_travel/', methods=['GET', 'POST']) +def save_travel(): #todo get available video sources from database + print("Saving travel") + + name = request.args.get('name') + destination = request.args.get('destination') + start_date = request.args.get('start_date', type=toDate) + end_date = request.args.get('end_date', type=toDate) + travel_id = str(uuid.uuid4()) + + travel = Travel(id=travel_id, name=name, lat="0", long="0", destination=destination, start_date=start_date, end_date=end_date) + travel.save() + + return jsonify(status_code = 200, message = "OK") + +@home_blu.route('/latlong/', methods=['GET', 'POST']) +def latlong(adress): + travel_id = request.args.get('travel_id') + if travel_id: + travel = Travel.get_by_id(travel_id) + print(travel.lat, travel.long) + if travel.lat != "0" and travel.long != "0": + print("Found travel with id {0} and cached lat and long".format(travel_id)) + return jsonify(lat=travel.lat, long=travel.long, status_code = 200, message = "OK") + else: + print("Travel with id {0} found in database but lat and long is not cached".format(travel_id)) + lat, lng = GeoCodeUtils.reverse_adress(adress) + travel.lat = lat + travel.long = lng + travel.save() + return jsonify(lat=lat, long=lng, status_code = 200, message = "OK") + else: + print("No travel id found") + return jsonify(status_code = 400, message = "No travel id found") diff --git a/controller/static/css/index.css b/controller/static/css/index.css index 1e5bdae..cd7f187 100644 --- a/controller/static/css/index.css +++ b/controller/static/css/index.css @@ -12,3 +12,17 @@ body { width: 200px; } +@media (max-width: 768px) { + .btn-responsive { + width: 30px; + height: 30px; + } +} + +@media (min-width: 769px) and (max-width: 992px) { + .btn-responsive { + width: 60px; + height: 60px; + } +} + diff --git a/controller/static/css/recording-screen-style.css b/controller/static/css/recording-screen-style.css index 2415a42..6ac56e9 100644 --- a/controller/static/css/recording-screen-style.css +++ b/controller/static/css/recording-screen-style.css @@ -5,7 +5,7 @@ } -@media (max-width: 768px) { +/* @media (max-width: 768px) { .btn-responsive { padding:2px 4px; font-size:80%; @@ -21,7 +21,7 @@ line-height: 1.2; } } - + */ .map-responsive{ diff --git a/controller/static/images/add-more-icon-circle.svg b/controller/static/images/add-more-icon-circle.svg new file mode 100644 index 0000000..842a2e4 --- /dev/null +++ b/controller/static/images/add-more-icon-circle.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/controller/static/images/add-more-icon-squared.png b/controller/static/images/add-more-icon-squared.png new file mode 100644 index 0000000..48626c9 Binary files /dev/null and b/controller/static/images/add-more-icon-squared.png differ diff --git a/controller/static/images/add-more-icon.svg b/controller/static/images/add-more-icon.svg index 842a2e4..4475476 100644 --- a/controller/static/images/add-more-icon.svg +++ b/controller/static/images/add-more-icon.svg @@ -1,22 +1 @@ - - - - - - - - - + \ No newline at end of file diff --git a/controller/static/js/auto-complete.js b/controller/static/js/auto-complete.js new file mode 100644 index 0000000..b858469 --- /dev/null +++ b/controller/static/js/auto-complete.js @@ -0,0 +1,6 @@ +var autocomplete = new kt.OsmNamesAutocomplete( + 'search', 'https://api.yourdomain.com/'); + +autocomplete.registerCallback(function(item) { + alert(JSON.stringify(item, ' ', 2)); +}); \ No newline at end of file diff --git a/controller/static/js/global_map.js b/controller/static/js/global_map.js new file mode 100644 index 0000000..98d61f6 --- /dev/null +++ b/controller/static/js/global_map.js @@ -0,0 +1,67 @@ + +var list_of_points = []; +var map; +var map_initiated = false; +var markers; +var gps = document.getElementById("gps_text"); + +function init_map() +{ + map = L.map('map-container-global'); + + googleStreets = L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{ + maxZoom: 20, + subdomains:['mt0','mt1','mt2','mt3'] + }).addTo(map); + + markers = new L.MarkerClusterGroup({ + spiderfyOnMaxZoom: false, + showCoverageOnHover: false, + zoomToBoundsOnClick: false + }); + + + map_initiated = true; +} + +//function that checks if a point is already on the map +function is_point_on_map(lat, long) { + for (var i = 0; i < list_of_points.length; i++) { + console.log(lat,list_of_points[i].lat, long, list_of_points[i].lng); + if (list_of_points[i].lat == lat && list_of_points[i].lng == long) { + return true; + } + } + return false; +} + + +function plot_map(lat, long, name, thumbnail_path, id) { + if (!map_initiated) { + init_map(); + } + + map.setView([lat,long], 13); + + var a = "/static/thumbnails/"+ thumbnail_path; + + var marker_html = ` ${name} `; + + var does_exist = is_point_on_map(lat, long); + + if(does_exist) + { + marker = L.marker([lat, long]).bindPopup(marker_html).openPopup(); + + markers.on('clusterclick', function (a) { + a.layer.zoomToBounds(); + }); + + markers.addLayer(marker).addTo(map); + }else + { + var marker = L.marker([lat, long]).addTo(map).bindPopup(marker_html).openPopup(); + } + + list_of_points.push(new L.LatLng(lat, long)); +} \ No newline at end of file diff --git a/controller/static/js/index.js b/controller/static/js/index.js index d45a7e6..c5fe012 100644 --- a/controller/static/js/index.js +++ b/controller/static/js/index.js @@ -1,6 +1,5 @@ -function deleteVideo(id){ - console.log(id); -} +var saveButtonModal = document.getElementById("save_request_button"); +var saveWanderpiModal = document.getElementById('saveTravelModal') function shareVideo(id){ @@ -15,4 +14,100 @@ function shareVideo(id){ } else { // fallback } -} \ No newline at end of file +} + +saveButtonModal.onclick = function() +{ + //get name, destionation, description, start time, end time, and duration from modal + var name = document.getElementById("name_input").value; + var destination = document.getElementById("destination_input").value; + var startTime = document.getElementById("start_date_input").value; + var endTime = document.getElementById("end_date_input").value; + + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + console.log(xhr.responseText); + + //parse json response and see if status code is 200 + var response = JSON.parse(xhr.responseText); + if (response.status_code == 200) { + window.location.href = "/"; + } + } + } + + var base_url = window.location.origin; + var url = new URL(base_url+"/save_travel/"); + + url.searchParams.append('name', name); + url.searchParams.append('destination', destination); + url.searchParams.append('start_date', startTime); + url.searchParams.append('end_date', endTime); + + xhr.open("POST", url.toString()); + xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xhr.send(); +}; + + +//function that inits leaflet map but shows text that says start recording to show map +//https://gis.stackexchange.com/questions/53394/select-two-markers-draw-line-between-them-in-leaflet +function initializeMapAndLocator(travel_id, travel_destination) +{ + var pathCoords = []; + var map = L.map(`map-container-google-${travel_id}`); + + googleStreets = L.tileLayer('http://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{ + maxZoom: 20, + subdomains:['mt0','mt1','mt2','mt3'] + }).addTo(map); + + + function onLocationFound(e) + { + pathCoords.push(e.latlng); + + var pathLine = L.polyline(pathCoords, {color: 'red'}).addTo(map); + + map.fitBounds(pathLine.getBounds()); + } + + map.on('locationfound', onLocationFound); + + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + var response = JSON.parse(xhr.responseText); + + if (response.status_code == 200) { + var lat = response.lat; + var long = response.long; + + console.log(lat, long); + + map.locate({setView: true, + maxZoom: 10, + watch:true, + enableHighAccuracy: true + }); + + pathCoords.push(new L.LatLng(lat, long)); + } + else + { + console.log(response.status_code); + } + } + } + + var base_url = window.location.origin; + var url = new URL(base_url+"/latlong/"+ travel_destination); + url.searchParams.append('travel_id', travel_id); + + xhr.open("POST", url.toString()); + xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xhr.send(); + +} + \ No newline at end of file diff --git a/controller/static/js/recorder.js b/controller/static/js/recorder.js index 3a230a1..db4eb7a 100644 --- a/controller/static/js/recorder.js +++ b/controller/static/js/recorder.js @@ -9,6 +9,8 @@ var saveButtonModal = document.getElementById("save_request_button"); var gps = document.getElementById("gps_text"); var map = L.map('map-container-google-1'); +var video_name = ""; +var travel_id = ""; saveButton.style.display = "none"; downloadLink.style.display = "none"; @@ -17,6 +19,7 @@ statusBadgeContainer.style.display = "none"; var lat = 0; var long = 0; +var video_id = 0; function initializeDropdown() { @@ -68,7 +71,16 @@ buttonRecord.onclick = function () { statusBadgeContainer.style.display = "block"; statusBadge.innerHTML = "Recording..."; - xhr.open("POST", "/record_status"); + var url = "/record_status"; + + console.log(video_id); + + if (video_id != 0) + { + url = "/record_status?video_id="+video_id; + } + + xhr.open("POST", url); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.send(JSON.stringify({status: "true"})); @@ -81,7 +93,10 @@ saveButtonModal.onclick = function () { xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); - window.location.href = "/"; + var response = JSON.parse(xhr.responseText); + if (response.status_code == 200) { + window.location.href = "/"; + } } } @@ -93,7 +108,7 @@ saveButtonModal.onclick = function () { url.searchParams.append('name', name_input.value); url.searchParams.append('lat', lat); url.searchParams.append('long', long); - + url.searchParams.append('travel_id', travel_id); xhr.open("POST", url.toString()); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); @@ -110,8 +125,10 @@ buttonStop.onclick = function () { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); obj = JSON.parse(xhr.responseText); - var video_id = obj.video_id; + video_id = obj.video_id; console.log(video_id); + video_name = obj.video_name; + gps.innerHTML = video_name; saveButton.style.display = "inline"; saveButton.value = video_id; @@ -130,10 +147,33 @@ buttonStop.onclick = function () { xhr.open("POST", "/record_status"); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); - xhr.send(JSON.stringify({status: "false"})); -}; + xhr.send(JSON.stringify({status: "false", lat: lat, long: long})); + setTimeout(function () { + statusBadgeContainer.style.display = "none"; + }, 2000); +}; +var saveWanderpiModal = document.getElementById('saveWanderpiModal') +saveWanderpiModal.addEventListener('show.bs.modal', function (event) { + // Button that triggered the modal + // var button = event.relatedTarget + // Extract info from data-bs-* attributes + var button = event.relatedTarget + // Extract info from data-bs-* attributes + travel_id = button.getAttribute('data-bs-whatever') + + var recipient = video_name; + // If necessary, you could initiate an AJAX request here + // and then do the updating in a callback. + // + // Update the modal's content. + //var modalTitle = exampleModal.querySelector('.modal-title') + var modalBodyInput = saveWanderpiModal.querySelector('.modal-body input') + + //modalTitle.textContent = 'New message to ' + recipient + modalBodyInput.value = recipient +}) //function that inits leaflet map but shows text that says start recording to show map function initializeMapAndLocator() diff --git a/controller/templates/global_map.html b/controller/templates/global_map.html new file mode 100644 index 0000000..8857915 --- /dev/null +++ b/controller/templates/global_map.html @@ -0,0 +1,88 @@ + + + + + + + + Wanderpi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+ +
+ + + {% block content %} + {% for item in wanderpis %} + + {% endfor %} + {% endblock %} +
+
+
+
+ +
+ + + + + diff --git a/controller/templates/index.html b/controller/templates/index.html index 3ebe6b1..03b503a 100644 --- a/controller/templates/index.html +++ b/controller/templates/index.html @@ -15,11 +15,22 @@ - + + + + + + + + + - - + @@ -27,35 +38,80 @@ - - - - - + + @@ -64,22 +120,39 @@
{% block content %} - {% if wanderpis|length > 0 %} + {% if travels|length > 0 %}
- {% for post in wanderpis %} + {% for post in travels %}
- +
- - -
{{ post.name }}
+
+ +
{{ post.name }}
+ +
+
+ {{ post.destination }} +
+ +
+ {{ post.start_date }} - {{ post.end_date }} +
+
+ + {{ post.created_date }} + + +
@@ -89,27 +162,20 @@
{{ post.name }}
- -
New Wanderpi
+ +
New Travel
{% else %} - +
- -
New Wanderpi
+ +
New Travel
@@ -119,6 +185,12 @@
New Wanderpi
+
+
+ +
+
+ diff --git a/controller/templates/record.html b/controller/templates/record.html index 18f258f..3ce470c 100644 --- a/controller/templates/record.html +++ b/controller/templates/record.html @@ -59,64 +59,59 @@ -
-
+
+
+ +
+
-
+
-
-
+
+
-
+
- +
-
diff --git a/controller/templates/travel_view.html b/controller/templates/travel_view.html new file mode 100644 index 0000000..2516968 --- /dev/null +++ b/controller/templates/travel_view.html @@ -0,0 +1,131 @@ + + + + + + + + Wanderpi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + {% block content %} + {% if wanderpis|length > 0 %} +
+ {% for post in wanderpis %} +
+
+ +
+ + +
{{ post.name }}
+ + +
+
+
+ {% endfor %} + +
+
+ +
+ +
New Wanderpi
+
+
+
+
+ {% else %} +
+
+ +
+ +
New Wanderpi
+
+
+
+ {% endif %} + {% endblock %} + +
+
+ + + + + diff --git a/controller/utils/camera.py b/controller/utils/camera.py index 01d0c89..15a5c42 100644 --- a/controller/utils/camera.py +++ b/controller/utils/camera.py @@ -12,6 +12,8 @@ def __init__(self, name, camera): fourcc = cv2.VideoWriter_fourcc(*'MP4V') self.video_id = str(uuid.uuid4()) self.path = './controller/static/videos/' + str(self.video_id) + '.mp4' + self.path_rel = '/controller/static/videos/' + str(self.video_id) + '.mp4' + self.out = cv2.VideoWriter(self.path, fourcc, 20.0, (640, 480)) self.latest_frame = None @@ -74,4 +76,6 @@ def stop_record(self): thumbnail_url = "./controller/static/thumbnails/thumbnail-%s.jpg" % str(self.recordingThread.video_id) cv2.imwrite(thumbnail_url, self.recordingThread.latest_frame) - return self.recordingThread.video_id \ No newline at end of file + return self.recordingThread.video_id + + \ No newline at end of file diff --git a/controller/utils/video_editor.py b/controller/utils/video_editor.py new file mode 100644 index 0000000..99d9a6d --- /dev/null +++ b/controller/utils/video_editor.py @@ -0,0 +1,21 @@ +from moviepy import * +from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip + +import os +class VideoEditor: + + @staticmethod + def AddTitleToVideo(video_path, title="My Holidays 2013"): + """ + Add title to video + """ + #video_path = os.path.abspath(video_path) + # video = VideoFileClip(video_path) + + # # Make the text. Many more options are available. + # txt_clip = ( TextClip(title,fontsize=70,color='white') + # .with_position('center') + # .with_duration(10) ) + + # result = CompositeVideoClip([video, txt_clip]) # Overlay text on video + # result.write_videofile(video_path, fps=25) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 09558ae..b8937ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ flask opencv-python flask-sqlalchemy -psycopg2 \ No newline at end of file +psycopg2 +moviepy +geopy