ShinyMK ShinyMK - 5 months ago 40
Vb.net Question

Updating Previously Made List?

So I make a List in a Background Worker with Start and End time etc for processing a TV Show list I put the Channel Name for the TV Show in it too. So now I wanna process the Channel's List aswell and it has the Channel Name and the Channel Livestream.

How can I make it so I add the Channel Stream to the List made in the Background Worker?

Code:

'Guide and Channel Information;
Public Class Channel

Public Property Channel As String
Public Property Stream As String
Public Property Genre As String
Public Property StartTime As String
Public Property EndTime As String
Public Property Programme As String
Public Property Description As String

Public Overrides Function ToString() As String
Return Channel
Return Stream
Return Genre
Return StartTime
Return EndTime
Return Programme
Return Description
End Function

End Class

'Make a List for the Guide Information;
Public Guide As New List(Of Channel)
Private Sub ProcessGuide_DoWork(sender As Object, e As DoWorkEventArgs) Handles ProcessGuide.DoWork

Try

Dim nodelist As XmlNodeList
Dim node As XmlNode

'Create the XML Document;
Dim xmld As XmlDocument
xmld = New XmlDocument()

'Load the XMLTV File;
xmld.Load("http://104.233.125.128/gmtplus0.xmltv")

'Loop through the Programme Nodes;
nodelist = xmld.SelectNodes("/tv/programme")
For Each node In nodelist

'Here I am adding the Channel etc, But I will also need the stream etc added which is why I have a sub later on in the code to get and add the Stream. But how do I add it to this same Guide List?
Guide.Add(New Channel() With {
.Channel = node.Attributes.GetNamedItem("channel").Value,
.StartTime = node.Attributes.GetNamedItem("start").Value,
.EndTime = node.Attributes.GetNamedItem("stop").Value,
.Programme = node.ChildNodes.Item(0).InnerText,
.Description = node.ChildNodes.Item(1).InnerText
})

Next

ProcessChannels.RunWorkerAsync()

Catch ex As Exception

'Error trapping
Console.Write(ex.ToString(), "TTTTTdsg")

End Try

End Sub

'Process the Channel Information;
Dim AllChannels As List(Of Channel) = New List(Of Channel)()
Dim EntertainmentChannels As List(Of Channel) = New List(Of Channel)()
Dim KidsChannels As List(Of Channel) = New List(Of Channel)()
Dim SportsChannels As List(Of Channel) = New List(Of Channel)()
Dim DocumentaryChannels As List(Of Channel) = New List(Of Channel)()
Dim NewsChannels As List(Of Channel) = New List(Of Channel)()
Dim MusicChannels As List(Of Channel) = New List(Of Channel)()
Public Sub ProcessChannels_DoWork(sender As Object, e As DoWorkEventArgs) Handles ProcessChannels.DoWork

'Try to Load the Channel Data;
Try

'Connect to the Proxy Source and Prepare the Response;
Dim source As Net.HttpWebRequest = Net.WebRequest.Create("http://gameshare.io/pragma/channels.php")
Dim response As Net.HttpWebResponse = source.GetResponse

'Load the HTML and Convert the JSON to a Readable Array;
Dim reader As IO.StreamReader = New IO.StreamReader(response.GetResponseStream())
Dim html As String = reader.ReadToEnd
Dim json = JsonConvert.DeserializeObject(html)

'=========================================================
'=========================================================
'Here is where I need to not add the Guide info to a NEW Channel/Guide/List I need to add it to the same one
'that is made in ProcessGuide_DoWork but how?

