PowerShell Fuzzy Lookup

You might know about my most favourite add-in for Microsoft Excel called Fuzzy Lookup. It’s the best, most accurate and fastest tool to compare a string of text against a table of data. For example, I can have a table of songs (my entire music library of 12K+ songs), then in another table I can have a list of songs that a radio station has played over the past week – for example. With Fuzzy lookup, I can compare the two tables. Table A which contains songs played on a radio station and Table B which represents my library. For each song in the Table A, give me the closest match of a song from Table B. It works beautifully and provides me a match score so I know how different the match was.

Trying to automate this using PowerShell has been challenging. I have an example here of a PowerShell which I wrote and pieced together. Some of the PowerShell functions I got from here, but not all. The rest of the script I wrote myself…

It does the following:

  • Runs through (2 radio stations as an example) on a continuous loop and captures the now playing song from their website. It has some smarts though for each time it’s run:
    • if its the same song, skip it
    • if the song has changed, capture it and do other things, keep reading…
  • The song just played is cleaned up a little bit using some regular expressions (RegEx). The clean up process is to help with a successful match.
  • The just played song is checked against a master library of my existing songs in Azure blob storage by using the various functions to do a Fuzzy Match.
  • Once a match is found, three things are written to Azure Table Storage:
    • the score of the match
    • the song captured from the radio station exactly – the just played song
    • the result of the song matched from my library

The screenshot below is taken from a list of songs in my Azure table storage account, the songs on the left are songs just played Vs songs on the right which are songs matched from my library. I have pointed to a few examples.

As you can see, it’s pretty accurate.

2016-10-17_1909

By the way, the tool used to access Azure storage accounts (including Azure Blob / Azure Tables / Azure Files) is the free Azure Storage Explorer.

