Skip to content

Commit 7bf2120

Browse files
Merge pull request #16 from PowerShellWeb/oEmbed-Init
`oEmbed` module initial release
2 parents 04958cc + 1beb518 commit 7bf2120

15 files changed

+928
-0
lines changed

.github/workflows/oEmbed-Build.yml

+510
Large diffs are not rendered by default.

Build/GitHub/Jobs/oEmbed.psd1

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
@{
2+
"runs-on" = "ubuntu-latest"
3+
if = '${{ success() }}'
4+
steps = @(
5+
@{
6+
name = 'Check out repository'
7+
uses = 'actions/checkout@v2'
8+
},
9+
@{
10+
name = 'GitLogger'
11+
uses = 'GitLogging/GitLoggerAction@main'
12+
id = 'GitLogger'
13+
},
14+
@{
15+
name = 'Use PSSVG Action'
16+
uses = 'StartAutomating/PSSVG@main'
17+
id = 'PSSVG'
18+
},
19+
@{
20+
name = 'Use PipeScript Action'
21+
uses = 'StartAutomating/PipeScript@main'
22+
id = 'PipeScript'
23+
},
24+
'RunEZOut',
25+
'RunHelpOut',
26+
@{
27+
name = 'Use PSJekyll Action'
28+
uses = 'PowerShellWeb/PSJekyll@main'
29+
id = 'PSJekyll'
30+
}
31+
)
32+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@{
2+
name = 'PublishTestResults'
3+
uses = 'actions/upload-artifact@main'
4+
with = @{
5+
name = 'PesterResults'
6+
path = '**.TestResults.xml'
7+
}
8+
if = '${{always()}}'
9+
}
10+
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#requires -Module PSDevOps
2+
Import-BuildStep -SourcePath (
3+
Join-Path $PSScriptRoot 'GitHub'
4+
) -BuildSystem GitHubWorkflow
5+
6+
Push-Location ($PSScriptRoot | Split-Path)
7+
New-GitHubWorkflow -Name "oEmbed Module Build" -On Push,
8+
PullRequest,
9+
Demand -Job TestPowerShellOnLinux,
10+
TagReleaseAndPublish, oEmbed -OutputPath .\.github\workflows\oEmbed-Build.yml
11+
12+
Pop-Location

Commands/Get-oEmbed.ps1

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
function Get-OEmbed {
2+
<#
3+
.SYNOPSIS
4+
Gets oEmbed data for a given URL.
5+
.DESCRIPTION
6+
Gets oEmbed data for a given URL.
7+
8+
[oEmbed](https://oembed.com/) is a format for allowing an embedded representation of a URL on third party sites.
9+
10+
Most social networks support oEmbed, so this little function lets you embed almost any social network post
11+
.EXAMPLE
12+
oEmbed -Url https://www.youtube.com/watch?v=UIR9Z_JdVhs
13+
#>
14+
[Alias('oEmbed')]
15+
[CmdletBinding(PositionalBinding=$false,SupportsShouldProcess,DefaultParameterSetName='Query')]
16+
param(
17+
# The URL
18+
[Parameter(Mandatory,Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName,ParameterSetName='Query')]
19+
[Uri]
20+
$Url,
21+
22+
# The maximum width of the returned image
23+
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
24+
[int]
25+
$MaxWidth,
26+
27+
# The maximum height of the returned image
28+
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
29+
[int]
30+
$MaxHeight,
31+
32+
# The format of the returned image
33+
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
34+
[string]
35+
$Format,
36+
37+
# Whether to force a refresh of the cached oEmbed data
38+
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='Query')]
39+
[switch]
40+
$Force,
41+
42+
# The name of an oEmbed provider. Wildcards are supported.
43+
[Parameter(Mandatory,ParameterSetName='ProviderByName')]
44+
[SupportsWildcards()]
45+
[string]
46+
$ProviderName,
47+
48+
# If set, will list the oEmbed providers.
49+
[Parameter(Mandatory,ParameterSetName='ProviderList')]
50+
[switch]
51+
$ProviderList
52+
)
53+
54+
begin {
55+
# If we haven't yet cached the list of oEmbed providers, do so now.
56+
if (-not $script:cachedOmbedProviders) {
57+
$script:cachedOmbedProviders = Invoke-RestMethod "https://oembed.com/providers.json"
58+
}
59+
# If we haven't yet cached the list of oEmbed endpoints, do so now.
60+
if (-not $script:openEmbeddings) {
61+
$script:openEmbeddings = $script:cachedOmbedProviders.Endpoints.Url -as [uri[]]
62+
}
63+
# Create a cache to store the oEmbed data in, if we haven't already done so.
64+
if (-not $script:oEmbedUrlCache) {
65+
$script:oEmbedUrlCache = [Ordered]@{}
66+
}
67+
68+
if (-not $script:oEmbedDomainCache) {
69+
$script:oEmbedDomainCache = [Ordered]@{}
70+
}
71+
72+
$oEmbedQueue = [Collections.Queue]::new()
73+
}
74+
75+
process {
76+
if ($PSCmdlet.ParameterSetName -eq 'ProviderList') {
77+
return $script:cachedOmbedProviders
78+
}
79+
# If we're asking for a Provider by Name
80+
if ($PSCmdlet.ParameterSetName -eq 'ProviderByName') {
81+
# filter the list of providers
82+
return $script:cachedOmbedProviders |
83+
# and return the name
84+
Where-Object Provider_Name -like $ProviderName
85+
}
86+
$matchingEndpoint =
87+
if (-not $script:oEmbedDomainCache[$url.DnsSafeHost]) {
88+
:oEmbedProvider foreach ($oEmbedProvider in $script:cachedOmbedProviders) {
89+
foreach ($oEmbedEndpoint in $oEmbedProvider.Endpoints) {
90+
foreach ($oEmbedScheme in $oEmbedEndpoint.Schemes) {
91+
if ($url -like $oEmbedScheme) {
92+
$script:oEmbedDomainCache[$url.DnsSafeHost] = $oEmbedEndpoint.url
93+
$script:oEmbedDomainCache[$url.DnsSafeHost]
94+
break oEmbedProvider
95+
}
96+
}
97+
}
98+
}
99+
} else {
100+
$script:oEmbedDomainCache[$url.DnsSafeHost]
101+
}
102+
103+
104+
if (-not $matchingEndpoint) {
105+
Write-Error "No oEmbed Provider found for URL '$url'"
106+
return
107+
}
108+
109+
$oEmbedUrl =
110+
"$($matchingEndpoint)?$(
111+
@(
112+
"url=$([Web.HttpUtility]::UrlEncode($Url) -replace '\+','%20')"
113+
if ($MaxWidth) {
114+
"maxwidth=$MaxWidth"
115+
}
116+
if ($MaxHeight) {
117+
"maxheight=$MaxHeight"
118+
}
119+
if ($Format) {
120+
"format=$Format"
121+
}
122+
) -join '&'
123+
)"
124+
125+
$oEmbedQueue.Enqueue([Ordered]@{
126+
Url = $Url
127+
oEmbedUrl = $oEmbedUrl
128+
})
129+
}
130+
131+
end {
132+
$counter = 0
133+
$total = $oEmbedQueue.Count
134+
$progressId = $MyInvocation.HistoryId
135+
foreach ($queueItem in $oEmbedQueue) {
136+
$url = $queueItem.Url
137+
$oEmbedUrl = $queueItem.oEmbedUrl
138+
if ($oEmbedQueue.Count -gt 1) {
139+
$counter++
140+
Write-Progress "oEmbed" "Retrieving oEmbed data for $url" -PercentComplete (
141+
$counter * 100 / $total
142+
) -Id $progressId
143+
}
144+
if (-not $script:oEmbedUrlCache[$oEmbedUrl] -or $Force) {
145+
if ($WhatIfPreference) { return $oEmbedUrl }
146+
if (-not $PSCmdlet.ShouldProcess($oEmbedUrl)) {
147+
Write-Verbose "Skipping $oEmbedUrl"
148+
continue
149+
} else {
150+
Write-Verbose "Retrieving $oEmbedUrl"
151+
}
152+
153+
$script:oEmbedUrlCache[$oEmbedUrl] = Invoke-RestMethod -Uri $oEmbedUrl |
154+
Add-Member NoteProperty 'Url' $Url -Force -PassThru |
155+
Add-Member NoteProperty 'oEmbedUrl' $oEmbedUrl -Force -PassThru
156+
$script:oEmbedUrlCache[$oEmbedUrl].pstypenames.insert(0,'OpenEmbedding')
157+
}
158+
159+
$script:oEmbedUrlCache[$oEmbedUrl]
160+
}
161+
162+
if ($oEmbedQueue.Count -gt 1) {
163+
Write-Progress "oEmbed" "Retrieving oEmbed data" -Completed -Id $progressId
164+
}
165+
}
166+
}

