Skip to content

oEmbed module initial release #16

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

Merged
merged 20 commits into from
Mar 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
510 changes: 510 additions & 0 deletions .github/workflows/oEmbed-Build.yml

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions Build/GitHub/Jobs/oEmbed.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@{
"runs-on" = "ubuntu-latest"
if = '${{ success() }}'
steps = @(
@{
name = 'Check out repository'
uses = 'actions/checkout@v2'
},
@{
name = 'GitLogger'
uses = 'GitLogging/GitLoggerAction@main'
id = 'GitLogger'
},
@{
name = 'Use PSSVG Action'
uses = 'StartAutomating/PSSVG@main'
id = 'PSSVG'
},
@{
name = 'Use PipeScript Action'
uses = 'StartAutomating/PipeScript@main'
id = 'PipeScript'
},
'RunEZOut',
'RunHelpOut',
@{
name = 'Use PSJekyll Action'
uses = 'PowerShellWeb/PSJekyll@main'
id = 'PSJekyll'
}
)
}
10 changes: 10 additions & 0 deletions Build/GitHub/Steps/PublishTestResults.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@{
name = 'PublishTestResults'
uses = 'actions/upload-artifact@main'
with = @{
name = 'PesterResults'
path = '**.TestResults.xml'
}
if = '${{always()}}'
}

12 changes: 12 additions & 0 deletions Build/oEmbed.GitHubWorkflow.PSDevOps.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#requires -Module PSDevOps
Import-BuildStep -SourcePath (
Join-Path $PSScriptRoot 'GitHub'
) -BuildSystem GitHubWorkflow

Push-Location ($PSScriptRoot | Split-Path)
New-GitHubWorkflow -Name "oEmbed Module Build" -On Push,
PullRequest,
Demand -Job TestPowerShellOnLinux,
TagReleaseAndPublish, oEmbed -OutputPath .\.github\workflows\oEmbed-Build.yml

