Recordings filename pattern (use the custom label)
Forum rules
★ Download the latest EPG123 here: https://garyan2.github.io/ <> Setup guide here: https://garyan2.github.io/install.html ★
★ Download the latest EPG123 here: https://garyan2.github.io/ <> Setup guide here: https://garyan2.github.io/install.html ★
-
- Posts: 145
- Joined: Thu Sep 10, 2020 8:03 am
- Location:
- HTPC Specs:
If it works like I think it does, an option to use the Service Callsign as Service Name and/or Call Sign might be nice.
- garyan2
- Posts: 7480
- Joined: Fri Nov 27, 2015 7:23 pm
- Location:
- HTPC Specs:
Not quite that simple, but of course doable. The MergedChannel, which is what is displayed on the left side of the client, consists of a primary channel and any number of secondary channels. At the end of a TV Setup scan, the primary channel is the tuner channel from the "Scanned Lineup". When guide listings are assigned to the merged channel, the listings take the primary channel position and the tuner channel gets moved to the secondary channels. If that is all that is done with your setup, it can be done fairly easily.
When a user merges or splits channels and tuners, or changes priority of the tuners within a merged channel, then things get a little complicated with multiple scanned channels in the secondary or tuning overrides created within the WMC database. Gets a little weird and can be difficult to trace.
What I currently have for copying to the clipboard is closer to an engineering lash-up rather than something that could be production worthy. If the merged channel primary is guide listings, I just look at the first channel in the merged channel secondary and if it is from a scanned lineup I use its' call sign. I don't look at any other secondary channels nor do I check the tuning overrides to find where the scanned channel may be. I think the best/easiest thing I could do would be to provide a command line parameter to do this, but it would only be used during the automatch procedure when mapping guide listings to a channel that has the scanned channel in the merged channel primary channel. I would read the scanned channel call sign, assign the guide listings to the merged channel which will become the primary channel, and assign a custom call sign using the captured scanned channel call sign.
- Gary
Keeping WMC alive beyond January 2020. https://garyan2.github.io
Keeping WMC alive beyond January 2020. https://garyan2.github.io
-
- Posts: 145
- Joined: Thu Sep 10, 2020 8:03 am
- Location:
- HTPC Specs:
The script to auto-magically collect the PSIP VCT short names from HDHomeRun device and customize the config has learned some new things.
Learned how to run HDHomeRun device channel detection scan
Learned to operate standalone (without a HDHR device) using guide number and name JSON data.
Learned to do a dry run
Factored into functions
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.json
Learned how to run HDHomeRun device channel detection scan
Learned to operate standalone (without a HDHR device) using guide number and name JSON data.
Learned to do a dry run
Factored into functions
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
Code: Select all
# Version: 20200928.1-alpha
# Status: alpha
# Standard file system locations
# Folder: C:\ProgramData\GaRyan2\epg123
# PS Script File: custom_callSign_serviceName.ps1
# JSON Data File: custom_callSign_serviceName.json
# Dry Run CFG File: custom_callSign_serviceName.Dry_Run.cfg
# Development environment:
# Windows 8.1 Pro w/Media Center
# PowerShell: 4.0
# SiliconDust: HDHomeRun CONNECT, Model: HDHR4-2US, Firmware: 20200907
# Criteria to use
$Dry_Run = $true # Save to $EPG_Data_Dir'\custom_callSign_serviceName.Dry_Run.cfg'
$ChLineUp = 'USA-OTA-97007' # Channel line up associated with HDHomeRun device
$EPG_Service = 'EPG123' # EPG service name
$EPG_Data_Dir = 'C:\ProgramData\GaRyan2\epg123' # Fully qualified EPG data dir path (without trailing slash)
$CFG_File = 'epg123.cfg' # EPG config file
$MXF_File = 'epg123.mxf' # EPG MXF file
$Custom_CallSign_flag = $true # Set/Replace (true), Remove (false) custom channel call sign
$Custom_Service_Name_flag = $true # Set/Replace (true), Remove (false) custom channel service name
# Can be used standalone with guide name and number JSON data: ex: [{"GuideNumber":"10.4","GuideName":"OPBKids"}]
$Retrive_Data_From_Device = $true # Retrieve channels & names from HDHomeRun device
$Device_Channel_Dectection_Scan = $false # Perform HDHomeRun device channel detection scan
#$Device_ID = '1040dddd' # HDHomeRun device ID (only for client utility scan)
$Device_Address = '192.168.2.202' # HDHomeRun device address
$Show = 'found' # HDHomeRun device channel line up query: all, found, favorites
# End Criteria to use
function Invoke-Main {
Get-Channels_Service_Name_Data
Edit-Configuration
}
#
# Retrieve PSIP VCT short name from HDHomeRun device. Or use default/fallback channel names.
#
function Get-Channels_Service_Name_Data {
if ($Retrive_Data_From_Device) {
$webclient = new-object System.Net.WebClient
$lineup_json_url = 'http://'+$Device_Address+'/lineup.json?show='+$Show
if ($Device_Channel_Dectection_Scan) {
Invoke-Device_Channel_Dectection_Scan_Web
# Invoke-Device_Channel_Dectection_Scan_Utility # SliconDust hdhomerun_config.exe
} else {
$json = $webclient.DownloadString($lineup_json_url)
}
}
try {
$vPSObject = $json | ConvertFrom-Json -ErrorAction Stop;
'Using data retrieved from device ('+$Device_Address+')'
'' # Blank line
# Save device retrieved, guide number and guide name abbreviated, json for default/fallback, don't overwrite existing
if (!(Test-Path $EPG_Data_Dir'\custom_callSign_serviceName.json')) {
$json = $json -Replace '\[\{', "[`r`n{" -Replace '},{', "},`r`n{" -Replace '}]', "}`r`n]" # Line-ized
$json = $json -Replace "({`"GuideNumber.*?GuideName`":`".*?`").*(},*)", "`$1`$2" # Only GuideNumber and GuideName
$json | Set-Content -Path $EPG_Data_Dir'\custom_callSign_serviceName.json'
}
} catch {
# Channel GuideNumber and GuideName json.
# Use in place of (retrieve from device false) or if HDHomeRun device query fails.
if (Test-Path $EPG_Data_Dir'\custom_callSign_serviceName.json') {
$json_fallback = Get-Content -path $EPG_Data_Dir'\custom_callSign_serviceName.json' -Raw
$vPSObject = $json_fallback | ConvertFrom-Json
'Using default/fallback data: '
$EPG_Data_Dir+'\custom_callSign_serviceName.json'
'' # Blank line
} else {
'Default/Fallback data not found: '
$EPG_Data_Dir+'\custom_callSign_serviceName.json'
'' # Blank line
pause
exit
}
}
$script:vPSObject = $vPSObject
}
#
# Set/Remove/Replace epg123.cfg customServiceName and customCallSign fields
#
function Edit-Configuration {
$epg123_cfg = Get-Content -path $EPG_Data_Dir'\'$CFG_File -Raw
$epg123_mxf = Get-Content -path $EPG_Data_Dir'\output\'$MXF_File -Raw
"Channel`tName`tSign`tSta ID`tLineup`tSrv ID"
foreach ($ch in $vPSObject) {
$ChNumber = $ch.GuideNumber.Split('.')
$ChName = $ch.GuideName
$ChCallSign = $ch.GuideName
# Get EPG channel lineup station ID, and perform Set/Remove/Replace
if ( $epg123_mxf -match `
"<Channel uid=`"!Channel!$ChLineUp!(?<station_id>[0-9]*)_"+$ChNumber[0]+"_"+$ChNumber[1]+"`" lineup=`"(?<lineup_id>l[0-9]*)`" " + `
"service=`"(?<service_id>.*?)`" number=`""+$ChNumber[0]+"`" subNumber=`""+$ChNumber[1]+"`" />" ) {
$station_id = $matches['station_id']; $RegexEscaped_station_id = [Regex]::Escape($station_id)
$lineup_id = $matches['lineup_id']; $RegexEscaped_lineup_id = [Regex]::Escape($lineup_id)
$service_id = $matches['service_id']; $RegexEscaped_service_id = [Regex]::Escape($service_id)
# Get existing custom service name string
$cSN = ( $epg123_cfg -match "<StationID CallSign=`".*?`".*?(?<Existing_customServiceName> customServiceName=`".*?`").*?>$RegexEscaped_station_id</StationID>" )
$Existing_customServiceName = $matches['Existing_customServiceName']; $RegexEscaped_Existing_customServiceName = [Regex]::Escape($Existing_customServiceName)
# Get existing custom call sign string
$cCS = ( $epg123_cfg -match "<StationID CallSign=`".*?`".*?(?<Existing_customCallSign> customCallSign=`".*?`").*?>$RegexEscaped_station_id</StationID>" )
$Existing_customCallSign = $matches['Existing_customCallSign']; $RegexEscaped_Existing_customCallSign = [Regex]::Escape($Existing_customCallSign)
if ($Custom_Service_Name_flag) { $customServiceName = " customServiceName=`"$ChName`"" }
if ($Custom_CallSign_flag) { $customCallSign = " customCallSign=`"$ChCallSign`"" }
# Set/Remove/Replace custom service name
$epg123_cfg = $epg123_cfg -replace `
"<StationID CallSign=`"(.*?)`"(.*?)$RegexEscaped_Existing_customServiceName(.*?)>$RegexEscaped_station_id</StationID>", `
"<StationID CallSign=`"`$1`"`$2$customServiceName`$3>$station_id</StationID>"
# Set/Remove/Replace custom call sign
$epg123_cfg = $epg123_cfg -replace `
"<StationID CallSign=`"(.*?)`"(.*?)$RegexEscaped_Existing_customCallSign(.*?)>$RegexEscaped_station_id</StationID>", `
"<StationID CallSign=`"`$1`"`$2$customCallSign`$3>$station_id</StationID>"
$ChNumber[0]+'.'+$ChNumber[1]+"`t"+$ChName+"`t"+$ChCallSign+"`t"+$station_id+"`t"+$lineup_id+"`t"+$service_id
}
}
'' # Blank line
'Customized epg123.cfg saved to: '
if ($Dry_Run) {
$EPG_Data_Dir+'\custom_callSign_serviceName.Dry_Run.cfg'
$epg123_cfg.TrimEnd() | Set-Content -Encoding UTF8 -Path $EPG_Data_Dir'\custom_callSign_serviceName.Dry_Run.cfg'
} else {
$EPG_Data_Dir+'\'+$CFG_File
$epg123_cfg.TrimEnd() | Set-Content -Encoding UTF8 -Path $EPG_Data_Dir'\'$CFG_File
}
}
#
# Run HDHomeRun device channel scan (web based)
#
function Invoke-Device_Channel_Dectection_Scan_Web {
$status_json_url = 'http://'+$Device_Address+'/lineup_status.json'
$scan_status = $webclient.DownloadString($status_json_url) | ConvertFrom-Json
if ($scan_status.ScanInProgress -eq 0) {
if ($scan_status.ScanPossible -eq 1) {
'Channel Detection: '
' Scanning device ('+$Device_Address+')...'
$lineup_scan_start_url = 'http://'+$Device_Address+'/lineup.post?scan=start&source='+$scan_status.Source
# Invoke-WebRequest -Uri $lineup_scan_start_url -Method POST
$webclient.UploadString($lineup_scan_start_url,'')
} else { 'Scan not possible. May not be an available tuner.' }
} else { 'Channel detection scan already in progress...' }
# While scanning...
Do {
$scan_status = $webclient.DownloadString($status_json_url) | ConvertFrom-Json
if ($scan_status.ScanInProgress -ge 1) {
$status = 'Found '+$scan_status.Found+' programs ('+$scan_status.Progress+'%)'
Write-Host "`r"$status -NoNewLine
Start-Sleep 1
}
} While ( $scan_status.ScanInProgress -eq 1)
$json = $webclient.DownloadString($lineup_json_url)
# Final status
$Found = ($json | ConvertFrom-Json).length; $Progress = 100
$status = "Found "+$Found+" programs ("+$Progress+"%)"
Write-Host "`r"$status
'' # Blank line
$script:json = $json
}
#
# Run HDHomeRun device channel scan; Return GuideNumber and GuideName JSON string (client utility based)
#
function Invoke-Device_Channel_Dectection_Scan_Utility {
$tuner0 = & 'C:\Program Files\Silicondust\HDHomeRun\hdhomerun_config.exe' $Device_ID get /tuner0/status
$tuner1 = & 'C:\Program Files\Silicondust\HDHomeRun\hdhomerun_config.exe' $Device_ID get /tuner1/status
"Scanning device $Device_ID for channels..."
if ($tuner0 -match "lock=none") {
$channel_scan = & 'C:\Program Files\Silicondust\HDHomeRun\hdhomerun_config.exe' $Device_ID 'scan' '/tuner0'
$Device_Channel_Dectection_Scan_Status = & 'C:\Program Files\Silicondust\HDHomeRun\hdhomerun_config.exe' $Device_ID 'get' '/lineup/scan'
} elseif ($tuner1 -match "lock=none") {
$channel_scan = & 'C:\Program Files\Silicondust\HDHomeRun\hdhomerun_config.exe' $Device_ID 'scan' '/tuner1'
$Device_Channel_Dectection_Scan_Status = & 'C:\Program Files\Silicondust\HDHomeRun\hdhomerun_config.exe' $Device_ID 'get' '/lineup/scan'
} else {
"No Tuner Available"
}
if ($Device_Channel_Dectection_Scan_Status -match "state=complete") {
$json = '['
$lines = $channel_scan.split("`r`n")
foreach ($line in $lines) {
$matched = $line -match "PROGRAM *[0-9]*: *(?<GuideNumber>[0-9]{1,2}\.[0-9]*) *(?<GuideName>.*)"
if ($matched) {
$json += '{"GuideNumber":"'+$matches['GuideNumber']+'","GuideName":"'+$matches['GuideName']+'"},'
}
}
$json = $json.TrimEnd(',')
$json += ']'
}
# Sort by ascending GuideNumber
$array = $json | ConvertFrom-Json
$array_sorted = $array | Sort-Object { [float]$_.GuideNumber }
$json = $array_sorted | ConvertTo-Json
$json = $json -Replace "[`r`n]","" -Replace " *{ *","{" -Replace " *} *", "}" -Replace "`": *`"","`":`"" -Replace "`", *`"","`",`""
$script:json = $json
}
Invoke-Main
'' # Blank line
pause # Wait for user to exit/close PS window
# NOTE:
# $json = Invoke-WebRequest http://$Device_Address/lineup.json?show=found # Doesn't work with UTF8 encoding
# Workaround A
# $json = Invoke-WebRequest -Uri 'http://$Device_Address/lineup.json?show=found' -Outfile $EPG_Data_Dir'\hdhrlineup.json'
# $json = Get-Content -Path $EPG_Data_Dir'\hdhrlineup.json' -Encoding UTF8 -Raw
# Remove-Item -Path $EPG_Data_Dir'\hdhrlineup.json' -Force
# Workaround B
# $webclient = new-object System.Net.WebClient
# $json = $webclient.DownloadString("http://$Device_Address/lineup.json?show=$Show")
Code: Select all
[
{"GuideNumber":"2.1","GuideName":"KATU"},
{"GuideNumber":"2.2","GuideName":"MeTV"},
{"GuideNumber":"2.3","GuideName":"Comet"},
{"GuideNumber":"2.4","GuideName":"Stadium"},
{"GuideNumber":"6.1","GuideName":"KOIN-HD"},
{"GuideNumber":"6.2","GuideName":"GetTV"},
{"GuideNumber":"6.3","GuideName":"Bounce"},
{"GuideNumber":"8.1","GuideName":"KGW"},
{"GuideNumber":"8.2","GuideName":"Crime"},
{"GuideNumber":"8.3","GuideName":"Quest"},
{"GuideNumber":"8.10","GuideName":"KGW"},
{"GuideNumber":"8.20","GuideName":"Crime"},
{"GuideNumber":"8.30","GuideName":"Quest"},
{"GuideNumber":"10.1","GuideName":"OPB"},
{"GuideNumber":"10.2","GuideName":"OPBPlus"},
{"GuideNumber":"10.3","GuideName":"OPBKids"},
{"GuideNumber":"10.4","GuideName":"OPB-FM"},
{"GuideNumber":"12.1","GuideName":"FOX 12"},
{"GuideNumber":"12.2","GuideName":"COZI"},
{"GuideNumber":"12.3","GuideName":"LAFF"},
{"GuideNumber":"12.4","GuideName":"DABL"},
{"GuideNumber":"22.1","GuideName":"ION"},
{"GuideNumber":"22.2","GuideName":"qubo"},
{"GuideNumber":"22.3","GuideName":"IONPlus"},
{"GuideNumber":"22.4","GuideName":"Shop"},
{"GuideNumber":"22.5","GuideName":"QVC"},
{"GuideNumber":"22.6","GuideName":"HSN"},
{"GuideNumber":"22.7","GuideName":"TLMD"},
{"GuideNumber":"24.1","GuideName":"TBN HD"},
{"GuideNumber":"24.2","GuideName":"Hilsong"},
{"GuideNumber":"24.3","GuideName":"SMILE"},
{"GuideNumber":"24.4","GuideName":"Enlace"},
{"GuideNumber":"24.5","GuideName":"POSITIV"},
{"GuideNumber":"26.1","GuideName":"SBN TV"},
{"GuideNumber":"26.2","GuideName":"Shop LC"},
{"GuideNumber":"26.3","GuideName":"ACE TV"},
{"GuideNumber":"26.4","GuideName":"Heartla"},
{"GuideNumber":"32.1","GuideName":"KRCW"},
{"GuideNumber":"32.2","GuideName":"Antenna"},
{"GuideNumber":"32.3","GuideName":"Courttv"},
{"GuideNumber":"32.4","GuideName":"TBD"},
{"GuideNumber":"47.1","GuideName":"KUNP-LD"},
{"GuideNumber":"47.2","GuideName":"TBD"},
{"GuideNumber":"47.3","GuideName":"Charge"},
{"GuideNumber":"49.1","GuideName":"Fox12+"},
{"GuideNumber":"49.2","GuideName":"Escape"},
{"GuideNumber":"49.3","GuideName":"Bounce"},
{"GuideNumber":"49.4","GuideName":"Grit"},
{"GuideNumber":"49.20","GuideName":"Escape"},
{"GuideNumber":"49.40","GuideName":"Grit"}
]
-
- Posts: 145
- Joined: Thu Sep 10, 2020 8:03 am
- Location:
- HTPC Specs:
Script to auto-magically collect the PSIP VCT short names from HDHomeRun device and customize the config has learned some more new things.
1) Learned to select channel detection scan method (web/client/none) from variable setting.
2) Learned to retrieve device information (model, firmware, ID)
3) Learned how not to be verbose (client utility)
4) Learned to dynamically find and select from more than two tuners (0/1) (client utility)
5) Learned to override settings with parameters passed on command line
Example command line shortcuts
See change log at end of file for previous changes.
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.json (Example)
1) Learned to select channel detection scan method (web/client/none) from variable setting.
2) Learned to retrieve device information (model, firmware, ID)
3) Learned how not to be verbose (client utility)
4) Learned to dynamically find and select from more than two tuners (0/1) (client utility)
5) Learned to override settings with parameters passed on command line
Example command line shortcuts
Code: Select all
Client Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040nnnn' -Device_Channel_Detection_Scan 'client' -Device_IP_Address ''
Default: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Retrieve_Data_From_Device $false
No Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.2.202' -Device_Channel_Detection_Scan 'none'
Verbose: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040nnnn' -Device_Channel_Detection_Scan 'client' -Device_IP_Address '' -Verbose $true
Web Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.2.202' -Device_Channel_Detection_Scan 'web'
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
Code: Select all
# Before using see observed issues, change log and development environment information at bottom.
# Version: 20201106.1-alpha
# Status: alpha
# Typical file system locations
# Folder: C:\ProgramData\GaRyan2\epg123
# PS Script File: custom_callSign_serviceName.ps1
# JSON Data File: custom_callSign_serviceName.json
# Dry Run CFG File: custom_callSign_serviceName.Dry_Run.cfg
# Criteria to use parameters (must be first line of script)
param(`
$Dry_Run = $true, ` # Save to $EPG_Data_Dir'\custom_callSign_serviceName.Dry_Run.cfg'
$CHLineUP = 'USA-OTA-97007', ` # Channel line up associated with HDHomeRun device
$EPG_Service = 'EPG123', ` # EPG service name
$EPG_Data_Dir = 'C:\ProgramData\GaRyan2\epg123', ` # Fully qualified EPG data dir path (without trailing slash)
$CFG_File = 'epg123.cfg', ` # EPG config file
$MXF_File = 'epg123.mxf', ` # EPG MXF file
$Custom_Service_Name_flag = $true, ` # Set/Replace (true), Remove (false) custom channel service name
$Custom_CallSign_flag = $true, ` # Set/Replace (true), Remove (false) custom channel call sign
# Can be used standalone with guide name and number JSON data: ex: [{"GuideNumber":"10.4","GuideName":"OPBKids"}]
$Retrieve_Data_From_Device = $true, ` # Retrieve channels & names from HDHomeRun device
$Device_Channel_Detection_Scan = 'none', ` # Perform HDHomeRun device channel detection scan: web, client, none
# HD HomeRun Web Utility Settings
# A web utility channel detection scan...
# 1) updates the device stored channel lineup
# 2) interrupts viewing and recording
$Device_IP_Address = '192.168.2.202', ` # HDHomeRun device IP address (required for web scan)
$Show = 'found', ` # HDHomeRun device channel line up web query: all, found, favorites
# HD HomeRun Client Utility Settings
# A client utility channel detection scan...
# 1) does not update the device stored channel lineup
# 2) may append a status comment to the channel name. ex: '(control)', '(encrypted)', '(no data)' Unknown what effects this may have.
$HDHR_Prog_Dir = 'C:\Program Files\Silicondust\HDHomeRun', ` # HDHomeRund installation folder path (without trailing slash)
$HDHR_Client_Utility = 'hdhomerun_config.exe', ` # HDHomeRun client configuration utility executable
$Device_ID = '1040nnnn', ` # Needed only for device without IP address
$Verbose = $false ` # Client utility verbose output
)
# End Criteria to use parameters
#
# Genesis
#
function Invoke-Main {
Get-Channels_Guide_Name_Data
Edit-Configuration
}
#
# Retrieve PSIP VCT short name from HDHomeRun device. Or use default/fallback channel names.
#
function Get-Channels_Guide_Name_Data {
if ($Retrieve_Data_From_Device) {
$webclient = new-object System.Net.WebClient
$lineup_json_url = 'http://'+$Device_IP_Address+'/lineup.json?show='+$Show
if ($Device_Channel_Detection_Scan -eq 'web') {
Invoke-Device_Channel_Detection_Scan_Web
} elseif ($Device_Channel_Detection_Scan -eq 'client') {
Invoke-Device_Channel_Detection_Scan_Utility # SliconDust hdhomerun_config.exe
} else {
Get-Device_Information 'web'
$json = $webclient.DownloadString($lineup_json_url)
$Device_Address = $Device_IP_Address
}
}
try {
$vPSObject = $json | ConvertFrom-Json -ErrorAction Stop;
'Using data retrieved from device ('+$Device_Address+')'
'' # Blank line
# Save device retrieved, guide number and guide name abbreviated, json for default/fallback, don't overwrite existing
if (!(Test-Path $EPG_Data_Dir'\custom_callSign_serviceName.json')) {
$json = $json -Replace '\[\{', "[`r`n{" -Replace '},{', "},`r`n{" -Replace '}]', "}`r`n]" # Line-ized (each channel)
$json = $json -Replace "({`"GuideNumber.*?GuideName`":`".*?`").*(},*)", "`$1`$2" # Only GuideNumber and GuideName
$json | Set-Content -Path $EPG_Data_Dir'\custom_callSign_serviceName.json'
}
} catch {
# Channel GuideNumber and GuideName json.
# Use in place of (retrieve from device false) or if HDHomeRun device query fails.
if (Test-Path $EPG_Data_Dir'\custom_callSign_serviceName.json') {
$json_fallback = Get-Content -path $EPG_Data_Dir'\custom_callSign_serviceName.json' -Raw
$vPSObject = $json_fallback | ConvertFrom-Json
'Using default/fallback data: '
$EPG_Data_Dir+'\custom_callSign_serviceName.json'
'' # Blank line
} else {
'Default/Fallback data not found: '
$EPG_Data_Dir+'\custom_callSign_serviceName.json'
'' # Blank line
pause; exit;
}
}
$script:vPSObject = $vPSObject
}
#
# Set/Remove/Replace epg123.cfg customServiceName and customCallSign fields
#
function Edit-Configuration {
$epg123_cfg = Get-Content -path $EPG_Data_Dir'\'$CFG_File -Raw
$epg123_mxf = Get-Content -path $EPG_Data_Dir'\output\'$MXF_File -Raw
"Channel`tName`tSign`tSta ID`tLineup`tSrv ID"
foreach ($ch in $vPSObject) {
$ChNumber = $ch.GuideNumber.Split('.')
$ChName = $ch.GuideName
$ChCallSign = $ch.GuideName
# Get EPG channel lineup station ID, and perform Set/Remove/Replace
if ( $epg123_mxf -match `
"<Channel uid=`"!Channel!$ChLineUp!(?<station_id>[0-9]*)_"+$ChNumber[0]+"_"+$ChNumber[1]+"`" lineup=`"(?<lineup_id>l[0-9]*)`" " + `
"service=`"(?<service_id>.*?)`" number=`""+$ChNumber[0]+"`" subNumber=`""+$ChNumber[1]+"`" />" ) {
$station_id = $matches['station_id']; $RegexEscaped_station_id = [Regex]::Escape($station_id)
$lineup_id = $matches['lineup_id']; $RegexEscaped_lineup_id = [Regex]::Escape($lineup_id)
$service_id = $matches['service_id']; $RegexEscaped_service_id = [Regex]::Escape($service_id)
# Get existing custom service name string
$cSN = ( $epg123_cfg -match "<StationID CallSign=`".*?`".*?(?<Existing_customServiceName> customServiceName=`".*?`").*?>$RegexEscaped_station_id</StationID>" )
$Existing_customServiceName = $matches['Existing_customServiceName']; $RegexEscaped_Existing_customServiceName = [Regex]::Escape($Existing_customServiceName)
# Get existing custom call sign string
$cCS = ( $epg123_cfg -match "<StationID CallSign=`".*?`".*?(?<Existing_customCallSign> customCallSign=`".*?`").*?>$RegexEscaped_station_id</StationID>" )
$Existing_customCallSign = $matches['Existing_customCallSign']; $RegexEscaped_Existing_customCallSign = [Regex]::Escape($Existing_customCallSign)
if ($Custom_Service_Name_flag) { $customServiceName = " customServiceName=`"$ChName`"" } else { $ChName = '' }
if ($Custom_CallSign_flag) { $customCallSign = " customCallSign=`"$ChCallSign`"" } else { $ChCallSign = '' }
# Set/Remove/Replace custom service name
$epg123_cfg = $epg123_cfg -replace `
"<StationID CallSign=`"(.*?)`"(.*?)$RegexEscaped_Existing_customServiceName(.*?)>$RegexEscaped_station_id</StationID>", `
"<StationID CallSign=`"`$1`"`$2$customServiceName`$3>$station_id</StationID>"
# Set/Remove/Replace custom call sign
$epg123_cfg = $epg123_cfg -replace `
"<StationID CallSign=`"(.*?)`"(.*?)$RegexEscaped_Existing_customCallSign(.*?)>$RegexEscaped_station_id</StationID>", `
"<StationID CallSign=`"`$1`"`$2$customCallSign`$3>$station_id</StationID>"
$ChNumber[0]+'.'+$ChNumber[1]+"`t"+$ChName+"`t"+$ChCallSign+"`t"+$station_id+"`t"+$lineup_id+"`t"+$service_id
}
}
'' # Blank line
'Customized epg123.cfg saved to: '
if ($Dry_Run) {
$EPG_Data_Dir+'\custom_callSign_serviceName.Dry_Run.cfg'
$epg123_cfg.TrimEnd() | Set-Content -Encoding UTF8 -Path $EPG_Data_Dir'\custom_callSign_serviceName.Dry_Run.cfg'
} else {
$EPG_Data_Dir+'\'+$CFG_File
$epg123_cfg.TrimEnd() | Set-Content -Encoding UTF8 -Path $EPG_Data_Dir'\'$CFG_File
}
}
#
# Run HDHomeRun device channel detection scan (web utility)
#
function Invoke-Device_Channel_Detection_Scan_Web {
Get-Device_Information 'web'
$status_json_url = 'http://'+$Device_IP_Address+'/lineup_status.json'
$scan_status = $webclient.DownloadString($status_json_url) | ConvertFrom-Json
if ($scan_status.ScanInProgress -eq 0) {
if ($scan_status.ScanPossible -eq 1) {
'Channel Detection: '
' Scanning device ('+$Device_IP_Address+')...'
$lineup_scan_start_url = 'http://'+$Device_IP_Address+'/lineup.post?scan=start&source='+$scan_status.Source
# Invoke-WebRequest -Uri $lineup_scan_start_url -Method POST
$webclient.UploadString($lineup_scan_start_url,'')
} else { 'Scan not possible. May not be an available tuner.' }
} else { 'Channel detection scan already in progress...' }
# While scanning...
Do {
$scan_status = $webclient.DownloadString($status_json_url) | ConvertFrom-Json
if ($scan_status.ScanInProgress -ge 1) {
$status = ' Found '+$scan_status.Found+' programs ('+$scan_status.Progress+'%)'
Write-Host "`r"$status -NoNewLine
Start-Sleep 1
}
} While ( $scan_status.ScanInProgress -eq 1)
$json = $webclient.DownloadString($lineup_json_url)
# Final status
$found = ($json | ConvertFrom-Json).length; $progress = 100
$status = ' Found '+$found+' programs ('+$progress+'%) '
Write-Host "`r"$status
'' # Blank line
$Script:json = $json; $Script:Device_Address = $Device_IP_Address
}
#
# Run HDHomeRun device channel detection scan; Return GuideNumber and GuideName JSON (client utility)
#
function Invoke-Device_Channel_Detection_Scan_Utility {
# Get Device ID from discover.json URL
if (!$Device_ID) {
if ($Device_IP_Address) {
# $Device = $webclient.DownloadString($discover_json_url) | ConvertFrom-Json
$Device = $webclient.DownloadString('http://'+$Device_IP_Address+'/discover.json') | ConvertFrom-Json
$Device_ID = $Device.DeviceID
} else {
'Device not found'
'' # Blank line
pause; exit;
}
}
Get-Device_Information 'client'
# Find/Select available tuner
for ($tuner = 0; $tuner -le 64; $tuner++) { # Loop depth failsafe (64)
$tuner_lockkey = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Device_ID get /tuner$tuner/lockkey
if ($tuner_lockkey -match "none" -Or $tuner_lockkey -match "ERROR") {
break
}
}
if ($tuner_lockkey -notmatch "none") {
' No Tuner Available'
'' # Blank line
pause; exit;
}
$channel_map = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Device_ID 'get' "/tuner$tuner/channelmap"
'Channel Detection: '
' Scanning device ('+$Device_ID+')... Tuner: '+$tuner+' '+$channel_map
'' # Blank line
& "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Device_ID 'scan' "/tuner$tuner" | ForEach-Object -Process { $found = 0; $progress = 0 } {
$matched = $_ -match "PROGRAM *[0-9]*: *(?<GuideNumber>[0-9]{1,2}\.[0-9]*) *(?<GuideName>.*)"
if ($matched) {
$matches['GuideName'] = $matches['GuideName'] -Replace "(.*?) \(control\)(.*)", "`$1`$2"
$matches['GuideName'] = $matches['GuideName'] -Replace "(.*?) \(encrypted\)(.*)", "`$1`$2"
$matches['GuideName'] = $matches['GuideName'] -Replace "(.*?) \(no data\)(.*)", "`$1`$2"
$json += '{"GuideNumber":"'+$matches['GuideNumber']+'","GuideName":"'+$matches['GuideName']+'"},'
$found++
}
$matched = $_ -match "SCANNING: .*? \(${channel_map}:(?<channel_number>[0-9]*)\).*"
if ($matched) {
$channel_number = $matches['channel_number']
if (!$total_channels) { $total_channels = $channel_number }
$status = ' Found '+$found+' programs ('+$progress+'%) Ch. '+$channel_number+' '
$progress = [int]((($total_channels - $channel_number + 1 ) / ($total_channels - 1)) * 100)
if (!$Verbose) {
Write-Host "`r"$status -NoNewLine
}
}
if ($Verbose) {
Write-Host $_
}
}
if ($Verbose) {
'' # Blank line
}
# Final Status
$status = ' Found '+$found+' programs ('+$progress+'%) '
Write-Host "`r"$status
'' # Blank line
if ($json) {
$json = $json.TrimEnd(',')
$json = '['+$json+']' # As array
# Sort by ascending GuideNumber
$array = $json | ConvertFrom-Json
$array_sorted = $array | Sort-Object { [Version]$_.GuideNumber }
$json = $array_sorted | ConvertTo-Json -Compress
}
$Script:json = $json; $Script:Device_Address = $Device_ID
}
#
# Get Device Information
#
function Get-Device_Information ($method){
if ($method -eq 'web') {
$discover_json_url = 'http://'+$Device_IP_Address+'/discover.json'
$Device = $webclient.DownloadString($discover_json_url) | ConvertFrom-Json
$Device.FriendlyName
'Model: '+$Device.ModelNumber
'Firmware: '+$Device.FirmwareVersion
'Device ID: '+$Device.DeviceID
'Device IP Address: '+$Device_IP_Address
} elseif ($method -eq 'client') {
$ModelNumber = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Device_ID get /sys/hwmodel
$FirmwareVersion = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Device_ID get /sys/version
'' # Blank line
'Model: '+$ModelNumber
'Firmware: '+$FirmwareVersion
'Device ID: '+$Device_ID
'Device IP Address: '+$Device_IP_Address
}
'' # Blank line
}
Invoke-Main
'' # Blank line
pause; exit; # Wait for user to exit/close PS window
# NOTE:
# $json = Invoke-WebRequest http://$Device_IP_Address/lineup.json?show=found # Doesn't work with UTF8 encoding
# Workaround A
# $json = Invoke-WebRequest -Uri 'http://$Device_IP_Address/lineup.json?show=found' -Outfile $EPG_Data_Dir'\hdhrlineup.json'
# $json = Get-Content -Path $EPG_Data_Dir'\hdhrlineup.json' -Encoding UTF8 -Raw
# Remove-Item -Path $EPG_Data_Dir'\hdhrlineup.json' -Force
# Workaround B
# $webclient = new-object System.Net.WebClient
# $json = $webclient.DownloadString("http://$Device_IP_Address/lineup.json?show=$Show")
# Development Environment
# Windows 8.1 Pro w/Media Center
# PowerShell: 4.0
# SiliconDust: HDHomeRun CONNECT, Model: HDHR4-2US, Firmware: 20200907
# Observed Issues
# Running a channel detection scan (web) interrupts in process viewing/recording signal.
# Running a channel detection scan (client) may append a status comment to the channel name. ex: '(control)', '(encrypted)', '(no data)' Unknown what effects this may have.
# Change Log
# Version: 20201106.1-alpha
# Learned to select channel detection scan method (web/client/none) from variable setting.
# Learned to retrieve device information (model, firmware, ID)
# Learned how not to be verbose (client utility)
# Learned to dynamically find and select from more than two tuners (0/1) (client utility)
# Learned to override settings with parameters passed on command line
# Example command line shortcuts
# Client Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040nnnn' -Device_Channel_Detection_Scan 'client' -Device_IP_Address ''
# Default: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Retrieve_Data_From_Device $false
# No Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.2.202' -Device_Channel_Detection_Scan 'none'
# Verbose: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040nnnn' -Device_Channel_Detection_Scan 'client' -Device_IP_Address '' -Verbose $true
# Web Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.2.202' -Device_Channel_Detection_Scan 'web'
# Version: 20200928.1-alpha
# Learned how to run HDHomeRun device channel detection scan
# Learned to operate standalone (without a HDHR device) using guide number and name JSON data.
# Learned to do a dry run
# Factored into functions
# Version: 20200926.1-alpha
# Genesis
Code: Select all
[
{"GuideNumber":"2.1","GuideName":"KATU"},
{"GuideNumber":"2.2","GuideName":"MeTV"},
{"GuideNumber":"2.3","GuideName":"Comet"},
{"GuideNumber":"2.4","GuideName":"Stadium"},
{"GuideNumber":"6.1","GuideName":"KOIN-HD"},
{"GuideNumber":"6.2","GuideName":"GetTV"},
{"GuideNumber":"6.3","GuideName":"Bounce"},
{"GuideNumber":"8.1","GuideName":"KGW"},
{"GuideNumber":"8.2","GuideName":"Crime"},
{"GuideNumber":"8.3","GuideName":"Quest"},
{"GuideNumber":"8.10","GuideName":"KGW"},
{"GuideNumber":"8.20","GuideName":"Crime"},
{"GuideNumber":"8.30","GuideName":"Quest"},
{"GuideNumber":"10.1","GuideName":"OPB"},
{"GuideNumber":"10.2","GuideName":"OPBPlus"},
{"GuideNumber":"10.3","GuideName":"OPBKids"},
{"GuideNumber":"10.4","GuideName":"OPB-FM"},
{"GuideNumber":"12.1","GuideName":"FOX 12"},
{"GuideNumber":"12.2","GuideName":"COZI"},
{"GuideNumber":"12.3","GuideName":"LAFF"},
{"GuideNumber":"12.4","GuideName":"DABL"},
{"GuideNumber":"22.1","GuideName":"ION"},
{"GuideNumber":"22.2","GuideName":"qubo"},
{"GuideNumber":"22.3","GuideName":"IONPlus"},
{"GuideNumber":"22.4","GuideName":"Shop"},
{"GuideNumber":"22.5","GuideName":"QVC"},
{"GuideNumber":"22.6","GuideName":"HSN"},
{"GuideNumber":"22.7","GuideName":"TLMD"},
{"GuideNumber":"24.1","GuideName":"TBN HD"},
{"GuideNumber":"24.2","GuideName":"Hilsong"},
{"GuideNumber":"24.3","GuideName":"SMILE"},
{"GuideNumber":"24.4","GuideName":"Enlace"},
{"GuideNumber":"24.5","GuideName":"POSITIV"},
{"GuideNumber":"26.1","GuideName":"SBN TV"},
{"GuideNumber":"26.2","GuideName":"Shop LC"},
{"GuideNumber":"26.3","GuideName":"ACE TV"},
{"GuideNumber":"26.4","GuideName":"Heartla"},
{"GuideNumber":"32.1","GuideName":"KRCW"},
{"GuideNumber":"32.2","GuideName":"Antenna"},
{"GuideNumber":"32.3","GuideName":"Courttv"},
{"GuideNumber":"32.4","GuideName":"TBD"},
{"GuideNumber":"47.1","GuideName":"KUNP-LD"},
{"GuideNumber":"47.2","GuideName":"TBD"},
{"GuideNumber":"47.3","GuideName":"Charge"},
{"GuideNumber":"49.1","GuideName":"Fox12+"},
{"GuideNumber":"49.2","GuideName":"Escape"},
{"GuideNumber":"49.3","GuideName":"Bounce"},
{"GuideNumber":"49.4","GuideName":"Grit"},
{"GuideNumber":"49.20","GuideName":"Escape"},
{"GuideNumber":"49.40","GuideName":"Grit"}
]
-
- Posts: 145
- Joined: Thu Sep 10, 2020 8:03 am
- Location:
- HTPC Specs:
Patch to accommodate the "matchName" field in "epg123.mxf"
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
Code: Select all
--- custom_callSign_serviceName.org.ps1 2020-11-06 21:04:39.000000000 -0800
+++ custom_callSign_serviceName.ps1 2021-01-11 17:02:34.000000000 -0800
@@ -3 +3 @@
-# Version: 20201106.1-alpha
+# Version: 20210111.1-alpha
@@ -127 +127 @@
- "service=`"(?<service_id>.*?)`" number=`""+$ChNumber[0]+"`" subNumber=`""+$ChNumber[1]+"`" />" ) {
+ "service=`"(?<service_id>.*?)`" matchName=`"(?<matchName>.*?)`" number=`""+$ChNumber[0]+"`" subNumber=`""+$ChNumber[1]+"`" />" ) {
@@ -360,0 +361,3 @@
+
+# Version: 20210111.1-alpha
+# Accommodate the "matchName" field in "epg123.mxf"
-
- Posts: 145
- Joined: Thu Sep 10, 2020 8:03 am
- Location:
- HTPC Specs:
Patch to include station call sign in display output.
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
Code: Select all
--- custom_callSign_serviceName.org.ps1 2021-01-11 17:02:34.000000000 -0800
+++ custom_callSign_serviceName.ps1 2021-02-28 15:39:16.000000000 -0800
@@ -3 +3 @@
-# Version: 20210111.1-alpha
+# Version: 20210228.1-alpha
@@ -117 +117 @@
- "Channel`tName`tSign`tSta ID`tLineup`tSrv ID"
+ "Channel`tCl Sign`tcName`tcSign`tSta ID`tLineup`tSrv ID"
@@ -140,0 +141,4 @@
+ # Get station call sign string
+ $oCS = ( $epg123_cfg -match "<StationID CallSign=`"(?<Existing_stationCallSign>.*?)`".*?>$RegexEscaped_station_id</StationID>" )
+ $Existing_stationCallSign = $matches['Existing_stationCallSign']; $RegexEscaped_Existing_stationCallSign = [Regex]::Escape($Existing_stationCallSign)
+
@@ -154 +158 @@
- $ChNumber[0]+'.'+$ChNumber[1]+"`t"+$ChName+"`t"+$ChCallSign+"`t"+$station_id+"`t"+$lineup_id+"`t"+$service_id
+ $ChNumber[0]+'.'+$ChNumber[1]+"`t"+$Existing_stationCallSign+"`t"+$ChName+"`t"+$ChCallSign+"`t"+$station_id+"`t"+$lineup_id+"`t"+$service_id
@@ -360,0 +365,3 @@
+
+# Version: 20210228.1-alpha
+# Include station call sign in display output.
-
- Posts: 145
- Joined: Thu Sep 10, 2020 8:03 am
- Location:
- HTPC Specs:
Script to auto-magically collect the PSIP VCT short names from HDHomeRun device and customize the config has received a few tweaks.
1) Clear the tuner channel after scan.
2) Use optional CustomGuideName field in JSON.
3) Use IP address for Utility_Device_Address (to avoid LAN broadcast when using device ID).
4) Include broadcast channel in display output and JSON.
Example command line shortcuts
See change log at end of file for previous changes.
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.json (Example)
1) Clear the tuner channel after scan.
2) Use optional CustomGuideName field in JSON.
3) Use IP address for Utility_Device_Address (to avoid LAN broadcast when using device ID).
4) Include broadcast channel in display output and JSON.
Example command line shortcuts
Code: Select all
Client Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040xxxx' -Device_Channel_Detection_Scan 'client' -Device_IP_Address ''
Default: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Retrieve_Data_From_Device $false
No Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.x.x' -Device_Channel_Detection_Scan 'none'
Verbose: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040xxxx' -Device_Channel_Detection_Scan 'client' -Device_IP_Address '' -Verbose $true
Web Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.x.x' -Device_Channel_Detection_Scan 'web'
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1
Code: Select all
# Before using see observed issues, change log and development environment information at bottom.
# Version: 20220922.3-alpha
# Status: alpha
# Typical file system locations
# Folder: C:\ProgramData\GaRyan2\epg123
# PS Script File: custom_callSign_serviceName.ps1
# JSON Data File: custom_callSign_serviceName.json
# Dry Run CFG File: custom_callSign_serviceName.Dry_Run.cfg
# Criteria to use parameters (must be first line of script)
param(`
$Dry_Run = $true, ` # Save to $EPG_Data_Dir'\custom_callSign_serviceName.Dry_Run.cfg'
$CHLineUP = 'USA-OTA-97xxx', ` # Channel line up associated with HDHomeRun device
$EPG_Service = 'EPG123', ` # EPG service name
$EPG_Data_Dir = 'C:\ProgramData\GaRyan2\epg123', ` # Fully qualified EPG data dir path (without trailing slash)
$CFG_File = 'epg123.cfg', ` # EPG config file
$MXF_File = 'epg123.mxf', ` # EPG MXF file
$Custom_Service_Name_flag = $true, ` # Set/Replace (true), Remove (false) custom channel service name
$Custom_CallSign_flag = $true, ` # Set/Replace (true), Remove (false) custom channel call sign
# Can be used standalone with guide name and number JSON data: ex: [{"GuideNumber":"10.4","GuideName":"OPBKids"}]
$Retrieve_Data_From_Device = $true, ` # Retrieve channels & names from HDHomeRun device
$Device_Channel_Detection_Scan = 'none', ` # Perform HDHomeRun device channel detection scan: web, client, none
# HD HomeRun Web Utility Settings
# A web utility channel detection scan...
# 1) updates the device stored channel lineup
# 2) interrupts viewing and recording
$Device_IP_Address = '192.168.x.x', ` # HDHomeRun device IP address (required for web scan)
$Show = 'found', ` # HDHomeRun device channel line up web query: all, found, favorites
# HD HomeRun Client Utility Settings
# A client utility channel detection scan...
# 1) does not update the device stored channel lineup
# 2) may append a status comment to the channel name. ex: '(control)', '(encrypted)', '(no data)' Unknown what effects this may have.
$HDHR_Prog_Dir = 'C:\Program Files\Silicondust\HDHomeRun', ` # HDHomeRund installation folder path (without trailing slash)
$HDHR_Client_Utility = 'hdhomerun_config.exe', ` # HDHomeRun client configuration utility executable
$Device_ID = '1040xxxx', ` # Needed only for device without IP address
$Verbose = $false ` # Client utility verbose output
)
# End Criteria to use parameters
#
# Genesis
#
function Invoke-Main {
Get-Channels_Guide_Name_Data
Edit-Configuration
}
#
# Retrieve PSIP VCT short name from HDHomeRun device. Or use default/fallback channel names.
#
function Get-Channels_Guide_Name_Data {
if ($Retrieve_Data_From_Device) {
$webclient = new-object System.Net.WebClient
$lineup_json_url = 'http://'+$Device_IP_Address+'/lineup.json?show='+$Show+'&tuning'
if ($Device_Channel_Detection_Scan -eq 'web') {
Invoke-Device_Channel_Detection_Scan_Web
} elseif ($Device_Channel_Detection_Scan -eq 'client') {
Invoke-Device_Channel_Detection_Scan_Utility # SliconDust hdhomerun_config.exe
} else {
Get-Device_Information 'web'
$json = $webclient.DownloadString($lineup_json_url)
$Device_Address = $Device_IP_Address
}
}
try {
$vPSObject = $json | ConvertFrom-Json -ErrorAction Stop;
'Using data retrieved from device ('+$Device_Address+')'
'' # Blank line
# Save device retrieved, guide number and guide name abbreviated, json for default/fallback, don't overwrite existing
if (!(Test-Path $EPG_Data_Dir'\custom_callSign_serviceName.json')) {
$json = $json -Replace '\[\{', "[`r`n{" -Replace '},{', "},`r`n{" -Replace '}]', "}`r`n]" # Line-ized (each channel)
$json = $json -Replace "({`"GuideNumber.*?GuideName`":`".*?`").*(},*)", "`$1`$2" # Only GuideNumber and GuideName
$json | Set-Content -Path $EPG_Data_Dir'\custom_callSign_serviceName.json'
}
} catch {
# Channel GuideNumber and GuideName json.
# Use in place of (retrieve from device false) or if HDHomeRun device query fails.
if (Test-Path $EPG_Data_Dir'\custom_callSign_serviceName.json') {
$json_fallback = Get-Content -path $EPG_Data_Dir'\custom_callSign_serviceName.json' -Raw
$vPSObject = $json_fallback | ConvertFrom-Json
'Using default/fallback data: '
$EPG_Data_Dir+'\custom_callSign_serviceName.json'
'' # Blank line
} else {
'Default/Fallback data not found: '
$EPG_Data_Dir+'\custom_callSign_serviceName.json'
'' # Blank line
pause; exit;
}
}
$script:vPSObject = $vPSObject
}
#
# Set/Remove/Replace epg123.cfg customServiceName and customCallSign fields
#
function Edit-Configuration {
$epg123_cfg = Get-Content -path $EPG_Data_Dir'\'$CFG_File -Raw
$epg123_mxf = Get-Content -path $EPG_Data_Dir'\output\'$MXF_File -Raw
"B-Cast`tDisplay`tStation`tCustom`tCustom`tStation`tLineup`tService"
"Channel`tChannel`tSign`tName`tSign`tID`tID`tID"
"-------`t-------`t-------`t-------`t-------`t-------`t-------`t-------"
foreach ($ch in $vPSObject) {
$PhyChNumber = $ch.ChannelNumber
$ChNumber = $ch.GuideNumber.Split('.')
$ChName = $ch.GuideName
$ChCallSign = $ch.GuideName
if ($ch.CustomGuideName) {
$ChName = $ch.CustomGuideName
$ChCallSign = $ch.CustomGuideName
}
# Get EPG channel lineup station ID, and perform Set/Remove/Replace
if ( $epg123_mxf -match `
"<Channel uid=`"!Channel!$ChLineUp!(?<station_id>[0-9]*)_"+$ChNumber[0]+"_"+$ChNumber[1]+"`" lineup=`"(?<lineup_id>l[0-9]*)`" " + `
"service=`"(?<service_id>.*?)`" matchName=`"(?<matchName>.*?)`" number=`""+$ChNumber[0]+"`" subNumber=`""+$ChNumber[1]+"`" />" ) {
$station_id = $matches['station_id']; $RegexEscaped_station_id = [Regex]::Escape($station_id)
$lineup_id = $matches['lineup_id']; $RegexEscaped_lineup_id = [Regex]::Escape($lineup_id)
$service_id = $matches['service_id']; $RegexEscaped_service_id = [Regex]::Escape($service_id)
# Get existing custom service name string
$cSN = ( $epg123_cfg -match "<StationID CallSign=`".*?`".*?(?<Existing_customServiceName> customServiceName=`".*?`").*?>$RegexEscaped_station_id</StationID>" )
$Existing_customServiceName = $matches['Existing_customServiceName']; $RegexEscaped_Existing_customServiceName = [Regex]::Escape($Existing_customServiceName)
# Get existing custom call sign string
$cCS = ( $epg123_cfg -match "<StationID CallSign=`".*?`".*?(?<Existing_customCallSign> customCallSign=`".*?`").*?>$RegexEscaped_station_id</StationID>" )
$Existing_customCallSign = $matches['Existing_customCallSign']; $RegexEscaped_Existing_customCallSign = [Regex]::Escape($Existing_customCallSign)
# Get station call sign string
$sCS = ( $epg123_cfg -match "<StationID CallSign=`"(?<Existing_stationCallSign>.*?)`".*?>$RegexEscaped_station_id</StationID>" )
$Existing_stationCallSign = $matches['Existing_stationCallSign']; $RegexEscaped_Existing_stationCallSign = [Regex]::Escape($Existing_stationCallSign)
if ($Custom_Service_Name_flag) { $customServiceName = " customServiceName=`"$ChName`"" } else { $ChName = '' }
if ($Custom_CallSign_flag) { $customCallSign = " customCallSign=`"$ChCallSign`"" } else { $ChCallSign = '' }
# Set/Remove/Replace custom service name
$epg123_cfg = $epg123_cfg -replace `
"<StationID CallSign=`"(.*?)`"(.*?)$RegexEscaped_Existing_customServiceName(.*?)>$RegexEscaped_station_id</StationID>", `
"<StationID CallSign=`"`$1`"`$2$customServiceName`$3>$station_id</StationID>"
# Set/Remove/Replace custom call sign
$epg123_cfg = $epg123_cfg -replace `
"<StationID CallSign=`"(.*?)`"(.*?)$RegexEscaped_Existing_customCallSign(.*?)>$RegexEscaped_station_id</StationID>", `
"<StationID CallSign=`"`$1`"`$2$customCallSign`$3>$station_id</StationID>"
# Display Channel Info
$PhyChNumber+"`t"+$ChNumber[0]+'.'+$ChNumber[1]+"`t"+$Existing_stationCallSign+"`t"+$ChName+"`t"+$ChCallSign+"`t"+$station_id+"`t"+$lineup_id+"`t"+$service_id
}
}
'' # Blank line
'Customized epg123.cfg saved to: '
if ($Dry_Run) {
$EPG_Data_Dir+'\custom_callSign_serviceName.Dry_Run.cfg'
$epg123_cfg.TrimEnd() | Set-Content -Encoding UTF8 -Path $EPG_Data_Dir'\custom_callSign_serviceName.Dry_Run.cfg'
} else {
$EPG_Data_Dir+'\'+$CFG_File
$epg123_cfg.TrimEnd() | Set-Content -Encoding UTF8 -Path $EPG_Data_Dir'\'$CFG_File
}
}
#
# Run HDHomeRun device channel detection scan (web utility)
#
function Invoke-Device_Channel_Detection_Scan_Web {
Get-Device_Information 'web'
$status_json_url = 'http://'+$Device_IP_Address+'/lineup_status.json'
$scan_status = $webclient.DownloadString($status_json_url) | ConvertFrom-Json
if ($scan_status.ScanInProgress -eq 0) {
if ($scan_status.ScanPossible -eq 1) {
'Channel Detection: '
' Scanning device ('+$Device_IP_Address+')...'
$lineup_scan_start_url = 'http://'+$Device_IP_Address+'/lineup.post?scan=start&source='+$scan_status.Source
# Invoke-WebRequest -Uri $lineup_scan_start_url -Method POST
$webclient.UploadString($lineup_scan_start_url,'')
} else { 'Scan not possible. May not be an available tuner.' }
} else { 'Channel detection scan already in progress...' }
# While scanning...
Do {
$scan_status = $webclient.DownloadString($status_json_url) | ConvertFrom-Json
if ($scan_status.ScanInProgress -ge 1) {
$status = ' Found '+$scan_status.Found+' programs ('+$scan_status.Progress+'%)'
Write-Host "`r"$status -NoNewLine
Start-Sleep 1
}
} While ( $scan_status.ScanInProgress -eq 1)
# $json = $webclient.DownloadString($lineup_json_url)
$Channel_Frequency_Map = @{
'57000000' = '2'
'63000000' = '3'
'69000000' = '4'
'79000000' = '5'
'85000000' = '6'
'177000000' = '7'
'183000000' = '8'
'189000000' = '9'
'195000000' = '10'
'201000000' = '11'
'207000000' = '12'
'213000000' = '13'
'473000000' = '14'
'579000000' = '15'
'485000000' = '16'
'491000000' = '17'
'497000000' = '18'
'503000000' = '19'
'509000000' = '20'
'515000000' = '21'
'521000000' = '22'
'527000000' = '23'
'533000000' = '24'
'539000000' = '25'
'545000000' = '26'
'551000000' = '27'
'557000000' = '28'
'563000000' = '29'
'569000000' = '30'
'575000000' = '31'
'581000000' = '32'
'587000000' = '33'
'593000000' = '34'
'599000000' = '35'
'605000000' = '36'
}
foreach ($ch in ($webclient.DownloadString($lineup_json_url) | ConvertFrom-Json)) {
$key = $ch.Frequency.ToString()
$channel_number = $Channel_Frequency_Map[$key]
$json += '{"ChannelNumber":"'+$channel_number+'","GuideNumber":"'+$ch.GuideNumber+'","GuideName":"'+$ch.GuideName+'"},'
# $json += '{"ChannelNumber":"'+$channel_number+'","GuideNumber":"'+$ch.GuideNumber+'","GuideName":"'+$ch.GuideName+'","CustomGuideName":""},'
$found++
}
# Final status
# $found = ('['+$json.TrimEnd(',')+']' | ConvertFrom-Json).length
$progress = 100
$status = ' Found '+$found+' programs ('+$progress+'%) '
Write-Host "`r"$status
'' # Blank line
if ($json) {
$json = $json.TrimEnd(',')
$json = '['+$json+']' # As array
# Sort by ascending GuideNumber
$array = $json | ConvertFrom-Json
$array_sorted = $array | Sort-Object { [Version]$_.GuideNumber }
$json = $array_sorted | ConvertTo-Json -Compress
}
$Script:json = $json; $Script:Device_Address = $Device_IP_Address
}
#
# Run HDHomeRun device channel detection scan; Return GuideNumber and GuideName JSON (client utility)
#
function Invoke-Device_Channel_Detection_Scan_Utility {
# Get Device ID from discover.json URL
if (!$Device_ID) {
if ($Device_IP_Address) {
# $Device = $webclient.DownloadString($discover_json_url) | ConvertFrom-Json
$Device = $webclient.DownloadString('http://'+$Device_IP_Address+'/discover.json') | ConvertFrom-Json
$Device_ID = $Device.DeviceID
} else {
'Device not found'
'' # Blank line
pause; exit;
}
}
# Use the device IP Address to avoid LAN broadcast
if ($Device_IP_Address) {
$Utility_Device_Address = $Device_IP_Address
} else {
$Utility_Device_Address = $Device_ID
}
Get-Device_Information 'client'
# Find/Select available tuner
for ($tuner = 0; $tuner -le 64; $tuner++) { # Loop depth failsafe (64)
$tuner_lockkey = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Utility_Device_Address get /tuner$tuner/lockkey
if ($tuner_lockkey -match "none" -Or $tuner_lockkey -match "ERROR") {
break
}
}
if ($tuner_lockkey -notmatch "none") {
' No Tuner Available'
'' # Blank line
pause; exit;
}
$channel_map = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Utility_Device_Address 'get' "/tuner$tuner/channelmap"
'Channel Detection: '
' Scanning device ('+$Device_ID+')... Tuner: '+$tuner+' '+$channel_map
'' # Blank line
& "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Utility_Device_Address 'scan' "/tuner$tuner" | ForEach-Object -Process { $found = 0; $progress = 0 } {
$matched = $_ -match "SCANNING: .*? \(${channel_map}:(?<channel_number>[0-9]*)\).*"
if ($matched) {
$channel_number = $matches['channel_number']
if (!$total_channels) { $total_channels = $channel_number }
$status = ' Found '+$found+' programs ('+$progress+'%) Ch. '+$channel_number+' '
$progress = [int]((($total_channels - $channel_number + 1 ) / ($total_channels - 1)) * 100)
if (!$Verbose) {
Write-Host "`r"$status -NoNewLine
}
}
$matched = $_ -match "PROGRAM *[0-9]*: *(?<GuideNumber>[0-9]{1,2}\.[0-9]*) *(?<GuideName>.*)"
if ($matched) {
$matches['GuideName'] = $matches['GuideName'] -Replace "(.*?) \(control\)(.*)", "`$1`$2"
$matches['GuideName'] = $matches['GuideName'] -Replace "(.*?) \(encrypted\)(.*)", "`$1`$2"
$matches['GuideName'] = $matches['GuideName'] -Replace "(.*?) \(no data\)(.*)", "`$1`$2"
$json += '{"ChannelNumber":"'+$channel_number+'","GuideNumber":"'+$matches['GuideNumber']+'","GuideName":"'+$matches['GuideName']+'"},'
# $json += '{"ChannelNumber":"'+$channel_number+'","GuideNumber":"'+$matches['GuideNumber']+'","GuideName":"'+$matches['GuideName']+'","CustomGuideName":""},'
$found++
}
if ($Verbose) {
Write-Host $_
}
}
# Clear the tuner channel
& "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Utility_Device_Address 'set' "/tuner$tuner/channel" "none"
if ($Verbose) {
'' # Blank line
}
# Final Status
$status = ' Found '+$found+' programs ('+$progress+'%) '
Write-Host "`r"$status
'' # Blank line
if ($json) {
$json = $json.TrimEnd(',')
$json = '['+$json+']' # As array
# Sort by ascending GuideNumber
$array = $json | ConvertFrom-Json
$array_sorted = $array | Sort-Object { [Version]$_.GuideNumber }
$json = $array_sorted | ConvertTo-Json -Compress
}
$Script:json = $json; $Script:Device_Address = $Device_ID
}
#
# Get Device Information
#
function Get-Device_Information ($method){
if ($method -eq 'web') {
$discover_json_url = 'http://'+$Device_IP_Address+'/discover.json'
$Device = $webclient.DownloadString($discover_json_url) | ConvertFrom-Json
$Device.FriendlyName
'Model: '+$Device.ModelNumber
'Firmware: '+$Device.FirmwareVersion
'Device ID: '+$Device.DeviceID
'Device IP Address: '+$Device_IP_Address
} elseif ($method -eq 'client') {
$ModelNumber = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Utility_Device_Address get /sys/hwmodel
$FirmwareVersion = & "$HDHR_Prog_Dir\$HDHR_Client_Utility" $Utility_Device_Address get /sys/version
'' # Blank line
'Model: '+$ModelNumber
'Firmware: '+$FirmwareVersion
'Device ID: '+$Device_ID
'Device IP Address: '+$Device_IP_Address
}
'' # Blank line
}
Invoke-Main
'' # Blank line
pause; exit; # Wait for user to exit/close PS window
# NOTE:
# $json = Invoke-WebRequest http://$Device_IP_Address/lineup.json?show=found # Doesn't work with UTF8 encoding
# Workaround A
# $json = Invoke-WebRequest -Uri 'http://$Device_IP_Address/lineup.json?show=found' -Outfile $EPG_Data_Dir'\hdhrlineup.json'
# $json = Get-Content -Path $EPG_Data_Dir'\hdhrlineup.json' -Encoding UTF8 -Raw
# Remove-Item -Path $EPG_Data_Dir'\hdhrlineup.json' -Force
# Workaround B
# $webclient = new-object System.Net.WebClient
# $json = $webclient.DownloadString("http://$Device_IP_Address/lineup.json?show=$Show")
# Development Environment
# Windows 8.1 Pro w/Media Center
# PowerShell: 4.0
# SiliconDust: HDHomeRun CONNECT, Model: HDHR4-2US, Firmware: 20200907
# Observed Issues
# Running a channel detection scan (web) interrupts in process viewing/recording signal.
# Running a channel detection scan (client) may append a status comment to the channel name. ex: '(control)', '(encrypted)', '(no data)' Unknown what effects this may have.
# Change Log
# Version: 20220922.3-alpha
# Clear the tuner channel after scan.
# Version: 20220922.2-alpha
# Use optional CustomGuideName field in JSON.
# Version: 20220922.1-alpha
# Use IP address for Utility_Device_Address (to avoid LAN broadcast when using device ID).
# Version: 20210719.1-alpha
# Include broadcast channel in display output and JSON.
# Version: 20210228.1-alpha
# Include station call sign in display output.
# Version: 20210111.1-alpha
# Accommodate the "matchName" field in "epg123.mxf"
# Version: 20201106.1-alpha
# Learned to select channel detection scan method (web/client/none) from variable setting.
# Learned to retrieve device information (model, firmware, ID)
# Learned how not to be verbose (client utility)
# Learned to dynamically find and select from more than two tuners (0/1) (client utility)
# Learned to override settings with parameters passed on command line
# Example command line shortcuts
# Client Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040xxxx' -Device_Channel_Detection_Scan 'client' -Device_IP_Address ''
# Default: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Retrieve_Data_From_Device $false
# No Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.x.x' -Device_Channel_Detection_Scan 'none'
# Verbose: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_ID '1040xxxx' -Device_Channel_Detection_Scan 'client' -Device_IP_Address '' -Verbose $true
# Web Scan: %SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe -Command "C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.ps1" -Device_IP_Address '192.168.x.x' -Device_Channel_Detection_Scan 'web'
# Version: 20200928.1-alpha
# Learned how to run HDHomeRun device channel detection scan
# Learned to operate standalone (without a HDHR device) using guide number and name JSON data.
# Learned to do a dry run
# Factored into functions
# Version: 20200926.1-alpha
# Genesis
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.json (Example)
Code: Select all
[
{"ChannelNumber":"24","GuideNumber":"2.1","GuideName":"KATU"},
{"ChannelNumber":"24","GuideNumber":"2.2","GuideName":"Charge!"},
{"ChannelNumber":"24","GuideNumber":"2.3","GuideName":"Comet"},
{"ChannelNumber":"24","GuideNumber":"2.4","GuideName":"TBD"},
{"ChannelNumber":"11","GuideNumber":"3.1","GuideName":"KVDO","CustomGuideName":"QVC"},
{"ChannelNumber":"25","GuideNumber":"6.1","GuideName":"KOIN-HD"},
{"ChannelNumber":"25","GuideNumber":"6.2","GuideName":"GetTV"},
{"ChannelNumber":"25","GuideNumber":"6.3","GuideName":"Rewind"},
{"ChannelNumber":"26","GuideNumber":"8.1","GuideName":"KGW"},
{"ChannelNumber":"26","GuideNumber":"8.2","GuideName":"Crime"},
{"ChannelNumber":"26","GuideNumber":"8.3","GuideName":"Quest"},
{"ChannelNumber":"26","GuideNumber":"8.4","GuideName":"Twist"},
{"ChannelNumber":"26","GuideNumber":"8.5","GuideName":"ThisTV"},
{"ChannelNumber":"26","GuideNumber":"8.6","GuideName":"TheGrio"},
{"ChannelNumber":"10","GuideNumber":"10.1","GuideName":"OPB"},
{"ChannelNumber":"10","GuideNumber":"10.2","GuideName":"OPBPlus"},
{"ChannelNumber":"10","GuideNumber":"10.3","GuideName":"OPBKids"},
{"ChannelNumber":"10","GuideNumber":"10.4","GuideName":"OPB-FM"},
{"ChannelNumber":"12","GuideNumber":"12.1","GuideName":"FOX 12"},
{"ChannelNumber":"12","GuideNumber":"12.2","GuideName":"COZI"},
{"ChannelNumber":"12","GuideNumber":"12.3","GuideName":"DABL"},
{"ChannelNumber":"12","GuideNumber":"12.4","GuideName":"FUTURE"},
{"ChannelNumber":"11","GuideNumber":"17.1","GuideName":"KWVT","CustomGuideName":"YTA"},
{"ChannelNumber":"20","GuideNumber":"20.1","GuideName":"KOXI-CD","CustomGuideName":"JewelryTV"},
{"ChannelNumber":"20","GuideNumber":"20.2","GuideName":"KOXI-CD","CustomGuideName":"NTD"},
{"ChannelNumber":"20","GuideNumber":"20.3","GuideName":"KOXI-CD","CustomGuideName":"BUZZR"},
{"ChannelNumber":"20","GuideNumber":"20.4","GuideName":"KOXI-CD","CustomGuideName":"TELEMUNDO"},
{"ChannelNumber":"20","GuideNumber":"20.5","GuideName":"KOXI-CD","CustomGuideName":"ShopLC"},
{"ChannelNumber":"20","GuideNumber":"20.6","GuideName":"KOXI-CD","CustomGuideName":"LX"},
{"ChannelNumber":"20","GuideNumber":"20.7","GuideName":"KOXI-CD","CustomGuideName":"MeTV"},
{"ChannelNumber":"22","GuideNumber":"22.1","GuideName":"ION"},
{"ChannelNumber":"22","GuideNumber":"22.2","GuideName":"Bounce"},
{"ChannelNumber":"22","GuideNumber":"22.3","GuideName":"Laff"},
{"ChannelNumber":"22","GuideNumber":"22.4","GuideName":"Defy TV"},
{"ChannelNumber":"22","GuideNumber":"22.5","GuideName":"TruReal"},
{"ChannelNumber":"22","GuideNumber":"22.6","GuideName":"NEWSY"},
{"ChannelNumber":"22","GuideNumber":"22.7","GuideName":"QVC"},
{"ChannelNumber":"22","GuideNumber":"22.8","GuideName":"HSN"},
{"ChannelNumber":"23","GuideNumber":"23.1","GuideName":"CRIME"},
{"ChannelNumber":"23","GuideNumber":"23.2","GuideName":"QVC"},
{"ChannelNumber":"23","GuideNumber":"23.3","GuideName":"HSN"},
{"ChannelNumber":"23","GuideNumber":"23.4","GuideName":"QVC2"},
{"ChannelNumber":"23","GuideNumber":"23.5","GuideName":"HSN2"},
{"ChannelNumber":"32","GuideNumber":"24.1","GuideName":"TBN HD"},
{"ChannelNumber":"32","GuideNumber":"24.2","GuideName":"inspire"},
{"ChannelNumber":"32","GuideNumber":"24.3","GuideName":"SMILE"},
{"ChannelNumber":"32","GuideNumber":"24.4","GuideName":"Enlace"},
{"ChannelNumber":"32","GuideNumber":"24.5","GuideName":"POSITIV"},
{"ChannelNumber":"11","GuideNumber":"27.1","GuideName":"KSLM","CustomGuideName":"theDove"},
{"ChannelNumber":"29","GuideNumber":"29.1","GuideName":"KJYY-LD","CustomGuideName":"TELEMUNDO"},
{"ChannelNumber":"29","GuideNumber":"29.2","GuideName":"TXitos"},
{"ChannelNumber":"29","GuideNumber":"29.3","GuideName":"MeTV"},
{"ChannelNumber":"29","GuideNumber":"29.4","GuideName":"AceTV"},
{"ChannelNumber":"29","GuideNumber":"29.5","GuideName":"HSN"},
{"ChannelNumber":"29","GuideNumber":"29.6","GuideName":"ShopLC"},
{"ChannelNumber":"29","GuideNumber":"29.7","GuideName":"Sonlife","CustomGuideName":"SBN"},
{"ChannelNumber":"31","GuideNumber":"31.1","GuideName":"KBLN-DT","CustomGuideName":"3ABN"},
{"ChannelNumber":"31","GuideNumber":"31.2","GuideName":"BLBN-2","CustomGuideName":"Better Health TV"},
{"ChannelNumber":"31","GuideNumber":"31.3","GuideName":"BLBN-3","CustomGuideName":"HOPE"},
{"ChannelNumber":"31","GuideNumber":"31.4","GuideName":"BLBN-4","CustomGuideName":"ESPRNZA"},
{"ChannelNumber":"24","GuideNumber":"32.1","GuideName":"KRCW","CustomGuideName":"CW"},
{"ChannelNumber":"25","GuideNumber":"32.2","GuideName":"Antenna"},
{"ChannelNumber":"25","GuideNumber":"32.3","GuideName":"Grit"},
{"ChannelNumber":"25","GuideNumber":"32.4","GuideName":"TBD"},
{"ChannelNumber":"11","GuideNumber":"37.1","GuideName":"Azteca"},
{"ChannelNumber":"21","GuideNumber":"42.1","GuideName":"KPXG-LD","CustomGuideName":"Daystar"},
{"ChannelNumber":"21","GuideNumber":"42.2","GuideName":"KPXG-ES","CustomGuideName":"Daystar-ES"},
{"ChannelNumber":"21","GuideNumber":"42.3","GuideName":"KPXG-LD","CustomGuideName":"Daystar"},
{"ChannelNumber":"34","GuideNumber":"47.1","GuideName":"KUNP-LD","CustomGuideName":"Univision"},
{"ChannelNumber":"34","GuideNumber":"47.2","GuideName":"Stadium"},
{"ChannelNumber":"34","GuideNumber":"47.3","GuideName":"Charge!"},
{"ChannelNumber":"12","GuideNumber":"49.1","GuideName":"Fox12+"},
{"ChannelNumber":"26","GuideNumber":"49.2","GuideName":"Mystery"},
{"ChannelNumber":"12","GuideNumber":"49.3","GuideName":"Circle"},
{"ChannelNumber":"26","GuideNumber":"49.4","GuideName":"CourtTV"}
]