README.md

+37
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,39 @@
11
# oEmbed
2+
23
Open Embedding with PowerShell
4+
5+
6+
## The oEmbed Standard
7+
8+
There's lots of content on the internet. How do we embed it?
9+
10+
11+
Over the years, most large sites seem to have agreeded on a standard or two.
12+
13+
[oEmbed](https://oEmbed.com/) is one such standard.
14+
15+
## The oEmbed module
16+
17+
This is a PowerShell module for oEmbed.
18+
19+
It lets you get open embeddings for any url that supports the [oEmbed standard](https://oEmbed.com/)
20+
21+
It contains a single command: `Get-Ombed`
22+
23+
This command is aliased to `oEmbed`
24+
25+
~~~PowerShell
26+
oEmbed "https://youtu.be/nHlJODYBLKs?si=XmWPX6ulPDlaEzO0"
27+
~~~
28+
29+
We can also list all the providers:
30+
31+
~~~PowerShell
32+
oEmbed -ProviderList
33+
~~~
34+
35+
Or get them by wildcard:
36+
37+
~~~PowerShell
38+
oEmbed -ProviderName *tube*
39+
~~~

Types/oEmbed/Get.ps1

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
foreach ($argument in $args) {
2+
oEmbed -Url $argument
3+
}

Types/oEmbed/get_oEmbedCache.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
return & $this { $script:oEmbedUrlCache }

Types/oEmbed/get_oEmbedProvider.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Get-OEmbed -ProviderList

Types/openEmbedding/ToString.ps1

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
return "$($this.HTML)"

Types/openEmbedding/get_Markdown.ps1

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
if ($this.thumbnail_url) {
2+
return "[![$($this.title)]($($this.thumbnail_url))]($($this.url))"
3+
}
4+
elseif ($this.title) {
5+
"[$($this.title)]($($this.url))"
6+
}

oEmbed.ps.psm1

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
$commandsPath = Join-Path $PSScriptRoot Commands
2+
[include('*-*')]$commandsPath
3+
4+
$myModule = $MyInvocation.MyCommand.ScriptBlock.Module
5+
$ExecutionContext.SessionState.PSVariable.Set($myModule.Name, $myModule)
6+
$myModule.pstypenames.insert(0, $myModule.Name)
7+
8+
New-PSDrive -Name $MyModule.Name -PSProvider FileSystem -Scope Global -Root $PSScriptRoot -ErrorAction Ignore
9+
10+
if ($home) {
11+
$MyModuleProfileDirectory = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) $MyModule.Name
12+
if (-not (Test-Path $MyModuleProfileDirectory)) {
13+
$null = New-Item -ItemType Directory -Path $MyModuleProfileDirectory -Force
14+
}
15+
New-PSDrive -Name "My$($MyModule.Name)" -PSProvider FileSystem -Scope Global -Root $MyModuleProfileDirectory -ErrorAction Ignore
16+
}
17+
18+
# Set a script variable of this, set to the module
19+
# (so all scripts in this scope default to the correct `$this`)
20+
$script:this = $myModule
21+
22+
#region Custom
23+
#endregion Custom
24+
25+
Export-ModuleMember -Alias * -Function * -Variable $myModule.Name
26+

oEmbed.psd1

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
@{
2+
RootModule = 'oEmbed.psm1'
3+
ModuleVersion = '0.0.1'
4+
GUID = 'dc68bf0c-b8b5-4f3d-9fc4-b6779f7e7d6a'
5+
Author = 'JamesBrundage'
6+
CompanyName = 'Start-Automating'
7+
Copyright = '(c) 2025 Start-Automating.'
8+
Description = 'Open Embeddings in PowerShell'
9+
FunctionsToExport = 'Get-oEmbed'
10+
AliasesToExport = 'oEmbed'
11+
TypesToProcess = 'oEmbed.types.ps1xml'
12+
PrivateData = @{
13+
PSData = @{
14+
# Tags applied to this module. These help with module discovery in online galleries.
15+
Tags = @('oEmbed','Embedding','Web','PowerShellWeb')
16+
# A URL to the license for this module.
17+
ProjectURI = 'https://github.com/PowerShellWeb/oEmbed'
18+
LicenseURI = 'https://github.com/PowerShellWeb/oEmbed/blob/main/LICENSE'
19+
ReleaseNotes = @'
20+
21+
> Like It? [Star It](https://github.com/PowerShellWeb/oEmbed)
22+
> Love It? [Support It](https://github.com/sponsors/StartAutomating)
23+
24+
Embed content from anywhere on the internet
25+
26+
## oEmbed 0.0.1
27+
28+
* Initial Release of oEmbed Module (#1)
29+
* `Get-oEmbed` gets embed content (#2)
30+
* `Get-oEmbed` is aliased to `oEmbed`
31+
'@
32+
}
33+
}
34+
35+
}
36+

oEmbed.psm1

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
$commandsPath = Join-Path $PSScriptRoot Commands
2+
:ToIncludeFiles foreach ($file in (Get-ChildItem -Path "$commandsPath" -Filter "*-*" -Recurse)) {
3+
if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1
4+
foreach ($exclusion in '\.[^\.]+\.ps1$') {
5+
if (-not $exclusion) { continue }
6+
if ($file.Name -match $exclusion) {
7+
continue ToIncludeFiles # Skip excluded files
8+
}
9+
}
10+
. $file.FullName
11+
}
12+
13+
$myModule = $MyInvocation.MyCommand.ScriptBlock.Module
14+
$ExecutionContext.SessionState.PSVariable.Set($myModule.Name, $myModule)
15+
$myModule.pstypenames.insert(0, $myModule.Name)
16+
17+
New-PSDrive -Name $MyModule.Name -PSProvider FileSystem -Scope Global -Root $PSScriptRoot -ErrorAction Ignore
18+
19+
if ($home) {
20+
$MyModuleProfileDirectory = Join-Path ([Environment]::GetFolderPath("LocalApplicationData")) $MyModule.Name
21+
if (-not (Test-Path $MyModuleProfileDirectory)) {
22+
$null = New-Item -ItemType Directory -Path $MyModuleProfileDirectory -Force
23+
}
24+
New-PSDrive -Name "My$($MyModule.Name)" -PSProvider FileSystem -Scope Global -Root $MyModuleProfileDirectory -ErrorAction Ignore
25+
}
26+
27+
# Set a script variable of this, set to the module
28+
# (so all scripts in this scope default to the correct `$this`)
29+
$script:this = $myModule
30+
31+
#region Custom
32+
#endregion Custom
33+
34+
Export-ModuleMember -Alias * -Function * -Variable $myModule.Name
35+
36+

0 commit comments

Comments
 (0)