Pop-Location
166 changes: 166 additions & 0 deletions Commands/Get-oEmbed.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
function Get-OEmbed {
<#
.SYNOPSIS
Gets oEmbed data for a given URL.
.DESCRIPTION
Gets oEmbed data for a given URL.

[oEmbed](https://oembed.com/) is a format for allowing an embedded representation of a URL on third party sites.

Most social networks support oEmbed, so this little function lets you embed almost any social network post
.EXAMPLE
oEmbed -Url https://www.youtube.com/watch?v=UIR9Z_JdVhs
#>
[Alias('oEmbed')]
[CmdletBinding(PositionalBinding=$false,SupportsShouldProcess,DefaultParameterSetName='Query')]
param(
# The URL
[Parameter(Mandatory,Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName,ParameterSetName='Query')]
[Uri]
$Url,

# The maximum width of the returned image
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
[int]
$MaxWidth,

# The maximum height of the returned image
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
[int]
$MaxHeight,

# The format of the returned image
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
[string]
$Format,

# Whether to force a refresh of the cached oEmbed data
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
[switch]
$Force,

# The name of an oEmbed provider. Wildcards are supported.
[Parameter(Mandatory,ParameterSetName='ProviderByName')]
[SupportsWildcards()]
[string]
$ProviderName,

# If set, will list the oEmbed providers.
[Parameter(Mandatory,ParameterSetName='ProviderList')]
[switch]
$ProviderList
)

begin {
# If we haven't yet cached the list of oEmbed providers, do so now.
if (-not $script:cachedOmbedProviders) {
$script:cachedOmbedProviders = Invoke-RestMethod "https://oembed.com/providers.json"
}
# If we haven't yet cached the list of oEmbed endpoints, do so now.
if (-not $script:openEmbeddings) {
$script:openEmbeddings = $script:cachedOmbedProviders.Endpoints.Url -as [uri[]]
}
# Create a cache to store the oEmbed data in, if we haven't already done so.
if (-not $script:oEmbedUrlCache) {
$script:oEmbedUrlCache = [Ordered]@{}
}

if (-not $script:oEmbedDomainCache) {
$script:oEmbedDomainCache = [Ordered]@{}
}

$oEmbedQueue = [Collections.Queue]::new()
}

process {
if ($PSCmdlet.ParameterSetName -eq 'ProviderList') {
return $script:cachedOmbedProviders
}
# If we're asking for a Provider by Name
if ($PSCmdlet.ParameterSetName -eq 'ProviderByName') {
# filter the list of providers
return $script:cachedOmbedProviders |
# and return the name
Where-Object Provider_Name -like $ProviderName
}
$matchingEndpoint =
if (-not $script:oEmbedDomainCache[$url.DnsSafeHost]) {
:oEmbedProvider foreach ($oEmbedProvider in $script:cachedOmbedProviders) {
foreach ($oEmbedEndpoint in $oEmbedProvider.Endpoints) {
foreach ($oEmbedScheme in $oEmbedEndpoint.Schemes) {
if ($url -like $oEmbedScheme) {
$script:oEmbedDomainCache[$url.DnsSafeHost] = $oEmbedEndpoint.url
$script:oEmbedDomainCache[$url.DnsSafeHost]
break oEmbedProvider
}
}
}
}
} else {
$script:oEmbedDomainCache[$url.DnsSafeHost]
}


if (-not $matchingEndpoint) {
Write-Error "No oEmbed Provider found for URL '$url'"
return
}

$oEmbedUrl =
"$($matchingEndpoint)?$(
@(
"url=$([Web.HttpUtility]::UrlEncode($Url) -replace '\+','%20')"
if ($MaxWidth) {
"maxwidth=$MaxWidth"
}
if ($MaxHeight) {
"maxheight=$MaxHeight"
}
if ($Format) {
"format=$Format"
}
) -join '&'
)"

$oEmbedQueue.Enqueue([Ordered]@{
Url = $Url
oEmbedUrl = $oEmbedUrl
})
}

end {
$counter = 0
$total = $oEmbedQueue.Count
$progressId = $MyInvocation.HistoryId
foreach ($queueItem in $oEmbedQueue) {
$url = $queueItem.Url
$oEmbedUrl = $queueItem.oEmbedUrl
if ($oEmbedQueue.Count -gt 1) {
$counter++
Write-Progress "oEmbed" "Retrieving oEmbed data for $url" -PercentComplete (
$counter * 100 / $total
) -Id $progressId
}
if (-not $script:oEmbedUrlCache[$oEmbedUrl] -or $Force) {
if ($WhatIfPreference) { return $oEmbedUrl }
if (-not $PSCmdlet.ShouldProcess($oEmbedUrl)) {
Write-Verbose "Skipping $oEmbedUrl"
continue
} else {
Write-Verbose "Retrieving $oEmbedUrl"
}

$script:oEmbedUrlCache[$oEmbedUrl] = Invoke-RestMethod -Uri $oEmbedUrl |
Add-Member NoteProperty 'Url' $Url -Force -PassThru |
Add-Member NoteProperty 'oEmbedUrl' $oEmbedUrl -Force -PassThru
$script:oEmbedUrlCache[$oEmbedUrl].pstypenames.insert(0,'OpenEmbedding')
}

$script:oEmbedUrlCache[$oEmbedUrl]
}

if ($oEmbedQueue.Count -gt 1) {
Write-Progress "oEmbed" "Retrieving oEmbed data" -Completed -Id $progressId
}
}
}
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,39 @@
# oEmbed

Open Embedding with PowerShell


## The oEmbed Standard

There's lots of content on the internet. How do we embed it?


Over the years, most large sites seem to have agreeded on a standard or two.

