Google Developer Expert
Auth0 Ambassador
Microsoft MVP
Developer Advocate @ Twitch/AWS
🏡 https://superdi.dev
🐦 @cotufa82
<likes> Food, Infrastructure, Food, Vue.js, Food, Travelling, IOT, Steven Universe </likes>
La adopción de la cultura DevOps, herramientas y practicas ágiles de ingeniería tienen, por sobre todas las cosas, el lindo efecto de incrementar la colaboración entre los roles de desarrollo y operaciones.
Uno de los principales problemas del pasado (pero también hoy en día en algunas realidades) es que el equipo de desarrollo no estaba interesado en la operación y el mantenimiento de un sistema una vez que se entregó al equipo de operaciones, mientras que este último no estaba realmente consciente de los objetivos de negocio, y por lo tanto, reacios a satisfacer las necesidades operativas del sistema (también denominados “caprichos de los desarrolladores”)
Vue.js Docs
Capturar stream de una webcam
Snap Pic con countdown (5seg)
Auto mode (extraemos canvas cada segundo, cuando it detecta una sonrisa, se detiene y.. snaps!)
Drawer para las capturas con y sin filtros
Click para Download
Compartir por SMS
import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify';
Vue.config.productionTip = false
console.log(process.env)
new Vue({
vuetify,
render: h => h(App)
}).$mount('#app')
main.js
data: () => ({
//
opentok_api_key: ENV.OPENTOK_API_KEY ? ENV.OPENTOK_API_KEY : "",
opentok_session_id: ENV.OPENTOK_SESSION_ID ? ENV.OPENTOK_SESSION_ID : "",
opentok_token: ENV.OPENTOK_TOKEN ? ENV.OPENTOK_TOKEN : "",
azure_face_api_subscription_key: ENV.AZURE_FACE_API_SUBSCRIPTION_KEY
? ENV.AZURE_FACE_API_SUBSCRIPTION_KEY
: "",
azure_face_api_endpoint: ENV.AZURE_FACE_API_ENDPOINT
? ENV.AZURE_FACE_API_ENDPOINT
: "",
………….
}),
<div id="publisher">
<v-overlay :absolute="true" :value="counter != 6">
<div style="font-size:150px;">{{ counter }}</div>
</v-overlay>
</div>
async initializeSession() {
var session = OT.initSession(this.opentok_api_key,
this.opentok_session_id);
this.publisher = OT.initPublisher("publisher",{insertMode: "append",
width: 400,height: 300
},
handleError
);
// Connect to the session
session.connect(this.opentok_token, error => {
// If the connection is successful, initialize a publisher and publish to the session
if (error) {
handleError(error);
} else {
session.publish(this.publisher, handleError);
}
});
}
<v-card-actions>
<v-btn @click="analyze()" v-if="manual == true" text class="analyze">
<v-img :src="require('./assets/snap.png')" contain height="50" />
</v-btn>
</v-card-actions>
analyze() {
let imageData = this.publisher.getImgData();
let blob = this.dataURItoBlob("data:image/png;base64," + imageData);
//Evaluates image emotion in azure cognitive face service
const xhr = new XMLHttpRequest();
xhr.open(
"POST",
`${this.azure_face_api_endpoint}face/v1.0/detect?returnFaceId=true&returnFaceLandmarks=false&returnFaceAttributes=emotion`
);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
let response = xhr.response[0];
if (response !== null && response !== undefined) {
............
//Emotion is present, so we evaluate happiness factor (between 0 and 1) if happiness is > 0.5 we take the snap
if (
response.faceAttributes.emotion.happiness !== null &&
response.faceAttributes.emotion.happiness !== undefined
) {
if (response.faceAttributes.emotion.happiness >= 0.5) {
//take the snap and put it in image array
this.images.push({
id: this.images.length + 1,
dataurl: "data:image/png;base64," + image
});
} else {
this.snackbar = true;
this.snackbar_message =
"Smiling is a requirement. Smile and we take your photo";
}
}
}
..............
};
xhr.responseType = "json";
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.setRequestHeader(
"Ocp-Apim-Subscription-Key",
this.azure_face_api_subscription_key
);
xhr.send(blob);
}, 6000);
}
## App.vue
<v-btn
@click="downloadImages()"
style="background-color:inherit !important; padding: 0 !important;"
>
# MANY LINES AFTER....
forceFileDownload(index, place) {
let imgs = null;
if (place == undefined) imgs = this.images;
else imgs = this.filteredImages;
let image_file = this.dataURItoBlob(imgs[index - 1].dataurl);
const url = window.URL.createObjectURL(image_file);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "snap_" + index + ".png"); //or any other extension
link.click();
},
downloadImages() {
for (let i = 0; i < this.images.length; i++) {
this.forceFileDownload(this.images[i].id);
}
for (let f = 0; f < this.filteredImages.length; f++) {
this.forceFileDownload(this.filteredImages[f].id, "filtered");
}
},
# re module is for regular expression ops
import nexmo, re, base64
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
from dotenv import load_dotenv
from os import environ, makedirs
from os.path import join, dirname, abspath, exists
from datetime import datetime
app = Flask(__name__, static_url_path="", static_folder="dist")
CORS(app)
# 10mb size is allowed
app.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024
# Get env vars from file
envpath = join(dirname(__file__), "./.env")
load_dotenv(envpath)
# Needed for uploading files
basedir = abspath(dirname(__file__))
client = nexmo.Client(
key=environ.get("NEXMO_API_KEY"), secret=environ.get("NEXMO_API_SECRET")
)
# Verify if snaps directory exists, if not then create it
if not exists("{}/dist/snaps".format(basedir)):
makedirs("{}/dist/snaps".format(basedir))
@app.route("/", methods=["GET"])
def home():
return send_from_directory("./dist/", "index.html")
@app.route("/send-mms", methods=["POST"])
def send_mms():
params = request.get_json() or request.form or request.args
if "phone" and "image" in params:
# Get the base64 image
image = re.sub(r"^data:image\/png;base64,", "", params["image"])
image = bytes(image, "utf-8")
phone = params["phone"]
# Get the current timestamp
filename_prefix = datetime.utcnow().isoformat()
filename = "{phone}-{prefix}.png".format(phone=phone, prefix=filename_prefix)
# Save image
imagedir = "{}/dist/snaps".format(basedir)
# Create the binary file in the snaps directory
imagefile = open(
"{imagedir}/{filename}".format(imagedir=imagedir, filename=filename), "wb"
)
# Write the bytes
imagefile.write(base64.decodebytes(image))
# Close file
imagefile.close()
# Send sms
response = client.send_message(
{
"from": environ.get("NEXMO_NUMBER"),
"to": phone,
"text": "Opentok-Nexmo, Your snap is ready: {site_url}/snaps/{filename}".format(
site_url=environ.get("SITE_URL"), filename=filename
),
}
)
if response["messages"][0]["status"] == "0":
return jsonify({"status": "success", "message": "All OK"}), 200
else:
return (
jsonify(
{
"status": "error",
"message": "Message failed with error: "
+ response["messages"][0]["error-text"],
}
),
200,
)
else:
return (
jsonify(
{
"status": "error",
"message": "Required params (phone, image) not provided",
}
),
200,
)
# re module is for regular expression ops
import nexmo, re, base64
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
from dotenv import load_dotenv
from os import environ, makedirs
from os.path import join, dirname, abspath, exists
from datetime import datetime
app = Flask(__name__, static_url_path="", static_folder="dist")
CORS(app)
# 10mb size is allowed
app.config["MAX_CONTENT_LENGTH"] = 10 * 1024 * 1024
# Get env vars from file
envpath = join(dirname(__file__), "./.env")
load_dotenv(envpath)
# Needed for uploading files
basedir = abspath(dirname(__file__))
client = nexmo.Client(
key=environ.get("NEXMO_API_KEY"), secret=environ.get("NEXMO_API_SECRET")
)
# Verify if snaps directory exists, if not then create it
if not exists("{}/dist/snaps".format(basedir)):
makedirs("{}/dist/snaps".format(basedir))
@app.route("/", methods=["GET"])
def home():
return send_from_directory("./dist/", "index.html")
@app.route("/send-mms", methods=["POST"])
def send_mms():
params = request.get_json() or request.form or request.args
if "phone" and "image" in params:
# Get the base64 image
image = re.sub(r"^data:image\/png;base64,", "", params["image"])
image = bytes(image, "utf-8")
phone = params["phone"]
# Get the current timestamp
filename_prefix = datetime.utcnow().isoformat()
filename = "{phone}-{prefix}.png".format(phone=phone, prefix=filename_prefix)
# Save image
imagedir = "{}/dist/snaps".format(basedir)
# Create the binary file in the snaps directory
imagefile = open(
"{imagedir}/{filename}".format(imagedir=imagedir, filename=filename), "wb"
)
# Write the bytes
imagefile.write(base64.decodebytes(image))
# Close file
imagefile.close()
# Send sms
response = client.send_message(
{
"from": environ.get("NEXMO_NUMBER"),
"to": phone,
"text": "Opentok-Nexmo, Your snap is ready: {site_url}/snaps/{filename}".format(
site_url=environ.get("SITE_URL"), filename=filename
),
}
)
if response["messages"][0]["status"] == "0":
return jsonify({"status": "success", "message": "All OK"}), 200
else:
return (
jsonify(
{
"status": "error",
"message": "Message failed with error: "
+ response["messages"][0]["error-text"],
}
),
200,
)
else:
return (
jsonify(
{
"status": "error",
"message": "Required params (phone, image) not provided",
}
),
200,
)
https://github.com/opentok-community/opentok-photobooth
@cotufa82
https://superdi.dev
https://slides.com/superdiana/smile-jump