|
1 |
| -# OpenTok Hello World Python |
| 1 | +# OpenTok Archiving Sample for Python |
2 | 2 |
|
3 |
| -This is a simple demo app that shows how you can use the OpenTok-Python-SDK to create Sessions, |
4 |
| -generate Tokens with those Sessions, and then pass these values to a JavaScript client that can |
5 |
| -connect and conduct a group chat. |
| 3 | +This is a simple demo app that shows how you can use the OpenTok Python SDK to archive (or record) |
| 4 | +Sessions, list archives that have been created, download the recordings, and delete the recordings. |
6 | 5 |
|
7 | 6 | ## Running the App
|
8 | 7 |
|
@@ -31,99 +30,151 @@ this but the simplest would be to do it right in your shell.
|
31 | 30 | Finally, start the server.
|
32 | 31 |
|
33 | 32 | ```
|
34 |
| -(venv)$ python helloworld.py |
| 33 | +(venv)$ python archiving.py |
35 | 34 | ```
|
36 | 35 |
|
37 |
| -Visit <http://127.0.0.1:5000/> in your browser. Open it again in a second window. Smile! You've just |
| 36 | +Visit <http://localhost:5000/> in your browser. Open it again in a second window. Smile! You've just |
38 | 37 | set up a group chat.
|
39 | 38 |
|
40 | 39 | ## Walkthrough
|
41 | 40 |
|
42 |
| -This demo application uses the [Flask web microframework](http://flask.pocoo.org/). It is similar to |
43 |
| -many other popular web frameworks. We are only covering the very basics of the framework, but you can |
44 |
| -learn more by following the links above. |
| 41 | +This demo application uses the same frameworks and libraries as the HelloWorld sample. If you have |
| 42 | +not already gotten familiar with the code in that project, consider doing so before continuing. |
45 | 43 |
|
46 |
| -### Main Application (helloworld.py) |
| 44 | +The explanations below are separated by page. Each section will focus on a route handler within the |
| 45 | +main application (archiving.py). |
47 | 46 |
|
48 |
| -The first thing done in this file is to import the dependencies we will be using. In this case that |
49 |
| -is the Flask web framework, the os module, and most importantly the OpenTok SDK. |
| 47 | +### Creating Archives – Host View |
50 | 48 |
|
51 |
| -```python |
52 |
| -from flask import Flask, render_template |
53 |
| -from opentok import OpenTok |
54 |
| -import os |
55 |
| -``` |
56 |
| - |
57 |
| -Next this file performs some basic checks on the environment. If it cannot find the `API_KEY`and |
58 |
| -`API_SECRET` environment variables, there is no point in continuing. |
| 49 | +Start by visiting the host page at <http://localhost:5000/host> and using the application to record |
| 50 | +an archive. Your browser will first ask you to approve permission to use the camera and microphone. |
| 51 | +Once you've accepted, your image will appear inside the section titled 'Host'. To start recording |
| 52 | +the video stream, press the 'Start Archiving' button. Once archiving has begun the button will turn |
| 53 | +green and change to 'Stop Archiving'. You should also see a red blinking indicator that you are |
| 54 | +being recorded. Wave and say hello! Stop archiving when you are done. |
59 | 55 |
|
60 |
| -The object `app` is our application and its initialized by instantiating an object from Flask. |
61 |
| -Then we initialize an instance of OpenTok as `opentok`. If this file is run as the main file, |
62 |
| -we should start running the app. |
| 56 | +Next we will see how the host view is implemented on the server. The route handler for this page is |
| 57 | +shown below: |
63 | 58 |
|
64 | 59 | ```python
|
65 |
| -app = Flask(__name__) |
66 |
| -opentok = OpenTok(api_key, api_secret) |
67 |
| - |
68 |
| -# ... |
69 |
| - |
70 |
| -if __name__ == "__main__": |
71 |
| - app.run() |
| 60 | +@app.route("/host") |
| 61 | +def host(): |
| 62 | + key = api_key |
| 63 | + session_id = session.session_id |
| 64 | + token = opentok.generate_token(session_id) |
| 65 | + return render_template('host.html', api_key=key, session_id=session_id, token=token) |
72 | 66 | ```
|
73 | 67 |
|
74 |
| -Now, lets discuss the Hello World application's functionality. We want to set up a group chat so |
75 |
| -that any client that visits a page will connect to the same OpenTok Session. Once they are connected |
76 |
| -they can Publish a Stream and Subscribe to all the other streams in that Session. So we just need |
77 |
| -one Session object, and it needs to be accessible every time a request is made. On the next line we |
78 |
| -simply call the `OpenTok` instance's `create_session` method to get a Session and store it in the |
79 |
| -`session` variable. Alternatively, `session_id`s are commonly stored in databses for applications |
80 |
| -that have many of them. |
| 68 | +If you've completed the HelloWorld walkthrough, this should look familiar. This handler simply |
| 69 | +generates the three strings that the client (JavaScript) needs to connect to the session: `api_key`, |
| 70 | +`session_id` and `token`. After the user has connected to the session, they press the |
| 71 | +'Start Archiving' button, which sends an XHR (or Ajax) request to the <http://localhost:5000/start> |
| 72 | +URL. The route handler for this URL is shown below: |
81 | 73 |
|
82 | 74 | ```python
|
83 |
| -session = opentok.create_session() |
| 75 | +@app.route("/start") |
| 76 | +def start(): |
| 77 | + archive = opentok.start_archive(session.session_id, name="Python Archiving Sample App") |
| 78 | + return archive.json() |
84 | 79 | ```
|
85 | 80 |
|
86 |
| -We only need one page, so we create one route handler for any HTTP GET requests to trigger. |
| 81 | +In this handler, the `start_archive()` method of the `opentok` instance is called with the `session_id` |
| 82 | +for the session that needs to be archived. The optional second argument is `name`, which is stored with |
| 83 | +the archive and can be read later. In this case, as in the HelloWorld sample app, there is |
| 84 | +only one session created and it is used here and for the participant view. This will trigger the |
| 85 | +recording to begin. The response sent back to the client's XHR request will be the JSON |
| 86 | +representation of the archive, which is returned from the `json()` method. The client is also |
| 87 | +listening for the `archiveStarted` event, and uses that event to change the 'Start Archiving' button |
| 88 | +to show 'Stop Archiving' instead. When the user presses the button this time, another XHR request |
| 89 | +is sent to the <http://localhost:5000/stop/<archive_id>> URL where `<archive_id>` represents the ID the |
| 90 | +client receives in the 'archiveStarted' event. The route handler for this request is shown below: |
87 | 91 |
|
88 | 92 | ```python
|
89 |
| -@app.route("/") |
90 |
| -def hello(): |
91 |
| - # ... |
| 93 | +@app.route("/stop/<archive_id>") |
| 94 | +def stop(archive_id): |
| 95 | + archive = opentok.stop_archive(archive_id) |
| 96 | + return archive.json() |
92 | 97 | ```
|
93 | 98 |
|
94 |
| -Now all we have to do is serve a page with the three values the client will need to connect to the |
95 |
| -session: `api_key`, `session_id`, and `token`. The `api_key` is available in the outer scope so we |
96 |
| -can just assign it. The `session_id` is available as the `session.session_id` attribute. The `token` |
97 |
| -is generated freshly on this request by calling the `generate_token` method of the `opentok` |
98 |
| -instance, and passing in the `session_id`. This is because a Token is a piece of information that |
99 |
| -carries a specific client's permissions in a certain Session. Ideally, as we've done here, you |
100 |
| -generate a unique token for each client that will connect. |
| 99 | +This handler is very similar to the previous one. Instead of calling the `start_archive()` method, |
| 100 | +the `stop_archive()` method is called. This method takes an `archive_id` as its parameter, which |
| 101 | +is different for each time a session starts recording. But the client has sent this to the server |
| 102 | +as part of the URL, so the `archive_id` argument from the route matcher is used to retrieve it. |
| 103 | + |
| 104 | +Now you have understood the three main routes that are used to create the Host experience of |
| 105 | +creating an archive. Much of the functionality is done in the client with JavaScript. That code can |
| 106 | +be found in the `public/js/host.js` file. Read about the |
| 107 | +[OpenTok.js JavaScript](http://tokbox.com/opentok/libraries/client/js/) library to learn more. |
| 108 | + |
| 109 | +### Past Archives |
| 110 | + |
| 111 | +Start by visiting the history page at <http://localhost:5000/history>. You will see a table that |
| 112 | +displays all the archives created with your API Key. If there are more than five, the older ones |
| 113 | +can be seen by clicking the "Older →" link. If you click on the name of an archive, your browser |
| 114 | +will start downloading the archive file. If you click the "Delete" link in the end of the row |
| 115 | +for any archive, that archive will be deleted and no longer available. Some basic information like |
| 116 | +when the archive was created, how long it is, and its status is also shown. You should see the |
| 117 | +archives you created in the previous sections here. |
| 118 | + |
| 119 | +We begin to see how this page is created by looking at the route handler for this URL: |
101 | 120 |
|
102 | 121 | ```python
|
103 |
| - key = api_key |
104 |
| - session_id = session.session_id |
105 |
| - token = opentok.generate_token(session_id) |
| 122 | +@app.route("/history") |
| 123 | +def history(): |
| 124 | + page = int(request.args.get('page', '1')) |
| 125 | + offset = (page - 1) * 5 |
| 126 | + archives = opentok.get_archives(offset=offset, count=5) |
| 127 | + |
| 128 | + show_previous = '/history?page=' + str(page-1) if page > 1 else None |
| 129 | + show_next = '/history?page=' + str(page+1) if archives.count > (offset + 5) else None |
| 130 | + |
| 131 | + return render_template('history.html', archives=archives, show_previous=show_previous, |
| 132 | + show_next=show_next) |
106 | 133 | ```
|
107 | 134 |
|
108 |
| -Now all we have to do is serve a page with those three values. Lets call our `render_template` |
109 |
| -helper that will pick up a template called `index.html` from the `templates/` directory in our |
110 |
| -application and pass in the variables for it to include on the page. |
| 135 | +This view is paginated so that we don't potentially show hundreds of rows on the table, which would |
| 136 | +be difficult for the user to navigate. So this code starts by figuring out which page needs to be |
| 137 | +shown, where each page is a set of 5 archives. The `page` number is read from the request's query |
| 138 | +string parameters as a string (defaulting to 1 if its not present) and then casted into an `int`. |
| 139 | +The `offset`, which represents how many archives are being skipped is always calculated as five |
| 140 | +times as many pages that are less than the current page, which is `(page - 1) * 5`. Now there is |
| 141 | +enough information to ask for a list of archives from OpenTok, which we do by calling the |
| 142 | +`get_archives()` method of the `opentok` instance. This method optionally takes the offset and count |
| 143 | +that was just calculated. If we are not at the first page, we can pass the view a string that |
| 144 | +contains the relative URL for the previous page. Similarly, we can also include one for the next |
| 145 | +page. Now the application renders the view using that information and the partial list of archives. |
| 146 | + |
| 147 | +At this point the template file `templates/history.html` handles |
| 148 | +looping over the array of archives and outputting the proper information for each column in the |
| 149 | +table. It also places a link to the download and delete routes around the archive's name and |
| 150 | +its delete button, respectively. |
| 151 | + |
| 152 | +The code for the download route handler is shown below: |
111 | 153 |
|
112 | 154 | ```python
|
113 |
| - return render_template('index.html', api_key=key, session_id=session_id, token=token) |
| 155 | +@app.route("/download/<archive_id>") |
| 156 | +def download(archive_id): |
| 157 | + archive = opentok.get_archive(archive_id) |
| 158 | + return redirect(archive.url) |
114 | 159 | ```
|
115 | 160 |
|
116 |
| -### Main Template (templates/index.html) |
| 161 | +The download URL for an archive is available as a property of an `Archive` instance. In order to get |
| 162 | +an instance to this archive, the `get_archive()` method of the `opentok` instance is used. The only |
| 163 | +parameter it needs is the `archive_id`. We use the same technique as above to read that `archive_id` |
| 164 | +from the URL. Lastly, we send a redirect response back to the browser so the download begins. |
117 | 165 |
|
118 |
| -This file simply sets up the HTML page for the JavaScript application to run, imports the |
119 |
| -JavaScript library, and passes the values created by the server into the JavaScript application |
120 |
| -inside `public/js/helloworld.js` |
| 166 | +The code for the delete route handler is shown below: |
121 | 167 |
|
122 |
| -### JavaScript Applicaton (static/js/helloworld.js) |
| 168 | +```python |
| 169 | +@app.route("/delete/<archive_id>") |
| 170 | +def delete(archive_id): |
| 171 | + opentok.delete_archive(archive_id) |
| 172 | + return redirect(url_for('history')) |
| 173 | +``` |
123 | 174 |
|
124 |
| -The group chat is mostly implemented in this file. At a high level, we connect to the given |
125 |
| -Session, publish a stream from our webcam, and listen for new streams from other clients to |
126 |
| -subscribe to. |
| 175 | +Once again the `archive_id` is retrieved from the URL of the request. This value is then passed the |
| 176 | +`delete_archive()` method of the `opentok` instance. Now that the archive has been deleted, a |
| 177 | +redirect response back to the first page of the history is sent back to the browser. |
127 | 178 |
|
128 |
| -For more details, read the comments in the file or go to the |
129 |
| -[JavaScript Client Library](http://tokbox.com/opentok/libraries/client/js/) for a full reference. |
| 179 | +That completes the walkthrough for this Archiving sample application. Feel free to continue to use |
| 180 | +this application to browse the archives created for your API Key. |
0 commit comments