'Process All Channels;
For i As Integer = 0 To json("Quantity").ToString - 1
For Each Row In json("All Channels")
AllChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Process Enterainment Channels
For i As Integer = 0 To json("EQuantity").ToString - 1
For Each Row In json("Entertainment")
EntertainmentChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Process Kids Channels;
For i As Integer = 0 To json("KQuantity").ToString - 1
For Each Row In json("Kids")
KidsChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Process Sports Channels;
For i As Integer = 0 To json("SQuantity").ToString - 1
For Each Row In json("Sports")
SportsChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Process Documentary Channels;
For i As Integer = 0 To json("DQuantity").ToString - 1
For Each Row In json("Documentary")
DocumentaryChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Process News Channels;
For i As Integer = 0 To json("NQuantity").ToString - 1
For Each Row In json("News")
NewsChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Process Music Channels;
For i As Integer = 0 To json("MQuantity").ToString - 1
For Each Row In json("Music")
MusicChannels.Add(New Channel() With {
.Channel = Row(i)("title").ToString,
.Stream = Row(i)("stream").ToString
})
Next
Next

'Add all the Channels to the Channels ComboBox;
ChannelsBox.Items.AddRange(allchannels.ToArray)

'The Channels are now ready to be used, Tell the user to Select a Channel;
ChannelsBox.Text = "Select a Channel..."

Catch ex As Exception

'Channel Data couldn't be loaded;
ErrorImage.Width = "640"
ErrorImage.Height = "391"
ErrorImage.Visible = True

End Try

End Sub

Answer

Your code needs to be significantly reworked. First, you should turn on Option Strict; code like this will not compile since it uses late-binding:

For Each Row In json("All Channels")

Fundamentally, once you parse the XML, you have one entry in the list for each channel. You cannot then iterate the json TV show list and plug them into that list: there are many TV shows for each channel. You don't have an array or list to store the multiple shows per channel.

There is also no need for a BackGroundWorker. Rather than looping thru an Object and manually creating those lists, if you deserialize to a typed object JSON.NET will create all those lists for you in just a few ticks.

So, you need one class to link a URL to a channel (TVGChannel) and one class to contain the data for a TV broadcast. One of the properties for the show list/Guide is the channel it is one. The XML data includes a Category for each show which would seem to be more interesting than a channel category.

Hopefully, the channel element in the XML really is designed to specify a channel indicated by title in the json and it is not an accident of the data that they match. It looks more like it is supposed to reference something in the channel section in the same XML, but those just have URLs.

Programme class

Public Enum ProgrammeGenre
    Entertainment
    Kids
    Sports
    Documentary
    News
    Music
    Other
End Enum

Public Class Programme
    Public Property Name As String
    Public Property Title As String                 ' episode Title
    Public Property Description As String

    Public Property Channel As TVGChannel

    Public Property Genre As ProgrammeGenre

    Public Property StartTime As DateTime
    Public Property EndTime As DateTime

    Public Property Category As String
    Public Property EpisodeId As String
    Public Property Stars As Decimal

    Public Sub New()
        Genre = ProgrammeGenre.Other
    End Sub
 End Class

As you can see, I expanded it for a few other elements and changed some types, most notably the start and end times to DateTime. The Category property stores the category from the XML, while the Genre is the result of which list it is in:

thisPrgURL = myProgrammes(n).Channel.stream

TVGContainer, TVGChannel

Public Class TVGChannel
    Public Property stream As String
    Public Property title As String

    Public Overrides Function ToString() As String
        Return title
    End Function
End Class

Public Class TVGContainer
    'Public Property Quantity As String
    '...
    <JsonProperty("All Channels")>
       Private Property _AllChannels As TVGChannel()()

    ' none of these are really very useful:
    Public Property Entertainment As TVGChannel()()
    Public Property Kids As TVGChannel()()
    Public Property Sports As TVGChannel()()
    Public Property Documentary As TVGChannel()()
    Public Property News As TVGChannel()()
    Public Property Music As TVGChannel()()

    Friend ReadOnly Property AllChannels As TVGChannel()
        Get
            Return _AllChannels(0)
        End Get
    End Property
End Class

None of those ?Quantity properties are of much value and the only role for the channel list subsets is for the Genre property on Programme - they could all be commented out. I dunno why they provide them as a jagged array; I didnt want to have to deal with it, so the JSON will go into _AllChannels when it is deserialized, but the code will use the AllChannels property.

