Create a Photo Booth with Vue.js and Flask: Part 2

Published June 26, 2020 by Diana Rodriguez

In Part 1 of this tutorial, we looked at how to use Vue.js and the Vonage Video API to create a photo booth app, complete with smile detection powered by the Azure Face API. In this post, you’ll learn how to add filters to your snaps, how to download all the images you’ve created, and how to send an SMS message to share a link to your images. Along the way, we’ll build out a Flask back end to serve up the application and handle the SMS messaging.

Source: https://github.com/opentok-community/opentok-photobooth/tree/python-backend

Deployed app: https://opentok-nexmo.azurewebsites.net

Vonage API Account

To complete this tutorial, you will need a Vonage API account (different from the Vonage Video account used in Part 1). If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.

Start building with Vonage

Filters

An important feature for our photo booth is the capability to apply filters (effects) to a selected snap. Once you transform your captured photo from the camera into a canvas, you can manipulate the image as you wish. For our app, we’ll have four filters: Grayscale, Sepia, Green, and Blue.
In terms of functionality, in the left drawer, we are going to add a switch component for filters. This switch component will be linked with the filters variable (false by default). When the switch component changes from false to true, a watcher executes the method in charge of generating the filtered images and adding them to the drawer on the right.

Let’s start by adding the switch component. In the left drawer section, paste the following code after the manual switch:

For filter generation, we will define a method called imgwfilter as follows:

This method receives two parameters: the first is the color pattern (the filter to apply), and the second is the density. Using the selected image, the method creates a new canvas, then loops through each pixel in the image applying the filter and adding the new image to the filteredImages array.

This method should be called when the filters switch changes to true. To capture this specific moment, we are going to create a new watcher for the filters variable. After the manual watcher, add the following code:

This watcher evaluates the moment the value changes to true. Once this happens, the watcher checks if an image exists to apply these filters to. If an image exists, the application generates four filtered images using the original image as a base.

The next step is rendering the filtered images in the template section. For this, we are going to use the right-side drawer. Add the following code right after the images loop:

This code will be renders each filtered image in the drawer on the right. When the user clicks on an image, it will trigger a download.

In the last part of the tutorial, forceFileDownload received a single parameter. We need to modify it to reuse it for the filtered images too. After modifications, we will have something like this:

We’ve modified several of the lines so that if the place parameter is not provided the application is going to download an image from the images array, and if the place parameter is equal to filtered then the method downloads a single image from the filteredImages array.

Save All Images at Once

A bonus option we can include in the configuration section (the left section) is to download all images by clicking a single button. For this, we are going to add the following code inside the left section just right after the filters switch:

There isn’t a lot to say about this code, other than we added a single button and assigned the downloadImages method to the click event. Now we define this function in the methods section as follows:

The downloadImages method loops through the images array (original images), and the filtered images array (images with effects), then downloads every single image reusing the forceFileDownload method.

Send a Link to Images through SMS

The only thing left is to provide a user interface to share these snaps with others using the Vonage Messaging API. First, we need to define additional variables in the data section to control certain states:

Next, we create a button on the left-side drawer:

This button is going to show us a dialog with the form UI to send our message, and at the same time it is going to create the image strip (using a method that merges all filtered images into a single image). The method is called getStripImage.

At the end of the methods section add:

Using the default width and height of a single OpenTok publisher, this method creates a horizontal canvas with these dimensions: (width*4,height). From there, it merges the images next to each other before the final result is stored in the stripedimage variable.

Now let’s add our invisible dialog to the Vue template. We are going to place it right after the end of the v-content component:

This form allows users to indicate the phone number where the selected image will be sent. When the user selects the image, selected2Nexmo is executed. This method refreshes the value of the variable sel2next that represents the selected image. Both the phone number and the image are required. If you don’t provide one or the other, you will see a warning.

To define the selected2Nexmo method, add the following code at the end of the methods section:

