commit
25a7e021f4
@ -1,5 +1,14 @@
|
||||
# 更新日志(Changelog)
|
||||
|
||||
## v1.1.4
|
||||
|
||||
### 2024/5/15
|
||||
|
||||
- 新增组播源功能(Added multicast source feature)
|
||||
- 新增控制开关,控制多种获取模式的启用状态(Added control switch to manage the activation status of various acquisition modes)
|
||||
- 新增严格匹配(Added strict matching)
|
||||
- 优化文件读取,提升模板初始化速度(Optimized file reading to improve initialization speed based on templates)
|
||||
|
||||
## v1.1.3
|
||||
|
||||
### 2024/5/8
|
||||
|
40
README-EN.md
40
README-EN.md
@ -1,4 +1,4 @@
|
||||
# Customization of TVBox TV Channel Menu and Automatic Verification and Update of Live Source Interfaces
|
||||
# TVBox Custom TV Channel Menu and Live Source Interface Automatic Verification and Update Tool
|
||||
|
||||
Customize channel menus and automatically obtain and update the latest live source interfaces based on template files, verify, and generate usable channel interface files.
|
||||
|
||||
@ -16,25 +16,31 @@ Customize channel menus and automatically obtain and update the latest live sour
|
||||
- Ensure update timeliness, configure to retrieve interfaces updated within a recent time range
|
||||
- Can filter ipv4, ipv6 interfaces
|
||||
- Blacklist feature: Interface domain and keywords
|
||||
- Customize the source of interface acquisition
|
||||
- Supports multiple ways of obtaining sources: online search, multicast source, subscription source
|
||||
|
||||
## Config
|
||||
|
||||
| Configuration Item | Default Value | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| source_file | "demo.txt" | Template file name |
|
||||
| final_file | "result.txt" | Generated file name |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | List of favorite channel names (used only to distinguish from regular channels, custom page retrieval quantity) |
|
||||
| favorite_page_num | 5 | Page retrieval quantity for favorite channels |
|
||||
| default_page_num | 3 | Page retrieval quantity for regular channels |
|
||||
| urls_limit | 10 | Number of interfaces per channel |
|
||||
| response_time_weight | 0.5 | Response time weight value (the sum of all weight values should be 1) |
|
||||
| resolution_weight | 0.5 | Resolution weight value (the sum of all weight values should be 1) |
|
||||
| recent_days | 30 | Retrieve interfaces updated within a recent time range (in days), reducing appropriately can avoid matching issues |
|
||||
| ipv_type | "ipv4" | The type of interface in the generated result, optional values: "ipv4", "ipv6", "all" |
|
||||
| domain_blacklist | ["epg.pw"] | Interface domain blacklist, used to filter out interfaces with low-quality, ad-inclusive domains |
|
||||
| url_keywords_blacklist | [] | Interface keyword blacklist, used to filter out interfaces containing specific characters |
|
||||
| extend_base_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | The source of interface acquisition, currently only compatible with specific content formats and fuzzy matching of some channel names |
|
||||
| Configuration Item | Default Value | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| source_file | "demo.txt" | Template file name |
|
||||
| final_file | "result.txt" | Generated file name |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | List of favorite channel names (used only to distinguish from regular channels, custom page retrieval quantity) |
|
||||
| open_online_search | True | Enable online search source feature |
|
||||
| favorite_page_num | 5 | Page retrieval quantity for favorite channels |
|
||||
| default_page_num | 3 | Page retrieval quantity for regular channels |
|
||||
| urls_limit | 10 | Number of interfaces per channel |
|
||||
| open_sort | True | Enable the sorting test function, it is recommended to turn it off if you are not using online search |
|
||||
| response_time_weight | 0.5 | Response time weight value (the sum of all weight values should be 1) |
|
||||
| resolution_weight | 0.5 | Resolution weight value (the sum of all weight values should be 1) |
|
||||
| recent_days | 30 | Retrieve interfaces updated within a recent time range (in days), reducing appropriately can avoid matching issues |
|
||||
| ipv_type | "ipv4" | The type of interface in the generated result, optional values: "ipv4", "ipv6", "all" |
|
||||
| domain_blacklist | ["epg.pw"] | Interface domain blacklist, used to filter out interfaces with low-quality, ad-inclusive domains |
|
||||
| url_keywords_blacklist | [] | Interface keyword blacklist, used to filter out interfaces containing specific characters |
|
||||
| open_subscribe | True | Enable subscription source feature |
|
||||
| subscribe_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | Subscription source list |
|
||||
| open_multicast | True | Enable multicast source function |
|
||||
| region_list | ["广东"] | Multicast source region list, for more regions please see the fofa_map.py file |
|
||||
| strict_match | False | Strict matching, when enabled, can minimize the issue of channel interface mismatch to the greatest extent, but at the same time, some fuzzy matching results may be lost |
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
40
README.md
40
README.md
@ -1,4 +1,4 @@
|
||||
# TVBox 电视频道菜单自定义与直播源接口自动校验与更新
|
||||
# TVBox 电视频道菜单自定义与直播源接口自动校验与更新工具
|
||||
|
||||
自定义频道菜单,根据模板文件的直播源接口,自动获取并更新最新的直播源接口,校验并生成可用的频道接口文件
|
||||
|
||||
@ -16,25 +16,31 @@
|
||||
- 保证更新时效性,配置获取最近时间范围内更新的接口
|
||||
- 可过滤 ipv4、ipv6 接口
|
||||
- 黑名单功能:接口域名与关键字
|
||||
- 自定义接口获取源
|
||||
- 支持多种获取源方式:线上检索、组播源、订阅源
|
||||
|
||||
## 配置
|
||||
|
||||
| 配置项 | 默认值 | 描述 |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| source_file | "demo.txt" | 模板文件名称 |
|
||||
| final_file | "result.txt" | 生成文件名称 |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | 关注频道名称列表(仅用于与常规频道区分,自定义获取分页数量) |
|
||||
| favorite_page_num | 5 | 关注频道获取分页数量 |
|
||||
| default_page_num | 3 | 常规频道获取分页数量 |
|
||||
| urls_limit | 10 | 单个频道接口数量 |
|
||||
| response_time_weight | 0.5 | 响应时间权重值(所有权重值总和应为 1) |
|
||||
| resolution_weight | 0.5 | 分辨率权重值 (所有权重值总和应为 1) |
|
||||
| recent_days | 30 | 获取最近时间范围内更新的接口(单位天),适当减小可避免出现匹配问题 |
|
||||
| ipv_type | "ipv4" | 生成结果中接口的类型,可选值:"ipv4"、"ipv6"、"all" |
|
||||
| domain_blacklist | ["epg.pw"] | 接口域名黑名单,用于过滤低质量含广告类域名的接口 |
|
||||
| url_keywords_blacklist | [] | 接口关键字黑名单,用于过滤含特定字符的接口 |
|
||||
| extend_base_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | 接口获取源,目前仅兼容特定内容格式与部分频道名称的模糊匹配 |
|
||||
| 配置项 | 默认值 | 描述 |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| source_file | "demo.txt" | 模板文件名称 |
|
||||
| final_file | "result.txt" | 生成文件名称 |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | 关注频道名称列表(仅用于与常规频道区分,自定义获取分页数量) |
|
||||
| open_online_search | True | 开启线上检索源功能 |
|
||||
| favorite_page_num | 5 | 关注频道获取分页数量 |
|
||||
| default_page_num | 3 | 常规频道获取分页数量 |
|
||||
| urls_limit | 10 | 单个频道接口数量 |
|
||||
| open_sort | True | 开启排序测试功能,若没有使用线上检索建议关闭 |
|
||||
| response_time_weight | 0.5 | 响应时间权重值(所有权重值总和应为 1) |
|
||||
| resolution_weight | 0.5 | 分辨率权重值 (所有权重值总和应为 1) |
|
||||
| recent_days | 30 | 获取最近时间范围内更新的接口(单位天),适当减小可避免出现匹配问题 |
|
||||
| ipv_type | "ipv4" | 生成结果中接口的类型,可选值:"ipv4"、"ipv6"、"all" |
|
||||
| domain_blacklist | ["epg.pw"] | 接口域名黑名单,用于过滤低质量含广告类域名的接口 |
|
||||
| url_keywords_blacklist | [] | 接口关键字黑名单,用于过滤含特定字符的接口 |
|
||||
| open_subscribe | True | 开启订阅源功能 |
|
||||
| subscribe_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | 订阅源列表 |
|
||||
| open_multicast | True | 开启组播源功能 |
|
||||
| region_list | ["广东"] | 组播源地区列表,更多地区请见 fofa_map.py 文件 |
|
||||
| strict_match | False | 严格匹配,开启可最大程度减少频道接口不匹配问题,同时会丢失部分模糊匹配结果 |
|
||||
|
||||
## 快速上手
|
||||
|
||||
|
@ -13,17 +13,23 @@ favorite_list = [
|
||||
"湖南卫视",
|
||||
"翡翠台",
|
||||
]
|
||||
open_online_search = True
|
||||
favorite_page_num = 5
|
||||
default_page_num = 3
|
||||
urls_limit = 10
|
||||
open_sort = True
|
||||
response_time_weight = 0.5
|
||||
resolution_weight = 0.5
|
||||
recent_days = 30
|
||||
ipv_type = "ipv4"
|
||||
domain_blacklist = ["epg.pw"]
|
||||
url_keywords_blacklist = []
|
||||
extend_base_urls = [
|
||||
open_subscribe = True
|
||||
subscribe_urls = [
|
||||
"https://m3u.ibert.me/txt/fmml_dv6.txt",
|
||||
"https://m3u.ibert.me/txt/o_cn.txt",
|
||||
"https://m3u.ibert.me/txt/j_iptv.txt",
|
||||
]
|
||||
open_multicast = True
|
||||
region_list = ["广东"]
|
||||
strict_match = False
|
||||
|
@ -57,21 +57,27 @@ Similar to editing the template, modify the running configuration
|
||||
|
||||
Adjust the configuration as needed. Below is the default configuration explanation:
|
||||
|
||||
| Configuration Item | Default Value | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| source_file | "demo.txt" | Template file name |
|
||||
| final_file | "result.txt" | Generated file name |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | List of favorite channel names (used only to distinguish from regular channels, custom page retrieval quantity) |
|
||||
| favorite_page_num | 5 | Page retrieval quantity for favorite channels |
|
||||
| default_page_num | 3 | Page retrieval quantity for regular channels |
|
||||
| urls_limit | 10 | Number of interfaces per channel |
|
||||
| response_time_weight | 0.5 | Response time weight value (the sum of all weight values should be 1) |
|
||||
| resolution_weight | 0.5 | Resolution weight value (the sum of all weight values should be 1) |
|
||||
| recent_days | 30 | Retrieve interfaces updated within a recent time range (in days), reducing appropriately can avoid matching issues |
|
||||
| ipv_type | "ipv4" | The type of interface in the generated result, optional values: "ipv4", "ipv6", "all" |
|
||||
| domain_blacklist | ["epg.pw"] | Interface domain blacklist, used to filter out interfaces with low-quality, ad-inclusive domains |
|
||||
| url_keywords_blacklist | [] | Interface keyword blacklist, used to filter out interfaces containing specific characters |
|
||||
| extend_base_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | The source of interface acquisition, currently only compatible with specific content formats and fuzzy matching of some channel names |
|
||||
| Configuration Item | Default Value | Description |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| source_file | "demo.txt" | Template file name |
|
||||
| final_file | "result.txt" | Generated file name |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | List of favorite channel names (used only to distinguish from regular channels, custom page retrieval quantity) |
|
||||
| open_online_search | True | Enable online search source feature |
|
||||
| favorite_page_num | 5 | Page retrieval quantity for favorite channels |
|
||||
| default_page_num | 3 | Page retrieval quantity for regular channels |
|
||||
| urls_limit | 10 | Number of interfaces per channel |
|
||||
| open_sort | True | Enable the sorting test function, it is recommended to turn it off if you are not using online search |
|
||||
| response_time_weight | 0.5 | Response time weight value (the sum of all weight values should be 1) |
|
||||
| resolution_weight | 0.5 | Resolution weight value (the sum of all weight values should be 1) |
|
||||
| recent_days | 30 | Retrieve interfaces updated within a recent time range (in days), reducing appropriately can avoid matching issues |
|
||||
| ipv_type | "ipv4" | The type of interface in the generated result, optional values: "ipv4", "ipv6", "all" |
|
||||
| domain_blacklist | ["epg.pw"] | Interface domain blacklist, used to filter out interfaces with low-quality, ad-inclusive domains |
|
||||
| url_keywords_blacklist | [] | Interface keyword blacklist, used to filter out interfaces containing specific characters |
|
||||
| open_subscribe | True | Enable subscription source feature |
|
||||
| subscribe_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | Subscription source list |
|
||||
| open_multicast | True | Enable multicast source function |
|
||||
| region_list | ["广东"] | Multicast source region list, for more regions please see the fofa_map.py file |
|
||||
| strict_match | False | Strict matching, when enabled, can minimize the issue of channel interface mismatch to the greatest extent, but at the same time, some fuzzy matching results may be lost |
|
||||
|
||||
## Step 4: Run Updates Locally (Recommended, Stable, Supports a large number of channel updates)
|
||||
|
||||
|
@ -57,20 +57,26 @@
|
||||
|
||||
按照您的需要适当调整配置,以下是默认配置说明
|
||||
| 配置项 | 默认值 | 描述 |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| source_file | "demo.txt" | 模板文件名称 |
|
||||
| final_file | "result.txt" | 生成文件名称 |
|
||||
| favorite_list | ["广东珠江","CCTV-1","CCTV-5","CCTV-5+","CCTV-13","广东体育","广东卫视","大湾区卫视","浙江卫视","湖南卫视","翡翠台"] | 关注频道名称列表(仅用于与常规频道区分,自定义获取分页数量) |
|
||||
| open_online_search | True | 开启线上检索源功能 |
|
||||
| favorite_page_num | 5 | 关注频道获取分页数量 |
|
||||
| default_page_num | 3 | 常规频道获取分页数量 |
|
||||
| urls_limit | 10 | 单个频道接口数量 |
|
||||
| open_sort | True | 开启排序测试功能,若没有使用线上检索建议关闭 |
|
||||
| response_time_weight | 0.5 | 响应时间权重值(所有权重值总和应为 1) |
|
||||
| resolution_weight | 0.5 | 分辨率权重值 (所有权重值总和应为 1) |
|
||||
| recent_days | 30 | 获取最近时间范围内更新的接口(单位天),适当减小可避免出现匹配问题 |
|
||||
| ipv_type | "ipv4" | 生成结果中接口的类型,可选值:"ipv4"、"ipv6"、"all" |
|
||||
| domain_blacklist | ["epg.pw"] | 接口域名黑名单,用于过滤低质量含广告类域名的接口 |
|
||||
| url_keywords_blacklist | [] | 接口关键字黑名单,用于过滤含特定字符的接口 |
|
||||
| extend_base_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | 接口获取源,目前仅兼容特定内容格式与部分频道名称的模糊匹配 |
|
||||
| open_subscribe | True | 开启订阅源功能 |
|
||||
| subscribe_urls | ["https://m3u.ibert.me/txt/fmml_dv6.txt",<br>"https://m3u.ibert.me/txt/o_cn.txt",<br>"https://m3u.ibert.me/txt/j_iptv.txt"] | 订阅源列表 |
|
||||
| open_multicast | True | 开启组播源功能 |
|
||||
| region_list | ["广东"] | 组播源地区列表,更多地区请见 fofa_map.py 文件 |
|
||||
| strict_match | False | 严格匹配,开启可最大程度减少频道接口不匹配问题,同时会丢失部分模糊匹配结果 |
|
||||
|
||||
## 步骤四:本地运行更新(推荐,稳定,支持大量频道更新)
|
||||
|
||||
|
25
fofa_map.py
Normal file
25
fofa_map.py
Normal file
@ -0,0 +1,25 @@
|
||||
region_url = {
|
||||
"广东": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iR3Vhbmdkb25nIg%3D%3D",
|
||||
"北京": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iQmVpamluZyI%3D",
|
||||
"湖南": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSHVuYW4i",
|
||||
"湖北": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSHViZWki",
|
||||
"浙江": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iWmhlamlhbmci",
|
||||
"上海": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iU2hhbmdoYWki",
|
||||
"天津": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iVGlhbmppbiI%3D",
|
||||
"江苏": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSmlhbmdzdSI%3D",
|
||||
"山东": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iU2hhbmRvbmci",
|
||||
"河南": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSGVuYW4i",
|
||||
"河北": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSGViZWki",
|
||||
"山西": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iU2hhbnhpIg%3D%3D",
|
||||
"陕西": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iU2hhYW54aSI%3D",
|
||||
"安徽": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iQW5odWki",
|
||||
"重庆": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iQ2hvbmdxaW5nIg%3D%3D",
|
||||
"福建": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iRnVqaWFuIg%3D%3D",
|
||||
"江西": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSmlhbmd4aSI%3D",
|
||||
"辽宁": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iTGlhb25pbmci",
|
||||
"黑龙江": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSGVpbG9uZ2ppYW5nIg%3D%3D",
|
||||
"吉林": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0i5ZCJ5p6XIg%3D%3D",
|
||||
"四川": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iU2ljaHVhbiI%3D",
|
||||
"云南": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iWXVubmFuIg%3D%3D",
|
||||
"香港": "https://fofa.info/result?qbase64=ImlwdHYvbGl2ZS96aF9jbi5qcyIgJiYgY291bnRyeT0iQ04iICYmIHJlZ2lvbj0iSEsi",
|
||||
}
|
86
main.py
86
main.py
@ -6,7 +6,8 @@ from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium_stealth import stealth
|
||||
|
||||
# from selenium_stealth import stealth
|
||||
import asyncio
|
||||
from bs4 import BeautifulSoup
|
||||
from utils import (
|
||||
@ -20,12 +21,17 @@ from utils import (
|
||||
useAccessibleUrl,
|
||||
getChannelsByExtendBaseUrls,
|
||||
checkUrlByPatterns,
|
||||
getFOFAUrlsFromRegionList,
|
||||
getChannelsByFOFA,
|
||||
mergeObjects,
|
||||
getTotalUrlsFromInfoList,
|
||||
)
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
import os
|
||||
from tqdm import tqdm
|
||||
import re
|
||||
import time
|
||||
|
||||
handler = RotatingFileHandler("result_new.log", encoding="utf-8")
|
||||
logging.basicConfig(
|
||||
@ -46,15 +52,15 @@ class UpdateSource:
|
||||
options.add_argument("blink-settings=imagesEnabled=false")
|
||||
options.add_argument("--log-level=3")
|
||||
driver = webdriver.Chrome(options=options)
|
||||
stealth(
|
||||
driver,
|
||||
languages=["en-US", "en"],
|
||||
vendor="Google Inc.",
|
||||
platform="Win32",
|
||||
webgl_vendor="Intel Inc.",
|
||||
renderer="Intel Iris OpenGL Engine",
|
||||
fix_hairline=True,
|
||||
)
|
||||
# stealth(
|
||||
# driver,
|
||||
# languages=["en-US", "en"],
|
||||
# vendor="Google Inc.",
|
||||
# platform="Win32",
|
||||
# webgl_vendor="Intel Inc.",
|
||||
# renderer="Intel Iris OpenGL Engine",
|
||||
# fix_hairline=True,
|
||||
# )
|
||||
return driver
|
||||
|
||||
def __init__(self):
|
||||
@ -64,10 +70,24 @@ class UpdateSource:
|
||||
channelNames = [
|
||||
name for _, channelObj in channelItems.items() for name in channelObj.keys()
|
||||
]
|
||||
extendResults = await getChannelsByExtendBaseUrls(channelNames)
|
||||
if config.open_subscribe:
|
||||
extendResults = await getChannelsByExtendBaseUrls(channelNames)
|
||||
if config.open_multicast:
|
||||
print(f"Getting channels by FOFA...")
|
||||
fofa_urls = getFOFAUrlsFromRegionList()
|
||||
fofa_results = {}
|
||||
for url in fofa_urls:
|
||||
if url:
|
||||
self.driver.get(url)
|
||||
time.sleep(10)
|
||||
fofa_source = re.sub(
|
||||
r"<!--.*?-->", "", self.driver.page_source, flags=re.DOTALL
|
||||
)
|
||||
fofa_channels = getChannelsByFOFA(fofa_source)
|
||||
fofa_results = mergeObjects(fofa_results, fofa_channels)
|
||||
total_channels = len(channelNames)
|
||||
pbar = tqdm(total=total_channels)
|
||||
pageUrl = await useAccessibleUrl()
|
||||
pageUrl = await useAccessibleUrl() if config.open_online_search else None
|
||||
wait = WebDriverWait(self.driver, 10)
|
||||
for cate, channelObj in channelItems.items():
|
||||
channelUrls = {}
|
||||
@ -76,11 +96,16 @@ class UpdateSource:
|
||||
pbar.set_description(
|
||||
f"Processing {name}, {total_channels - pbar.n} channels remaining"
|
||||
)
|
||||
infoList = []
|
||||
for url, date, resolution in extendResults.get(name, []):
|
||||
if url and checkUrlByPatterns(url):
|
||||
infoList.append((url, None, resolution))
|
||||
if pageUrl:
|
||||
info_list = []
|
||||
if config.open_subscribe:
|
||||
for url, date, resolution in extendResults.get(name, []):
|
||||
if url and checkUrlByPatterns(url):
|
||||
info_list.append((url, None, resolution))
|
||||
if config.open_multicast:
|
||||
for url in fofa_results.get(name, []):
|
||||
if url and checkUrlByPatterns(url):
|
||||
info_list.append((url, None, None))
|
||||
if config.open_online_search and pageUrl:
|
||||
self.driver.get(pageUrl)
|
||||
search_box = wait.until(
|
||||
EC.presence_of_element_located(
|
||||
@ -127,7 +152,7 @@ class UpdateSource:
|
||||
for result in results:
|
||||
url, date, resolution = result
|
||||
if url and checkUrlByPatterns(url):
|
||||
infoList.append((url, date, resolution))
|
||||
info_list.append((url, date, resolution))
|
||||
except Exception as e:
|
||||
print(f"Error on page {page}: {e}")
|
||||
continue
|
||||
@ -136,15 +161,26 @@ class UpdateSource:
|
||||
if not github_actions or (
|
||||
pbar.n <= 200 and github_actions == "true"
|
||||
):
|
||||
sorted_data = await sortUrlsBySpeedAndResolution(infoList)
|
||||
if sorted_data:
|
||||
channelUrls[name] = getTotalUrls(sorted_data)
|
||||
for (url, date, resolution), response_time in sorted_data:
|
||||
logging.info(
|
||||
f"Name: {name}, URL: {url}, Date: {date}, Resolution: {resolution}, Response Time: {response_time}ms"
|
||||
if config.open_sort:
|
||||
sorted_data = await sortUrlsBySpeedAndResolution(info_list)
|
||||
if sorted_data:
|
||||
channelUrls[name] = getTotalUrls(sorted_data)
|
||||
for (
|
||||
url,
|
||||
date,
|
||||
resolution,
|
||||
), response_time in sorted_data:
|
||||
logging.info(
|
||||
f"Name: {name}, URL: {url}, Date: {date}, Resolution: {resolution}, Response Time: {response_time}ms"
|
||||
)
|
||||
else:
|
||||
channelUrls[name] = filterUrlsByPatterns(
|
||||
channelObj[name]
|
||||
)
|
||||
else:
|
||||
channelUrls[name] = filterUrlsByPatterns(channelObj[name])
|
||||
channelUrls[name] = filterUrlsByPatterns(
|
||||
getTotalUrlsFromInfoList(info_list)
|
||||
)
|
||||
else:
|
||||
channelUrls[name] = filterUrlsByPatterns(channelObj[name])
|
||||
except Exception as e:
|
||||
|
131
utils.py
131
utils.py
@ -14,6 +14,8 @@ from urllib.parse import urlparse
|
||||
import requests
|
||||
import re
|
||||
from bs4 import NavigableString
|
||||
import fofa_map
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
def getChannelItems():
|
||||
@ -21,39 +23,33 @@ def getChannelItems():
|
||||
Get the channel items from the source file
|
||||
"""
|
||||
# Open the source file and read all lines.
|
||||
try:
|
||||
user_source_file = (
|
||||
"user_" + config.source_file
|
||||
if os.path.exists("user_" + config.source_file)
|
||||
else getattr(config, "source_file", "demo.txt")
|
||||
)
|
||||
with open(user_source_file, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
user_source_file = (
|
||||
"user_" + config.source_file
|
||||
if os.path.exists("user_" + config.source_file)
|
||||
else getattr(config, "source_file", "demo.txt")
|
||||
)
|
||||
|
||||
# Create a dictionary to store the channels.
|
||||
channels = {}
|
||||
current_category = ""
|
||||
pattern = r"^(.*?),(?!#genre#)(.*?)$"
|
||||
# Create a dictionary to store the channels.
|
||||
channels = defaultdict(lambda: defaultdict(list))
|
||||
current_category = ""
|
||||
pattern = r"^(.*?),(?!#genre#)(.*?)$"
|
||||
|
||||
for line in lines:
|
||||
with open(user_source_file, "r", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if "#genre#" in line:
|
||||
# This is a new channel, create a new key in the dictionary.
|
||||
current_category = line.split(",")[0]
|
||||
channels[current_category] = {}
|
||||
else:
|
||||
# This is a url, add it to the list of urls for the current channel.
|
||||
match = re.search(pattern, line)
|
||||
if match is not None:
|
||||
name = match.group(1).strip()
|
||||
url = match.group(2).strip()
|
||||
if name not in channels[current_category]:
|
||||
channels[current_category][name] = [url]
|
||||
elif url and url not in channels[current_category][name]:
|
||||
if url and url not in channels[current_category][name]:
|
||||
channels[current_category][name].append(url)
|
||||
return channels
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
return channels
|
||||
|
||||
|
||||
async def getChannelsByExtendBaseUrls(channel_names):
|
||||
@ -63,7 +59,7 @@ async def getChannelsByExtendBaseUrls(channel_names):
|
||||
channels = {}
|
||||
pattern = r"^(.*?),(?!#genre#)(.*?)$"
|
||||
sub_pattern = r"_\((.*?)\)|_\[(.*?)\]|频道"
|
||||
for base_url in config.extend_base_urls:
|
||||
for base_url in config.subscribe_urls:
|
||||
try:
|
||||
print(f"Processing extend base url: {base_url}")
|
||||
try:
|
||||
@ -94,7 +90,11 @@ async def getChannelsByExtendBaseUrls(channel_names):
|
||||
link_dict[key] = [value]
|
||||
found_channels = []
|
||||
for channel_name in channel_names:
|
||||
sub_channel_name = re.sub(sub_pattern, "", channel_name).lower()
|
||||
sub_channel_name = (
|
||||
channel_name.lower()
|
||||
if config.strict_match
|
||||
else re.sub(sub_pattern, "", channel_name).lower()
|
||||
)
|
||||
values = link_dict.get(sub_channel_name)
|
||||
if values:
|
||||
if channel_name in channels:
|
||||
@ -175,12 +175,6 @@ def checkNameMatch(name, result_name):
|
||||
result_name,
|
||||
re.IGNORECASE,
|
||||
):
|
||||
print(
|
||||
"Name test match:",
|
||||
name.lower(),
|
||||
result_name.lower(),
|
||||
name.lower() == result_name.lower(),
|
||||
)
|
||||
return name.lower() == result_name.lower()
|
||||
else:
|
||||
return True
|
||||
@ -308,6 +302,16 @@ def getTotalUrls(data):
|
||||
return list(dict.fromkeys(total_urls))
|
||||
|
||||
|
||||
def getTotalUrlsFromInfoList(infoList):
|
||||
"""
|
||||
Get the total urls from info list
|
||||
"""
|
||||
total_urls = [
|
||||
url for url, _, _ in infoList[: min(len(infoList), config.urls_limit)]
|
||||
]
|
||||
return list(dict.fromkeys(total_urls))
|
||||
|
||||
|
||||
def is_ipv6(url):
|
||||
"""
|
||||
Check if the url is ipv6
|
||||
@ -387,3 +391,74 @@ async def useAccessibleUrl():
|
||||
return baseUrl1
|
||||
else:
|
||||
return baseUrl2
|
||||
|
||||
|
||||
def getFOFAUrlsFromRegionList():
|
||||
"""
|
||||
Get the FOFA url from region
|
||||
"""
|
||||
region_list = getattr(config, "region_list", [])
|
||||
urls = []
|
||||
for region in region_list:
|
||||
region_url = getattr(fofa_map, "region_url")
|
||||
if region in region_url:
|
||||
urls.append(region_url[region])
|
||||
return urls
|
||||
|
||||
|
||||
def getChannelsByFOFA(source):
|
||||
"""
|
||||
Get the channel by FOFA
|
||||
"""
|
||||
urls = set(re.findall(r"https?://[\w\.-]+:\d+", source))
|
||||
channels = {}
|
||||
for url in urls:
|
||||
try:
|
||||
response = requests.get(url + "/iptv/live/1000.json?key=txiptv", timeout=2)
|
||||
try:
|
||||
json_data = response.json()
|
||||
if json_data["code"] == 0:
|
||||
try:
|
||||
for item in json_data["data"]:
|
||||
if isinstance(item, dict):
|
||||
item_name = item.get("name").strip()
|
||||
item_url = item.get("url").strip()
|
||||
if item_name and item_url:
|
||||
total_url = url + item_url
|
||||
if item_name not in channels:
|
||||
channels[item_name] = [total_url]
|
||||
else:
|
||||
channels[item_name].append(total_url)
|
||||
except Exception as e:
|
||||
# print(f"Error on fofa: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
# print(f"{url}: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
# print(f"{url}: {e}")
|
||||
continue
|
||||
return channels
|
||||
|
||||
|
||||
def mergeObjects(*objects):
|
||||
"""
|
||||
Merge objects
|
||||
"""
|
||||
merged_dict = {}
|
||||
for obj in objects:
|
||||
if not isinstance(obj, dict):
|
||||
raise TypeError("All input objects must be dictionaries")
|
||||
for key, value in obj.items():
|
||||
if key not in merged_dict:
|
||||
merged_dict[key] = set()
|
||||
if isinstance(value, set):
|
||||
merged_dict[key].update(value)
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
merged_dict[key].add(item)
|
||||
else:
|
||||
merged_dict[key].add(value)
|
||||
for key, value in merged_dict.items():
|
||||
merged_dict[key] = list(value)
|
||||
return merged_dict
|
||||
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"version": "1.1.3"
|
||||
"version": "1.1.4"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user