Form level variables

Private myPrograms As List(Of Programme)
Private ChannelGuide As TVGContainer

Parse the Guide

You need to have the Guide built before you process the XML so the channels are available to assign to each show:

Private Sub ParseGuide()

    Dim jstr = from whereever
    ChannelGuide = JsonConvert.DeserializeObject(Of TVGContainer)(jstr)

End Sub

By deserialing to a typed object, there is no need to parse all that stuff one by one, and it should be much (much) faster; ChannelGuide will contain all those arrays.

Create the Program List (XML)

I made significant changes to how the XML is processed. Most were in the interest of speed, less maintenance or more reliable processing:

Private Sub ParseXMLTV()
    Dim nodelist As XmlNodeList
    Dim node As XmlNode

    Dim xmld As New XmlDocument()
    xmld.Load("http://104.233.125.128/gmtplus0.xmltv")

    myPrograms = New List(Of Programme)
    nodelist = xmld.SelectNodes("/tv/programme")
    Dim p As Programme

    For Each node In nodelist
        p = New Programme()

        Dim dt = DateTimeOffset.ParseExact(node.Attributes.GetNamedItem("start").Value,
                           "yyyyMMddHHmmss KKKK", CultureInfo.InvariantCulture).DateTime
        p.StartTime = dt
        dt = DateTimeOffset.ParseExact(node.Attributes.GetNamedItem("stop").Value,
                           "yyyyMMddHHmmss KKKK", CultureInfo.InvariantCulture).DateTime
        p.EndTime = dt

        ' now, find the channel indicated
        Dim ch = node.Attributes.GetNamedItem("channel").Value
        If String.IsNullOrEmpty(ch) = False Then
            Dim tvgc = ChannelGuide.AllChannels.FirstOrDefault(Function(q) q.title = ch)
            If tvgc IsNot Nothing Then
                p.Channel = tvgc
            Else
                ' ToDo: add an UNKNOWN channel to avoid NRE
            End If
        End If

        p.Genre = GetGenre(ch)

        For Each n As XmlNode In node.ChildNodes
            Select Case n.Name
                Case "title"
                    p.Name = n.InnerText
                Case "sub-title"
                    p.Title = n.InnerText
                Case "desc"
                    p.Description = n.InnerText
                Case "star-rating"
                    p.Stars = Convert.ToDecimal(n.InnerText.Split(" "c)(0))
                Case "episode-num"
                    p.EpisodeId = n.InnerText
                Case "category"
                    p.Category = n.InnerText
            End Select
        Next

        myPrograms.Add(p)

    Next
End Sub

Private Function GetGenre(ch As String) As ProgrammeGenre
    ' this is not needed - the XML provides a show category
    ' "Drama" or "SitCom" is more interesting than `Entertainment`

    If ChannelGuide.Documentary(0).
                FirstOrDefault(Function(f) f.title = ch) IsNot Nothing Then
        Return ProgrammeGenre.Documentary
    ElseIf ChannelGuide.Entertainment(0).
                FirstOrDefault(Function(f) f.title = ch) IsNot Nothing Then
        Return ProgrammeGenre.Entertainment
    ElseIf ... 
        ' repeat for others
    Else
        Return ProgrammeGenre.Other
    End If
End Function

Usage

ParseGuide()
If ChannelGuide IsNot Nothing AndAlso ChannelGuide.AllChannels.Count > 0 Then
    ParseXMLTV()
End If
dgvTV.DataSource = myPrograms

The whole thing take 6 seconds to run (including download times and posting to the DGV), so there isnt really a reason for a BackGroundWorker. It would be faster without the cruft of fishing out the show Genre based on a channel category...and there are a lot of both of them that are missing.

enter image description here

The EndTime, Category, Episode ID and Star Rating are all scrolled off to the right.


The DateTime parsing may need work, but I have no idea where I can find the docs for this API.