Skip to content

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

Open
wants to merge 20 commits into
base: master
Choose a base branch
from

Conversation

riccardobl
Copy link
Member

@riccardobl riccardobl commented Nov 26, 2024

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.

allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'
referrerPolicy='strict-origin-when-cross-origin'
/>
</div>
Copy link
Member Author

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

@riccardobl riccardobl marked this pull request as draft November 26, 2024 11:44

export default {
Query: {
fetchEmbedMeta: async (parent, { provider, args }, { models, me }) => {
Copy link
Member Author

@riccardobl riccardobl Nov 26, 2024

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: {
Copy link
Member Author

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')
Copy link
Member Author

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

where: { hash }
}))?.meta

if (meta?.updatedAt && (Date.now() - meta.updatedAt.getTime()) / 1000 > EMBED_META_CACHE_DURATION_S) {
Copy link
Member Author

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

@riccardobl riccardobl marked this pull request as ready for review November 26, 2024 12:05
Copy link
Contributor

@Copilot Copilot AI left a 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')

@riccardobl riccardobl added the enhancement improvements to existing features label Nov 26, 2024
Copy link
Member

@ekzyis ekzyis left a 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?

@riccardobl
Copy link
Member Author

riccardobl commented Nov 30, 2024

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.

@huumn
Copy link
Member

huumn commented Dec 1, 2024

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 "Item" so that we don't have to join (or do an additional query) when items are queried.

(I'd prefer images/videos be normalized like your embed code and denormalize in a trigger like this, but that's another issue.)

Thoughts?

@riccardobl riccardobl requested a review from Copilot December 2, 2024 11:49
Copy link
Contributor

@Copilot Copilot AI left a 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}&amp;clipt=${clipt}`}

This comment was marked as off-topic.

@riccardobl riccardobl marked this pull request as draft December 2, 2024 12:42
@riccardobl riccardobl requested a review from Copilot December 2, 2024 15:46
Copy link
Contributor

@Copilot Copilot AI left a 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.

@riccardobl riccardobl marked this pull request as ready for review December 2, 2024 17:51
@riccardobl
Copy link
Member Author

riccardobl commented Dec 2, 2024

I've moved the fetcher into a worker and added some logic to avoid refetches.
The system is extensible by registering a function of type MetaFetcher to a provider key in lib/embedFetcher/MetaFetchers.

@riccardobl riccardobl requested review from Copilot and huumn December 2, 2024 17:54
Copy link
Contributor

@Copilot Copilot AI left a 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 contains videoId, clipId, and clipt 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.

if (!id) {
console.warn('[fetchEmbedMeta] embed', provider, "doesn't have an id, skipping")
continue
}
Copy link
Member Author

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)
Copy link
Member Author

@riccardobl riccardobl Dec 2, 2024

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

Copy link
Member

@huumn huumn left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement improvements to existing features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

YouTube "clip" embed
3 participants