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:
parent
89e6fd6ac0
commit
d1923d9708
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal 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 }}
|
@ -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
|
||||
|
||||
|
@ -14,5 +14,6 @@ type Server struct {
|
||||
EPGs *atomic.Pointer[[]epg.Epg]
|
||||
Channels *atomic.Pointer[[]channel.Channel]
|
||||
|
||||
mode string
|
||||
udpxyHost string
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
@ -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,
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
|
||||
const (
|
||||
appName = "iptvChannel"
|
||||
version = "0.0.3"
|
||||
version = "0.0.4"
|
||||
desc = "Sources: https://www.github.com/thank243/iptvChannel"
|
||||
)
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user