$RadioFormat = 'Kiis'
$WritetoTableStorage = 'Yes'
$TableName = "JustPlayedKiis"
#Define the storage account and context.
$StorageAccountName = "StorageAccountName"
$StorageAccountKey = "StorageAccountKey"
$Ctx = New-AzureStorageContext $StorageAccountName StorageAccountKey $StorageAccountKey
#region Clear the cache (variables)
$kiis1065result = @()
$kiis1011result = @()
#endregion
Function CheckSong ($song){
$Global:Results = @()
$1stResults = @()
$2ndResults = @()
$Global:Library | % {
$h = new-object psObject | select DistanceNumber,Song
$h.DistanceNumber = (Get-LongestCommonSubstring $song ($_ -replace '\(.*\)','')).length
$h.Song = $_
$1stResults += $h
}
($1stResults | sort DistanceNumber | select Last 50).song | % {
$x = new-object psObject | select DistanceNumber,Song
$x.DistanceNumber = Get-FuzzyMatchScore $song ($_ -replace '\(.*\)','')
$x.Song = $_
$2ndResults += $x
}
if((($2ndResults | sort Descending DistanceNumber)[0]).DistanceNumber -lt '1600'){
$null = (($song -split '')[0]) -match '[0-9A-Za-z\x2d]+';$artist = $matches[0]
$title = (($song -split '')[1])
foreach($2ndresult in $2ndResults){
if(($2ndresult.Song -split '')[0] -match $artist -and ($2ndresult.Song -split '')[1] -match $title)
{$Global:Results = $2ndresult}
}
}
if((!($Global:Results))) {$Global:Results = $2ndResults}
}
Function Get-ExistingSongLibrary ($Ctx) {
#$Containers = Get-AzureStorageContainer -Context $Ctx -ErrorAction SilentlyContinue
$RawLibrary = Get-AzureStorageBlob Container music Context $Ctx | ? {$_.Name -like '*.mp3'}
$Global:Library = @()
foreach($lib in $RawLibrary){
$Global:Library += ($Lib.Name).Split('/') -replace '.mp3' | select Last 1
}
}
Function MissingSongsTable ($song, $songrequest, $RadioFormat, $DistanceNumber){
$MissingTableName = "MissingSongs"
#Retrieve the table if it already exists.
$table = Get-AzureStorageTable –Name $MissingTableName Context $Ctx ErrorAction Ignore
#Create a new table if it does not exist.
if ($table -eq $null)
{
Do{$table = New-AzureStorageTable –Name $MissingTableName Context $Ctx; Start-Sleep Seconds 2} until((Get-AzureStorageTable –Name $MissingTableName Context $Ctx ErrorAction Ignore) -ne $null)
}
#query table entities
#Create a table query.
$query = New-Object Microsoft.WindowsAzure.Storage.Table.TableQuery
$datenow = (Get-Date).Year.ToString() + (Get-Date).DayOfYear.ToString() + ((Get-Date).DayOfWeek).ToString()
$dateyesterday = (Get-Date).AddDays(-1).Year.ToString() + (Get-Date).AddDays(-1).DayOfYear.ToString() + ((Get-Date).AddDays(-1).DayOfWeek).ToString()
$filterString = "(PartitionKey eq '$datenow' or PartitionKey eq '$dateyesterday')"
$query.FilterString = $filterString
#Execute the query.
$entities = $table.CloudTable.ExecuteQuery($query)
$time = Get-Date Format yyyyMMddHH:mm:ss
$PartitionKey = (Get-Date).Year.ToString() + (Get-Date).DayOfYear.ToString() + ((Get-Date).DayOfWeek).ToString()
# Row Number Variable
if(($entities | sort Timestamp | select Last 1).PartitionKey -eq $PartitionKey){
$rowNumber = ($entities | sort Timestamp | select Last 1).RowKey -replace("Row");$rowNumber = [INT]$rowNumber;$rowNumber++}
if(($entities | sort Timestamp | select Last 1).PartitionKey -ne $PartitionKey){$rowNumber = 1}
# ID number Variable
if(($entities | sort Timestamp | select Last 1).PartitionKey -eq $PartitionKey){
$ID = ($entities | sort Timestamp | select Last 1).Properties.ID.PropertyAsObject;$ID++}
if(($entities | sort Timestamp | select Last 1).PartitionKey -ne $PartitionKey){$ID = 1}
$entity = New-Object TypeName Microsoft.WindowsAzure.Storage.Table.DynamicTableEntity ArgumentList $partitionKey, "Row$rowNumber"
$entity.Properties.Add("SongPlayed", $song)
$entity.Properties.Add("SongLookup", $songrequest)
$entity.Properties.Add("DistanceNumber", $DistanceNumber)
$entity.Properties.Add("RadioFormat", $RadioFormat)
$entity.Properties.Add("ID", $id)
$entity.Properties.Add("Time", $time)
$table.CloudTable.Execute([Microsoft.WindowsAzure.Storage.Table.TableOperation]::Insert($entity))
}
Function SongsTable ($songrequest, $song, $RadioFormat, $DistanceNumber){
#Create a table query.
$query = New-Object Microsoft.WindowsAzure.Storage.Table.TableQuery
$datenow = (Get-Date).Year.ToString() + (Get-Date).DayOfYear.ToString() + ((Get-Date).DayOfWeek).ToString()
$dateyesterday = (Get-Date).AddDays(-1).Year.ToString() + (Get-Date).AddDays(-1).DayOfYear.ToString() + ((Get-Date).AddDays(-1).DayOfWeek).ToString()
$filterString = "(PartitionKey eq '$datenow' or PartitionKey eq '$dateyesterday')"
$query.FilterString = $filterString
#Execute the query.
$entities = $table.CloudTable.ExecuteQuery($query)
$time = Get-Date Format yyyyMMddHH:mm:ss
$PartitionKey = (Get-Date).Year.ToString() + (Get-Date).DayOfYear.ToString() + ((Get-Date).DayOfWeek).ToString()
# Row Number Variable
if(($entities | sort Timestamp | select Last 1).PartitionKey -eq $PartitionKey){
$rowNumber = ($entities | sort Timestamp | select Last 1).RowKey -replace("Row");$rowNumber = [INT]$rowNumber;$rowNumber++}
if(($entities | sort Timestamp | select Last 1).PartitionKey -ne $PartitionKey){$rowNumber = 1}
# ID number Variable
if(($entities | sort Timestamp | select Last 1).PartitionKey -eq $PartitionKey){
$ID = ($entities | sort Timestamp | select Last 1).Properties.ID.PropertyAsObject;$ID++}
if(($entities | sort Timestamp | select Last 1).PartitionKey -ne $PartitionKey){$ID = 1}
$entity = New-Object TypeName Microsoft.WindowsAzure.Storage.Table.DynamicTableEntity ArgumentList $partitionKey, "Row$rowNumber"
$entity.Properties.Add("SongPlayed", $song)
$entity.Properties.Add("SongLookup", $songrequest)
$entity.Properties.Add("DistanceNumber", $DistanceNumber)
$entity.Properties.Add("RadioFormat", $RadioFormat)
$entity.Properties.Add("ID", $id)
$entity.Properties.Add("Time", $time)
$table.CloudTable.Execute([Microsoft.WindowsAzure.Storage.Table.TableOperation]::Insert($entity))
}
function Get-FuzzyMatchScore {
[CmdletBinding()]
param (
[Parameter(Position = 0)]
[string] $Search,
[Parameter(Position = 1)]
[string] $String
)
$score = 100
# Use approximate string matching to get some values needed to calculate the score of the result
$longestCommonSubstring = Get-LongestCommonSubstring String1 $String String2 $Search
$levenshteinDistance = Get-LevenshteinDistance String1 $String String2 $Search
$commonPrefix = Get-CommonPrefix String1 $String String2 $Search
# By running the result through this regex pattern we get the length of the match as well as the
# the index of where the match starts. The shorter the match length and the index, the more
# score will be added for the match.
$regexMatchFilter = $Search.ToCharArray() -join '.*?'
$match = Select-String InputObject $String Pattern $regexMatchFilter AllMatches
$matchLength = ($match.Matches | Sort-Object Length | Select-Object First 1).Value.Length
$matchIndex = ($match.Matches | Sort-Object Length | Select-Object First 1).Index
# Calculate score
$score = $score $levenshteinDistance
$score = $score * $longestCommonSubstring.Length
$score = $score $matchLength
$score = $score $matchIndex
if ($commonPrefix) {
$score = $score + $commonPrefix.Length
}
Write-Output $score
}
function Get-HammingDistance {
<#
.SYNOPSIS
Get the Hamming Distance between two strings or two positive integers.
.DESCRIPTION
The Hamming distance between two strings of equal length is the number of positions at which the
corresponding symbols are different. In another way, it measures the minimum number of substitutions
required to change one string into the other, or the minimum number of errors that could have
transformed one string into the other. Note! Even though the original Hamming algorithm only works for
strings of equal length, this function supports strings of unequal length as well.
The function also calculates the Hamming distance between two positive integers (considered as binary
values); that is, it calculates the number of bit substitutions required to change one integer into
the other.
.EXAMPLE
Get-HammingDistance 'karolin' 'kathrin'
Calculate the Hamming distance between the two strings. The result is 3.
.EXAMPLE
Get-HammingDistance 'karolin' 'kathrin' -NormalizedOutput
Calculate the normalized Hamming distance between the two strings. The result is 0.571428571428571.
.EXAMPLE
Get-HammingDistance -Int1 61 -Int2 15
Calculate the hamming distance between 61 and 15. The result is 3.
.LINK
http://en.wikipedia.org/wiki/Hamming_distance
https://communary.wordpress.com/
https://github.com/gravejester/Communary.PASM
.NOTES
Author: Øyvind Kallstad
Date: 03.11.2014
Version: 1.0
#>
[CmdletBinding(DefaultParameterSetName = 'String')]
param (
[Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'String')]
[ValidateNotNullOrEmpty()]
[string] $String1,
[Parameter(Position = 1, Mandatory = $true, ParameterSetName = 'String')]
[ValidateNotNullOrEmpty()]
[string] $String2,
[Parameter(Position = 0, Mandatory = $true, ParameterSetName = 'Integer')]
[ValidateNotNullOrEmpty()]
[uint32] $Int1,
[Parameter(Position = 1, Mandatory = $true, ParameterSetName = 'Integer')]
[ValidateNotNullOrEmpty()]
[uint32] $Int2,
# Makes matches case-sensitive. By default, matches are not case-sensitive.
[Parameter(ParameterSetName = 'String')]
[switch] $CaseSensitive,
# Normalize the output value. When the output is not normalized the maximum value is the length of the longest string, and the minimum value is 0,
# meaning that a value of 0 is a 100% match. When the output is normalized you get a value between 0 and 1, where 1 indicates a 100% match.
[Parameter(ParameterSetName = 'String')]
[switch] $NormalizeOutput
)
try {
if ($PSCmdlet.ParameterSetName -eq 'String') {
# handle case insensitivity
if (-not($CaseSensitive)) {
$String1 = $String1.ToLowerInvariant()
$String2 = $String2.ToLowerInvariant()
}
# set initial distance
$distance = 0
# get max and min length of the input strings
$maxLength = [Math]::Max($String1.Length,$String2.Length)
$minLength = [Math]::Min($String1.Length,$String2.Length)
# calculate distance for the length of the shortest string
for ($i = 0; $i -lt $minLength; $i++) {
if (-not($String1[$i] -ceq $String2[$i])) {
$distance++
}
}
# add the remaining length to the distance
$distance = $distance + ($maxLength $minLength)
if ($NormalizeOutput) {
Write-Output (1 ($distance / $maxLength))
}
else {
Write-Output $distance
}
}
else {
$distance = 0
$value = $Int1 -bxor $Int2
while ($value -ne 0) {
$distance++
$value = $value -band ($value 1)
}
Write-Output $distance
}
}
catch {
Write-Warning $_.Exception.Message
}
}
function Get-LevenshteinDistance {
<#
.SYNOPSIS
Get the Levenshtein distance between two strings.
.DESCRIPTION
The Levenshtein Distance is a way of quantifying how dissimilar two strings (e.g., words) are to one another by counting the minimum number of operations required to transform one string into the other.
.EXAMPLE
Get-LevenshteinDistance 'kitten' 'sitting'
.LINK
http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C.23
http://en.wikipedia.org/wiki/Edit_distance
https://communary.wordpress.com/
https://github.com/gravejester/Communary.PASM
.NOTES
Author: Øyvind Kallstad
Date: 07.11.2014
Version: 1.0
#>
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[string]$String1,
[Parameter(Position = 1)]
[string]$String2,
# Makes matches case-sensitive. By default, matches are not case-sensitive.
[Parameter()]
[switch] $CaseSensitive,
# A normalized output will fall in the range 0 (perfect match) to 1 (no match).
[Parameter()]
[switch] $NormalizeOutput
)
if (-not($CaseSensitive)) {
$String1 = $String1.ToLowerInvariant()
$String2 = $String2.ToLowerInvariant()
}
$d = New-Object 'Int[,]' ($String1.Length + 1), ($String2.Length + 1)
try {
for ($i = 0; $i -le $d.GetUpperBound(0); $i++) {
$d[$i,0] = $i
}
for ($i = 0; $i -le $d.GetUpperBound(1); $i++) {
$d[0,$i] = $i
}
for ($i = 1; $i -le $d.GetUpperBound(0); $i++) {
for ($j = 1; $j -le $d.GetUpperBound(1); $j++) {
$cost = [Convert]::ToInt32((-not($String1[$i1] -ceq $String2[$j1])))
$min1 = $d[($i1),$j] + 1
$min2 = $d[$i,($j1)] + 1
$min3 = $d[($i1),($j1)] + $cost
$d[$i,$j] = [Math]::Min([Math]::Min($min1,$min2),$min3)
}
}
$distance = ($d[$d.GetUpperBound(0),$d.GetUpperBound(1)])
if ($NormalizeOutput) {
Write-Output (1 ($distance) / ([Math]::Max($String1.Length,$String2.Length)))
}
else {
Write-Output $distance
}
}
catch {
Write-Warning $_.Exception.Message
}
}
function Get-LongestCommonSubstring {
<#
.SYNOPSIS
Get the longest common substring of two strings.
.DESCRIPTION
Get the longest common substring of two strings.
.EXAMPLE
Get-LongestCommonSubstring 'Karolin' 'kathrin' -CaseSensitive
.LINK
https://fuzzystring.codeplex.com/
http://en.wikipedia.org/wiki/Longest_common_substring_problem
https://communary.wordpress.com/
https://github.com/gravejester/Communary.PASM
.NOTES
Adapted to PowerShell from code by Kevin Jones (https://fuzzystring.codeplex.com/)
Author: Øyvind Kallstad
Date: 03.11.2014
Version: 1.0
#>
[CmdletBinding()]
param (
[Parameter(Position = 0)]
[string] $String1,
[Parameter(Position = 1)]
[string] $String2,
[Parameter()]
[switch] $CaseSensitive
)
if (-not($CaseSensitive)) {
$String1 = $String1.ToLowerInvariant()
$String2 = $String2.ToLowerInvariant()
}
$array = New-Object 'Object[,]' $String1.Length, $String2.Length
$stringBuilder = New-Object System.Text.StringBuilder
$maxLength = 0
$lastSubsBegin = 0
for ($i = 0; $i -lt $String1.Length; $i++) {
for ($j = 0; $j -lt $String2.Length; $j++) {
if ($String1[$i] -cne $String2[$j]) {
$array[$i,$j] = 0
}
else {
if (($i -eq 0) -or ($j -eq 0)) {
$array[$i,$j] = 1
}
else {
$array[$i,$j] = 1 + $array[($i 1),($j 1)]
}
if ($array[$i,$j] -gt $maxLength) {
$maxLength = $array[$i,$j]
$thisSubsBegin = $i $array[$i,$j] + 1
if($lastSubsBegin -eq $thisSubsBegin) {
[void]$stringBuilder.Append($String1[$i])
}
else {
$lastSubsBegin = $thisSubsBegin
$stringBuilder.Length = 0
[void]$stringBuilder.Append($String1.Substring($lastSubsBegin, (($i + 1) $lastSubsBegin)))
}
}
}
}
}
Write-Output $stringBuilder.ToString()
}
function Get-CommonPrefix {
<#
.SYNOPSIS
Find the common prefix of two strings.
.DESCRIPTION
This function will get the common prefix of two strings; that is, all
the letters that they share, starting from the beginning of the strings.
.EXAMPLE
Get-CommonPrefix 'Card' 'Cartoon'
Will get the common prefix of both string. Should output 'car'.
.LINK
https://communary.wordpress.com/
https://github.com/gravejester/Communary.PASM
.INPUTS
System.String
.OUTPUTS
System.String
.NOTES
Author: Øyvind Kallstad
Date: 03.11.2014
Version 1.1
Dependencies: none
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$String1,
[Parameter(Mandatory = $true, Position = 1)]
[ValidateNotNullOrEmpty()]
[string]$String2,
# Maximum length of the returned prefix.
[Parameter()]
[int]$MaxPrefixLength,
# Makes matches case-sensitive. By default, matches are not case-sensitive.
[Parameter()]
[switch] $CaseSensitive
)
if (-not($CaseSensitive)) {
$String1 = $String1.ToLowerInvariant()
$String2 = $String2.ToLowerInvariant()
}
$outputString = New-Object 'System.Text.StringBuilder'
$shortestStringLength = [Math]::Min($String1.Length,$String2.Length)
# Let the maximum prefix length be the same as the length of the shortest of
# the two input strings, unless defined by the MaxPrefixLength parameter.
if (($shortestStringLength -lt $MaxPrefixLength) -or ($MaxPrefixLength -eq 0)) {
$MaxPrefixLength = $shortestStringLength
}
# Loop from the start and add any characters found that are equal
for ($i = 0; $i -lt $MaxPrefixLength; $i++) {
if ($String1[$i] -ceq $String2[$i]) {
[void]$outputString.Append($String1[$i])
}
else { break }
}
Write-Output $outputString.ToString()
}
function FinalActions ($song, $RadioFormat, $eJukeboxHost) {
$songrequest = (($Global:Results | sort Descending DistanceNumber)[0]).Song
$DistanceNumber = (($Global:Results | sort Descending DistanceNumber)[0]).DistanceNumber
Write-Host NoNewline "Song from Lookup: " ForegroundColor Green; Write-Host $songrequest ForegroundColor Magenta
Write-Host NoNewline "Distance Number from Lookup: " ForegroundColor Green; Write-Host $DistanceNumber ForegroundColor Magenta
# Song Might be missing, add to Missing Songs Table
if($DistanceNumber -lt '1000'){
if($WritetoTableStorage -eq 'Yes'){MissingSongsTable $song $songrequest $RadioFormat $DistanceNumber}
} else { # Request the song
if($WritetoTableStorage -eq 'Yes'){SongsTable $songrequest $song $RadioFormat $DistanceNumber}
}
}
while ($true){
Get-ExistingSongLibrary $Ctx
#endregion
#####################################
#region Capture Kiis Australia (Sydney/Melbourne)
#Kiis 1065 – Sydney
$matches = @()
Start-Sleep Seconds 1
Write-Host "Kiis 1065"
$kiis1065listsong = @()
$uri = (Invoke-WebRequest 'http://media.arn.com.au/xml/mix1065_now.xml').content
Start-Sleep Seconds 1
$null = $uri -match '<now_playing>(.|\n)*artist><!\[CDATA\[([^]]*)\]\]></artist(.|\n)*</now_playing>';$artist = $matches[2]
$null = $uri -match '<now_playing>(.|\n)*title\sgeneric="False"><!\[CDATA\[([^]]*)\]\]></title(.|\n)*</now_playing>';$title = $matches[2]
$kiis1065song = "{0} – {1}" -f $artist, $title
Write-Host NoNewline "Now playing on Kiis 1065 " ForegroundColor Cyan;Write-Host $kiis1065song ForegroundColor Yellow
if((!($kiis1065result -eq $kiis1065song)) -and `
($kiis1065song -notmatch "Kiis") -and `
($kiis1065song -match "..\s-\s..") -and `
($kiis1065song -notmatch "Audio Type Changed")){
$kiis1065result = $kiis1065song
#Write to table
$song = $kiis1065song
#query table entities
#Region Do stuff with the song http://www.ascii-code.com/
$Cleansong = $kiis1065song -replace '\(.*\)',' ' -replace("&amp;","&") -replace ' ',' ' -replace ' feat. ',' ' `
-replace ' ft. ',' ' -replace ' ft ',' ' `
-replace '[\xC0-\xC5]','A' -replace '[\xC8-\xCB]','E' -replace '[\xCC-\xCF]','I' `
-replace '[\xD2-\xD6]','O' -replace '[\xD9-\xDC]','U' -replace '[\xe8-\xEB]','e' `
-replace '[\xEC-\xEF]','i' -replace '[\xF2-\xF6]','o' -replace '[\xF9-\xFC]','u' `
-replace '[\xE0-\xE5]','o' -replace '[\xD1]','N' -replace '[\x91\x92\x60]','\x27' `
-replace '[\x93\x94]','\x22' -replace '[^\x30-\x39\x41-\x5A\x61-\x7A\x20\x2D\x26\x27\x22\x2E\x2C]+',' ' `
-replace ' ',' ' -replace 'The ',''
CheckSong $Cleansong
FinalActions $song $RadioFormat $eJukeboxHost
}
#####################################
#####################################
#Kiis 1011 – Melbourne
$matches = @()
Start-Sleep Seconds 1
Write-Host "Kiis 1011"
$kiis1011listsong = @()
$uri = (Invoke-WebRequest 'http://media.arn.com.au/xml/KIIS1011_now.xml').content
Start-Sleep Seconds 1
$null = $uri -match '<now_playing>(.|\n)*artist><!\[CDATA\[([^]]*)\]\]></artist(.|\n)*</now_playing>';$artist = $matches[2]
$null = $uri -match '<now_playing>(.|\n)*title\sgeneric="False"><!\[CDATA\[([^]]*)\]\]></title(.|\n)*</now_playing>';$title = $matches[2]
$kiis1011song = "{0} – {1}" -f $artist, $title
Write-Host NoNewline "Now playing on Kiis 1011 " ForegroundColor Cyan;Write-Host $kiis1011song ForegroundColor Yellow
if((!($kiis1011result -eq $kiis1011song)) -and `
($kiis1011song -notmatch "Kiis") -and `
($kiis1011song -match "..\s-\s..") -and `
($kiis1011song -notmatch "Audio Type Changed")){
$kiis1011result = $kiis1011song
#Write to table
$song = $kiis1011song
#query table entities
#Region Do stuff with the song http://www.ascii-code.com/
$Cleansong = $kiis1011song -replace '\(.*\)',' ' -replace("&amp;","&") -replace ' ',' ' -replace ' feat. ',' ' `
-replace ' ft. ',' ' -replace ' ft ',' ' `
-replace '[\xC0-\xC5]','A' -replace '[\xC8-\xCB]','E' -replace '[\xCC-\xCF]','I' `
-replace '[\xD2-\xD6]','O' -replace '[\xD9-\xDC]','U' -replace '[\xe8-\xEB]','e' `
-replace '[\xEC-\xEF]','i' -replace '[\xF2-\xF6]','o' -replace '[\xF9-\xFC]','u' `
-replace '[\xE0-\xE5]','o' -replace '[\xD1]','N' -replace '[\x91\x92\x60]','\x27' `
-replace '[\x93\x94]','\x22' -replace '[^\x30-\x39\x41-\x5A\x61-\x7A\x20\x2D\x26\x27\x22\x2E\x2C]+',' ' `
-replace ' ',' ' -replace 'The ',''
CheckSong $Cleansong
FinalActions $song $RadioFormat $eJukeboxHost
}
#####################################
#endregion
}

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s