Recordings filename pattern (use the custom label)

An evolving, supported alternative to Rovi
Forum rules
★ Download the latest EPG123 here: https://garyan2.github.io/ <> Setup guide here: https://garyan2.github.io/install.html
NOYB

Posts: 145
Joined: Thu Sep 10, 2020 8:03 am
Location:

HTPC Specs: Show details

#21

Post by NOYB » Sat Sep 26, 2020 9:34 pm

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.

User avatar
garyan2

Posts: 7438
Joined: Fri Nov 27, 2015 7:23 pm
Location:

HTPC Specs: Show details

#22

Post by garyan2 » Sat Sep 26, 2020 11:03 pm

NOYB wrote: Sat Sep 26, 2020 9:34 pm 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.
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

NOYB

Posts: 145
Joined: Thu Sep 10, 2020 8:03 am
Location:

HTPC Specs: Show details

#23

Post by NOYB » Mon Sep 28, 2020 10:29 am

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

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")
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.json

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"}
]

NOYB

Posts: 145
Joined: Thu Sep 10, 2020 8:03 am
Location:

HTPC Specs: Show details

#24

Post by NOYB » Sat Nov 07, 2020 4:56 am

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

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'
See change log at end of file for previous changes.

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
C:\ProgramData\GaRyan2\epg123\custom_callSign_serviceName.json (Example)

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"}
]

NOYB

Posts: 145
Joined: Thu Sep 10, 2020 8:03 am
Location:

HTPC Specs: Show details

#25

Post by NOYB » Tue Jan 12, 2021 12:46 am

Patch to accommodate the "matchName" field in "epg123.mxf"

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"

NOYB

Posts: 145
Joined: Thu Sep 10, 2020 8:03 am
Location:

HTPC Specs: Show details

#26

Post by NOYB » Mon Mar 01, 2021 9:30 am

Patch to include station call sign in display output.

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.

NOYB

Posts: 145
Joined: Thu Sep 10, 2020 8:03 am
Location:

HTPC Specs: Show details

#27

Post by NOYB » Mon Nov 07, 2022 7:49 am

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

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'
See change log at end of file for previous changes.

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"}
]

Post Reply