Update ZTEG and HWTC API implementations and logging changes
This commit updates ZTEG and HWTC implementation with refreshed logic around handling channels and EPGs data. It also includes minor changes in logging information for clarity. Deleted 'config.example.yaml' file. Additionally, implemented new test cases to validate these changes.
This commit is contained in:
parent
e4c79cd316
commit
70628dd938
@ -2,11 +2,11 @@ package api
|
||||
|
||||
type Client interface {
|
||||
GetChannels() ([]Channel, error)
|
||||
GetEPGs(id int) ([]Epg, error)
|
||||
GetEPGs(id string) ([]Epg, error)
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
ChannelID int
|
||||
ChannelID string
|
||||
ChannelName string
|
||||
ChannelURL string
|
||||
TimeShiftURL string
|
||||
|
@ -2,37 +2,29 @@ package hwtc
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func bytesToChannels(resp []byte) ([]Channel, error) {
|
||||
re := regexp.MustCompile(`(?s)ChannelID="\d*".*?ChannelFECPort="\d*"`)
|
||||
data := re.FindAllString(string(resp), -1)
|
||||
re := regexp.MustCompile(`ChannelID="\w+".*?ChannelFECPort="\d+"`)
|
||||
data := re.FindAll(resp, -1)
|
||||
|
||||
var channelMaps []map[string]any
|
||||
var channelMaps []map[string]string
|
||||
re2 := regexp.MustCompile(`画中画|单音轨`)
|
||||
for i := range data {
|
||||
if re2.MatchString(data[i]) {
|
||||
if re2.Match(data[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
d := data[i]
|
||||
res := strings.Split(d, ",")
|
||||
kvMap := make(map[string]any)
|
||||
res := strings.Split(string(d), ",")
|
||||
kvMap := make(map[string]string)
|
||||
for ii := range res {
|
||||
kvs := strings.SplitN(res[ii], "=", 2)
|
||||
val := strings.Trim(kvs[1], "\"")
|
||||
|
||||
if kvs[0] == "ChannelID" {
|
||||
channelID, _ := strconv.Atoi(val)
|
||||
kvMap[kvs[0]] = channelID
|
||||
} else {
|
||||
kvMap[kvs[0]] = val
|
||||
}
|
||||
|
||||
kvMap[kvs[0]] = val
|
||||
}
|
||||
channelMaps = append(channelMaps, kvMap)
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ package hwtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (c *Client) updateCookie() error {
|
||||
resp, err := c.cli.R().SetQueryParams(map[string]string{
|
||||
resp, err := c.cli.R().SetFormData(map[string]string{
|
||||
"UserID": c.userId,
|
||||
"Authenticator": c.authenticator,
|
||||
}).Post("ValidAuthenticationHWCTC.jsp")
|
||||
@ -14,20 +13,13 @@ func (c *Client) updateCookie() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var isLogin bool
|
||||
cookie := new(http.Cookie)
|
||||
|
||||
cookies := resp.Cookies()
|
||||
for i := range cookies {
|
||||
if cookies[i].Name == "JSESSIONID" && len(cookies[i].Value) > 0 {
|
||||
cookie = cookies[i]
|
||||
isLogin = true
|
||||
c.cli.SetCookie(cookies[i])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if isLogin {
|
||||
c.cli.SetCookie(cookie)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("no valid cookie")
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package hwtc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -14,8 +14,7 @@ import (
|
||||
|
||||
func New(conf *config.Config) *Client {
|
||||
r := &Client{
|
||||
cli: resty.New().SetRetryCount(3).SetBaseURL(fmt.Sprintf("%s/EPG/jsp", conf.Api.ApiHost)),
|
||||
|
||||
cli: resty.New().SetRetryCount(3).SetBaseURL(fmt.Sprintf("%s/EPG/jsp", conf.Api.ApiHost)),
|
||||
userId: conf.Api.Auth["userid"],
|
||||
authenticator: conf.Api.Auth["authenticator"],
|
||||
}
|
||||
@ -23,16 +22,16 @@ func New(conf *config.Config) *Client {
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *Client) getEPGBytes(channelId int) ([]byte, error) {
|
||||
func (c *Client) getEPGBytes(channelId string) ([]byte, error) {
|
||||
var buf []byte
|
||||
for i := 0; i < 3; i++ {
|
||||
resp, err := c.cli.R().ForceContentType("text/html;charset=UTF-8").SetQueryParam("channelId", strconv.Itoa(channelId)).
|
||||
resp, err := c.cli.R().ForceContentType("text/html;charset=UTF-8").SetQueryParam("channelId", channelId).
|
||||
Get("stliveplay_30/en/getTvodData.jsp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.Contains(resp.String(), "(\"resignon\",\"1\")") {
|
||||
if strings.Contains(resp.String(), "resignon") {
|
||||
time.Sleep(time.Second * 3)
|
||||
if err := c.updateCookie(); err != nil {
|
||||
return nil, err
|
||||
@ -46,7 +45,7 @@ func (c *Client) getEPGBytes(channelId int) ([]byte, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetEPGs(id int) ([]api.Epg, error) {
|
||||
func (c *Client) GetEPGs(id string) ([]api.Epg, error) {
|
||||
buf, err := c.getEPGBytes(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -72,8 +71,6 @@ func (c *Client) GetEPGs(id int) ([]api.Epg, error) {
|
||||
}
|
||||
|
||||
func (c *Client) getChannelBytes() ([]byte, error) {
|
||||
var buf []byte
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
resp, err := c.cli.R().
|
||||
Get("getchannellistHWCTC.jsp")
|
||||
@ -81,18 +78,16 @@ func (c *Client) getChannelBytes() ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.Contains(resp.String(), "(\"resignon\",\"1\")") {
|
||||
if strings.Contains(resp.String(), "resignon") {
|
||||
time.Sleep(time.Second * 3)
|
||||
if err := c.updateCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
buf = resp.Body()
|
||||
break
|
||||
return resp.Body(), nil
|
||||
}
|
||||
return buf, nil
|
||||
return nil, errors.New("retry after 3 times")
|
||||
}
|
||||
|
||||
func (c *Client) GetChannels() ([]api.Channel, error) {
|
||||
|
@ -29,7 +29,7 @@ type Channel struct {
|
||||
ChannelFCCIP string `mapstructure:"ChannelFCCIP"`
|
||||
ChannelFCCPort string `mapstructure:"ChannelFCCPort"`
|
||||
ChannelFECPort string `mapstructure:"ChannelFECPort"`
|
||||
ChannelID int `mapstructure:"ChannelID"`
|
||||
ChannelID string `mapstructure:"ChannelID"`
|
||||
ChannelLocked string `mapstructure:"ChannelLocked"`
|
||||
ChannelLogURL string `mapstructure:"ChannelLogURL"`
|
||||
ChannelName string `mapstructure:"ChannelName"`
|
||||
|
38
api/zteg/channel.go
Normal file
38
api/zteg/channel.go
Normal file
@ -0,0 +1,38 @@
|
||||
package zteg
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
func bytesToChannels(resp []byte) ([]Channel, error) {
|
||||
re := regexp.MustCompile(`ChannelID="\w+".*?ChannelFCCPort="\d+"`)
|
||||
data := re.FindAll(resp, -1)
|
||||
|
||||
var channelMaps []map[string]string
|
||||
re2 := regexp.MustCompile(`PIP`)
|
||||
for i := range data {
|
||||
if re2.Match(data[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
d := data[i]
|
||||
res := strings.Split(string(d), ",")
|
||||
kvMap := make(map[string]string)
|
||||
for ii := range res {
|
||||
kvs := strings.SplitN(res[ii], "=", 2)
|
||||
val := strings.Trim(kvs[1], "\"")
|
||||
kvMap[kvs[0]] = val
|
||||
}
|
||||
channelMaps = append(channelMaps, kvMap)
|
||||
}
|
||||
|
||||
var channels []Channel
|
||||
if err := mapstructure.Decode(&channelMaps, &channels); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return channels, nil
|
||||
}
|
31
api/zteg/channel_test.go
Normal file
31
api/zteg/channel_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
package zteg
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/thank243/iptvChannel/config"
|
||||
)
|
||||
|
||||
func TestGetChannels(t *testing.T) {
|
||||
c := config.ReadConfig()
|
||||
r := New(c)
|
||||
|
||||
resp, err := r.getChannelBytes()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// resp, err := os.ReadFile("channel.bin")
|
||||
// if err != nil {
|
||||
// t.Error(err)
|
||||
// }
|
||||
|
||||
channels, err := bytesToChannels(resp)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
|
||||
for _, channel := range channels {
|
||||
t.Log(channel.ChannelName)
|
||||
}
|
||||
}
|
25
api/zteg/cookie.go
Normal file
25
api/zteg/cookie.go
Normal file
@ -0,0 +1,25 @@
|
||||
package zteg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func (c *Client) updateCookie() error {
|
||||
resp, err := c.cli.R().SetFormData(map[string]string{
|
||||
"UserID": c.userId,
|
||||
"Authenticator": c.authenticator,
|
||||
}).Post("platform/auth.jsp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cookies := resp.Cookies()
|
||||
for i := range cookies {
|
||||
if cookies[i].Name == "JSESSIONID" && len(cookies[i].Value) > 0 {
|
||||
c.cli.SetCookie(cookies[i])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("no valid cookie")
|
||||
}
|
@ -1,4 +1,35 @@
|
||||
package zteg
|
||||
|
||||
import (
|
||||
"github.com/go-resty/resty/v2"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
cli *resty.Client
|
||||
|
||||
userId string
|
||||
authenticator string
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
BeginTime string `mapstructure:"BeginTime"`
|
||||
ChannelPurchased string `mapstructure:"ChannelPurchased"`
|
||||
LocalTimeShift string `mapstructure:"LocalTimeShift"`
|
||||
UserTeamChannelID string `mapstructure:"UserTeamChannelID"`
|
||||
ChannelFCCServerAddr string `mapstructure:"ChannelFCCServerAddr"`
|
||||
ChannelFCCIP string `mapstructure:"ChannelFCCIP"`
|
||||
ChannelFCCPort string `mapstructure:"ChannelFCCPort"`
|
||||
ChannelID string `mapstructure:"ChannelID"`
|
||||
ChannelLogURL string `mapstructure:"ChannelLogURL"`
|
||||
ChannelName string `mapstructure:"ChannelName"`
|
||||
UserChannelID string `mapstructure:"UserChannelID"`
|
||||
ChannelSDP string `mapstructure:"ChannelSDP"`
|
||||
ChannelType string `mapstructure:"ChannelType"`
|
||||
ChannelURL string `mapstructure:"ChannelURL"`
|
||||
Interval string `mapstructure:"Interval"`
|
||||
Lasting string `mapstructure:"Lasting"`
|
||||
PositionX string `mapstructure:"PositionX"`
|
||||
PositionY string `mapstructure:"PositionY"`
|
||||
TimeShift string `mapstructure:"TimeShift"`
|
||||
TimeShiftURL string `mapstructure:"TimeShiftURL"`
|
||||
}
|
||||
|
@ -1,18 +1,86 @@
|
||||
package zteg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-resty/resty/v2"
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/transform"
|
||||
|
||||
"github.com/thank243/iptvChannel/api"
|
||||
"github.com/thank243/iptvChannel/config"
|
||||
)
|
||||
|
||||
func New(conf *config.Config) *Client {
|
||||
return new(Client)
|
||||
r := &Client{
|
||||
cli: resty.New().SetRetryCount(3).SetBaseURL(fmt.Sprintf("%s/iptvepg", conf.Api.ApiHost)),
|
||||
userId: conf.Api.Auth["userid"],
|
||||
authenticator: conf.Api.Auth["authenticator"],
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *Client) GetEPGs(id int) ([]api.Epg, error) {
|
||||
// GetEPGs todo
|
||||
func (c *Client) GetEPGs(id string) ([]api.Epg, error) {
|
||||
return []api.Epg{}, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetChannels() ([]api.Channel, error) {
|
||||
return []api.Channel{}, nil
|
||||
func (c *Client) getChannelBytes() ([]byte, error) {
|
||||
for i := 0; i < 3; i++ {
|
||||
resp, err := c.cli.R().SetFormData(map[string]string{
|
||||
"MAIN_WIN_SRC": "/iptvepg/empty.jsp",
|
||||
"NEED_UPDATE_STB": "1",
|
||||
"BUILD_ACTION": "FRAMESET_BUILDER",
|
||||
}).Post("function/frameset_builder.jsp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.Contains(resp.String(), "resignon") {
|
||||
time.Sleep(time.Second * 3)
|
||||
if err := c.updateCookie(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// convert gbk to utf-8
|
||||
buf, err := io.ReadAll(transform.NewReader(bytes.NewReader(resp.Body()), simplifiedchinese.GBK.NewDecoder()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
return nil, errors.New("retry after 3 times")
|
||||
}
|
||||
|
||||
func (c *Client) GetChannels() ([]api.Channel, error) {
|
||||
buf, err := c.getChannelBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chs, err := bytesToChannels(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var channels []api.Channel
|
||||
for i := range chs {
|
||||
ch := chs[i]
|
||||
channels = append(channels, api.Channel{
|
||||
ChannelID: ch.ChannelID,
|
||||
ChannelName: ch.ChannelName,
|
||||
ChannelURL: ch.ChannelURL,
|
||||
TimeShiftURL: ch.TimeShiftURL,
|
||||
})
|
||||
}
|
||||
|
||||
return channels, nil
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
@ -116,7 +115,7 @@ func (s *Server) getEPGs(c echo.Context) error {
|
||||
ch := channels[i]
|
||||
// create channel, format: <channel id="1"><display-name lang="zh">CCTV1</display-name></channel>
|
||||
channelXml := tv.CreateElement("channel")
|
||||
channelXml.CreateAttr("id", strconv.Itoa(ch.ChannelID))
|
||||
channelXml.CreateAttr("id", ch.ChannelID)
|
||||
name := channelXml.CreateElement("display-name")
|
||||
name.CreateAttr("lang", "zh")
|
||||
name.CreateText(ch.ChannelName)
|
||||
|
@ -1,13 +0,0 @@
|
||||
LogLevel: debug # trace, debug, info, warn, error, fatal
|
||||
Cron: "5 */12 * * *" # same as Cron Expressions
|
||||
MaxConcurrent: 5 # not more than 16
|
||||
Address: 0.0.0.0:8888
|
||||
Mode: udpxy # udpxy, igmp, url
|
||||
UdpxyHost: "http://YOUR_UDPXY_ADDR" # not blank if mode is udpxy
|
||||
|
||||
Api:
|
||||
Provider: hwtc # hwtc, zteg
|
||||
ApiHost: "http://YOUR_API_HOST"
|
||||
Auth:
|
||||
UserID: "YOUR_IPTV_ID"
|
||||
Authenticator: "YOUR_PRIVATE_KEY"
|
@ -75,7 +75,7 @@ func (c *Controller) Start() error {
|
||||
config.GetVersion(), c.conf.LogLevel, c.maxConcurrent, strings.ToUpper(c.conf.Mode), c.conf.Api.Provider)
|
||||
|
||||
log.Info("Starting service..")
|
||||
log.Info("Fetch EPGs and Channels data on initial startup")
|
||||
log.Info("Fetch Channels and EPGs data on initial startup")
|
||||
c.Run()
|
||||
time.Sleep(time.Second)
|
||||
|
||||
@ -105,7 +105,7 @@ func (c *Controller) Run() {
|
||||
}
|
||||
|
||||
if err := c.fetchEPGs(); err != nil {
|
||||
return
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
2
go.mod
2
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/viper v1.18.2
|
||||
golang.org/x/text v0.14.0
|
||||
)
|
||||
|
||||
require (
|
||||
@ -36,7 +37,6 @@ require (
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
Loading…
Reference in New Issue
Block a user