This repository has been archived by the owner on Nov 5, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add experimental YouTube caption integration that doesn't really work
- Loading branch information
1 parent
ed3e85d
commit b2bbc2a
Showing
13 changed files
with
419 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
const youtube = require('express').Router(); | ||
const axios = require('axios'); | ||
const rateLimit = require('express-rate-limit'); | ||
|
||
const rateLimitWindowMinutes = 5; | ||
const requestsAllowedPerSecond = 1; // Frontend limits to one request per second | ||
const rateLimitLeeway = 10; | ||
const rateLimiter = rateLimit({ | ||
windowMs: rateLimitWindowMinutes * 60 * 1000, | ||
max: rateLimitWindowMinutes * requestsAllowedPerSecond * 60 + rateLimitLeeway, | ||
}); | ||
|
||
youtube.use('/', rateLimiter); | ||
|
||
youtube.post('/', async (req, res) => { | ||
const { apiPath, transcript } = req.body; | ||
if (!apiPath || !transcript) { | ||
return res.sendStatus(400); | ||
} | ||
|
||
try { | ||
// Verify this is actually a URL and a YouTube closed caption URL | ||
const url = new URL(apiPath); | ||
if ( | ||
url.origin !== 'http://upload.youtube.com' || | ||
url.pathname !== '/closedcaption' | ||
) { | ||
throw new Error(); | ||
} | ||
} catch (e) { | ||
return res.sendStatus(400); | ||
} | ||
|
||
// At one point I thought doing this might improve caption delay | ||
// in YouTube but I don't think so | ||
const offsetTimestampSeconds = 0; | ||
|
||
const body = `${ | ||
new Date(new Date().getTime() - 1000 * offsetTimestampSeconds) | ||
.toISOString() | ||
.split('Z')[0] | ||
}\n${transcript}\n`; | ||
|
||
axios | ||
.post(apiPath, body, { | ||
headers: { | ||
'Content-Type': 'text/plain', | ||
}, | ||
}) | ||
.then(() => { | ||
// We got a successful response | ||
res.sendStatus(200); | ||
}) | ||
.catch((e) => { | ||
if (e.code === 'ENOTFOUND') { | ||
return res.sendStatus(404); | ||
} else { | ||
const errorCode = | ||
e && e.response && e.response.status ? e.response.status : undefined; | ||
switch (errorCode) { | ||
case 400: | ||
return res | ||
.status(400) | ||
.send( | ||
`Error: The Zoom meeting has not started yet or it has already ended.` | ||
); | ||
default: | ||
return res | ||
.status(520) | ||
.send(`Something went wrong. (${errorCode || 'Unknown error'})`); | ||
} | ||
} | ||
}); | ||
}); | ||
|
||
module.exports = youtube; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<template> | ||
<div> | ||
<img | ||
:src="channel.iconPath" | ||
class="w-100 col-6 d-block mx-auto mt-2 mb-3" | ||
alt="YouTube" | ||
/> | ||
<p class="lead text-center"> | ||
Send real-time captions to a YouTube live stream. | ||
</p> | ||
<hr /> | ||
<ol> | ||
<li> | ||
In YouTube Studio, set up a live stream. Go to stream settings and | ||
enable closed captions. | ||
</li> | ||
<li> | ||
Select "Post captions to URL." | ||
</li> | ||
<li> | ||
Copy the captions ingestion URL and paste it here. | ||
</li> | ||
</ol> | ||
<div class="card card-body"> | ||
<div | ||
v-if="savedChannel && savedChannel.error" | ||
class="alert alert-warning small" | ||
> | ||
<strong class="text-danger"> | ||
<fa icon="exclamation-triangle" /> Error: | ||
</strong> | ||
{{ savedChannel.error }} | ||
</div> | ||
<label for="url" class="small"> | ||
YouTube captions ingestion URL | ||
</label> | ||
<input | ||
id="url" | ||
name="url" | ||
v-model="url" | ||
autofocus | ||
class="form-control" | ||
type="url" | ||
placeholder="YouTube captions ingestion URL" | ||
/> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
props: { | ||
channel: { | ||
required: true, | ||
type: Object, | ||
}, | ||
savedChannel: { | ||
required: false, | ||
type: Object, | ||
}, | ||
}, | ||
mounted() { | ||
this.url = this.savedChannel?.parameters?.url; | ||
}, | ||
data() { | ||
return { | ||
url: null, | ||
}; | ||
}, | ||
watch: { | ||
url: { | ||
immediate: true, | ||
handler(url) { | ||
this.$emit('parametersUpdated', { | ||
url, | ||
}); | ||
if (this.url) { | ||
this.$emit('formValid'); | ||
} else { | ||
this.$emit('formInvalid'); | ||
} | ||
}, | ||
}, | ||
}, | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.