Build An Audio AI App Transcripts
Chapter: Feature 1: Transcripts
Lecture: UI for Transcription

Login or purchase this course to watch this video and the rest of the course contents.
0:00 Well, it's awesome that we have our transcribe core functionality done, but the UI, the UI, let's go back to the UI.
0:14 We're playing with this AMA on here. Notice already that the transcript behaviors actually kind of working, right?
0:25 Or when there was no transcript, like let's go to another one, it says create transcript. And we go down here to the AMA and it says view transcript.
0:39 That is excellent. But the way we got that to happen was typing weird URLs into there. Let's go and make it now so that we can click on this.
0:50 Right now it just puts this hashtag transcribe at the top, not really helpful, is it?
0:56 So let's wire in our HTMX behavior here to call that function that we were previously doing by hand. So we're going to need to go to this URL here.
1:11 I'm going to just copy that because it's going to be a good little template for us.
1:17 And then over speaking of templates, if we go to podcasts and episode, well, here's where
1:23 we got that create transcript and let's wrap that around a bit.
1:28 So what we want to do is we want to say instead of just going to the URL, we want to have some HTMX behaviors.
1:36 First of all, over here, I have installed, I've installed HTMX, the site here.
1:43 So if we go down, you can see right here at the bottom of the page, we're including this HTMX minified version into the website. That's it.
1:55 It's installed. There's no post build step. There's none of that weirdness.
1:58 It's just included in that thing, that layout, shared layout thing includes it throughout the site.
2:04 So given that it's installed, we need to say what is going to cause this URL to be called. Okay. Well, let's specify the URL first.
2:15 So we can do HTMX and you can see all the different things. We're going to do a get to something like this URL, but notice this is just standard text.
2:26 The action is going to be transcribe. It's always the same, but the podcast somewhere up here, you can see the podcast is passed in.
2:37 So we need to pass in that and the way you do this in Chameleon is you say dollar curly things and the podcast is an object.
2:46 So we can just say podcast ID and then the episode, same deal, episode dot episode number like that.
2:57 So that's going to say, here's where we go when an action is taken with HTMX, but what causes the action? It could be a keystroke.
3:06 It could be a mouse over. It could be a timer. We want our trigger to just be click. So when we click the button, go do this thing.
3:17 Let's see what that does. Refresh this page and I'll actually move this up so you can see below if anything happens. Click that.
3:31 Now what showed up in here is really funky, but it's working. See the bottom? And in a minute, it's going to say it's done. But what is this?
3:42 Well, remember when we went to that URL, it gave us back this little fragment of JSON to say what was going on.
3:49 Okay, I'll leave this going here a little bit at the bottom so you can kind of see when it is actually done.
3:57 So what happens is when you have one of these HTMX behaviors, it just says, I'm going to
4:02 go to this URL and replace this element with whatever I got back. What I should have sent back is I should have sent back some HTML.
4:14 Not just this particular button, but I want this whole section to turn into like, hey, we're working right now. Please don't do more stuff.
4:22 So I can say HX target equals this. So instead of replacing the element that caused it, it's going to look up here and do that.
4:34 And I'm going to do HX swap is outer HTML. So take this whole div, basically delete that and put in whatever we put back from the server.
4:49 And what we'll return from the server is something that says, hey, we're working on it. Just hold on. So let's go back to our AI view here.
5:03 And instead of returning this, we want to somehow render a template.
5:11 Now the way this has been working throughout this whole site, and if you've seen it, if
5:15 you've been paying attention, you'll already know, but I'm using a library called FastAPI
5:19 Chameleon that I created actually, that allows us just to wrap or assign a template here. So let's go back.
5:29 And instead of doing this business, we're going to say FastAPI Chameleon, import this. If you're like, I don't like Chameleon, I want Jinja.
5:41 There's also a FastAPI Jinja template that is derived from this that you can use as well.
5:45 So regardless of what template language you're using, you can do whatever you want here.
5:50 Let's go to the template section and I'll make a new folder called AI to match the views.
5:58 And in here, let's make, go for us, regular file that I'll call HTML. I don't want all that stuff in there. So I'll call this job running dot HTML.
6:13 And so we're going to say this is going to be AI and then job running like that. And this is where this thing comes in.
6:22 So we're going to return to that dictionary and return the VM dot to dictionary. You don't need to say start a job there because it already started.
6:34 And the other important thing is we've got to set the job ID to job dot ID that we got back from the database and the background service.
6:42 Because this ID is going to be used right there later. Let's put something meaningful in here because a blank page is going to look pretty blank.
6:53 So let's go over here and I have an image actually, and static image and it's these
6:58 little working busy ball sort of, hey, the site is working type of thing. And then let's put this into a div like that.
7:09 And then it's going to grab the job name that comes out of the view model. Again, that's why we're using it and where's it right here.
7:17 And while we're returning this right, everything, all the stuff here, all those things are accessible to it.
7:25 So at first, we're just going to say it's running and nothing else. Let's go ahead and just recompile and restart that. And it says it's done.
7:37 Now it says the transcript exists. I'm going to have to just go find another one. Let's do this one. And we'll go ahead and kick that off.
7:44 And look at that. Boom. Transcribing. Hang tight. That's excellent. That is, that is really cool.
7:54 I think it tells you like, look, it's working and we'll just let it run for a second. Keep your eye on the bottom there. Hey, it's done.
8:04 The formatting is weird. I got to work on that. But it still just says this. So we need some way to tie that other piece together.
8:13 Here, this check job. Okay. Let's use another type of deal here. Say the template is job completed.
8:28 And we're going to go into this one and say, check the status. If it's done, we want to give them some kind of UI that says your job is done.
8:38 Click here to view the transcripts. Otherwise we're just going to leave it with that spinning background thing that we got from here. All right.
8:46 We're going to need to make this job completed HTML as well as in be completed. There we go. And let me put a little bit different HTML there.
8:57 I'm going to say success. The whatever it is, transcript in this case is ready. Click here to view it. Okay. And it'll be like, click here.
9:07 This is all part of that view model data that gets passed back. As it could be a transcript, it could be a summary, et cetera. Okay.
9:14 So we're going to return that back when it is finished. But the other trick is, well, how do we get this once it, this goes back to that page.
9:23 Remember we click the button, HTML swapped it out and here's plain HTML. We need to say somehow to HTML, check again, keep checking.
9:34 Just ask, is that job done? Hey, is that job done? When it is, then do something. Okay. Then show the completed.
9:40 So in order to get that to happen, we're going to go up here. I'm going to do some more stuff here as well.
9:46 HTML target is this, and we're going to use a timer. So when this gets sent back, it's just going to say, Hey, just check every second or so
9:56 and see what's going on. Now let's line these up so you can get a little better look at them.
10:04 For the URL, we're going to set the HX get to just go to AI check dash status job ID. Right there.
10:16 The ID comes from the view model when you pass it up over so it can use that to loop back around.
10:22 And again, the trigger is going to be every, how often do you want it run? Every three seconds.
10:29 So just call that URL every three seconds and swap out the outer HTML with whatever comes back. So how do we make that happen here?
10:40 Right now, it just prints out something. So we're going to need to use this template to respond, depending on whether it's finished or not.
10:47 Now over here in the view model thing, we have this check job view model already.
10:54 So what we want to do is just create that on this side and I create one of those. See what does it need? It needs the request and it needs the job ID.
11:09 So request is good. We already have that. And this is going to be, it needs to be parsed over from a string.
11:17 I don't think, don't think fast API will do it. If it did, that would be cool, but I'm just going to assume that it doesn't.
11:24 We'll say await VM dot load. We go over here and look at this real quick.
11:31 It has to do, basically this get us the job and check and see if it's finished and so on as before. Okay, so it's going to do that here.
11:42 And let's test. We'll say if not, if for some reason that failed, it returns an error message.
11:48 We're just going to use this thing in the infrastructure section called the web utils that can return an error status code.
11:56 And this will return a fast API or starlet response with those values set. Okay. If it's not finished, maybe we'll just print out something.
12:05 The job, just that it's still running, something like that. Just give us a little bit of feedback that there's something going on here.
12:14 We don't want to show the completed HTML using the completed template. We want to use this one up here, this job running.
12:23 So we got to write a little bit different than the standard code here.
12:26 We go to fast API chameleon and we can give it a response and we just set the template name and then says, what is the template data you want to pass?
12:35 We're going to pass the template data by saying VM dot to dict like this. And we want to return that.
12:44 Otherwise we'll just return a VM to dictionary as well, but it's going to use that template. Okay.
12:52 It's not a lot of code here for all those little weird edge cases, right? So when it comes into check, it's going to say, is the job done?
12:59 This check it's going to be done triggered by HTMX's polling trigger. It's going to load it up, load all the details, check the job.
13:07 If there's some kind of error, like the episode doesn't even exist, well, we'll just say, sorry, no idea.
13:14 If it's still running, we'll do a little print and we'll send back the same HTML as before.
13:22 This one, it'll replace it, but then it'll start checking again. And eventually when it is done and we just return the details and this will render the
13:33 completed UI, which says your job is done, click here to see it. All right, let's test this out and see how it goes.
13:40 Over here we have this one that's never came back, never told us anything. So that wasn't great, but it did finish and there's this transcript.
13:48 Let's go find one that doesn't have its transcript. This episode where we just ship open source.
13:52 In this episode we announced Brian and I that we'd created three different open source projects that week and put them on PyPI. That was kind of crazy.
13:59 It has a transcript as well. Let's try this one. Okay, no transcript here. Fantastic. So we should be able to do this.
14:09 And let's go ahead and hit create transcript, transcribing, hang tight. Should see the check in two seconds. There we go. The job is still running.
14:18 Look at that, it's checking. Job is still running. Still running, you can't quite see because it just says the same thing over and over again.
14:29 I go over here and erase it. Shouldn't a second say still running. It is. Hold on. We'll let assembly AI do their magic and see what happens.
14:45 It's done. Oh my gosh. And look at that. Just a second or two later it says success. Your transcript is ready. View the transcript.
14:53 We're not quite ready to view the transcript yet, are we? But it's done. And look, no more. If I erase it, there's no more polling. Is the job done?
15:02 Once it's done, how does it stop? How does it stop polling? Over here when it has this, it returns this HTML version that says check. Here's a new one.
15:12 And the new one says check. Here's a new one. The new one says check. But when it's done, it says here.
15:18 Here is some plain HTML with no triggers or HTML or anything. It's just, here's your HTML. So this thing is done. There's no more interaction.
15:27 Very, very cool. So we've got this whole end to end. Create this background job. Use HTMX to kick it off and watch it.
15:35 Send it into this whole infrastructure with AsyncIO. And then it kicks off this asynchronous transcription at Assembly AI. And then it polls for it.
15:45 Just check in the database. Hey, the thing you put in there, has it marked done yet? No? Okay. We'll keep checking. When it is, here we go.
15:52 The last thing to do is have a UI around this viewing of the transcript. And they are in the database.
15:57 And if I refresh it, you can see View Transcript because it exists for this episode.


Talk Python's Mastodon Michael Kennedy's Mastodon