├── .github └── workflows │ └── go.yml ├── README.md ├── go.mod ├── go.sum ├── main.go ├── merge ├── group.go ├── m3u.go └── merge.go ├── merged.m3u ├── sub.yaml ├── youtube.m3u ├── youtube.yaml └── youtube └── youtube.go /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | schedule: 10 | - cron: '0 0/2 * * *' 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.19 23 | 24 | - name: Build 25 | run: go build -o main . 26 | 27 | - name: Generate m3u 28 | run: | 29 | chmod +x ./main 30 | ./main 31 | rm main 32 | 33 | - name: Commit 34 | run: | 35 | git config --local user.email "popeyelau@gmail.com" 36 | git config --local user.name "Popeye Lau" 37 | git pull 38 | git add . 39 | git commit -m "update merged.m3u" 40 | 41 | - name: Push changes 42 | uses: ad-m/github-push-action@master 43 | with: 44 | github_token: ${{ secrets.GITHUB_TOKEN }} 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # M3U Merger 2 | 3 | ## 自定义 4 | 5 | 1. fork 本仓库 6 | 7 | 2. 开启 `Workflow` 读写权限 8 | 9 | - 打开 https://github.com/你的名字/itv/settings/actions 10 | - 将`Workflow permissions` 勾选 `Read and write permissions` 11 | 12 | 3. 新建一个 GitHub Token 13 | - 打开 https://github.com/settings/tokens 14 | - `Generate new token` - `Generate new token (classic)` 名字填 `GITHUB_TOKEN` 15 | 16 | 4. 修改配置 17 | ``` 18 | #sub.yaml 19 | - group: '港澳台' 20 | urls: 21 | - "https://telegram-feiyangdigital.v1.mk/gudou.m3u" 22 | - "https://ghproxy.com/https://raw.githubusercontent.com/fanmingming/live/main/tv/m3u/global.m3u" 23 | keywords: "discovery,viu,tvb,hbo,eleven,香港,台湾,中天,东森,東森,纬来,緯來,靖天,八大,台视,臺視,寰宇,寰宇,博斯,龙华,龍華,影迷,爱尔达,愛爾達,中天,年代,民视,民視,翡翠,凤凰,鳳凰,星卫,星衛,臺灣" 24 | ``` 25 | 26 | ## 订阅地址 27 | 28 | `https://raw.githubusercontent.com/popeyelau/itv/main/merged.m3u` 29 | `https://ghproxy.com/https://raw.githubusercontent.com/popeyelau/itv/main/merged.m3u` 30 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module itv 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/dlclark/regexp2 v1.8.0 7 | github.com/etherlabsio/go-m3u8 v1.0.0 8 | gopkg.in/yaml.v2 v2.4.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AlekSi/pointer v1.0.0/go.mod h1:1kjywbfcPFCmncIxtk6fIEub6LKrfMz3gc5QKVOSOA8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/dlclark/regexp2 v1.8.0 h1:rJD5HeGIT/2b5CDk63FVCwZA3qgYElfg+oQK7uH5pfE= 4 | github.com/dlclark/regexp2 v1.8.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 5 | github.com/etherlabsio/go-m3u8 v1.0.0 h1:d3HJVr8wlbvJO20ksKEyvDYf4bcM7v8YV3W83fHswL0= 6 | github.com/etherlabsio/go-m3u8 v1.0.0/go.mod h1:RzDiaXgaYnIEzZUmVUD/xMRFR7bY7U5JaCnp8XYLmXU= 7 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 8 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 9 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 10 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 11 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 12 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 13 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | m "itv/merge" 5 | y "itv/youtube" 6 | ) 7 | 8 | func main() { 9 | m.Run() 10 | y.Run() 11 | } 12 | -------------------------------------------------------------------------------- /merge/group.go: -------------------------------------------------------------------------------- 1 | package merger 2 | 3 | type Group struct { 4 | Group string `yaml:"group"` 5 | Urls []string `yaml:"urls"` 6 | Keywords string `yaml:"keywords"` 7 | Track []Track 8 | } 9 | 10 | type Channnel struct { 11 | Name string `yaml:"name"` 12 | Url string `yaml:"url"` 13 | Resolution string `yaml:"resolution"` 14 | } 15 | -------------------------------------------------------------------------------- /merge/m3u.go: -------------------------------------------------------------------------------- 1 | package merger 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "net/http" 10 | "os" 11 | "regexp" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | // Playlist is a type that represents an m3u playlist containing 0 or more tracks 17 | type Playlist struct { 18 | Tracks []Track 19 | } 20 | 21 | // A Tag is a simple key/value pair 22 | type Tag struct { 23 | Name string 24 | Value string 25 | } 26 | 27 | // Track represents an m3u track with a Name, Lengh, URI and a set of tags 28 | type Track struct { 29 | Name string 30 | Length int 31 | URI string 32 | Tags []Tag 33 | } 34 | 35 | func (m *Track) buildTags(group *Group) { 36 | var logo string 37 | for _, tag := range m.Tags { 38 | if tag.Name == "tvg-logo" { 39 | logo = tag.Value 40 | } 41 | } 42 | m.Tags = []Tag{ 43 | { 44 | Name: "group-title", 45 | Value: group.Group, 46 | }, 47 | { 48 | Name: "tvg-name", 49 | Value: m.Name, 50 | }, 51 | { 52 | Name: "tvg-logo", 53 | Value: logo, 54 | }, 55 | } 56 | } 57 | 58 | // Parse parses an m3u playlist with the given file name and returns a Playlist 59 | func Parse(fileName string) (Playlist, error) { 60 | var f io.ReadCloser 61 | 62 | if strings.HasPrefix(fileName, "http://") || strings.HasPrefix(fileName, "https://") { 63 | data, err := http.Get(fileName) 64 | if err != nil { 65 | return Playlist{}, 66 | fmt.Errorf("unable to open playlist URL: %v", err) 67 | } 68 | f = data.Body 69 | } else { 70 | file, err := os.Open(fileName) 71 | if err != nil { 72 | return Playlist{}, 73 | fmt.Errorf("unable to open playlist file: %v", err) 74 | } 75 | f = file 76 | } 77 | defer f.Close() 78 | 79 | onFirstLine := true 80 | scanner := bufio.NewScanner(f) 81 | tagsRegExp, _ := regexp.Compile("([a-zA-Z0-9-]+?)=\"([^\"]+)\"") 82 | playlist := Playlist{} 83 | 84 | for scanner.Scan() { 85 | line := scanner.Text() 86 | if onFirstLine && !strings.HasPrefix(line, "#EXTM3U") { 87 | return Playlist{}, 88 | errors.New("invalid m3u file format. Expected #EXTM3U file header") 89 | } 90 | 91 | onFirstLine = false 92 | 93 | if strings.HasPrefix(line, "#EXTINF") { 94 | line := strings.Replace(line, "#EXTINF:", "", -1) 95 | trackInfo := strings.Split(line, ",") 96 | if len(trackInfo) < 2 { 97 | return Playlist{}, 98 | errors.New("invalid m3u file format. Expected EXTINF metadata to contain track length and name data") 99 | } 100 | length, parseErr := strconv.Atoi(strings.Split(trackInfo[0], " ")[0]) 101 | if parseErr != nil { 102 | return Playlist{}, errors.New("unable to parse length") 103 | } 104 | name := strings.Trim(trackInfo[1], " ") 105 | if len(trackInfo) > 2 { 106 | name = trackInfo[2] 107 | } 108 | track := &Track{name, length, "", nil} 109 | tagList := tagsRegExp.FindAllString(line, -1) 110 | 111 | for i := range tagList { 112 | tagInfo := strings.Split(tagList[i], "=") 113 | tag := &Tag{tagInfo[0], strings.Replace(tagInfo[1], "\"", "", -1)} 114 | track.Tags = append(track.Tags, *tag) 115 | } 116 | playlist.Tracks = append(playlist.Tracks, *track) 117 | } else if strings.HasPrefix(line, "#") || line == "" { 118 | continue 119 | } else if len(playlist.Tracks) == 0 { 120 | return Playlist{}, 121 | errors.New("URI provided for playlist with no tracks") 122 | 123 | } else { 124 | playlist.Tracks[len(playlist.Tracks)-1].URI = strings.TrimSpace(line) 125 | } 126 | } 127 | 128 | return playlist, nil 129 | } 130 | 131 | // Marshall Playlist to an m3u file. 132 | func Marshall(p Playlist) (io.Reader, error) { 133 | buf := new(bytes.Buffer) 134 | w := bufio.NewWriter(buf) 135 | if err := MarshallInto(p, w); err != nil { 136 | return nil, err 137 | } 138 | 139 | return buf, nil 140 | } 141 | 142 | // MarshallInto a *bufio.Writer a Playlist. 143 | func MarshallInto(p Playlist, into *bufio.Writer) error { 144 | into.WriteString("#EXTM3U\n") 145 | for _, track := range p.Tracks { 146 | into.WriteString("#EXTINF:") 147 | into.WriteString(fmt.Sprintf("%d, ", track.Length)) 148 | for i := range track.Tags { 149 | if i == len(track.Tags)-1 { 150 | into.WriteString(fmt.Sprintf("%s=%q", track.Tags[i].Name, track.Tags[i].Value)) 151 | continue 152 | } 153 | into.WriteString(fmt.Sprintf("%s=%q ", track.Tags[i].Name, track.Tags[i].Value)) 154 | } 155 | if len(track.Tags) > 0 { 156 | into.WriteString(", ") 157 | } 158 | 159 | into.WriteString(fmt.Sprintf("%s\n%s\n", track.Name, track.URI)) 160 | } 161 | 162 | return into.Flush() 163 | } 164 | -------------------------------------------------------------------------------- /merge/merge.go: -------------------------------------------------------------------------------- 1 | package merger 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "sort" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "gopkg.in/yaml.v2" 16 | ) 17 | 18 | var trackUrls []string 19 | var confs []*Group 20 | 21 | func Run() { 22 | 23 | data, err := ioutil.ReadFile("sub.yaml") 24 | if err != nil { 25 | fmt.Printf("err: %v\n", err) 26 | return 27 | } 28 | err = yaml.Unmarshal(data, &confs) 29 | if err != nil { 30 | return 31 | } 32 | 33 | trackUrls = []string{} 34 | 35 | wg := sync.WaitGroup{} 36 | wg.Add(len(confs)) 37 | for _, conf := range confs { 38 | go func(conf *Group) { 39 | defer wg.Done() 40 | parseConf(conf) 41 | }(conf) 42 | } 43 | wg.Wait() 44 | merge() 45 | } 46 | 47 | func parseConf(conf *Group) { 48 | wg := sync.WaitGroup{} 49 | wg.Add(len(conf.Urls)) 50 | for _, url := range conf.Urls { 51 | go func(url string) { 52 | defer wg.Done() 53 | parseM3u(url, conf) 54 | }(url) 55 | } 56 | wg.Wait() 57 | } 58 | 59 | func parseM3u(url string, group *Group) { 60 | playlist, err := Parse(url) 61 | if err != nil { 62 | fmt.Println(err) 63 | return 64 | } 65 | 66 | var tracks []Track 67 | for _, track := range playlist.Tracks { 68 | if filter(track, group) && !isRequested(track.URI) { 69 | tracks = append(tracks, track) 70 | } 71 | } 72 | 73 | if len(tracks) == 0 { 74 | fmt.Println("no channels") 75 | return 76 | } 77 | 78 | wg := sync.WaitGroup{} 79 | wg.Add(len(tracks)) 80 | for _, track := range tracks { 81 | go func(track Track) { 82 | defer wg.Done() 83 | trackUrls = append(trackUrls, track.URI) 84 | if ping(track.URI) { 85 | track.buildTags(group) 86 | group.Track = append(group.Track, track) 87 | } 88 | }(track) 89 | } 90 | wg.Wait() 91 | } 92 | 93 | func ping(url string) bool { 94 | resp, err := request(url, "HEAD", 3*time.Second, true) 95 | if err != nil { 96 | return false 97 | } 98 | 99 | code := resp.StatusCode 100 | isValid := isValidRespCode(code) 101 | 102 | return isValid 103 | } 104 | 105 | func request(url string, method string, timeout time.Duration, checkRedirect bool) (*http.Response, error) { 106 | client := &http.Client{ 107 | Timeout: timeout, 108 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 109 | if checkRedirect { 110 | return http.ErrUseLastResponse 111 | } 112 | if len(via) >= 10 { 113 | return errors.New("stopped after 10 redirects") 114 | } 115 | return nil 116 | }, 117 | } 118 | req, err := http.NewRequest(method, url, nil) 119 | if err != nil { 120 | return nil, err 121 | } 122 | resp, err := client.Do(req) 123 | if err != nil { 124 | return nil, err 125 | } 126 | return resp, nil 127 | } 128 | 129 | func merge() { 130 | var tracks []Track 131 | 132 | for _, v := range confs { 133 | tracks = append(tracks, v.Track...) 134 | } 135 | 136 | sort.Slice(tracks, func(i, j int) bool { 137 | return strings.TrimSpace(tracks[i].Name) < strings.TrimSpace(tracks[j].Name) 138 | }) 139 | 140 | playlist := Playlist{Tracks: tracks} 141 | reader, err := Marshall(playlist) 142 | if err != nil { 143 | fmt.Println(err) 144 | return 145 | } 146 | b := reader.(*bytes.Buffer) 147 | _ = ioutil.WriteFile("./merged.m3u", b.Bytes(), os.ModePerm) 148 | if err != nil { 149 | fmt.Println(err) 150 | return 151 | } 152 | } 153 | 154 | func filter(track Track, conf *Group) bool { 155 | var name, group string 156 | for _, tag := range track.Tags { 157 | if tag.Name == "group-title" { 158 | group = strings.ToLower(tag.Value) 159 | } 160 | } 161 | name = strings.ToLower(track.Name) 162 | keywords := strings.Split(conf.Keywords, ",") 163 | 164 | for _, keyword := range keywords { 165 | if strings.Contains(name, keyword) || strings.Contains(group, keyword) { 166 | return true 167 | } 168 | } 169 | return false 170 | } 171 | 172 | func isRequested(url string) bool { 173 | for _, v := range trackUrls { 174 | if v == url { 175 | return true 176 | } 177 | } 178 | return false 179 | } 180 | 181 | func isValidRespCode(statusCode int) bool { 182 | return (statusCode >= 200 && statusCode < 300) || statusCode == 302 183 | } 184 | -------------------------------------------------------------------------------- /merged.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1, group-title="港澳台" tvg-name="TVB J2" tvg-logo="", TVB J2 3 | https://j3.lhlh.app/mv.m3u8?checkedby:iptvcat.com 4 | #EXTINF:-1, group-title="港澳台" tvg-name="TVBS" tvg-logo="", TVBS 5 | http://38.64.72.148:80/hls/modn/list/4005/chunklist0.m3u8 6 | #EXTINF:-1, group-title="港澳台" tvg-name="TVBS亚洲" tvg-logo="https://epg.112114.xyz/logo/TVBS.png", TVBS亚洲 7 | http://38.64.72.148/hls/modn/list/4005/chunklist1.m3u8 8 | #EXTINF:-1, group-title="港澳台" tvg-name="TVBS新闻" tvg-logo="", TVBS新闻 9 | http://38.64.72.148/hls/modn/list/4006/chunklist0.m3u8 10 | #EXTINF:-1, group-title="港澳台" tvg-name="TVBS新闻" tvg-logo="", TVBS新闻 11 | http://38.64.72.148:80/hls/modn/list/4006/chunklist1.m3u8 12 | #EXTINF:-1, group-title="港澳台" tvg-name="TVBS新闻" tvg-logo="", TVBS新闻 13 | http://38.64.72.148:80/hls/modn/list/4006/chunklist0.m3u8 14 | #EXTINF:-1, group-title="港澳台" tvg-name="TVB功夫台" tvg-logo="", TVB功夫台 15 | https://cn-hbyc2-dx-live-01.bilivideo.com/live-bvc/545768/live_179302204_68184269.m3u8 16 | #EXTINF:-1, group-title="港澳台" tvg-name="TVB明珠台2" tvg-logo="", TVB明珠台2 17 | https://epg.pm/stream/f8d5ee5dae5bc40a4582cfb40a7d7063310dfda47ce05b86b5aa9ff5e50c9172.m3u8 18 | #EXTINF:-1, group-title="港澳台" tvg-name="TVB翡翠华丽" tvg-logo="", TVB翡翠华丽 19 | http://23.237.10.66:16410 20 | #EXTINF:-1, group-title="港澳台" tvg-name="三立NEWS+" tvg-logo="https://epg.112114.xyz/logo/三立NEWS+.png", 三立NEWS+ 21 | https://raw.githubusercontent.com/etag2000/IPTV/YTlive/setlive.m3u8 22 | #EXTINF:-1, group-title="港澳台" tvg-name="三立NEWS+" tvg-logo="https://epg.112114.xyz/logo/三立NEWS+.png", 三立NEWS+ 23 | https://ytm2.yuanhsing.cf/SETLIVE.m3u8 24 | #EXTINF:-1, group-title="港澳台" tvg-name="东森新闻美洲" tvg-logo="", 东森新闻美洲 25 | http://38.64.72.148:80/hls/modn/list/2015/chunklist0.m3u8 26 | #EXTINF:-1, group-title="港澳台" tvg-name="中天新闻" tvg-logo="", 中天新闻 27 | https://epg.pm/stream/526f164cdabe04bee921865bb05037255c22ccbeb50cafd3a4ec83cc00514447.m3u8 28 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰中文" tvg-logo="https://live.fanmingming.com/tv/fenghuangzhongwen.png", 凤凰中文 29 | http://playtv-live.ifeng.com:80/live/06OLEGEGM4G.m3u8 30 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰中文" tvg-logo="https://live.fanmingming.com/tv/fenghuangzhongwen.png", 凤凰中文 31 | https://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8 32 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰中文" tvg-logo="https://live.fanmingming.com/tv/fenghuangzhongwen.png", 凤凰中文 33 | http://playtv-live.ifeng.com/live/06OLEGEGM4G.m3u8 34 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰中文" tvg-logo="", 凤凰中文 35 | http://playtv-live.ifeng.com/live/06OLEGEGM4G_tv1.m3u8 36 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰中文" tvg-logo="", 凤凰中文 37 | http://111.59.189.40:8445/tsfile/live/1020_1.m3u8 38 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰中文" tvg-logo="https://epg.112114.xyz/logo/凤凰中文.png", 凤凰中文 39 | http://play-live.ifeng.com/live/06OLEGEGM4G.m3u8 40 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰电影" tvg-logo="", 凤凰电影 41 | http://111.59.189.40:8445/tsfile/live/1022_1.m3u8 42 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰电影" tvg-logo="", 凤凰电影 43 | http://111.59.189.40:8445/tsfile/live/1022_1.m3u8?zgangd 44 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="https://live.fanmingming.com/tv/fenghuangzixun.png", 凤凰资讯 45 | http://playtv-live.ifeng.com:80/live/06OLEEWQKN4.m3u8 46 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="https://live.fanmingming.com/tv/fenghuangzixun.png", 凤凰资讯 47 | http://playtv-live.ifeng.com/live/06OLEEWQKN4.m3u8 48 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="", 凤凰资讯 49 | http://111.59.189.40:8445/tsfile/live/1021_1.m3u8 50 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="https://epg.112114.xyz/logo/凤凰资讯.png", 凤凰资讯 51 | http://play-live.ifeng.com/live/06OLEEWQKN4.m3u8 52 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="https://live.fanmingming.com/tv/fenghuangzixun.png", 凤凰资讯 53 | https://playtv-live.ifeng.com/live/06OLEEWQKN4.m3u8 54 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="", 凤凰资讯 55 | http://playtv-live.ifeng.com/live/06OLEEWQKN4_tv2.m3u8 56 | #EXTINF:-1, group-title="港澳台" tvg-name="凤凰资讯" tvg-logo="", 凤凰资讯 57 | http://playtv-live.ifeng.com:80/live/06OLEEWQKN4_tv2.m3u8 58 | #EXTINF:-1, group-title="港澳台" tvg-name="台视新闻" tvg-logo="", 台视新闻 59 | http://38.64.72.148:80/hls/modn/list/4013/chunklist1.m3u8 60 | #EXTINF:-1, group-title="港澳台" tvg-name="台视新闻" tvg-logo="", 台视新闻 61 | http://38.64.72.148:80/hls/modn/list/4013/chunklist0.m3u8 62 | #EXTINF:-1, group-title="港澳台" tvg-name="台视新闻HD" tvg-logo="", 台视新闻HD 63 | https://epg.pm/stream/8655006acfa3857ec51f6427fa2ea9ca167b38fc53ae84d1645bc0ce8b4060ba.m3u8 64 | #EXTINF:-1, group-title="港澳台" tvg-name="无线新闻" tvg-logo="https://epg.112114.xyz/logo/无线新闻.png", 无线新闻 65 | https://cdn.hklive.tv/xxxx/83/index.m3u8 66 | #EXTINF:-1, group-title="港澳台" tvg-name="无线新闻" tvg-logo="https://epg.112114.xyz/logo/无线新闻.png", 无线新闻 67 | http://2022.stream-link.org/playlist/tvb_news 68 | #EXTINF:-1, group-title="港澳台" tvg-name="明珠台" tvg-logo="https://epg.112114.xyz/logo/明珠台.png", 明珠台 69 | http://159.75.85.63:8765/telegram@feiyangdigital-mingzhu.m3u8 70 | #EXTINF:-1, group-title="港澳台" tvg-name="有线新闻" tvg-logo="https://epg.112114.xyz/logo/有线新闻台.png", 有线新闻 71 | http://cm61-10-2-143.hkcable.com.hk/live_freedirect/freehd209_h.live/playlist.m3u8 72 | #EXTINF:-1, group-title="港澳台" tvg-name="有线新闻" tvg-logo="https://epg.112114.xyz/logo/有线新闻台.png", 有线新闻 73 | http://61.10.2.140:80/live_freedirect/freehd209_h.live/chunklist_w135209556.m3u8 74 | #EXTINF:-1, group-title="港澳台" tvg-name="民视新闻HD" tvg-logo="", 民视新闻HD 75 | https://epg.pm/stream/c4e4884645d94d0d2e76e47c691546493b7031348b663eb9a67769b0096f8b7e.m3u8 76 | #EXTINF:-1, group-title="港澳台" tvg-name="民视新闻HD" tvg-logo="", 民视新闻HD 77 | http://38.64.72.148:80/hls/modn/list/4012/chunklist1.m3u8 78 | #EXTINF:-1, group-title="港澳台" tvg-name="民视新闻台" tvg-logo="", 民视新闻台 79 | http://38.64.72.148:80/hls/modn/list/4012/chunklist0.m3u8 80 | #EXTINF:-1, group-title="港澳台" tvg-name="翡翠台" tvg-logo="https://epg.112114.xyz/logo/翡翠台.png", 翡翠台 81 | http://159.75.85.63:8765/telegram@feiyangdigital-feicui.m3u8 82 | #EXTINF:-1, group-title="港澳台" tvg-name="香港佛陀" tvg-logo="", 香港佛陀 83 | http://js1.amtb.cn/liveedge/_definst_/livetv/chunklist.m3u8 84 | #EXTINF:-1, group-title="港澳台" tvg-name="香港佛陀" tvg-logo="", 香港佛陀 85 | https://js1.amtb.cn/liveedge/_definst_/livetv/playlist.m3u8 86 | #EXTINF:-1, group-title="港澳台" tvg-name="香港卫视文旅" tvg-logo="", 香港卫视文旅 87 | https://myun-hw-s3.myun.tv/melj80jz/lxx27bol/1551285536499984128.m3u8 88 | #EXTINF:-1, group-title="港澳台" tvg-name="香港面包台" tvg-logo="https://epg.112114.xyz/logo/香港面包台.png", 香港面包台 89 | https://video.bread-tv.com:8091/hls-live24/online/index.m3u8 90 | -------------------------------------------------------------------------------- /sub.yaml: -------------------------------------------------------------------------------- 1 | #sub.yaml 2 | - group: '港澳台' 3 | urls: 4 | - "https://raw.githubusercontent.com/fanmingming/live/main/tv/m3u/global.m3u" 5 | - "https://raw.githubusercontent.com/whpsky/iptv/main/chinatv.m3u" 6 | - "https://telegram-feiyangdigital.v1.mk/gudou.m3u" 7 | keywords: "discovery,viu,tvb,hbo,eleven,香港,台湾,中天,东森,東森,纬来,緯來,靖天,八大,台视,臺視,寰宇,寰宇,博斯,龙华,龍華,影迷,爱尔达,愛爾達,中天,年代,民视,民視,翡翠,凤凰,鳳凰,星卫,星衛,臺灣" # -------------------------------------------------------------------------------- /youtube.m3u: -------------------------------------------------------------------------------- 1 | #EXTM3U 2 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/kwdUyHz_Exg/hq720_live.jpg", Rainy Night & Relaxing Jazz 3 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY4GFBZKFhgbq64fwCQ/ip/172.177.96.38/id/kwdUyHz_Exg.1/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr3---sn-nja7sner.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/iB/mm/44/mn/sn-nja7sner/ms/lva/mv/u/mvi/3/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRAIgaOvJEqDzIaqknMpJj1QrhBz8h_atMkSmEZ_5wHOr354CICRlQ88kEWl8lFnPAq4WcNUgU1Z4YpCJM-qKNWIr_t13/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRAIgSqR1v-SI2Te6C0yoeFzYD6FbD5L6-d-_GmWRXr6bgJoCIEEVDaIMsQkJPVPsK0QhpGxPVihUDcu-k-DHxWpxpO-Z/playlist/index.m3u8 4 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/ylYJSBUgaMA/hq720_live.jpg", 民视新闻台 5 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY8LLBIzP8wSjkp2YCA/ip/172.177.96.38/id/ylYJSBUgaMA.3/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr5---sn-nja7snle.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/iR/mm/44/mn/sn-nja7snle/ms/lva/mv/u/mvi/5/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRQIhAJrjh_MFjyxSv0y2qYS7eDzSJT5D_KI_lRXJjDzfBEqhAiBGGlRVE8MvIpqzi15RGRbRuQ14cdFWUmAxuwWTDzKn2A%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRQIgJj2q0v83AVKAleDYeaMbRYfs2cVa1U-2x_hMoJ-hjSgCIQDBANqsq1ZL9GWdr0dRTTJHGNLhv4JH-q1SjgVI3-QXsg%3D%3D/playlist/index.m3u8 6 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/xL0ch83RAK8/hq720_live.jpg", 台视新闻台 7 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY7TBBMfk8wTExbTQAQ/ip/172.177.96.38/id/xL0ch83RAK8.4/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-nja7snll.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/r6/mm/44/mn/sn-nja7snll/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRgIhAKBaLFPt5QFiVikB2u5fH_vwuo7Z7GtR7og8qusWOIhQAiEA_uEhUVYeVYkTMZcj88jsAq2SO6aKWioipaSbeSA3I7M%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRgIhANSbEKuD-fkbEVCUHarKp3XnKBEal1t7Pczy0BpEZAx_AiEA-bsBXtfsWnF5nK1ULYKLRn-meR1gamn3mvh3Zfb3wz8%3D/playlist/index.m3u8 8 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/5n0y6b0Q25o/hq720_live.jpg", 鏡新聞 9 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY6fNBKaHhwbawJz4Ag/ip/172.177.96.38/id/5n0y6b0Q25o.2/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr4---sn-nja7sner.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/di/mm/44/mn/sn-nja7sner/ms/lva/mv/u/mvi/4/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRQIhAI8K4B8st1k7h2TrJJR1R_hPJIpGyCoJvgLpKeusMVcOAiBg3Toj7NKqjMiqJNq_jolGjXf3Ol-FuTvbh_Q0i0BVIw%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRQIhAPV-IuPTOlUWjk1soApy4IQZwIUR_KIGhOIId_DmShpmAiBPRoNMtVdixf5xIGu24dIpFl0rwfjoW_rV7SNAucWDwQ%3D%3D/playlist/index.m3u8 10 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/R2iMq5LKXco/hq720_live.jpg", 东森新闻台 11 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY6HHBNHi8wTwxaigBw/ip/172.177.96.38/id/R2iMq5LKXco.4/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-nja7sne6.googlevideo.com/playlist_duration/30/manifest_duration/30/gcr/us/vprv/1/playlist_type/DVR/mh/Ij/mm/44/mn/sn-nja7sne6/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,gcr,vprv,playlist_type/sig/AOq0QJ8wRQIgBIlSK9bXUYYfBwQu5gnrj-aSisKoP78cbBewYo5HWi0CIQDvJOMIeaiHAJjwz3Y0lxjPTV1lVU8g1RSBstQH3OPMNw%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRgIhAMTsNzuy4wsIjmJMSX8ZPIifFgbzjzcj8jkfd7YfGUkwAiEAq_AF5N6gAr2Tt5unCNYpuTqJhqBmP7L5h87nLRvu22M%3D/playlist/index.m3u8 12 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/jlldIiGA5bU/hq720_live.jpg", 三立财经新闻台inews 13 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY__PBInn8wS7_pvICw/ip/172.177.96.38/id/jlldIiGA5bU.2/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr4---sn-njaeyn7s.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/nH/mm/44/mn/sn-njaeyn7s/ms/lva/mv/u/mvi/4/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRQIhANX9U8TXsXGNfMukOpIouYnx4EJVfvv02flHfoRvWSOUAiAoANbbzJOlkRG1XX_XrdqXFwGg15cBgiufJQysbUlItA%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRAIgGZW3W7xzpdWyRwnRbqIvGOhf4GRaHjuhqDN_pn1AMs8CIFHsSlt8PHsbiwVVcrieA2BuRjoXy-ONcVonQH_AWVJ-/playlist/index.m3u8 14 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/TCnaIE_SAtM/hq720_live.jpg", 中视新闻台 15 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY7vABISc8wSAsIS4Aw/ip/172.177.96.38/id/TCnaIE_SAtM.4/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr3---sn-nja7snll.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/eb/mm/44/mn/sn-nja7snll/ms/lva/mv/u/mvi/3/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRQIgcR_SDJxHsI0DF-nMPL_MaRxt8-cPN5aRDF5d9FZS9zMCIQCqBnaBeGe8APBwZZ0z8Qj9sL4zFg3MYwMxI_dX2cx6Ig%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRAIgR7tO1A9UDS7PJP3kZtQWAUm9iV-mmHEz_CQI2CxJNPACIAc9SUvlH1QdiI6ASahGz2TxUoVzEaMdYz8OGup9QmD1/playlist/index.m3u8 16 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/FoBfXvlOR6I/hq720_live.jpg", 三立新闻台 17 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY4vKBIzXhgayh6_oBQ/ip/172.177.96.38/id/FoBfXvlOR6I.64/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr5---sn-nja7snle.googlevideo.com/playlist_duration/30/manifest_duration/30/gcr/us/vprv/1/playlist_type/DVR/mh/54/mm/44/mn/sn-nja7snle/ms/lva/mv/u/mvi/5/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,gcr,vprv,playlist_type/sig/AOq0QJ8wRAIgeb-ugcVayWuHwx32U3aWUh5r-jq36yja2RxU7z0aP2sCIEUzDQnKs9Sjmde1q5IemAez3ZcrNDsiJm9s1UcQ3Uao/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRQIgVkrza-7rTbRkTlLZ2sOWQj9yRFxMcP0fROvr5Ng00bUCIQCYDzoBVBlwvX0F-U7EQe6H_FHxCqt49xM_6FegYdAd8A%3D%3D/playlist/index.m3u8 18 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/2CDRu6-Vgfo/hq720_live.jpg", Shinjuku FM 19 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY77ABIWLhwa6hZzIBA/ip/172.177.96.38/id/2CDRu6-Vgfo.2/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-nja7sne6.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/j0/mm/44/mn/sn-nja7sne6/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRQIhAJtqKsVxB8bQazfo62Vi9TyX7x7v0W78EUu-GdkSYuvXAiAQT4gpECBJUMpdjr4vEoKytmvSn_hPZ4YLJKxDKoX9oA%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRAIgBChQYt96LKH4b1Uv7E_RDAo-pnYe4VAQYkFaR8IITzACIGGhU1LK9H3jH3ush6tPq_S7rKUl7aMdysZ4wYP2TkmR/playlist/index.m3u8 20 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/dYOePmu1wy8/hq720_live.jpg", Winter Coffee Shop 21 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY8bDBMO_8wSw97joBQ/ip/172.177.96.38/id/dYOePmu1wy8.1/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-nja7sne6.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/rz/mm/44/mn/sn-nja7sne6/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRAIgfTWtTYnP7myT3tFJJsKZ3B_nEgztzjvQp0B6Pb-V1XICIAbSsxU6Lj59ki3kU5jRCnY9T6PZ71hJuYXRmoe7zYIi/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRgIhAJ0nK3ZNehuuRnb08jAK8DQw5DPgPjGGkSlewg4e4no-AiEAxC2m5000ik2SpaZvIHPYYEWBBfZgV_7Pos8q8ZS9qOo%3D/playlist/index.m3u8 22 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/B7Zp3d6xXWw/hq720_live.jpg", 寰宇新闻 23 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY8-_BIPo8wThqJ5w/ip/172.177.96.38/id/B7Zp3d6xXWw.7/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-nja7snle.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/Xk/mm/44/mn/sn-nja7snle/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRgIhAIJ714jeq2c8twfNZC-L2c0ZhUpPOW1t-R1p9OTijbxsAiEAkfBHVa_FzJl9W3tbshFe17gJkXgPChSA6IZJemvmTIk%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRgIhAMOlk5ntJwgcGhx-4tKhk2LAcaNBPr0LwwqUZorhhfmTAiEA9MHHwa8bBhSyi1CADyVtfPPZ6rdv_ItZ9yfvrnTHptI%3D/playlist/index.m3u8 24 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/_QbRXRnHMVY/hq720_live.jpg", 中天综合台 25 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY5TOBIXjhgbMgrKgAQ/ip/172.177.96.38/id/_QbRXRnHMVY.3/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-nja7sne6.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/Du/mm/44/mn/sn-nja7sne6/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/beids/24472383/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRAIgY9nuieEIeaKFwj2XPiJ9_G4fWtD-h247jvb1b9y1mKoCICDFW_KdE2ZtFnooEL1GYMu-y5hviT9gm3szI2OskDPW/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRQIhANk6MI819atRmERopQB5pCfFCXXxaItiP0XWy2r5UEE0AiBb7dK9nTzvlETtloHl_sBxP9Ggwp6UrsUdBGUXcsL1tQ%3D%3D/playlist/index.m3u8 26 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/WHEPzbFA3hw/hq720_live.jpg", 东森财经新闻台 27 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY9jJBMjn8wSiv6HoBg/ip/172.177.96.38/id/WHEPzbFA3hw.2/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr3---sn-nja7sner.googlevideo.com/playlist_duration/30/manifest_duration/30/vprv/1/playlist_type/DVR/mh/Y3/mm/44/mn/sn-nja7sner/ms/lva/mv/u/mvi/3/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,vprv,playlist_type/sig/AOq0QJ8wRQIhAMlZd3wCuWbKPd6U0RPGAov8A2DxRbvYombYoBHDeMRyAiBGL48z4bdgf1sB92guFfNUHTMU0n9OSPKdRR2SePxwmQ%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRgIhANxvyXQxdJkaX0eymgMWIYFKS11tQq4Fb6I9EsTYwB-AAiEAyU_Egs8_TYXkTpdBMd1cc2xXNnCFEG1Fwf0riTmzhlA%3D/playlist/index.m3u8 28 | #EXTINF:-1, tvg-logo="https://i.ytimg.com/vi/2mCSYvcfhtc/hq720_live.jpg", TVBS 新闻台 29 | https://manifest.googlevideo.com/api/manifest/hls_playlist/expire/1676878585/ei/mc7yY-nMBIe68wTX-pX4Dw/ip/172.177.96.38/id/2mCSYvcfhtc.2/itag/96/source/yt_live_broadcast/requiressl/yes/ratebypass/yes/live/1/sgoap/gir%3Dyes%3Bitag%3D140/sgovp/gir%3Dyes%3Bitag%3D137/hls_chunk_host/rr1---sn-njaeyn7z.googlevideo.com/playlist_duration/30/manifest_duration/30/gcr/us/vprv/1/playlist_type/DVR/mh/9_/mm/44/mn/sn-njaeyn7z/ms/lva/mv/u/mvi/1/pl/19/dover/11/pacing/0/keepalive/yes/fexp/24007246/mt/1676855650/sparams/expire,ei,ip,id,itag,source,requiressl,ratebypass,live,sgoap,sgovp,playlist_duration,manifest_duration,gcr,vprv,playlist_type/sig/AOq0QJ8wRQIgdFCEAFBDJc35DXFsvmsV9HtnJ89Isa_oENN3icyNoyMCIQDOaT6z2tESCNcd_GPz5LK7nLecqlgdpAIF5APYkF8F1g%3D%3D/lsparams/hls_chunk_host,mh,mm,mn,ms,mv,mvi,pl/lsig/AG3C_xAwRQIgDihgRmWl3Ttoqf-smEFvqFDf55FExJErZbmW53qUNzoCIQCcWMKMCrXKNwR08ozU1hZ4ocjYsmXl8BmUtgzojxN2Kg%3D%3D/playlist/index.m3u8 30 | -------------------------------------------------------------------------------- /youtube.yaml: -------------------------------------------------------------------------------- 1 | #youtube.yaml 2 | - name: "TVBS 新闻台" 3 | url: "https://www.youtube.com/watch?v=2mCSYvcfhtc" 4 | resolution: "1080" 5 | - name: "民视新闻台" 6 | url: "https://www.youtube.com/watch?v=ylYJSBUgaMA" 7 | resolution: "1080" 8 | - name: "三立新闻台" 9 | url: "https://www.youtube.com/watch?v=FoBfXvlOR6I" 10 | resolution: "1080" 11 | - name: "三立财经新闻台inews" 12 | url: "https://www.youtube.com/watch?v=jlldIiGA5bU" 13 | resolution: "1080" 14 | - name: "中天综合台" 15 | url: "https://www.youtube.com/watch?v=_QbRXRnHMVY" 16 | resolution: "1080" 17 | - name: "东森新闻台" 18 | url: "https://www.youtube.com/watch?v=R2iMq5LKXco" 19 | resolution: "1080" 20 | - name: "东森财经新闻台" 21 | url: "https://www.youtube.com/watch?v=WHEPzbFA3hw" 22 | resolution: "1080" 23 | - name: "华视" 24 | url: "https://www.youtube.com/watch?v=wM0g8EoUZ_E" 25 | resolution: "1080" 26 | - name: "中视新闻台" 27 | url: "https://www.youtube.com/watch?v=TCnaIE_SAtM" 28 | resolution: "1080" 29 | - name: "台视新闻台" 30 | url: "https://www.youtube.com/watch?v=xL0ch83RAK8" 31 | resolution: "1080" 32 | - name: "寰宇新闻" 33 | url: "https://www.youtube.com/watch?v=B7Zp3d6xXWw" 34 | resolution: "1080" 35 | - name: "鏡新聞" 36 | url: "https://www.youtube.com/watch?v=5n0y6b0Q25o" 37 | resolution: "1080" 38 | - name: "Winter Coffee Shop" 39 | url: "https://www.youtube.com/watch?v=dYOePmu1wy8" 40 | resolution: "1080" 41 | - name: "Shinjuku FM" 42 | url: "https://www.youtube.com/watch?v=2CDRu6-Vgfo" 43 | resolution: "1080" 44 | - name: "Rainy Night & Relaxing Jazz" 45 | url: "https://www.youtube.com/watch?v=kwdUyHz_Exg" 46 | resolution: "1080" 47 | -------------------------------------------------------------------------------- /youtube/youtube.go: -------------------------------------------------------------------------------- 1 | package youtube 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | m "itv/merge" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | "strconv" 13 | "sync" 14 | "time" 15 | 16 | "github.com/dlclark/regexp2" 17 | "github.com/etherlabsio/go-m3u8/m3u8" 18 | "gopkg.in/yaml.v2" 19 | ) 20 | 21 | var conf []m.Channnel 22 | 23 | func Run() { 24 | data, err := ioutil.ReadFile("youtube.yaml") 25 | if err != nil { 26 | fmt.Printf("err: %v\n", err) 27 | return 28 | } 29 | err = yaml.Unmarshal(data, &conf) 30 | if err != nil { 31 | return 32 | } 33 | 34 | wg := sync.WaitGroup{} 35 | playlist := m.Playlist{ 36 | Tracks: []m.Track{}, 37 | } 38 | 39 | wg.Add(len(conf)) 40 | for _, c := range conf { 41 | go func(c m.Channnel) { 42 | defer wg.Done() 43 | part, _ := url.Parse(c.Url) 44 | stream := getLiveUrl(c.Url, c.Resolution) 45 | if len(stream) < 1 { 46 | return 47 | } 48 | 49 | playlist.Tracks = append(playlist.Tracks, m.Track{ 50 | Name: c.Name, 51 | Length: -1, 52 | URI: stream, 53 | Tags: []m.Tag{{ 54 | Name: "tvg-logo", 55 | Value: fmt.Sprintf("https://i.ytimg.com/vi/%s/hq720_live.jpg", part.Query().Get("v")), 56 | }}, 57 | }) 58 | }(c) 59 | } 60 | wg.Wait() 61 | 62 | reader, err := m.Marshall(playlist) 63 | if err != nil { 64 | fmt.Println(err) 65 | return 66 | } 67 | b := reader.(*bytes.Buffer) 68 | _ = ioutil.WriteFile("./youtube.m3u", b.Bytes(), os.ModePerm) 69 | if err != nil { 70 | fmt.Println(err) 71 | } 72 | } 73 | 74 | func getLiveUrl(url, resolution string) string { 75 | client := &http.Client{ 76 | Timeout: time.Second * 5, 77 | CheckRedirect: func(req *http.Request, via []*http.Request) error { 78 | return http.ErrUseLastResponse 79 | }, 80 | } 81 | r, _ := http.NewRequest("GET", url, nil) 82 | r.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") 83 | resp, err := client.Do(r) 84 | if err != nil { 85 | return "" 86 | } 87 | 88 | body, _ := io.ReadAll(resp.Body) 89 | str := string(body) 90 | defer resp.Body.Close() 91 | 92 | reg := regexp2.MustCompile(`(?<=hlsManifestUrl":").*\.m3u8`, regexp2.RE2) 93 | res, _ := reg.FindStringMatch(str) 94 | if res == nil { 95 | return "" 96 | } 97 | stream := res.Captures[0].String() 98 | quality := getResolution(stream, resolution) 99 | if quality != nil { 100 | return *quality 101 | } 102 | return stream 103 | } 104 | 105 | func getResolution(liveurl string, quality string) *string { 106 | client := &http.Client{Timeout: time.Second * 5} 107 | r, _ := http.NewRequest("GET", liveurl, nil) 108 | r.Header.Add("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") 109 | resp, err := client.Do(r) 110 | if err != nil { 111 | return nil 112 | } 113 | playlist, err := m3u8.Read(resp.Body) 114 | defer resp.Body.Close() 115 | if err != nil { 116 | return nil 117 | } 118 | 119 | size := playlist.ItemSize() 120 | 121 | if size < 1 { 122 | return nil 123 | } 124 | 125 | mapping := map[string]string{} 126 | for _, item := range playlist.Playlists() { 127 | mapping[strconv.Itoa(item.Resolution.Height)] = item.URI 128 | } 129 | 130 | if stream, ok := mapping[quality]; ok { 131 | return &stream 132 | } 133 | 134 | stream := &playlist.Playlists()[size-1].URI 135 | return stream 136 | } 137 | --------------------------------------------------------------------------------