Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
368a459617
|
|||
|
6c50fda1cd
|
|||
|
dcf91c86d9
|
|||
|
77c646d1f1
|
|||
|
ea11381d07
|
|||
|
6c9b7015a6
|
|||
|
a346d9e2d7
|
|||
|
9c4f09db81
|
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
const VERSION = "0.1.0"
|
||||
const VERSION = "0.2.0"
|
||||
|
||||
// Arguments описывает аргументы командной строки
|
||||
type Arguments struct {
|
||||
|
||||
@@ -12,8 +12,7 @@ import (
|
||||
"axenov/iptv-checker/app/playlist"
|
||||
"axenov/iptv-checker/app/tagfile"
|
||||
"axenov/iptv-checker/app/utils"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"log"
|
||||
"maps"
|
||||
@@ -52,26 +51,28 @@ func PrepareListsToCheck(files []string, urls []string, codes []string) []playli
|
||||
}
|
||||
}
|
||||
|
||||
ini, err := inifile.Init(app.Args.IniPath)
|
||||
if err != nil {
|
||||
log.Printf("Warning: %s, all --code flags will be ignored\n", err)
|
||||
return lists
|
||||
}
|
||||
|
||||
if len(codes) > 0 {
|
||||
for _, plsCode := range codes {
|
||||
list := ini.Lists[plsCode]
|
||||
if list.Url == "" {
|
||||
log.Printf("Warning: playlist [%s] not found in ini-file, skipping\n", plsCode)
|
||||
continue
|
||||
}
|
||||
lists = append(lists, list)
|
||||
if len(lists) == 0 || len(codes) > 0 {
|
||||
ini, err := inifile.Init(app.Args.IniPath)
|
||||
if err != nil {
|
||||
log.Printf("Warning: %s, all --code flags will be ignored\n", err)
|
||||
return lists
|
||||
}
|
||||
} else {
|
||||
lists = slices.Collect(maps.Values(ini.Lists))
|
||||
if int(app.Args.RandomCount) > 0 && int(app.Args.RandomCount) <= len(lists) {
|
||||
rand.Shuffle(len(lists), func(i int, j int) { lists[i], lists[j] = lists[j], lists[i] })
|
||||
lists = lists[:app.Args.RandomCount]
|
||||
|
||||
if len(codes) > 0 {
|
||||
for _, plsCode := range codes {
|
||||
list := ini.Lists[plsCode]
|
||||
if list.Url == "" {
|
||||
log.Printf("Warning: playlist [%s] not found in ini-file, skipping\n", plsCode)
|
||||
continue
|
||||
}
|
||||
lists = append(lists, list)
|
||||
}
|
||||
} else {
|
||||
lists = slices.Collect(maps.Values(ini.Lists))
|
||||
if int(app.Args.RandomCount) > 0 && int(app.Args.RandomCount) <= len(lists) {
|
||||
rand.Shuffle(len(lists), func(i int, j int) { lists[i], lists[j] = lists[j], lists[i] })
|
||||
lists = lists[:app.Args.RandomCount]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +80,8 @@ func PrepareListsToCheck(files []string, urls []string, codes []string) []playli
|
||||
}
|
||||
|
||||
// CheckPlaylists проверяет плейлисты и возвращает их же с результатами проверки
|
||||
func CheckPlaylists(lists []playlist.Playlist) {
|
||||
step := 0
|
||||
func CheckPlaylists(lists []playlist.Playlist) (int, int) {
|
||||
step, onlineCount, offlineCount := 0, 0, 0
|
||||
count := len(lists)
|
||||
tagBlocks = tagfile.Init(app.Args.TagsPath)
|
||||
|
||||
@@ -112,12 +113,14 @@ func CheckPlaylists(lists []playlist.Playlist) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Cannot read playlist [%s]: %s", pls.Url, err)
|
||||
log.Printf("Cannot read playlist [%s]: %s\n", pls.Url, err)
|
||||
offlineCount++
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("Parsing content...")
|
||||
pls.IsOnline = true
|
||||
onlineCount++
|
||||
pls = pls.Parse()
|
||||
|
||||
log.Printf("Parsed, checking channels (%d)...\n", len(pls.Channels))
|
||||
@@ -126,10 +129,7 @@ func CheckPlaylists(lists []playlist.Playlist) {
|
||||
lists[idx] = pls
|
||||
}
|
||||
|
||||
if app.Args.NeedJson {
|
||||
marshal, _ := json.Marshal(lists)
|
||||
fmt.Println(string(marshal))
|
||||
}
|
||||
return onlineCount, offlineCount
|
||||
}
|
||||
|
||||
// CheckChannels проверяет каналы и возвращает их же с результатами проверки
|
||||
@@ -140,6 +140,11 @@ func CheckChannels(pls playlist.Playlist) playlist.Playlist {
|
||||
}
|
||||
|
||||
count := len(pls.Channels)
|
||||
if count == 0 {
|
||||
log.Println("There are no channels to check, skipping")
|
||||
return pls
|
||||
}
|
||||
|
||||
timeout, routines := calcParameters(count)
|
||||
httpClient := http.Client{Timeout: timeout}
|
||||
chSemaphores := make(chan struct{}, routines)
|
||||
@@ -160,6 +165,7 @@ func CheckChannels(pls playlist.Playlist) playlist.Playlist {
|
||||
|
||||
tvChannel.Tags = getTagsForChannel(tvChannel)
|
||||
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
req, err := http.NewRequest("GET", tvChannel.URL, nil)
|
||||
tvChannel.CheckedAt = time.Now().Unix()
|
||||
if err != nil {
|
||||
@@ -251,9 +257,9 @@ func CheckChannels(pls playlist.Playlist) playlist.Playlist {
|
||||
log.Printf(
|
||||
"Checked successfully! online=%d onlinePercent=%.2f%% offline=%d offlinePercent=%.2f%% elapsedTime=%.2fs",
|
||||
pls.OnlineCount,
|
||||
float64(pls.OnlineCount)/float64(len(pls.Channels))*100,
|
||||
float32(pls.OnlineCount)/float32(len(pls.Channels))*100,
|
||||
pls.OfflineCount,
|
||||
float64(pls.OfflineCount)/float64(len(pls.Channels))*100,
|
||||
float32(pls.OfflineCount)/float32(len(pls.Channels))*100,
|
||||
time.Since(startTime).Seconds(),
|
||||
)
|
||||
|
||||
@@ -262,33 +268,18 @@ func CheckChannels(pls playlist.Playlist) playlist.Playlist {
|
||||
|
||||
// calcParameters вычисляет оптимальное количество горутин и таймаут запроса
|
||||
func calcParameters(count int) (time.Duration, int) {
|
||||
// коэффициент нагрузки
|
||||
var k float32
|
||||
// чем ниже, тем больше горутин, меньше таймаут, быстрее проверка, хуже результаты
|
||||
// чем выше, тем меньше горутин, больше таймаут, медленнее проверка, лучше результаты
|
||||
var routines int
|
||||
var percentage float32
|
||||
|
||||
switch true {
|
||||
case count >= 4000:
|
||||
k = 5
|
||||
case count >= 3000:
|
||||
k = 4.5
|
||||
case count >= 2500:
|
||||
k = 4
|
||||
case count >= 2000:
|
||||
k = 3.5
|
||||
case count >= 1500:
|
||||
k = 3
|
||||
case count >= 1000:
|
||||
k = 2.5
|
||||
case count >= 500:
|
||||
k = 2
|
||||
case count >= 100:
|
||||
k = 1.5
|
||||
default:
|
||||
k = 1
|
||||
if count <= 100 {
|
||||
routines = count
|
||||
} else {
|
||||
percentage = float32(runtime.NumCPU()) * 0.075
|
||||
for percentage >= 1 {
|
||||
percentage *= 0.5
|
||||
}
|
||||
routines = int(float32(count) * percentage)
|
||||
}
|
||||
|
||||
routines := int(float32(count) / k / float32(runtime.NumCPU()))
|
||||
if routines > 500 {
|
||||
routines = 500
|
||||
}
|
||||
@@ -296,7 +287,7 @@ func calcParameters(count int) (time.Duration, int) {
|
||||
routines = 1
|
||||
}
|
||||
|
||||
timeout := 10/k + 2
|
||||
timeout := 10 / float32(count) * 150
|
||||
if timeout > 10 {
|
||||
timeout = 10
|
||||
}
|
||||
@@ -306,7 +297,7 @@ func calcParameters(count int) (time.Duration, int) {
|
||||
|
||||
duration := time.Duration(timeout) * time.Second
|
||||
log.Printf(
|
||||
"Check parameters calculated: count=%d timeout=%.2fs routines=%d\n",
|
||||
"Check parameters calculated count=%d timeout=%.2fs routines=%d\n",
|
||||
count,
|
||||
duration.Seconds(),
|
||||
routines,
|
||||
|
||||
@@ -65,20 +65,20 @@ func Init(path string) []TagBlock {
|
||||
pathNormalized, _ := utils.ExpandPath(path)
|
||||
_, err := os.Stat(pathNormalized)
|
||||
if err != nil {
|
||||
log.Println("Warning: tagfile load error (", err, "), all channels will be untagged")
|
||||
log.Println("Warning: all channels will be untagged due to error:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(pathNormalized)
|
||||
if err != nil {
|
||||
log.Println("Warning: tagfile load error (", err, "), all channels will be untagged")
|
||||
log.Println("Warning: all channels will be untagged due to error:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var blocks []TagBlock
|
||||
err = json.Unmarshal(content, &blocks)
|
||||
if err != nil {
|
||||
log.Println("Warning: tagfile load error (", err, "), all channels will be untagged")
|
||||
log.Println("Warning: all channels will be untagged due to error:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package utils
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -55,6 +56,7 @@ func Fetch(url string) ([]byte, error) {
|
||||
"Mozilla/5.0 WINK/1.31.1 (AndroidTV/9) HlsWinkPlayer",
|
||||
)
|
||||
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
httpClient := http.Client{Timeout: 5 * time.Second}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
|
||||
27
cmd/check.go
27
cmd/check.go
@@ -10,9 +10,11 @@ import (
|
||||
"axenov/iptv-checker/app"
|
||||
"axenov/iptv-checker/app/checker"
|
||||
"axenov/iptv-checker/app/logger"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// checkCmd represents the file command
|
||||
@@ -25,14 +27,23 @@ var checkCmd = &cobra.Command{
|
||||
files, _ := cmd.Flags().GetStringSlice("file")
|
||||
urls, _ := cmd.Flags().GetStringSlice("url")
|
||||
codes, _ := cmd.Flags().GetStringSlice("code")
|
||||
|
||||
if len(files) < 1 && len(urls) < 1 && len(codes) < 1 {
|
||||
log.Println("ERROR: You should provide at least one of --file, --url or --code flags")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
lists := checker.PrepareListsToCheck(files, urls, codes)
|
||||
checker.CheckPlaylists(lists)
|
||||
|
||||
startTime := time.Now()
|
||||
onlineCount, offlineCount := checker.CheckPlaylists(lists)
|
||||
|
||||
log.Printf(
|
||||
"Done! count=%d online=%d offline=%d elapsedTime=%.2fs\n",
|
||||
len(lists),
|
||||
onlineCount,
|
||||
offlineCount,
|
||||
time.Since(startTime).Seconds(),
|
||||
)
|
||||
|
||||
if app.Args.NeedJson {
|
||||
marshal, _ := json.Marshal(lists)
|
||||
fmt.Println(string(marshal))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user