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