8 Commits

5 changed files with 73 additions and 69 deletions

View File

@@ -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 {

View File

@@ -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,6 +51,7 @@ func PrepareListsToCheck(files []string, urls []string, codes []string) []playli
}
}
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)
@@ -74,13 +74,14 @@ func PrepareListsToCheck(files []string, urls []string, codes []string) []playli
lists = lists[:app.Args.RandomCount]
}
}
}
return lists
}
// 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,

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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))
}
},
}