Problem/Motivation
Currently, when https://oembed.com/providers.json is fetched to generate a list of available oEmbed providers, there is no opportunity to alter the list of providers.
This would be useful in scenarios such as 1) Peer Tube and 2) oEmbed providers that may be organization-specific. Peer Tube is a decentralized video platform. The user can search videos across multiple instance of PeerTube with different domain names. Re custom oEmbed providers, the University of Nebraska-Lincoln hosts its own video service, which will soon support oEmbed. This functionality is needed by this organization.
Proposed resolution
Provide hook_oembed_providers_alter(). (Media already provides hook_oembed_resource_url_alter()).
Remaining tasks
Submit patchTests pass- Subsystem maintainer review
Change record
User interface changes
None.
API changes
Adding hook_oembed_providers_alter().
Data model changes
None.
Release notes snippet
TBD
Original report by phjou
I have studied the code from the oEmbed system in the core and I have discovered that it is based on the list provided by https://oembed.com/providers.json
This is a good list to start but I am wondering how to add a provider to this list?
You can host a new file with the additional services and change the settings "oembed_providers_url". But this method is really complicated to just support more providers.Moreover I am creating this issue in order to support PeerTube which is a decentralized video platform. The user can search videos across multiple instance of PeerTube with different domain names. Such a complicated way to support a new provider is just impossible for a simple contributor.
In order to resolve this problem:
- The user should be able to override the default URL. The issue already exists for another reason: #2999018
- We should be able to alter the provider list by using a hook. What do you think about adding an alter call in the getAll function from Drupal\media\OEmbed\ProviderRepository?
Comments
Comment #2
phjouComment #3
chris burge commentedUpdating version to 8.8.x-dev. Patch is coming.
Comment #4
chris burge commentedAttached patch provides
hook_oembed_providers_alter(). Tests have been updated.Comment #5
chris burge commentedComment #6
chris burge commentedComment #7
chris burge commentedComment #8
phenaproximaI don't know if we want to have both #2999018: Expose oEmbed provider URL setting in the Media configuration form and this hook. If you can provide your own providers.json, do you really need this hook? The only "advantage" the hook provides is that you can alter the providers dynamically depending on various conditions -- sorta (they are strongly cached). But that goes against what the provider data is for; it's meant to be static database of oEmbed providers you want to support. An alter hook for this data does not, IMHO, make a lot of sense.
For now, I'll leave this issue open, but I'm in favor of closing this one out. If there is some other justification for adding this hook, I'd like a framework manager to weigh in on those potential pros and cons.
Comment #9
chris burge commentedRe #8, the primary benefit of using
hook_oembed_providers_alter()to add a provider is that it's a maintenance-free solution. If you provide your own providers.json file, then you are [probably] forking the authoritative source. Once you've done that, now you must periodically merge in any changes from upstream. Withhook_oembed_providers_alter(), you never have to revisit it.Comment #10
seanbI agree with the alter hook is more flexible than #2999018: Expose oEmbed provider URL setting in the Media configuration form and it also avoids exposing a setting a lot of users might not understand. So +1 from me for closing #2999018: Expose oEmbed provider URL setting in the Media configuration form as a duplicate and use an alter hook instead.
Comment #11
phenaproximaThe approach has been signed off by a subsystem maintainer, so removing that tag.
Comment #12
phjouThe other advantage in addition of being more flexible is that without a hook to alter the providers, we have to override the default list. That's what I did in the Peertube module: it takes the default list and add more providers inside it. But if there is another contrib module that needs to do the same, both modules will not be able to work together. The last enabled module will override the config to use his own list. So using a hook will solve that problem and give the opportunity to the contrib modules to easily support new providers without conflicts.
Comment #13
chris burge commentedI'm happy to make whatever changes come up during code review :-)
Comment #14
zipymonkey commentedThe patch in #4 is working for us. I am able to add our instance of kaltura mediaspace to the list of approved providers.
Comment #15
joewhitsittWe are using #4 as well for the University of Iowa's Kaltura/Mediaspace.
Comment #16
zipymonkey commented@joewhitsitt I'm having issues with pulling down a proper thumbnail from Kaltura/Mediaspace (see https://www.drupal.org/project/drupal/issues/3080666). Are you having similar issues? Did you figure out a work around?
Comment #18
chris burge commentedTests still pass on 8.9.x. Per #14 and #15, setting to RTBC.
Comment #19
phenaproximaSorry to kick this back, but it looks like it still needs a change record before it can be committed. :(
Comment #20
chris burge commentedChange record added: Added hook_oembed_providers_alter() to allow oEmbed provider list to be altered
Comment #21
phenaproximaCould the change record get a bit more detail about the hook? For example, we should state that it runs before providers are cached, and that it operates on raw provider information, before they are converted to Provider value objects. Also, maybe we want to mention that one could consult media.api.php for further information. While I'm at it, I'd also like to expand the doc comment in that file so that we state everything I just mentioned, and examples of why you'd want to implement this hook.
I'm really sorry to keep moving the goal posts on this one, but we're adding a whole new hook here and it's important that it be properly documented. There's a good chance committers would raise these concerns as well.
Comment #22
chris burge commentedUpdated patch with modified doc block per #21 (also caught a typo). Change record is updated.
Comment #23
phenaproximaI like that. Restoring RTBC.
Comment #24
chris matthews commentedWould it be possible to back port this patch to 8.8.x or has that ship sailed?
Comment #25
phenaproximaThis is a feature request, so I don't think it will be backported.
Comment #26
chris burge commented@Chris Matthews - We're running 8.8.1 with patch #22. It's fairly self-contained in what it does, so the only risk, from our point of view, is that it needs rerolled at some point between 8.8.1 and 8.9.0.
Comment #28
chris burge commentedSetting back to RTBC. That test failure is random. See #3103492: Random fail in WidgetUploadTest.
Comment #29
alexpott+1 on pointing out that altering occurs pre-cache. I think we should point out that implementations need to avoid anything dynamic.
However given the need seems to be to add new providers I'm wondering whether an alter hook is really the best way forward here. There are a couple of ways we could go forward - we could implement some yaml parsing thing to add new providers or we could say that modules such as Peer Tube should be encouraged to decorate the service by doing something like:
And then Drupal\peertube\ProviderRepository gets the info from the inner service and adds its own.
Is there a (usual) case for altering the existing list of providers rather than just adding?
Adding an argument in the middle of a constructor is hard from an BC point of view.
Comment #30
chris burge commentedWe're using
hook_oembed_providers_alter()to add a custom provider. We're not modifying providers. There may be a valid case for modifying a provider; however, someone else will need to jump in with that use case.Re service decoration, this makes sense from a CS standpoint; however, this feature becomes accessible to only senior developers at that point.
Re parsing yaml, this doesn't seem like an unreasonable approach. It remains accessible to junior developers. Are you thinking something like 'my_module.media.oembed.yml'?
One of the benefits of
hook_oembed_providers_alter()is that it would be easy for developers to find on api.drupal.org and the documentation is automatically handled.Comment #31
phjouI am not very familiar with "decorates". I just read a little article about it. For the quick look, decorating allows you to override the content of the methods of the service
If you have two contrib modules that decorates the service "media.oembed.provider_repository", will it work?
For PeerTube, I don't see any usual case for altering the existing list of providers, just adding the new provider with multiple endpoints.
I don't think YML parsing is an option for PeerTube. Your list is completely dynamic since it is a decentralized platform (there are hundreds of different websites matching that provider), I am building the list using PHP. Users are allowed to add new instances of Peertube using a config form. If we introduce a YML file, I will have to generate it and I guess it will introduce changes inside the contrib module which is probably a bad idea.
Comment #32
chris burge commentedNew patch attached that uses the YAML approach. Because it's a change in direction, there's no interdiff.
If we go this direction, we'll need to rename the issue, update the issue summary, and modify the change record.
(Note: The patch inserts a new argument in the middle of
ProviderRepository::_construct, which I know was objected to in the patch #22; however, adding the argument to the end violates Drupal code standards, so in the middle it went.)Comment #33
phenaproximaI don't think we actually need the providers to be plugins -- the reason being, they don't provide any functionality at all. I suspect we can simply use \Drupal\Component\Discovery\YamlDiscovery to find providers in YAML files, without any need for plugin stuff.
Comment #34
phenaproximaThis, I just realized, is something we could do with a contrib project. All we'd need to do is decorate the provider repository with a YamlDiscovery thing that will scan modules for MODULE.oembed_providers.yml. So that said, maybe we could do this more quickly/easily in contrib?
Comment #35
chris burge commentedPlugin code is removed.
@phjou - Is my understanding correct that you need to dynamically add providers from user-provided config? If that's the case, can you confirm that the current approach (yaml) will not support your use case?
@phenaproxima - Quick and easy, yes.
Comment #36
phjou@chris-burge Yes, the Yml files will not solve my use case. You can't know in advance the URLs of the instances of the platform the user wants to use. I definitely need to be able to add myself the provider with PHP. YML files are good for centralized platforms but not at all for decentralized ones.
Comment #37
chris burge commentedIf we’re just adding providers and aren’t altering them, then we’d be looking at a function something like
hook_media_oembed_provider_add(). That is the type of procedural code we’ve moved away from. So then the challenge becomes how to register providers at runtime [from config]. I think we might be left with a contrib module that decorates the provider repository and provides a UI and defines config for providers. (I suppose it could also scan for YAML. There are multiple ways to register menu items. We could allow two ways to define providers - config and module YAML files.)Comment #38
phjouA contrib module that provides a config and YML sounds good to me, but this contrib module will have to provide a hook to modify the list.
So at the end the contrib module will have the same feature as the hook_oembed_providers_alter I guess?
One of the feature of the Peertube module I wanted to code is providing endpoints based on users choices like the language or category of the videos instances: https://joinpeertube.org/instances#instances-list
It will define the Peertube provider with the endpoints that match those criteria, so I will need a hook to modify it because parsing the config to find the provider sounds like a bad idea no?
Comment #39
chris burge commentedIf we do go the contrib route, we could also add an option to disable the call to grab the JSON provider file. If you're only allowing custom providers, then there's no need to download and parse the provider JSON file. We would actually use that feature.
Comment #40
phjouI am not sure to follow you because you can want both JSON parsing and config for different providers (one same provider can't be included in both).
Or do you mean the JSON parsing is a performance issue and it justifies disabling it with a global option?
Comment #41
chris burge commented@phjou - It could provide an option to disable the downloading and parsing of providers.json. It sounds like in your case that you would not enable that option because you need one or more providers defined by providers.json. In the event that a site only used custom providers, then downloading and parsing providers.json would be unnecessary (thus a potential performance hit).
Comment #42
ankithashettyI had a requirement to add the custom providers to the existing providers' list and the solution provided here was just what I needed!
I applied the patch provided in #22. Even then when I try to add a URL of the custom provider in the remote-video type field, I am not able to embed it. On debugging I was able to find the following error.
Error: Call to a member function getEndpoints() on null in Drupal\media\OEmbed\UrlResolver->getResourceUrl() (line 161 of /app/web/core/modules/media/src/OEmbed/UrlResolver.php).
I am new to Drupal so facing all these difficulties. KIndly if someone could guide me here I would be grateful.
Thank You.
Comment #43
chris burge commentedWe've added the ability to define custom oEmbed providers in the oEmbed Providers module. They're defined as config entities. AFAIK, this solution addresses every use case described herein. If needed, adding
hook_oembed_providers_alter()would be really easy. @phjou - Does this address your use case?Comment #44
phjouSo nice, thanks! I will need to find some time to test it. If I can't find a moment before the 23rd of april, I should have some time after that date.
Comment #45
chris burge commentedComment #47
slv_ commentedWhile https://www.drupal.org/project/drupal/issues/2999018 is closed as a duplicate of this, I see the point of the UI not WSODing out when the provider url is not available. Imho a media overview page should never crash because a 3rd-party service can't be reached. Status / warning messages are fine, shouldn't the WSOD be tackled as well?
Comment #48
phenaproximaYes, it should be, but it's out of scope for this issue. Would you be willing to open a separate issue to work on that?
Comment #49
phenaproximaComment #50
weekbeforenextI'm using the patch from #22 to alter oEmbed provider information fixing bugs before the oEmbed.com information is updated.
I like the idea of using the YAML file, but I need the ability to alter multiple providers.
Thanks all!
Comment #51
chris burge commentedNo.
Alter - no. Add multiple - yes.
If there were interest, we would add
hook_oembed_providers_alter()to the oEmbed Providers module. It's basically a matter of lifting the code from #22. Part of the reason we wrote the module was to avoid running multiple core patches.Comment #52
phenaproximaI wonder if we shouldn't wontfix this issue in core. It seems like the oEmbed Providers module fills the gap pretty well, and if we could implement the hook there, then I don't think we'd need to do this in core (although we could certainly revisit the idea if enough need arose).
Comment #53
chris burge commentedIssue created: #3177117: Provide hook_oembed_providers_alter().
Comment #55
chris matthews commentedFWIW, since Media already provides
hook_oembed_resource_url_alter()it makes sense to me to providehook_oembed_providers_alter()in Core. The oEmbed Providers contrib module does fill in the gap, but it seems like a pretty wide gap that "should" be handled in Core.Comment #56
randell commentedWell, sites went down yesterday. At the very least, the dependency to an external resource should not be a Core default as it serves like a ticking time bomb.
Comment #57
chris burge commented@randell - It's being discussed here: #3186184: Make oEmbed provider repository more fault-tolerant if provider database is unavailable.
Comment #60
lobsterr commentedI have rerolled the patch #3008119-22: Provide hook_oembed_providers_alter(). I can't create interdiff, because it is to much work to do taking into account it could be applied only for 9.1.*. Basically, it is absolutely the same. I have tested with the stable 9.3.0 version
Comment #61
phjouJust realized I never answered here, the oEmbed Providers module is enough since it is providing the hook.
Comment #62
ranjith_kumar_k_u commentedComment #64
lobsterr commented@ranjith_kumar_k_u - What is the point to reupload basically the same patch without any changes. Why do you do this ?
Comment #65
ankithashettyThe test bot in #60 says the patch failed to apply in core 9.4.x ( https://www.drupal.org/pift-ci-job/2284443 ), may be thats why a corrected patch is re-uploaded in #62 for 9.4.x?
Anyways, as suggested in #61 by @phjou, the oEmbed providers contributed module provides us the hook and additional cool features. I have used the module and it's great! :)
Have a great day everyone, cheers!
Comment #66
matthieuscarset commentedAgree with @ankithashetty in #65
The
oembed_providersmodule works and solve this issue.I think it's good to keep solution as a separate contrib module for now.
There was not much activity on this thread for over year so there is certainly not an urgent need to modify
core.Marking this issue as Postponed.
Linking to OEmbed Providers module again for the record.
Comment #68
chris matthews commentedIn #52 Adam (phenaproxima) said,
FWIW, I also think this feature request issue should be closed (won't fix) in favor of the contrib solution.
Comment #69
tstoecklerSo the issue is that while the contrib module works it currently has to override and circumvent a lot of the logic in core because of core's missing flexiblity. This is definitely not ideal and it would be great if that situation could be improved so that the contrib module has to do less work, this issue being one fairly straightforward way to go about it. If the hook is not deemed appropriate by core maintainers it would be great if a different approach to allow the same or similar flexibility could be discussed before rejecting the issue altogether.
That's just my two cents and if the current state of the module is the best we can get that's also fine, I just personally think we could do better.
Comment #70
steinmb commentedI have been re-reading this issue and looking at some of the changes rolled to core media since the it was started and I not sure about this issue. Perhaps it need rescoping. Would love to hear @Chris-Burge thoughts on what might make life easier for modules like oEmbed Providers (#52).
Comment #71
chris burge commentedIt would be nice if there were a way to call the decorated
->getAll()method inside the decorator->getAll()method (like you wouldparent::method()with an extended class); however, the fact that we're using key-value store as a caching mechanism makes that unworkable. We would want to control caching in the decorator method and disable it in the decorated method. I think we're stuck with how we're doing things now.That actually makes me think that the current implementation of
hook_oembed_providers_alter()has a fairly major bug. The key-value store is set to store providers for 7 days (604800 seconds). There's no way to clear it. You just have to wait up to 7 days. So.. I would expecthook_oembed_providers_alter()would only run if the key-value value has expired, not every time->getAll()is executed. Am I thinking about that correctly?Comment #74
chris matthews commentedSeems as though there are 3 paths that could be taken ...
hook_oembed_providers_alter()hook_oembed_providers_alter()in Core per the original proposed resolution in the ISWhat is the will of the council?
Comment #75
chris burge commented@Chris Matthews - Thanks for reanimating this issue.
Re #71, the first part is still an issue, but it probably falls under the "would be nice" category. We could break the current
->getAll()method into two methods. The first [new] method would solely be responsible for fetching (no caching). The second method would be->getAll(). It would call the first method and then cache the results in key/value before returning the providers list. From a BC standpoint, it would be 100% non-breaking. Then - the oembed_providers module could call the new fetch-only method instead of->getAll(). Now that I think about it. It would be pretty easy to implement and to add test coverage. This isn't a heavy lift.The second part was addressed in #3296300: Add UI to clear provider list from keyvalue. If
hook_oembed_providers_alter()were implemented in core, then I think we would also want to address the caching issue in core - namely how to clear the values cached in key/value if someone doesn't want to wait 7 days. That might be a can of worms not worth opening.What about this?
ProviderRepositoryclass in core.hook_oembed_providers_alter()in contrib.Comment #76
chris matthews commentedBoth make sense to me.
Comment #77
chris burge commentedI dove into
ProviderRepository, and now that I'm in the code, I'm seeing it's going to be a bit more challenging to refactor->getAll()into two methods. We'd also have to updateProviderRepositoryInterrface, which creates a BC issue. So - on second thought, let's not move forward on that.In terms of this issue, I think we're good to change the status to 'Closed (won't fix)'.