Update controller concurrency and Dockerfile, provide detailed version info

Enhanced the 'controller.go' file to enforce a maximum concurrency limit of 16. Also made changes in Dockerfile including setting a new repository mirror, adding git for using git commit hash at build which can be displayed through 'version.go'. This commit leads to an improved application performance, more streamlined build process and detailed version information.

Update CI/CD workflow in GitHub Actions

Changed the 'ci.yml' configuration file to trigger build process on each push event for any tag. This change optimizes the workflow to ensure code is tested and integrated on every version increment without waiting for a pull request.
This commit is contained in:
Senis 2024-01-15 19:59:26 +08:00
parent 89e6fd6ac0
commit d1923d9708
No known key found for this signature in database
11 changed files with 126 additions and 56 deletions

31
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: goreleaser
on:
push:
tags:
- '*'
permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
-
name: Set up Go
uses: actions/setup-go@v4
-
name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,17 +1,16 @@
FROM golang:alpine AS builder
RUN go env -w GOPROXY=https://goproxy.cn,direct
WORKDIR /build
COPY . .
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.nju.edu.cn/g' /etc/apk/repositories
RUN apk update --no-cache && apk add --no-cache git
RUN go mod tidy
RUN go build -ldflags="-s -w -X 'github.com/thank243/iptvChannel/config.commit=$(git rev-parse --short HEAD)'" -trimpath -v -o /app/main main.go
RUN go build -ldflags="-s -w \
-X 'github.com/thank243/iptvChannel/config.commit=$(git rev-parse --short HEAD) build: $(date)'" \
-trimpath -v -o /app/main main.go
FROM alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.nju.edu.cn/g' /etc/apk/repositories
RUN apk update --no-cache && apk add --no-cache ca-certificates tzdata
ENV TZ Asia/Shanghai

View File

@ -14,5 +14,6 @@ type Server struct {
EPGs *atomic.Pointer[[]epg.Epg]
Channels *atomic.Pointer[[]channel.Channel]
mode string
udpxyHost string
}

View File

