Audio Back End
Cover Page
DUE Wed, 02/12, 2 pm
We first prepare Chatter
back end to support audio.
Install updates
Remember to install updates available to your Ubuntu back end. If N
in the following notice you see when you ssh to your back-end server is not 0,
N updates can be applied immediately.
run the following:
server$ sudo apt update
server$ sudo apt upgrade
Failure to update your packages could lead to the lab back end not performing correctly and also make you vulnerable to security hacks.
If you see *** System restart required ***
when you ssh to your server, please run:
server$ sync
server$ sudo reboot
Your ssh session will be ended at the server. Wait a few minutes for the system to reboot before you ssh to your server again.
Modified Chatter
API data formats
In this lab, we allow user to post an audio clip with their chatt
.
As in previous labs, the chatt
s retrieval API will send back all accumulated chatts in the form of a JSON array of string arrays. In addition to the elements “username”, “message”, “id”, and “timestamp” of the first lab, each string array now carries one additional element which is a base64-encoded string of the audio clip:
[
["username0", "message0", "id0", "timestamp0", "base64-encoded audio0"],
["username1", "message1", "id1", "timestamp1", "base64-encoded audio1"],
...
]
To post a chatt
, the client correspondingly sends a JSON object with keys “username”, “message”, and “audio”, where the value for key “audio” is a base64-encoded string. If no audio is posted, the value may be a JSON null
or the empty string (""
). For example:
{
"username": "YOUR_UNIQNAME",
"message": "Hello world!",
"audio": ""
}
In terms of testing, unfortunately there’s no practical way to include a Base64-encoded sample audio string, nor a way to play it back without a front-end to decode it.
Database table
We first add a new column named audio
of type text
to the chatts
table in your chatterdb
database:
- Log into an interactive PostgreSQL (
psql
) session as userpostgres
- Connect to the
chatterdb
database - Clear your
chatts
table of all oldchatt
s, use the SQL command:TRUNCATE TABLE chatts;
- Add a new column to
chatts
to store audio that has been encoded into base64 string. Type the following:ALTER TABLE chatts ADD COLUMN audio TEXT;
Columns of type
TEXT
allow the user to store strings of undetermined length. - To verify that you’ve added the new column to the
chatts
table, enter:SELECT * FROM chatts;
Make sure you get back the following result (though perhaps more stretched out):
username | message | id | time | audio | ----------+----------+----+------+-------+ (0 rows)
If so congratulations! You have successfully added the new column!
- Exit PostgreSQL
Rename column
If you want to change a column name, for example, from “voice” to “audio”,
you first remove the “voice” column from your chatts
table using:
ALTER TABLE chatts DROP COLUMN voice;
and then add a new “audio” column as per above.
Web server
We now add new URL paths and their corresponding handlers to your back-end server.
server$ cd ~/441/chatterd
Switching back-end stack
To switch back-end stack, you do not need to set up a new AWS/GCP server, you can re-use the same server. You can also re-use your public key certificate and private key. All of the back-end alternatives assume the same file locations for these. You do not need to move them. All aletrnatives also work with the same PostgreSQL set up. You can re-use your existing chatterdb
PostgreSQL database and chatts
table. But you would need to set up the alternate back-end stack you want to switch to from scratch, following the chatter
lab back-end spec starting from the Web server framework section to the end of the spec before continuing with the steps below.
In particular, should you decide to switch from a Go/Rust backend to a JavaScript or Python backend, or vice versa, update your /etc/systemd/system/chatterd.service
file to point to the new back-end server and do:
server$ sudo systemctl daemon-reload
server$ sudo systemctl restart chatterd
You don’t need to edit the chatterd.service
file but only need to restart chatterd
if you’re switching between Go and Rust backends. See the Chatter
lab spec for instructions on how to edit the chatterd.service
.
Go with Echo
Go with Echo
Editing handlers.go
Add one new property to the Chatt
struct in handlers.go
:
type Chatt struct {
// . . .
Audio *string `json:"audio"`
}
To the back-end database, the audio data is just a string. Since the audio string optional, we set the type to *string
, which can take on a null pointer, nil
in Go.
To handle audio
data uploads, make a copy of your postchatt()
function inside your handlers.go
file and name the copy postaudio()
. Replace the call to chatterDB.Exec()
with:
chatterDB.Exec(background, `INSERT INTO chatts (username, message, id, audio) VALUES ($1, $2, gen_random_uuid(), $3)`, chatt.Username, chatt.Message, chatt.Audio)
which extracts the audio
data from the JSON object and insert it into the chatts
table, along with the rest of its associated chatt
.
Next, make a copy of your getchatts()
function inside your handlers.go
file and name the copy getaudio()
. Replace the SELECT
statement with `SELECT username, message, id, time, audio FROM chatts ORDER BY time DESC`
. This statement will retrieve all data, including our new audio string from the PostgreSQL database.
Still in getaudio()
, replace the rows.Scan()
call in the for rows.Next() {}
block with:
rows.Scan(&chatt.Username, &chatt.Message, &chatt.Id, &chatt.Timestamp, &chatt.Audio)
and if the returned err
is not nil
:
chattArr = append(chattArr, []any{chatt.Username, chatt.Message, chatt.Id, chatt.Timestamp, chatt.Audio})
In addition to the original columns, we added reading the audio
column and included it in the chatt
data returned to the front end.
Save and exit handlers.go
.
Routing for new URLs
For the newly added getaudio()
and postaudio()
functions, add the following new routes to the routes
array in main.go
:
var routes = []Route {
// . . .
{"GET", "/getaudio/", getaudio},
{"POST", "/postaudio/", postaudio},
}
Save and exit main.go
.
Go
is a compiled language, like C/C++ and unlike Python, which is an interpreted language. This means you must run go build
each and every time you made changes to your code, for the changes to show up in your executable.
Rebuild and restart chatterd
:
server$ go build
server$ sudo systemctl restart chatterd
JavaScript with Express
JavaScript with Express
Editing handlers.ts
Add one new property to the Chatt
interface in handlers.ts
:
interface Chatt {}
// . . .
audio: string | null
To the back-end database, the audio data is just a string.
To handle audio
data uploads, make a copy of your postchatt()
function inside your handlers.ts
file and name the copy postaudio()
. Replace the line with the INSERT
statement with:
await chatterDB`INSERT INTO chatts (username, message, id, audio) VALUES (${chatt.username}, ${chatt.message}, ${randomUUID()}, ${chatt.audio})`
which extracts the audio
data from the JSON object and insert it into the chatts
table, along with the rest of its associated chatt
.
Next, make a copy of your getchatts()
function inside your handlers.ts
file and name the copy getaudio()
. Replace the declaration of chatts
with:
const chatts = await chatterDB`SELECT username, message, id, time, audio FROM chatts ORDER BY time DESC`.values()
This will retrieve all data, including our new audio string from the PostgreSQL database.
Save and exit handlers.ts
.
Routing for new URLs
For the newly added getaudio()
and postaudio()
functions, add the following new routes to the Express
app
instantiation in main.ts
:
.get('/getaudio/', handlers.getaudio)
.post('/postaudio/', handlers.postaudio)
Save and exit main.ts
. Rebuild and restart chatterd
:
server$ npx tsc
server$ sudo systemctl restart chatterd
Python with Starlette
Python with Starlette
Editing handlers.py
Add one new property to the Chatt
struct in handlers.py
:
class Chatt:
// . . .
audio: Optional[str] = None
To the back-end database, the audio data is just a string.
To handle audio
data uploads, make a copy of your postchatt()
function inside your handlers.py
file and name the copy postaudio()
. Replace the call to cursor.execute()
with:
await cursor.execute('INSERT INTO chatts (username, message, id, audio) VALUES '
'(%s, %s, gen_random_uuid(), %s);', (chatt.username, chatt.message, chatt.audio))
which extracts the audio
data from the JSON object and insert it into the chatts
table, along with the rest of its associated chatt
.
Next, make a copy of your getchatts()
function inside your handlers.py
file and name the copy getaudio()
. Replace the SELECT
statement with 'SELECT username, message, id, time, audio FROM chatts ORDER BY time DESC;'
. This statement will retrieve all data, including our new audio string from the PostgreSQL database.
Save and exit handlers.py
.
Routing for new URLs
For the newly added getaudio()
and postaudio()
functions, add the following new routes to the routes
array in main.py
:
Route('/getaudio/', handlers.getaudio, methods=['GET']),
Route('/postaudio/', handlers.postaudio, methods=['POST']),
Save and exit main.py
and restart chatterd
:
server$ sudo systemctl restart chatterd
Rust with axum
Rust with axum
Editing handlers.rs
Add one new property to the Chatt
struct in handlers.rs
:
pub struct Chatt {
// . . .
audio: Option<String>,
}
To the back-end database, the audio data is just a string.
To handle audio
data uploads, make a copy of your postchatt()
function inside your handlers.rs
file and name the copy postaudio()
. Replace the parameters in the call to chatterDB.execute()
with:
"INSERT INTO chatts (username, message, id, audio) VALUES ($1, $2, gen_random_uuid(), $3)",
&[&chatt.username, &chatt.message, &chatt.audio],
which extracts the audio from the JSON object and insert it into the chatts
table, along with the rest of its associated chatt
.
Next, make a copy of your getchatts()
function inside your handlers.rs
file and name the copy getaudio()
. Replace the SELECT
statement with: "SELECT username, message, id, time, audio FROM chatts ORDER BY time DESC"
. This statement will retrieve all data, including our new audio data from the PostgreSQL database.
Still in getaudio()
, replace the chattArr.push(/*...*/);
line with:
chattArr.push(vec![
row.get(0),
row.get(1),
Some(row.get::<usize, Uuid>(2).to_string()),
Some(row.get::<usize, DateTime<Local>>(3).to_string()),
row.get(4),
]);
In addition to the original columns, we added reading the audio
column and included it in the chatt
data returned to the front end.
Save and exit handlers.rs
.
Routing for new URLs
For the newly added getaudio()
and postaudio()
functions, add the following new routes to the Router
instantiation of the router
variable in main.rs
, before the .layer(/*...*/)
line:
.route("/getaudio/", get(handlers::getaudio))
.route("/postaudio/", post(handlers::postaudio))
Save and exit main.rs
. Rebuild and restart chatterd
:
server$ cargo build --release
server$ sudo systemctl restart chatterd
Submitting your back end
- Commit new changes to the local repo at your back end with:
server$ cd ~/441/chatterd server$ git commit -am "audio back end"
and push new changes to the remote GitHub repo with:
server$ git push
- If
git push
fails due to new changes made to the remote repo, you will need to rungit pull
first. Then you may have to resolve any conflicts before you cangit push
again.
Congratulations! You’ve completed the back end modifications necessary to support audio
!
Prepared for EECS 441 by Wendan Jiang, Ollie Elmgren, Benjamin Brengman, Mark Wassink, Alexander Wu, Chenglin Li, Yibo Pi, and Sugih Jamin | Last updated: December 9th, 2024 |