-
-
Notifications
You must be signed in to change notification settings - Fork 124
support youtube clip embedding #1652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
components/embed.js
Outdated
allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share' | ||
referrerPolicy='strict-origin-when-cross-origin' | ||
/> | ||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
react-youtube doesn't support clips yet
api/resolvers/embed.js
Outdated
|
||
export default { | ||
Query: { | ||
fetchEmbedMeta: async (parent, { provider, args }, { models, me }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to happen serverside because youtube doesn't set cors headers. See #879
lib/url.js
Outdated
clipId: id, | ||
clipt: null | ||
}, | ||
fetchMeta: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this new field is used to tell the code to fetch additional metadata from an external provider
lib/validate.js
Outdated
@@ -487,6 +487,10 @@ export const pushSubscriptionSchema = object({ | |||
auth: string().required('required').trim() | |||
}) | |||
|
|||
export const youtubeClipMetaFetchArgsSchema = object({ | |||
clipId: string().required('required').matches(/^[\w_]+$/, 'invalid clip id').max(64, 'clip id is too long') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i don't really know the length of yt clip ids, i've used 64 to be conservative
api/resolvers/embed.js
Outdated
where: { hash } | ||
}))?.meta | ||
|
||
if (meta?.updatedAt && (Date.now() - meta.updatedAt.getTime()) / 1000 > EMBED_META_CACHE_DURATION_S) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't cache this forever, so that the system recovers at some point if youtube does breaking changes
f936208
to
45cd454
Compare
45cd454
to
4c8a785
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 5 out of 10 changed files in this pull request and generated no suggestions.
Files not reviewed (5)
- prisma/migrations/20241126113801_embed_meta/migration.sql: Language not supported
- prisma/schema.prisma: Language not supported
- api/typeDefs/index.js: Evaluated as low risk
- components/embed.js: Evaluated as low risk
- lib/constants.js: Evaluated as low risk
Comments skipped due to low confidence (1)
lib/validate.js:491
- [nitpick] The error message 'invalid clip id' could be more descriptive. Consider changing it to 'Clip ID must be alphanumeric and can include underscores'.
clipId: string().required('required').matches(/^[\w_]+$/, 'invalid clip id').max(64, 'clip id is too long')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there something wrong with cachedFetcher
from lib/fetch
?
I thought we discussed that we want to use the same caching implementation like we do for price, blockheight, fees etc.
Or wasn't this changed yet so this isn't actually ready for review yet?
cacheFetcher uses in-memory cache, it is good for price, blockheight and fees because they all are a single values that come from a single source, but using it to store clips metadata can potentially make the cache grow too large. Using the database and its memory management capabilities makes more sense in this case, also because this cache can last for a very long time (even for ever if we want to), since these values should never change unless youtube updates its internals. |
I like that you created a table for embed meta! Rather than doing a "lazy load" of embed meta, I think it might be better to do it as job in the worker like we do with images/video, so that the client doesn't need to query for the embed meta. That method has worked well for us. We can also add a trigger to denormalize the embed meta into its (I'd prefer images/videos be normalized like your embed code and denormalize in a trigger like this, but that's another issue.) Thoughts? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 5 out of 15 changed files in this pull request and generated 1 suggestion.
Files not reviewed (10)
- prisma/migrations/20241202100121_embedmeta/migration.sql: Language not supported
- prisma/schema.prisma: Language not supported
- worker/fetchEmbedMeta.js: Evaluated as low risk
- api/paidAction/itemUpdate.js: Evaluated as low risk
- api/typeDefs/item.js: Evaluated as low risk
- components/comment.js: Evaluated as low risk
- components/item-full.js: Evaluated as low risk
- worker/index.js: Evaluated as low risk
- api/paidAction/itemCreate.js: Evaluated as low risk
- fragments/comments.js: Evaluated as low risk
<iframe | ||
title='Youtube Clip' | ||
allowFullScreen | ||
src={`https://www.youtube.com/embed/${videoId}?clip=${clipId}&clipt=${clipt}`} |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 15 out of 15 changed files in this pull request and generated no suggestions.
I've moved the fetcher into a worker and added some logic to avoid refetches. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 5 out of 16 changed files in this pull request and generated 1 suggestion.
Files not reviewed (11)
- prisma/migrations/20241202100121_embedmeta/migration.sql: Language not supported
- prisma/schema.prisma: Language not supported
- components/comment.js: Evaluated as low risk
- components/item-full.js: Evaluated as low risk
- api/paidAction/itemCreate.js: Evaluated as low risk
- worker/index.js: Evaluated as low risk
- api/typeDefs/item.js: Evaluated as low risk
- api/paidAction/itemUpdate.js: Evaluated as low risk
- fragments/items.js: Evaluated as low risk
- fragments/comments.js: Evaluated as low risk
- components/text.js: Evaluated as low risk
Comments skipped due to low confidence (1)
components/embed.js:182
- Ensure that the
meta
object containsvideoId
,clipId
, andclipt
properties before constructing the iframe URL. Add a check to handle missing properties gracefully.
if (provider === 'youtube-clip') {
|
||
const content = metaProp.getAttribute('content') | ||
|
||
const fClipId = content.match(/clip=([^&]+)/)[1] |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
if (!id) { | ||
console.warn('[fetchEmbedMeta] embed', provider, "doesn't have an id, skipping") | ||
continue | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some parsers in lib/url/parseEmbedUrl have undefined id, this is a bit counter intuitive, but they are valid for the rest of the code.
We need an id to cache the embed in the database, so instead of trying generate one by hashing stuff, i've decided to just add a warning here, so if any other dev is trying to add server side meta fetching for those providers, but forgets to add an id, he will have something to debug why it doesn't work.
const embeds = urls | ||
.map(url => parseEmbedUrl(url)) // parse every unique url as a potential embed (initial url based deduplication) | ||
.filter(embed => !!embed) // if it is not an embed, we can skip it | ||
.reduce((acc, embed) => { // deduplicate embeds by id and provider (they might have different urls but be resolved to the same embed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would someone want to embed the same thing more than once?
Idk, to abuse the system, or meme, either way, better to try to stop it here rather than firing multiple fetches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will probably sit until after jan 3.
We haven't had anyone share a youtube clip in months, so on that level this may not be worth it for youtube clips alone, but it'll be worth merging if the logic is also useful for other stuff. Hard to tell atm, but let's keep it open until we have more time for nice-to-haves.
Description
Closes #879
This PR uses the field fetchMeta and a supporting api to fetch additional metadata needed to embed the video bypassing the lack of cors headers.