[oEmbed](https://oEmbed.com/) is one such standard.

## The oEmbed module

This is a PowerShell module for oEmbed.

It lets you get open embeddings for any url that supports the [oEmbed standard](https://oEmbed.com/)

It contains a single command: `Get-Ombed`

This command is aliased to `oEmbed`

~~~PowerShell
oEmbed "https://youtu.be/nHlJODYBLKs?si=XmWPX6ulPDlaEzO0"
~~~

We can also list all the providers:

~~~PowerShell
oEmbed -ProviderList
~~~

Or get them by wildcard:

~~~PowerShell
oEmbed -ProviderName *tube*
~~~
3 changes: 3 additions & 0 deletions Types/oEmbed/Get.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
foreach ($argument in $args) {
oEmbed -Url $argument
}
1 change: 1 addition & 0 deletions Types/oEmbed/get_oEmbedCache.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return & $this { $script:oEmbedUrlCache }
1 change: 1 addition & 0 deletions Types/oEmbed/get_oEmbedProvider.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Get-OEmbed -ProviderList
1 change: 1 addition & 0 deletions Types/openEmbedding/ToString.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return "$($this.HTML)"
6 changes: 6 additions & 0 deletions Types/openEmbedding/get_Markdown.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
if ($this.thumbnail_url) {
return "[![$($this.title)]($($this.thumbnail_url))]($($this.url))"
}
elseif ($this.title) {
"[$($this.title)]($($this.url))"
}
26 changes: 26 additions & 0 deletions oEmbed.ps.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
$commandsPath = Join-Path $PSScriptRoot Commands
[include('*-*')]$commandsPath

$myModule = $MyInvocation.MyCommand.ScriptBlock.Module
$ExecutionContext.SessionState.PSVariable.Set($myModule.Name, $myModule)
$myModule.pstypenames.insert(0, $myModule.Name)

New-PSDrive -Name $MyModule.Name -PSProvider FileSystem -Scope Global -Root $PSScriptRoot -ErrorAction Ignore

if ($home) {
$MyModuleProfileDirectory = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) $MyModule.Name
if (-not (Test-Path $MyModuleProfileDirectory)) {
$null = New-Item -ItemType Directory -Path $MyModuleProfileDirectory -Force
}
New-PSDrive -Name "My$($MyModule.Name)" -PSProvider FileSystem -Scope Global -Root $MyModuleProfileDirectory -ErrorAction Ignore
}

# Set a script variable of this, set to the module
# (so all scripts in this scope default to the correct `$this`)
$script:this = $myModule

#region Custom
#endregion Custom

Export-ModuleMember -Alias * -Function * -Variable $myModule.Name

36 changes: 36 additions & 0 deletions oEmbed.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@{
RootModule = 'oEmbed.psm1'
ModuleVersion = '0.0.1'
GUID = 'dc68bf0c-b8b5-4f3d-9fc4-b6779f7e7d6a'
Author = 'JamesBrundage'
CompanyName = 'Start-Automating'
Copyright = '(c) 2025 Start-Automating.'
Description = 'Open Embeddings in PowerShell'
FunctionsToExport = 'Get-oEmbed'
AliasesToExport = 'oEmbed'
TypesToProcess = 'oEmbed.types.ps1xml'
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('oEmbed','Embedding','Web','PowerShellWeb')
# A URL to the license for this module.
ProjectURI = 'https://github.com/PowerShellWeb/oEmbed'
LicenseURI = 'https://github.com/PowerShellWeb/oEmbed/blob/main/LICENSE'
ReleaseNotes = @'

> Like It? [Star It](https://github.com/PowerShellWeb/oEmbed)
> Love It? [Support It](https://github.com/sponsors/StartAutomating)

Embed content from anywhere on the internet

## oEmbed 0.0.1

* Initial Release of oEmbed Module (#1)
* `Get-oEmbed` gets embed content (#2)
* `Get-oEmbed` is aliased to `oEmbed`
'@
}
}

}

36 changes: 36 additions & 0 deletions oEmbed.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
$commandsPath = Join-Path $PSScriptRoot Commands
:ToIncludeFiles foreach ($file in (Get-ChildItem -Path "$commandsPath" -Filter "*-*" -Recurse)) {
if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1
foreach ($exclusion in '\.[^\.]+\.ps1$') {
if (-not $exclusion) { continue }
if ($file.Name -match $exclusion) {
continue ToIncludeFiles # Skip excluded files
}
}
. $file.FullName
}

$myModule = $MyInvocation.MyCommand.ScriptBlock.Module
$ExecutionContext.SessionState.PSVariable.Set($myModule.Name, $myModule)
$myModule.pstypenames.insert(0, $myModule.Name)

New-PSDrive -Name $MyModule.Name -PSProvider FileSystem -Scope Global -Root $PSScriptRoot -ErrorAction Ignore

if ($home) {
$MyModuleProfileDirectory = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) $MyModule.Name
if (-not (Test-Path $MyModuleProfileDirectory)) {
$null = New-Item -ItemType Directory -Path $MyModuleProfileDirectory -Force
}
New-PSDrive -Name "My$($MyModule.Name)" -PSProvider FileSystem -Scope Global -Root $MyModuleProfileDirectory -ErrorAction Ignore
}

# Set a script variable of this, set to the module
# (so all scripts in this scope default to the correct `$this`)
$script:this = $myModule

#region Custom
#endregion Custom

Export-ModuleMember -Alias * -Function * -Variable $myModule.Name


Loading
Loading