From fa26e010295e9e1a33a55b07b9fb83585ab2298c Mon Sep 17 00:00:00 2001 From: "guorong.zheng" <360996299@qq.com> Date: Mon, 4 Mar 2024 11:21:55 +0800 Subject: [PATCH] feat:Sort based on the weights of response time and resolution --- README-EN.md | 66 ++++++++++++++++++++++---------- README.md | 52 ++++++++++++++++++------- config.py | 3 +- main.py | 106 +++++++++++++++++++++++++-------------------------- 4 files changed, 138 insertions(+), 89 deletions(-) diff --git a/README-EN.md b/README-EN.md index c031b34882..23ac6c0b3f 100644 --- a/README-EN.md +++ b/README-EN.md @@ -1,41 +1,67 @@ -Automatically obtain and update the latest live broadcast interface link according to the basic template live broadcast source interface file +# TVBox Custom Channel Menu and Live Source Interface Auto-update + +Customize channel menus, automatically fetch and update the latest live source interfaces based on the template file, and generate usable channel interface files. [中文](./README.md) | English ## Features -- Interface verification, filter invalid interfaces, sorting rules: date, speed, resolution -- Scheduled execution, updated every 12 hours -- You can set key focus channels and configure the number of pages to get separately -- Pagination results acquisition (configurable number of pages, total number of interfaces) +- Customize templates to generate the channel categories and order you want +- Interface validation to filter out invalid interfaces +- Comprehensive sorting based on response time and resolution +- Scheduled execution, updates every 12 hours +- Set up key focus channels and configure the number of pages fetched separately +- Pagination results fetching (configurable number of pages and total number of interfaces) ## How to Use -1. Fork this project, turn on Action workflow read and write permissions, Settings → Actions → General → Workflow permissions → Read and write permissions → Save -2. Modify the demo.txt template file, subsequent updates will be based on the content of this file -3. Modify config.py (optional): +1. Fork this project and enable read and write permissions for the Action workflow: + + - Settings → Actions → General → Workflow permissions → Read and write permissions → Save + +2. Modify the demo.txt template file to the channel categories and order you want. Subsequent updates will be based on the content of this file. +3. Modify the configuration (optional): + + #### config.py: + + - source_file: Template file, default value: demo.txt + - final_file: Generated file, default value: result.txt + - favorite_list: List of focus channel names + - favorite_page_num: Number of pages fetched for focus channels, default value: 5 + - default_page_num: Number of pages fetched for regular channels, default value: 3 + - urls_limit: Number of interfaces, default value: 15 + - response_time_weight: Response time weight value, default value: 0.5 + - resolution_weight: Resolution weight value, default value: 0.5 + + #### .github/workflows/main.yml: + + - If you want to modify the update frequency (default is 12 hours), you can modify the on:schedule:- cron field + +4. result.txt is the updated live source interface file, source.json is the data source file (currently only for sharing) +5. It is recommended to access the live source and data source files via proxy (xxx is your repository path): + - https://mirror.ghproxy.com/raw.githubusercontent.com/xxx/result.txt + - https://mirror.ghproxy.com/raw.githubusercontent.com/xxx/source.json + +## Update Log -- source_file: Template file, default value: demo.txt -- final_file: Generated file, default value: result.txt -- favorite_list: List of focus channel names -- favorite_page_num: Number of pages to get for focus channels, default value: 5 -- default_page_num: Number of pages to get for regular channels, default value: 3 -- urls_limit: Number of interfaces, default value: 15 -- filter_invalid_url: Whether to filter invalid interfaces, default: True +### 2024/3/4 -4. result.txt is the updated live broadcast interface file, source.json is the data source file +- Added configuration items: response time and resolution weight values +- Removed configuration items: whether to filter invalid interfaces, always perform filtering +- Removed sorting by date, using response time and resolution as sorting rules +- Updated README: added modification update frequency, file proxy description, update log ## Disclaimer -This project is for providing resources for programming learning and research. The data collected in the project comes from the Internet, and the developer does not guarantee the accuracy, completeness, or reliability of the data. +This project is provided for programming learning and research resources. The data collected in the project comes from the network, and the developer does not make any guarantees about the accuracy, completeness, or reliability of the data. -The developer is not responsible for any direct or indirect losses that may be caused by the use of these codes or data. Users should judge the legality and risk of their use on their own. +The developer is not responsible for any direct or indirect losses that may be caused by the use of these codes or data. Users should judge the legality and risk of their use by themselves. -The code and data of this project are for learning and research purposes only and must not be used for any commercial purposes. Anyone or organization using it should comply with relevant laws and regulations, respect and protect the rights and interests of developers. +The code and data of this project are only for learning and research use, and must not be used for any commercial purposes. Anyone or organization should abide by relevant laws and regulations when using it, respect and protect the rights and interests of developers. If you use the code or data of this project, it means that you have understood and agreed to this disclaimer. If you do not agree with this disclaimer, you should stop using the code and data of this project immediately. -In addition, the code and data of this project may be updated from time to time, but there is no guarantee of the timeliness and accuracy of the updates, nor the stability and functionality of the code. +In addition, the code and data of this project may be updated irregularly, but there is no guarantee of the timeliness and accuracy of the update, nor the stability and functionality of the code. In any case, the developer and any contributor do not assume any responsibility for any damage or other liability caused by the use or inability to use the code or data of this project. diff --git a/README.md b/README.md index f00ec39edb..171549a8c6 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,55 @@ -# TVBox 电视直播源接口自动更新 +# TVBox 电视频道菜单自定义与直播源接口自动更新 -根据基础模板直播源接口文件自动获取并更新最新直播源接口链接 +自定义频道菜单,根据模板文件的直播源接口,自动获取并更新最新的直播源接口,生成可用的频道接口文件 [English](./README-EN.md) | 中文 ## 特点 -- 接口效验,过滤无效接口,排序规则:日期、速度、分辨率 +- 自定义模板,生成您想要的频道分类与频道顺序 +- 接口验效,过滤无效接口 +- 按响应时间、分辨率综合权衡排序 - 定时执行,每隔 12 小时执行更新一次 - 可设置重点关注频道,单独配置获取分页的数量 - 分页结果获取(可配置页数、总接口数量) ## 使用方法 -1. Fork 此项目,开启 Action 工作流可读写权限,Settings → Actions → General → Workflow permissions → Read and write permissions → Save -2. 修改 demo.txt 模板文件,后续更新根据此文件内容进行更新 -3. 修改配置 config.py(可选): +1. Fork 此项目,开启 Action 工作流可读写权限: -- source_file:模板文件,默认值:demo.txt -- final_file:生成文件,默认值:result.txt -- favorite_list:关注频道名称列表 -- favorite_page_num:关注频道获取分页数量,默认值:5 -- default_page_num:常规频道获取分页数量,默认值:3 -- urls_limit:接口数量,默认值:15 -- filter_invalid_url:是否过滤无效接口,默认值:True + - Settings → Actions → General → Workflow permissions → Read and write permissions → Save -4. result.txt 为更新后的直播源接口文件,source.json 为数据源文件 +2. 修改 demo.txt 模板文件,修改成您想要的频道分类与频道顺序,后续更新根据此文件内容进行更新。 +3. 修改配置(可选): + + #### config.py: + + - source_file:模板文件,默认值:demo.txt + - final_file:生成文件,默认值:result.txt + - favorite_list:关注频道名称列表 + - favorite_page_num:关注频道获取分页数量,默认值:5 + - default_page_num:常规频道获取分页数量,默认值:3 + - urls_limit:接口数量,默认值:15 + - response_time_weight:响应时间权重值,默认值:0.5 + - resolution_weight:分辨率权重值,默认值:0.5 + + #### .github/workflows/main.yml: + + - 如果您想修改更新频率(默认 12 小时),可修改 on:schedule:- cron 字段 + +4. result.txt 为更新后的直播源接口文件,source.json 为数据源文件(目前仅作分享使用) +5. 建议采用代理的方式访问直播源与数据源文件(xxx 为您的仓库路径): + - https://mirror.ghproxy.com/raw.githubusercontent.com/xxx/result.txt + - https://mirror.ghproxy.com/raw.githubusercontent.com/xxx/source.json + +## 更新日志 + +### 2024/3/4 + +- 增加配置项:响应时间与分辨率权重值 +- 移除配置项:是否过滤无效接口,始终执行过滤 +- 移除按日期排序,采用响应时间与分辨率作为排序规则 +- 更新 README:增加修改更新频率、文件代理说明、更新日志 ## 免责声明 diff --git a/config.py b/config.py index 68cac6ee05..24e364e554 100644 --- a/config.py +++ b/config.py @@ -18,4 +18,5 @@ favorite_page_num = 5 default_page_num = 3 urls_limit = 15 -filter_invalid_url = True +response_time_weight = 0.5 +resolution_weight = 0.5 diff --git a/main.py b/main.py index dcb9847925..b81f28733a 100644 --- a/main.py +++ b/main.py @@ -10,7 +10,6 @@ import aiohttp import asyncio from bs4 import BeautifulSoup -from datetime import datetime import re @@ -79,24 +78,52 @@ async def getSpeed(self, url): else: return url, float("inf") - async def compareSpeed(self, infoList): + async def compareSpeedAndResolution(self, infoList): response_times = await asyncio.gather( - *(self.getSpeed(url) for url, _, _ in infoList) + *(self.getSpeed(url) for url, _ in infoList) ) - # Filter out invalid links if filter_invalid_url is True - if config.filter_invalid_url: - valid_responses = [ - (info, rt) - for info, rt in zip(infoList, response_times) - if rt[1] != float("inf") - ] - else: - valid_responses = list(zip(infoList, response_times)) - sorted_res = sorted(valid_responses, key=lambda x: x[1][1]) - infoList_new = [ - (url, date, resolution) for (url, date, resolution), _ in sorted_res + valid_responses = [ + (info, rt) + for info, rt in zip(infoList, response_times) + if rt[1] != float("inf") ] - return infoList_new + + def extract_resolution(resolution_str): + numbers = re.findall(r"\d+x\d+", resolution_str) + if numbers: + width, height = map(int, numbers[0].split("x")) + return width * height + else: + return 0 + + default_response_time_weight = 0.5 + default_resolution_weight = 0.5 + response_time_weight = getattr( + config, "response_time_weight", default_response_time_weight + ) + resolution_weight = getattr( + config, "resolution_weight", default_resolution_weight + ) + # Check if weights are valid + if not ( + 0 <= response_time_weight <= 1 + and 0 <= resolution_weight <= 1 + and response_time_weight + resolution_weight == 1 + ): + response_time_weight = default_response_time_weight + resolution_weight = default_resolution_weight + + def combined_key(item): + (_, resolution), response_time = item + resolution_value = extract_resolution(resolution) if resolution else 0 + return ( + -(response_time_weight * response_time[1]) + + resolution_weight * resolution_value + ) + + sorted_res = sorted(valid_responses, key=combined_key) + urls = [url for (url, _), _ in sorted_res] + return urls def removeFile(self): if os.path.exists(config.final_file): @@ -148,51 +175,22 @@ async def visitPage(self, channelItems): info_div = ( m3u8_div.find_next_sibling("div") if m3u8_div else None ) - date = resolution = None + resolution = None if info_div: info_text = info_div.text.strip() - date, resolution = ( - ( - info_text.partition(" ")[0] - if info_text.partition(" ")[0] - else None - ), - ( - info_text.partition(" ")[2].partition("•")[2] - if info_text.partition(" ")[2].partition("•")[2] - else None - ), + resolution = ( + info_text.partition(" ")[2].partition("•")[2] + if info_text.partition(" ")[2].partition("•")[2] + else None ) - infoList.append((url, date, resolution)) + infoList.append((url, resolution)) except Exception as e: print(f"Error on page {page}: {e}") continue try: - infoList.sort( - key=lambda x: ( - x[1] is not None, - datetime.strptime(x[1], "%m-%d-%Y") if x[1] else None, - ), - reverse=True, - ) # Sort by date - infoList = await self.compareSpeed(infoList) # Sort by speed - - def extract_resolution(resolution_str): - numbers = re.findall(r"\d+x\d+", resolution_str) - if numbers: - width, height = map(int, numbers[0].split("x")) - return width * height - else: - return 0 - - infoList.sort( - key=lambda x: ( - x[2] is not None, - extract_resolution(x[2]) if x[2] else 0, - ), - reverse=True, - ) # Sort by resolution - urls = list(dict.fromkeys(url for url, _, _ in infoList)) + urls = list( + dict.fromkeys(await self.compareSpeedAndResolution(infoList)) + ) # Sort by speed and resolution channelUrls[name] = (urls or channelObj[name])[: config.urls_limit] except Exception as e: print(f"Error on sorting: {e}")