@ -23,10 +23,12 @@ import (
// It returns the created Server instance.
func New(c *config.Config) *Server {
s := &Server{
Echo: echo.New(),
Echo: echo.New(),
Channels: new(atomic.Pointer[[]channel.Channel]),
EPGs: new(atomic.Pointer[[]epg.Epg]),
mode: c.Mode,
udpxyHost: c.UdpxyHost,
Channels: new(atomic.Pointer[[]channel.Channel]),
EPGs: new(atomic.Pointer[[]epg.Epg]),
}
s.Echo.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
@ -59,18 +61,10 @@ func New(c *config.Config) *Server {
return nil
},
}),
middleware.Recover(),
middleware.GzipWithConfig(middleware.GzipConfig{Level: 5}),
)
level, err := log.ParseLevel(c.LogLevel)
if err != nil {
log.Panic(err)
}
log.SetLevel(level)
if level == log.DebugLevel || level == log.TraceLevel {
log.SetReportCaller(true)
}
g := s.Echo.Group("/api/v1")
g.GET("/getChannels", s.getChannels)
g.GET("/getEpgs", s.getEPGs)
@ -90,13 +84,17 @@ func (s *Server) getChannels(c echo.Context) error {
for _, ch := range channels {
name := ch.ChannelName
addr, err := url.Parse(ch.ChannelURL)
addr, err := s.buildChannelUrl(&ch)
if err != nil {
logger := log.WithFields(log.Fields{
"ChannelName": ch.ChannelName,
})
logger.Debug(err)
continue
}
b.WriteString(fmt.Sprintf("#EXTINF:-1, tvg-id=\"%d\" tvg-name=\"%s\", %s\n", ch.ChannelID, name, name))
b.WriteString(fmt.Sprintf("%s/rtp/%s\n", s.udpxyHost, addr.Host))
b.WriteString(fmt.Sprintf("%s\n", addr))
}
return c.Blob(http.StatusOK, "text/plain;charset=UTF-8", b.Bytes())
@ -147,3 +145,24 @@ func (s *Server) getEPGs(c echo.Context) error {
b, _ := doc.WriteToBytes()
return c.Blob(http.StatusOK, "text/xml", b)
}
func (s *Server) buildChannelUrl(ch *channel.Channel) (string, error) {
switch s.mode {
case "UDPXY":
addr, err := url.Parse(ch.ChannelURL)
if err != nil {
return "", err
}
return fmt.Sprintf("%s/rtp/%s", s.udpxyHost, addr.Host), nil
case "IGMP":
addr, err := url.Parse(ch.ChannelURL)
if err != nil {
return "", err
}
return fmt.Sprintf("rtp://%s", addr.Host), nil
case "URL":
return ch.TimeShiftURL, nil
default:
return "", fmt.Errorf("unsupported mode: %s", s.mode)
}
}

View File

@ -9,8 +9,6 @@ import (
"time"
"github.com/mitchellh/mapstructure"
"github.com/thank243/iptvChannel/infra"
)
func BytesToValidEPGs(resp []byte) ([]Epg, error) {
@ -70,12 +68,12 @@ func (e *Epg) filterValidEPG(tz *time.Location) error {
func (e *Epg) fixEndTime(tz *time.Location) (time.Time, error) {
// time format: 20231228001700
endTime, err := infra.StrToTime(e.EndTimeFormat, tz)
endTime, err := strToTime(e.EndTimeFormat, tz)
if err != nil {
return time.Time{}, err
}
beginTime, err := infra.StrToTime(e.BeginTimeFormat, tz)
beginTime, err := strToTime(e.BeginTimeFormat, tz)
if err != nil {
return time.Time{}, err
}
@ -87,3 +85,12 @@ func (e *Epg) fixEndTime(tz *time.Location) (time.Time, error) {
return endTime, nil
}
func strToTime(t string, tz *time.Location) (time.Time, error) {
toTime, err := time.ParseInLocation("20060102150405", t, tz)
if err != nil {
return time.Time{}, err
}
return toTime, nil
}

View File

@ -1,8 +1,10 @@
LogLevel: debug
Cron: "5 */12 * * *"
MaxConcurrent: 5
LogLevel: debug # trace, debug, info, warn, error, fatal
Cron: "5 */12 * * *" # same as Cron Expressions
MaxConcurrent: 5 # not more than 16
ApiHost: "http://YOUR_API_HOST"
UserID: "YOUR_IPTV_ID"
Authenticator: "YOUR_PRIVATE_KEY"
Address: 0.0.0.0:8888
UdpxyHost: "http://YOUR_UDPXY_ADDR"
Mode: udpxy # udpxy, igmp, url
UdpxyHost: "http://YOUR_UDPXY_ADDR" # not blank if mode is udpxy

View File

@ -11,14 +11,7 @@ type Config struct {
Authenticator string `yaml:"Authenticator"`
// Controller
Mode string `yaml:"Mode"`
Address string `yaml:"Address"`
UdpxyHost string `yaml:"UdpxyHost"`
}
var LogLevel = map[string]uint8{
"DEBUG": 1,
"INFO": 2,
"WARN": 3,
"ERROR": 4,
"OFF": 5,
}

View File

@ -6,7 +6,7 @@ import (
const (
appName = "iptvChannel"
version = "0.0.3"
version = "0.0.4"
desc = "Sources: https://www.github.com/thank243/iptvChannel"
)

View File

@ -1,8 +1,11 @@
package controller
import (
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/robfig/cron/v3"
log "github.com/sirupsen/logrus"
@ -15,6 +18,28 @@ import (
)
func New(c *config.Config) (*Controller, error) {
// set log level
level, err := log.ParseLevel(c.LogLevel)
if err != nil {
log.Panic(err)
}
log.SetLevel(level)
if level == log.DebugLevel || level == log.TraceLevel {
log.SetReportCaller(true)
}
// set provide mode
c.Mode = strings.ToUpper(c.Mode)
switch c.Mode {
case "UDPXY":
if c.UdpxyHost == "" {
return nil, errors.New("udpxy host is null")
}
case "IGMP", "URL":
default:
return nil, fmt.Errorf("unsupported mode: %s", c.Mode)
}
ctrl := &Controller{
conf: c,
req: req.New(c),
@ -22,12 +47,14 @@ func New(c *config.Config) (*Controller, error) {
cron: cron.New(),
maxConcurrent: c.MaxConcurrent,
}
// check max concurrent
if c.MaxConcurrent > 16 {
ctrl.maxConcurrent = 16
}
_, err := ctrl.cron.AddJob(c.Cron, cron.NewChain(cron.SkipIfStillRunning(cron.DefaultLogger)).Then(ctrl))
if err != nil {
// set cron job skip if still running
if _, err := ctrl.cron.AddJob(c.Cron, cron.NewChain(cron.SkipIfStillRunning(cron.DefaultLogger)).Then(ctrl)); err != nil {
return nil, err
}
@ -35,11 +62,13 @@ func New(c *config.Config) (*Controller, error) {
}
func (c *Controller) Start() error {
fmt.Printf("%s\nLogLevel: %s, MaxConcurrent: %d\n", config.GetVersion(), c.conf.LogLevel, c.maxConcurrent)
log.Info("Starting service..")
fmt.Printf("%s\nLogLevel: %s, MaxConcurrent: %d, Mode: %s\n",
config.GetVersion(), c.conf.LogLevel, c.maxConcurrent, strings.ToUpper(c.conf.Mode))
log.Info("Starting service..")
log.Info("Fetch EPGs and Channels data on initial startup")
c.Run()
time.Sleep(time.Second)
// start cron job
c.cron.Start()
@ -117,7 +146,10 @@ func (c *Controller) fetchEPGs() error {
sem <- true // enter semaphore, will block if there are maxConcurrent tasks running already
ch := channels[i]
logger := log.WithField("channelId", ch.ChannelID)
logger := log.WithFields(log.Fields{
"ChannelId": ch.ChannelID,
"ChannelName": ch.ChannelName,
})
logger.Debug("start get EPGs")
resp, err := c.req.GetEPGBytes(ch.ChannelID)
if err != nil {

View File

@ -1,14 +0,0 @@
package infra
import (
"time"
)
func StrToTime(t string, tz *time.Location) (time.Time, error) {
toTime, err := time.ParseInLocation("20060102150405", t, tz)
if err != nil {
return time.Time{}, err
}
return toTime, nil
}

View File

@ -15,7 +15,7 @@ import (
func main() {
c, err := controller.New(config.ReadConfig())
if err != nil {
panic(err)
log.Panic(err)
}
go func() {