This method receives two parameters: the id of the image (imgid) and the category (filtered). Depending on the category (striped, filtered, or undefined), the application decides where to search. In the case of striped, the imgid is not needed because stripedimage is not an array.
When the user clicks the sendMMS button the form is sent.

Add the sendMMS method to the methods section as follows:

The sendMMS function validates the required fields, and if there are empty fields the application is going to show us the snackbar with any error messages.

If data was provided, then the application performs a post request to our backend endpoint using Axios. As we mentioned at the beginning of this tutorial, our backend is going to serve the static site (built using the vue-cli) and also will be serving as an endpoint to send the SMS (this.site_url + "/send-mms" represent this endpoint).

We provide the data to send (in this case the phone number and the image), and then we wait for the response from the service using Axios promises. The catch statement is triggered when an error occurs, and a snackbar will show the details related to this error. We use the then statement to evaluate if the response succeeds—if so, the application shows a snackbar indicating the message was sent successfully.

Build a Back End

In this section, we are going to configure a Flask server. This back-end application is going to serve the static front end (built with vue-cli) and defines the /send-mms endpoint to send the SMS with the selected image.

Note: Compatible Python versions (Python >= 3.6, pip3)

Install Dependencies

To install required modules, use this requirements.txt file as a reference. This one was created with pip freeze and contains the modules you need to install. Copy it and save it as requirements.txt.

Then execute:

Create the Environment

Once dependencies have been installed by pip, create your backend directory (we recommend you create this folder alongside the Vue application directory).

Once inside the backend directory, create the .env file. We are going to define some environment variables here related to the Vonage account credentials:

Replace the empty strings with your Vonage account credentials, which you can find on the dashboard. Be sure to include your virtual number and the SITE_URL (the URL where your application is running, e.g. http://localhost).

Create the Server

Create the file server.py and add the following code:

The code above imports all the modules that our Flask application needs to work correctly. Among the most important we have:

  • nexmo: for sending an SMS
  • flask: for creating our backend application
  • flask_cors: to make our /send-mms endpoint accessible

We can init our Flask application by adding the following:

Note: Pay attention to the app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 line. Here we are telling Flask to allow big files to be uploaded. This is because we will be sending the selected image in the Axios request, and these images tend can be quite big.

The next step is to initialize the nexmo client. As you can see, first we load the environment file to be able to extract the credentials. Then we create our client:

Note: We define the nexmo client as a global variable to be available wherever it is needed.

Next, we check to see if the snap directory exists inside the dist directory. Using the basedir, the application verifies if this directory exists, and if not, it creates it. When the client sends the selected image, the Flask application is going to save the image inside this directory.

With all these defined, we can proceed to work on our endpoints. The first, and the most important, is the one that serves our application:

To achieve this, Flask uses send_from_directory to read inside a directory and serves all the available HTML static content.

Next, we define the /send-mms endpoint. The front-end application is going use this to share a link to the picture, through SMS:

This endpoint validates if phone and image were provided. If not, the endpoint returns a JSON object with an error. If the required data is there, then the endpoint generates a binary version of the image and saves it into the snaps folder. But first, using the phone number and the current date-time, it assigns a unique name to the images.

Our app uses the nexmo client to send an SMS with the URL of the image. If the request succeeds then it returns a message with the success status. If it fails, our endpoint returns a JSON with the error details.

Local Deployment

Front End

Go to the main directory of your Vue application and run:

This will generate the dist directory with all the static files for the frontend application.

Back End

To start serving your application, go to the main directory of your backend application and run:

And that’s it! Enjoy your new photo booth app, and reach out to us on Twitter or the Vonage Developer Community Slack if you have any questions.

Leave a Reply

Your email address will not be published.

Get the latest posts from Nexmo’s next-generation communications blog delivered to your inbox.

By signing up to our communications blog, you accept our privacy policy , which sets out how we use your data and the rights you have in respect of your data. You can opt out of receiving our updates by clicking the unsubscribe link in the email or by emailing us at [email protected].