Merge pull request from Guovin/master

Update: v1.1.4
This commit is contained in:
Govin 2024-05-15 17:08:01 +08:00 committed by GitHub
commit 25a7e021f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 281 additions and 106 deletions

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

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

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

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

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

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