├── Database.db ├── LICENSE ├── README.md ├── app.py ├── insomnia-api-testing-file └── Insomnia_2022-08-04.json ├── requirements.txt ├── static ├── css │ ├── main.css │ ├── showimg.css │ └── takepictures.css └── js │ ├── home.js │ └── takepictures.js └── templates ├── base.html ├── getallpictures.html ├── home.html ├── showimage.html └── takepictures.html /Database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-internetix/flask-api-with-auth-webrtc/4d5f31abec1ae3ea46256c3a01723feaa295533d/Database.db -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ajit Yadav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flask-api-with-auth-webrtc -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # Imports necessary libraries 2 | from flask import Flask, render_template, request, redirect, jsonify, make_response 3 | from flask_limiter import Limiter 4 | from flask_limiter.util import get_remote_address 5 | from werkzeug.utils import secure_filename 6 | from werkzeug.security import generate_password_hash, check_password_hash 7 | import os 8 | import json 9 | import uuid 10 | from datetime import datetime, timedelta 11 | from functools import wraps 12 | from flask_sqlalchemy import SQLAlchemy 13 | # jwt imports 14 | import jwt 15 | 16 | # Define the app 17 | app = Flask(__name__) 18 | 19 | 20 | # secret key 21 | app.config['SECRET_KEY'] = 'Internetix' #didn't used env because of convinience 22 | 23 | 24 | # database name 25 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///Database.db' 26 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True 27 | 28 | # intializing sqlalchemy 29 | db = SQLAlchemy(app) 30 | 31 | # databse model 32 | 33 | 34 | class User(db.Model): 35 | id = db.Column(db.Integer, primary_key=True) 36 | public_id = db.Column(db.String(50), unique=True) 37 | name = db.Column(db.String(100)) 38 | email = db.Column(db.String(70), unique=True) 39 | password = db.Column(db.String(80)) 40 | 41 | # limiter configs 42 | 43 | 44 | limiter = Limiter(app, key_func=get_remote_address, 45 | default_limits=['5 per minute']) 46 | 47 | 48 | # Jwt decorator 49 | 50 | def token_required(f): 51 | @wraps(f) 52 | def decorated(*args, **kwargs): 53 | token = None 54 | # cheking token exist or not 55 | 56 | if 'x-access-token' in request.headers: 57 | token = request.headers['x-access-token'] 58 | 59 | if not token: 60 | return jsonify({'message': 'please provide token'}), 401 61 | 62 | try: 63 | # decoding token 64 | data = jwt.decode(token, app.config['SECRET_KEY'],algorithms=["HS256"]) 65 | current_user = User.query.filter_by( 66 | public_id=data['public_id']).first() 67 | 68 | except Exception as e: 69 | return jsonify({ 70 | 'message': 'Token invalid', 71 | 'error': e 72 | }), 401 73 | 74 | return f(current_user, *args, **kwargs) 75 | 76 | return decorated 77 | 78 | 79 | @app.get('/user') 80 | @token_required 81 | def get_all_users(current_user): 82 | # querying the database 83 | # for all the entries in it 84 | users = User.query.all() 85 | 86 | # converting the query objects 87 | # to list of jsons 88 | output = [] 89 | for user in users: 90 | # appending the user data json 91 | # to the response list 92 | output.append({ 93 | 'public_id': user.public_id, 94 | 'name': user.name, 95 | 'email': user.email 96 | }) 97 | 98 | return jsonify({'users': output}) 99 | 100 | 101 | @app.post('/login') 102 | def login(): 103 | # creates dictionary of form data 104 | auth = request.form 105 | print(auth) 106 | 107 | if not auth or not auth.get('email') or not auth.get('password'): 108 | # returns 401 if any email or / and password is missing 109 | return make_response( 110 | 'Could not verify', 111 | 401, 112 | {'WWW-Authenticate': 'Basic realm ="Login required !!"'} 113 | ) 114 | 115 | user = User.query\ 116 | .filter_by(email=auth.get('email'))\ 117 | .first() 118 | 119 | if not user: 120 | # returns 401 if user does not exist 121 | return make_response( 122 | 'Could not verify', 123 | 401, 124 | {'WWW-Authenticate': 'Basic realm ="User does not exist !!"'} 125 | ) 126 | 127 | if check_password_hash(user.password, auth.get('password')): 128 | # generates the JWT Token 129 | token = jwt.encode({ 130 | 'public_id': user.public_id, 131 | 'exp': datetime.utcnow() + timedelta(minutes=30) 132 | }, app.config['SECRET_KEY']) 133 | 134 | return make_response(jsonify({'token': token}), 201) 135 | # returns 403 if password is wrong 136 | return make_response( 137 | 'Could not verify', 138 | 403, 139 | {'WWW-Authenticate': 'Basic realm ="Wrong Password !!"'} 140 | ) 141 | 142 | 143 | # signup route 144 | @app.post('/signup') 145 | def signup(): 146 | # creates a dictionary of the form data 147 | data = request.form 148 | # gets name, email and password 149 | name, email = data.get('name'), data.get('email') 150 | password = data.get('password') 151 | 152 | # checking for existing user 153 | user = User.query\ 154 | .filter_by(email=email)\ 155 | .first() 156 | if not user: 157 | # database ORM object 158 | user = User( 159 | public_id=str(uuid.uuid4()), 160 | name=name, 161 | email=email, 162 | password=generate_password_hash(password) 163 | ) 164 | # insert user 165 | db.session.add(user) 166 | db.session.commit() 167 | 168 | return make_response('Successfully registered.', 201) 169 | else: 170 | # returns 202 if user already exists 171 | return make_response('User already exists. Please Log in.', 202) 172 | 173 | 174 | # Get a welcoming message once you start the server. 175 | @app.route('/') 176 | def home(): 177 | return render_template('home.html') 178 | 179 | @app.route('/takepictures') 180 | def takepictures(): 181 | return render_template('takepictures.html') 182 | 183 | 184 | @app.route('/showimage/') 185 | def showimage(filename): 186 | # return render_template('showimage.html') 187 | return f"FILE NAME : {filename} " 188 | 189 | 190 | @app.post('/upload') 191 | @limiter.limit("5/minute") 192 | def upload_file(): 193 | try: 194 | if(request.files['my_file']): 195 | try: 196 | file = request.files['my_file'] 197 | upload_directory = f"{os.getcwd()}/uploads" 198 | filepath = os.path.exists(upload_directory) 199 | if not filepath: 200 | os.mkdir(upload_directory) 201 | file.save( 202 | f"{upload_directory}/{secure_filename(file.filename)}") 203 | else: 204 | # return f"{url :/showimage/{file.filename}'}" 205 | file.save( 206 | f"{upload_directory}/{secure_filename(file.filename)}") 207 | 208 | return json.dumps({"url": f"/showimage/{file.filename}"}) 209 | except Exception as e: 210 | return json.dumps("{message: something went wrong}") 211 | else: 212 | return json.dumps("{message:provide a file}") 213 | 214 | except Exception as e: 215 | return json.dumps(f'{e}, Provide a file') 216 | 217 | @app.post('/uploadWithAuth') 218 | @token_required 219 | @limiter.limit("5/minute") 220 | def upload_file_with_auth(current_user): 221 | try: 222 | if(request.files['my_file']): 223 | try: 224 | file = request.files['my_file'] 225 | upload_directory = f"{os.getcwd()}/uploads" 226 | filepath = os.path.exists(upload_directory) 227 | if not filepath: 228 | os.mkdir(upload_directory) 229 | file.save( 230 | f"{upload_directory}/{secure_filename(file.filename)}") 231 | else: 232 | # return f"{url :/showimage/{file.filename}'}" 233 | file.save( 234 | f"{upload_directory}/{secure_filename(file.filename)}") 235 | 236 | return json.dumps({"url": f"/showimage/{file.filename}"}) 237 | except Exception as e: 238 | return json.dumps("{message: something went wrong}") 239 | else: 240 | return json.dumps("{message:provide a file}") 241 | 242 | except Exception as e: 243 | return json.dumps(f'{e}, Provide a file') 244 | 245 | 246 | @app.post('/capture') 247 | def capture(): 248 | file = request.files['capture_img'] 249 | if(file): 250 | upload_directory = f"{os.getcwd()}/static/imguploads" 251 | filepath = os.path.exists(upload_directory) 252 | if not filepath: 253 | os.mkdir(upload_directory) 254 | file.save(f"{upload_directory}/{secure_filename(file.filename)}") 255 | else: 256 | file.save(f"{upload_directory}/{secure_filename(file.filename)}") 257 | return jsonify({"message":"image uploaded"}) 258 | 259 | 260 | @app.get('/showallpictures') 261 | def showallpictures(): 262 | list_of_images = os.listdir('static/imguploads') 263 | url_list = [] 264 | for images in list_of_images: 265 | url = f"imguploads/{images}" 266 | url_list.append(url) 267 | 268 | return render_template('getallpictures.html',src=url_list) 269 | 270 | # If the file is run directly,start the app. 271 | if __name__ == '__main__': 272 | app.run() 273 | -------------------------------------------------------------------------------- /insomnia-api-testing-file/Insomnia_2022-08-04.json: -------------------------------------------------------------------------------- 1 | {"_type":"export","__export_format":4,"__export_date":"2022-08-04T13:04:24.883Z","__export_source":"insomnia.desktop.app:v2022.2.1","resources":[{"_id":"req_85067ab0b13747498156af10f3fc299f","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659617250466,"created":1659617239274,"url":"http://localhost:5000/takepictures","name":"/takepictures","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1659617239274,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","parentId":null,"modified":1659413520277,"created":1659413520277,"name":"backend","description":"","scope":"design","_type":"workspace"},{"_id":"req_507df24398874519b3377d337cf94cec","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659617193677,"created":1659617175636,"url":"http://localhost:5000/showallpictures","name":"/showallpictures","description":"","method":"GET","body":{},"parameters":[],"headers":[],"authentication":{},"metaSortKey":-1659617175636,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_a6fc403683c64c75a239e86b7d8b8484","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659617519365,"created":1659617053716,"url":"http://localhost:5000/uploadWithAuth","name":"upload-with-auth","description":"","method":"POST","body":{"mimeType":"multipart/form-data","params":[{"id":"pair_009bfdacd1854e49bf81aebf19398ed8","name":"my_file","value":"","description":"","type":"file","fileName":"/home/charon/JavaScript30/11 - Custom Video Player/index.html"}]},"parameters":[],"headers":[{"name":"Content-Type","value":"multipart/form-data","id":"pair_92b43ac6b027492890b367a21f890c01"},{"id":"pair_06d06e4a9d2f4602847720f5d398fcbd","name":"x-access-token","value":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwdWJsaWNfaWQiOiI4MGExM2E1ZC0xMDRhLTQwMzEtOGUyZS00YWFmNGYzOWI2Y2IiLCJleHAiOjE2NTk2MTg4MjV9.0gs48YV2OMnzRmTiqRs4UvzQLwbMud1VqPhUexGLUY8","description":""}],"authentication":{},"metaSortKey":-1659617053716,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_ad06c78e6ec545a4b4878cbbe72101e7","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659617135089,"created":1659511223216,"url":"http://localhost:5000/upload","name":"/upload-without-auth","description":"","method":"POST","body":{"mimeType":"multipart/form-data","params":[{"id":"pair_98ed974bcbc746eea9f4cace9342bc84","name":"my_file","value":"","description":"","type":"file","fileName":"/home/charon/GitHub/Freelance_works/HotProducts.csv"}]},"parameters":[],"headers":[{"name":"Content-Type","value":"multipart/form-data","id":"pair_32b9c0cc1101453e961577a482009986"}],"authentication":{},"metaSortKey":-1659511223216,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_1d57b418513e4e25a31dc7ae4c5ec781","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659511086821,"created":1659509372589,"url":"http://localhost:5000/signup","name":"/signup","description":"","method":"POST","body":{"mimeType":"multipart/form-data","params":[{"id":"pair_1875bea05cc042b48845d5fafe765ce6","name":"email","value":"someone@gmail.com","description":""},{"id":"pair_9aed0784d8154dc3906cf735507a672d","name":"name","value":"neon","description":""},{"id":"pair_7342f74093b34937bb315b0af4bc38d3","name":"password","value":"@laddin@123","description":""}]},"parameters":[],"headers":[{"name":"Content-Type","value":"multipart/form-data","id":"pair_cd5bc550b2aa40c9a839545f8efdb30f"}],"authentication":{},"metaSortKey":-1659509372589,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_3933de2776044b8f891ba2f40c1fb943","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659617033162,"created":1659509320150,"url":"http://localhost:5000/login","name":"/login","description":"","method":"POST","body":{"mimeType":"multipart/form-data","params":[{"id":"pair_1b84d06a30b84db3bddacb29b62de496","name":"email","value":"someone@gmail.com","description":""},{"id":"pair_f48f1f40e2b74e3eae6d4e331ed2979c","name":"password","value":"@laddin@123","description":""}]},"parameters":[],"headers":[{"name":"Content-Type","value":"multipart/form-data","id":"pair_1180a4d3a0144408a159826265c5578e"}],"authentication":{},"metaSortKey":-1659509320150,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_155b3604daf34f9a95367aa97c015750","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659511202624,"created":1659413546258,"url":"localhost:5000/user","name":"/user ","description":"","method":"GET","body":{},"parameters":[],"headers":[{"id":"pair_fc0e499795f6459c998580bb805e9c72","name":"x-access-token","value":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwdWJsaWNfaWQiOiI4MGExM2E1ZC0xMDRhLTQwMzEtOGUyZS00YWFmNGYzOWI2Y2IiLCJleHAiOjE2NTk1MTI5OTB9.7Vwp58SlEzpcUam6P0wYWK_t9hg4lUfcaw68sAS-XXw","description":""},{"id":"pair_0dc4ae941dcd4dcd8541e0131cffce1c","name":"","value":"","description":""}],"authentication":{},"metaSortKey":-1659413546258,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_df83333b4143f258631e2b748d604df5e5d9ced9","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659413520294,"created":1659413520294,"name":"Base Environment","data":{},"dataPropertyOrder":null,"color":null,"isPrivate":false,"metaSortKey":1659413520294,"_type":"environment"},{"_id":"jar_df83333b4143f258631e2b748d604df5e5d9ced9","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659413520296,"created":1659413520296,"name":"Default Jar","cookies":[],"_type":"cookie_jar"},{"_id":"spc_cb93d78e21674d56862d2fd16becfbe9","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659413520279,"created":1659413520279,"fileName":"backend","contents":"","contentType":"yaml","_type":"api_spec"},{"_id":"uts_9f584fa3a12e410bbb87b67f96c7eb98","parentId":"wrk_dca1010eabd54c5db73fa24ecc87eb1b","modified":1659512342454,"created":1659512342454,"name":"backedn","_type":"unit_test_suite"}]} -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cffi==1.15.1 2 | click==8.1.3 3 | commonmark==0.9.1 4 | cryptography==37.0.4 5 | Deprecated==1.2.13 6 | Flask==2.1.3 7 | Flask-Limiter==2.5.0 8 | flask-localtunnel==1.0.5 9 | Flask-SQLAlchemy==2.5.1 10 | greenlet==1.1.2 11 | gunicorn==20.1.0 12 | itsdangerous==2.1.2 13 | Jinja2==3.1.2 14 | limits==2.7.0 15 | MarkupSafe==2.1.1 16 | packaging==21.3 17 | pycparser==2.21 18 | Pygments==2.12.0 19 | PyJWT==2.4.0 20 | pyparsing==3.0.9 21 | rich==12.5.1 22 | SQLAlchemy==1.4.39 23 | typing_extensions==4.3.0 24 | uuid==1.30 25 | Werkzeug==2.2.1 26 | wrapt==1.14.1 27 | PyJWT==2.4.0 28 | -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Uchen&display=swap'); 2 | *{ 3 | margin:0; 4 | padding: 0; 5 | } 6 | 7 | body{ 8 | background: rgb(188, 232, 247); 9 | font-family: Georgia, 'Times New Roman', Times, serif; 10 | font-family: 'Uchen', serif; 11 | width: 100vw; 12 | height: 100vh; 13 | } 14 | 15 | .getallpic{ 16 | max-width: 300px; 17 | max-height: 200px; 18 | } 19 | 20 | Button { 21 | background-color: #4CAF50; /* Green */ 22 | border: none; 23 | color: white; 24 | padding: 15px 32px; 25 | text-align: center; 26 | text-decoration: none; 27 | display: inline-block; 28 | font-size: 16px; 29 | cursor: pointer; 30 | } 31 | 32 | Button:hover{ 33 | background-color: black; 34 | 35 | 36 | } 37 | 38 | label{ 39 | font-size: medium; 40 | } 41 | 42 | input[type=button], input[type=submit], input[type=file],#file-upload-button { 43 | background-color: #04AA6D; 44 | border: none; 45 | color: white; 46 | padding: 16px 32px; 47 | text-decoration: none; 48 | margin: 4px 2px; 49 | cursor: pointer; 50 | } 51 | 52 | label{ 53 | display: block; 54 | text-align: center; 55 | } 56 | 57 | .form-wrapper{ 58 | width:100vw; 59 | height: 100vh; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; 63 | flex-direction: column; 64 | 65 | } 66 | 67 | input{ 68 | display: block; 69 | } 70 | 71 | .form { 72 | display: flex; 73 | align-items: center; 74 | justify-content: center; 75 | flex-direction: column; 76 | 77 | } -------------------------------------------------------------------------------- /static/css/showimg.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | body{ 4 | 5 | background: #000; 6 | } -------------------------------------------------------------------------------- /static/css/takepictures.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: black; 3 | font-size: medium; 4 | text-align: center; 5 | 6 | 7 | } 8 | 9 | body { 10 | width: 100vw; 11 | height: 100vh; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | flex-direction: column; 16 | } 17 | 18 | .camera-controls { 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | margin: 10px; 23 | } 24 | 25 | .camera-controls>button { 26 | margin: 10px; 27 | } 28 | 29 | .camera-wrapper{ 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | flex-direction: column; 34 | } 35 | 36 | 37 | .video-elem { 38 | border: 5px solid black; 39 | width: 344px; 40 | height: 237px; 41 | } 42 | 43 | video { 44 | width: 344px; 45 | height: 237px; 46 | } 47 | 48 | #images{ 49 | height: auto; 50 | overflow: scroll; 51 | display: flex; 52 | } 53 | 54 | img{ 55 | width: 286px; 56 | height: 237px; 57 | } 58 | 59 | @media screen and (max-width: 600px) { 60 | video { 61 | 62 | width: 286px; 63 | height: 237px; 64 | } 65 | #images{ 66 | width: 286px; 67 | height: 237px; 68 | overflow: scroll; 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-direction: column; 73 | } 74 | 75 | .camera-wrapper{ 76 | display: flex; 77 | align-items: center; 78 | justify-content: center; 79 | flex-direction: column; 80 | } 81 | } 82 | 83 | 84 | -------------------------------------------------------------------------------- /static/js/home.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded',()=>{ 2 | const submitBtn = document.getElementById('submitBtn'); 3 | const fileInput = document.getElementById('fileInput'); 4 | 5 | 6 | submitBtn.addEventListener('click',(e)=>{ 7 | e.preventDefault; 8 | const file = fileInput.files[0]; 9 | console.log(file) 10 | let formdata = new FormData(); 11 | formdata.append('my_file',file) 12 | 13 | fetch('/upload',{ 14 | method:"POST", 15 | body:formdata 16 | }).then(response => response.json()).then(result => 17 | window.location.href = result.url).catch(err=> alert(err)) 18 | 19 | 20 | 21 | }) 22 | 23 | 24 | 25 | }) -------------------------------------------------------------------------------- /static/js/takepictures.js: -------------------------------------------------------------------------------- 1 | 2 | document.addEventListener('DOMContentLoaded', () => { 3 | 4 | const startButton = document.querySelector('button#start'); 5 | const captureButton = document.querySelector('button#capture') 6 | const imagesDiv = document.querySelector('#images'); 7 | const videoElem = document.querySelector('video'); 8 | const canvas = document.querySelector('.photo'); 9 | const ctx = canvas.getContext('2d'); 10 | const stopBtn = document.querySelector('#stop') 11 | let stream 12 | 13 | startButton.addEventListener('click', async () => { 14 | 15 | startButton.disabled = true; 16 | 17 | stream = await navigator.mediaDevices.getUserMedia({ video: true }); 18 | videoElem.srcObject = stream; 19 | videoElem.play() 20 | 21 | }) 22 | 23 | 24 | function GetFramesOnCanvas() { 25 | const width = videoElem.videoWidth; 26 | const height = videoElem.videoHeight; 27 | canvas.width = width; 28 | canvas.height = height; 29 | 30 | return setInterval(() => { 31 | ctx.drawImage(videoElem, 0, 0, width, height) 32 | }, 16) 33 | 34 | 35 | } 36 | 37 | function takePhoto() { 38 | const imageData = canvas.toDataURL('image/jpg'); 39 | const link = document.createElement('a'); 40 | console.log(imageData) 41 | sendCapturedImage(imageData) 42 | 43 | link.href = imageData; 44 | link.setAttribute('download','image') 45 | link.innerHTML = `captured-image`; 46 | imagesDiv.insertAdjacentElement("afterBegin",link) 47 | } 48 | 49 | function sendCapturedImage(data) { 50 | const file = DataURIToBlob(data) 51 | let formdata = new FormData(); 52 | const randomString = () => Math.random().toString(36) + Date.now().toString(36) 53 | formdata.append('capture_img',file,`image${randomString()}.jpg`) 54 | 55 | fetch('/capture', { 56 | method: "POST", 57 | body: formdata 58 | 59 | }).then(response => response.json()).then(result => console.log(result)).catch(err => console.log(err)) 60 | 61 | function DataURIToBlob(dataURI) { 62 | const splitDataURI = dataURI.split(',') 63 | const byteString = splitDataURI[0].indexOf('base64') >= 0 ? atob(splitDataURI[1]) : decodeURI(splitDataURI[1]) 64 | const mimeString = splitDataURI[0].split(':')[1].split(';')[0] 65 | 66 | const ia = new Uint8Array(byteString.length) 67 | for (let i = 0; i < byteString.length; i++) 68 | ia[i] = byteString.charCodeAt(i) 69 | 70 | return new Blob([ia], { type: mimeString }) 71 | } 72 | 73 | } 74 | captureButton.addEventListener('click', takePhoto) 75 | 76 | stopBtn.addEventListener('click',()=>{ 77 | 78 | clearInterval(GetFramesOnCanvas); 79 | // close the camera 80 | stream.getTracks().forEach((track) => track.stop()); 81 | startButton.disabled = false; 82 | }); 83 | videoElem.addEventListener('canplay', GetFramesOnCanvas); 84 | 85 | 86 | 87 | 88 | 89 | }) -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% block head %}{% endblock %} 9 | 10 | 11 | {% block body %}{% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /templates/getallpictures.html: -------------------------------------------------------------------------------- 1 | {% extends 'home.html' %} 2 | 3 | {% block head%} 4 | 5 | {% endblock %} 6 | 7 | 8 | {% block body %} 9 | 10 |
11 | {% for link in src %} 12 | 13 | {% endfor %} 14 | 15 |
16 | 17 | 18 | {% endblock %} -------------------------------------------------------------------------------- /templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block head%} 4 | Welcome to Homepage 5 | 6 | {% endblock %} 7 | 8 | 9 | {% block body %} 10 |
11 |
12 |
13 | 14 | 15 |
16 |
17 | 18 |
19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | {% endblock %} -------------------------------------------------------------------------------- /templates/showimage.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block head %} 4 | 5 | 6 | 7 | {% endblock %} 8 | 9 | 10 | {% block body %} 11 | 12 | {% endblock %} -------------------------------------------------------------------------------- /templates/takepictures.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block head%} 5 | 6 | take pictures here 7 | 8 | 9 | {%endblock%} 10 | 11 | 12 | {% block body%} 13 | 14 |

click on start to begin

15 | 16 |
17 |
18 | 19 | 20 |
21 | 22 |
23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | {% endblock%} --------------------------------------------------------------------------------