当前位置:首页>python>Python网络电视播放器 v1.0

Python网络电视播放器 v1.0

  • 2026-07-02 19:19:05
Python网络电视播放器 v1.0

网络电视播放器 v1.0

基于 PyQt5 + VLC 的桌面网络电视播放器,左侧频道列表切换,右侧实时播放,支持音量调节、截图、全屏、收藏、自动切换备用源。

作者信息

项目
内容
作者
杨少平
公众号
Python学在坚持
微信
ysp2338084
版本
v1.0

运行方式

# 1. 安装 VLC 播放器(必须)#    下载地址: https://www.videolan.org/#    Windows 安装 64 位版本# 2. 安装 Python 依赖pip install PyQt5 python-vlc# 3. 启动python ysp/code/tv_player.py

快捷键

快捷键
功能
Space
播放/暂停
F11
全屏切换
ESC
退出全屏
Ctrl+S
截图

界面布局

+------------------+-----------------------------------------------+|  频道列表         |                                               |+------------------+                                               ||  搜索频道...      |                                               |+------------------+                                               ||  * 收藏频道       |                                               ||    CCTV-1 综合    |            视频播放区域                        ||  > 央视频道       |           (VLC 渲染)                          ||    CCTV-1 综合    |                                               ||    CCTV-2 财经    |                                               ||    CCTV-3 综艺    |                                               ||    ...            |                                               ||  > 卫视频道       |                                               ||    湖南卫视       |                                               ||    浙江卫视       |                                               ||    ...            |                                               ||  > 地方/其他      |-----------------------------------------------+|  共 20 个频道     |  [播放] [停止] 音量[====] 截图 全屏  CCTV-1    |+------------------+-----------------------------------------------+

功能说明

频道列表(左侧)

  • 20 个预置频道,分 3 组:央视(10)、卫视(8)、地方/其他(2)
  • 搜索框实时过滤频道名
  • 单击频道立即切换播放
  • 右键菜单收藏/取消收藏,收藏频道置顶显示
  • 当前播放频道高亮(红色选中)

视频播放(右侧)

  • 基于 VLC 引擎,支持 M3U8/HTTP/RTSP 全协议
  • 自动缓冲,网络缓存 1000ms
  • 双击全屏,ESC 退出

自动切换备用源

  • 每个频道可配置多个备用 URL
  • 播放失败自动切换下一个源
  • 所有源都失败时显示"所有源均不可用"
  • 状态栏实时显示:加载中/缓冲中/播放中/错误

播放控制(底部)

  • 播放/暂停按钮(圆形红色)
  • 停止按钮
  • 音量滑块(0~100%)+ 静音切换
  • 截图按钮:保存到 screenshots/ 目录,文件名含频道名+时间戳
  • 全屏按钮

数据持久化

  • 记住上次观看的频道
  • 记住收藏列表
  • 记住窗口大小
  • 配置文件:.tv_config.json

预置频道列表

央视频道(10个)

频道
源地址
CCTV-1 综合
http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8
CCTV-2 财经
http://ivi.bupt.edu.cn/hls/cctv2hd.m3u8
CCTV-3 综艺
http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8
CCTV-4 中文国际
http://ivi.bupt.edu.cn/hls/cctv4hd.m3u8
CCTV-5 体育
http://ivi.bupt.edu.cn/hls/cctv5hd.m3u8
CCTV-6 电影
http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8
CCTV-7 国防军事
http://ivi.bupt.edu.cn/hls/cctv7hd.m3u8
CCTV-8 电视剧
http://ivi.bupt.edu.cn/hls/cctv8hd.m3u8
CCTV-9 纪录
http://ivi.bupt.edu.cn/hls/cctv9hd.m3u8
CCTV-10 科教
http://ivi.bupt.edu.cn/hls/cctv10hd.m3u8

卫视频道(8个)

频道
源地址
湖南卫视
http://219.151.31.38/.../hnwshd/4000000/mnf.m3u8
江苏卫视
http://121.24.98.226:8090/hls/38/index.m3u8
浙江卫视
http://121.24.98.226:8090/hls/39/index.m3u8
东方卫视
http://121.24.98.226:8090/hls/40/index.m3u8
北京卫视
http://ivi.bupt.edu.cn/hls/btv1hd.m3u8
广东卫视
http://tv.gdtv.ah.cn/live/01.m3u8
湖北卫视
http://124.160.184.108/.../3bfabc1fe16a4282b50ea095928c1f60.m3u8
四川卫视
rtsp://218.6.174.207/sctv1

地方/其他(2个)

频道
源地址
山东齐鲁频道
http://121.24.98.226:8090/hls/121/index.m3u8
凤凰卫视中文台
http://121.24.98.226:8090/hls/168/index.m3u8

关键代码解析

关键代码一:VLC 播放器封装与自动切换备用源

classVLCPlayer:defplay(self, urls, name=""):"""播放频道,urls 为备用源列表"""        self.current_urls = urls      # 保存所有备用源        self.current_url_idx = 0# 当前使用第几个源        self._play_url(urls[0])       # 先尝试第一个deftry_next_source(self):"""当前源失败,自动切换下一个"""        self.current_url_idx += 1if self.current_url_idx < len(self.current_urls):            self._play_url(self.current_urls[self.current_url_idx])returnTrue# 还有备用源returnFalse# 所有源都试完了def_play_url(self, url):        media = self.instance.media_new(url)        media.add_option(":network-caching=1000")  # 1秒网络缓存        self.player.set_media(media)        self.player.play()

设计思路:每个频道配置一个 URL 列表而非单个地址。播放时从第一个开始,主窗口的定时器每 3 秒检测播放状态,连续 2 次检测到 Error 状态就调用 try_next_source() 切换到下一个备用源,直到所有源都尝试完毕。

应用场景:公开 IPTV 源不稳定,同一频道配置 2-3 个不同 CDN 节点的地址,一个挂了自动切另一个,用户无感知。


关键代码二:频道列表分组 + 搜索过滤 + 收藏置顶

def_populate_channels(self):# 1. 收藏分组置顶if self.favorites:        fav_group = QTreeWidgetItem(["收藏频道"])for group, channels in CHANNELS.items():for ch in channels:if ch["name"in self.favorites:                    item = QTreeWidgetItem(fav_group, [ch["name"]])                    item.setData(0, Qt.UserRole, ch["urls"])# 2. 按分组填充所有频道for group, channels in CHANNELS.items():        group_item = QTreeWidgetItem([group])for ch in channels:            item = QTreeWidgetItem(group_item, [ch["name"]])            item.setData(0, Qt.UserRole, ch["urls"])      # URL列表存在UserRole            item.setData(0, Qt.UserRole+1, ch["name"])     # 频道名存在UserRole+1def_filter_channels(self, keyword):# 遍历所有分组和子项,隐藏不匹配的for group in all_groups:        visible = 0for child in group.children:            match = keyword in child.text().lower()            child.setHidden(not match)if match: visible += 1        group.setHidden(visible == 0)

设计思路:QTreeWidget 天然支持分组折叠。频道的 URL 列表和名称通过 setData(Qt.UserRole) 存储在 item 上,点击时直接取出传给 VLC 播放。搜索过滤只操作 setHidden(),不重建树,性能好。收藏列表持久化到 JSON 文件。


关键代码三:播放状态监控与自动重连

def_start_monitor(self):    self.monitor_timer = QTimer()    self.monitor_timer.timeout.connect(self._check_state)    self.monitor_timer.start(3000)  # 每3秒检查一次    self._error_count = 0def_check_state(self):    state = self.vlc_player.get_state()if state == Playing:        self.lbl_state.setText("播放中")        self._error_count = 0# 正常播放,重置错误计数elif state == Error:        self._error_count += 1if self._error_count >= 2:      # 连续2次错误才切换(避免误判)if self.vlc_player.try_next_source():                self.lbl_state.setText("切换备用源...")                self._error_count = 0else:                self.lbl_state.setText("所有源均不可用")elif state in (Opening, Buffering):        self.lbl_state.setText("加载中...")

设计思路:QTimer 每 3 秒轮询 VLC 播放状态。用 _error_count 做防抖,避免网络波动导致的瞬间错误触发切源。连续 2 次(6秒)检测到 Error 才认定当前源不可用。状态栏用不同颜色区分:绿色=播放中,橙色=加载/切源,红色=全部失败。

代码架构

tv_player.py+-- CHANNELS (dict)        # 预置频道源(分组 -> 频道列表 -> 多备用URL)+-- CONFIG_FILE            # 配置文件路径+-- VLCPlayer              # VLC 播放器封装|   +-- play(urls)         # 播放(传入备用源列表)|   +-- try_next_source()  # 自动切换备用源|   +-- stop/pause         # 停止/暂停|   +-- set_volume         # 音量控制|   +-- snapshot           # 截图+-- TVPlayerApp            # 主窗口    +-- 左侧: QTreeWidget  # 频道列表(分组+搜索+收藏)    +-- 右侧: QFrame       # VLC 视频渲染区    +-- 底部: 控制栏        # 播放/音量/截图/全屏    +-- QTimer             # 状态监控(3秒轮询,自动切源)    +-- JSON 持久化        # 上次频道/收藏/窗口大小

自定义频道

在代码顶部的 CHANNELS 字典中添加即可:

CHANNELS = {"央视频道": [...],"卫视频道": [...],"我的频道": [   # 新增分组        {"name""自定义台1""urls": ["http://xxx/stream1.m3u8",   # 主源"http://yyy/stream1.m3u8",   # 备用源        ]},    ],}

每个频道的 urls 列表可以放多个地址,播放失败会自动按顺序切换。

常见问题

问题
解决方案
启动报错 "No module named vlc"
pip install python-vlc
 并安装 VLC 播放器
画面黑屏无内容
频道源可能失效,等待自动切换备用源
有画面无声音
检查音量滑块是否为 0,检查系统音量
截图保存在哪
screenshots/ 目录下,文件名含频道名和时间戳
如何添加频道
修改代码顶部 CHANNELS 字典
RTSP 源播放卡顿
RTSP 对网络要求较高,建议优先使用 M3U8 源

依赖

PyQt5python-vlc

https://www.videolan.org/vlc/

另需安装 VLC 播放器(https://www.videolan.org/),Windows 选 64 位版本。

-- coding: utf-8 --"""网络电视播放器 Lite v1.2 - PyQt5 + mpv功能:频道切换/本地视频/播放控制/音量/截图/全屏/主题/导入导出作者:杨少平 | 公众号:Python学在坚持运行:python tv_player_lite.py依赖:pip install PyQt5 python-mpv前提:Windows需下载mpv-dev并把libmpv-2.dll放到脚本同目录或系统PATH下载: https://sourceforge.net/projects/mpv-player-windows/files/libmpv/"""import sys, os, json, localefrom datetime import datetimefrom PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QLabel, QLineEdit, QPushButton, QSlider, QTreeWidget, QTreeWidgetItem,QSplitter, QFrame, QFileDialog, QMessageBox, QComboBox, QMenu, QShortcut)from PyQt5.QtCore import Qt, QTimerfrom PyQt5.QtGui import QFont, QColor, QKeySequenceCHANNELS = {"央视频道": [{"name""CCTV-1 综合""urls": ["http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8"]},{"name""CCTV-2 财经""urls": ["http://ivi.bupt.edu.cn/hls/cctv2hd.m3u8"]},{"name""CCTV-3 综艺""urls": ["http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8"]},{"name""CCTV-4 中文国际""urls": ["http://ivi.bupt.edu.cn/hls/cctv4hd.m3u8"]},{"name""CCTV-5 体育""urls": ["http://ivi.bupt.edu.cn/hls/cctv5hd.m3u8"]},{"name""CCTV-6 电影""urls": ["http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8"]},{"name""CCTV-7 国防军事""urls": ["http://ivi.bupt.edu.cn/hls/cctv7hd.m3u8"]},{"name""CCTV-8 电视剧""urls": ["http://ivi.bupt.edu.cn/hls/cctv8hd.m3u8"]},{"name""CCTV-9 纪录""urls": ["http://ivi.bupt.edu.cn/hls/cctv9hd.m3u8"]},{"name""CCTV-10 科教""urls": ["http://ivi.bupt.edu.cn/hls/cctv10hd.m3u8"]},],"卫视频道": [{"name""湖南卫视""urls": ["http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8"]},{"name""江苏卫视""urls": ["http://121.24.98.226:8090/hls/38/index.m3u8"]},{"name""浙江卫视""urls": ["http://121.24.98.226:8090/hls/39/index.m3u8"]},{"name""东方卫视""urls": ["http://121.24.98.226:8090/hls/40/index.m3u8"]},{"name""北京卫视""urls": ["http://ivi.bupt.edu.cn/hls/btv1hd.m3u8"]},{"name""广东卫视""urls": ["http://tv.gdtv.ah.cn/live/01.m3u8"]},{"name""湖北卫视""urls": ["http://124.160.184.108/live/5/45/3bfabc1fe16a4282b50ea095928c1f60.m3u8"]},],"地方/其他": [{"name""山东齐鲁频道""urls": ["http://121.24.98.226:8090/hls/121/index.m3u8"]},{"name""凤凰卫视中文台""urls": ["http://121.24.98.226:8090/hls/168/index.m3u8"]},],}CONFIG_FILE = os.path.join(os.path.dirname(os.path.abspath(file)), ".tv_lite_config.json")THEMES = {"经典白": {"left_bg":"#f5f5f5","title_bg":"#e0e0e0","search_bg":"#fff","search_fg":"#333","tree_bg":"#f5f5f5","tree_fg":"#333","tree_hover":"#e3f2fd","tree_sel":"#1976d2","tree_sel_fg":"#fff","ctrl_bg":"#e0e0e0","accent":"#1976d2","btn_fg":"#fff","btn_bg":"#1976d2","video_bg":"#222","status_bg":"#e0e0e0","status_fg":"#555","group_fg":"#1976d2","count_fg":"#888","channel_fg":"#1976d2","state_fg":"#888","slider_groove":"#bbb","slider_handle":"#1976d2","slider_sub":"#1976d2",},"暗夜蓝": {"left_bg":"#1a1a2e","title_bg":"#16213e","search_bg":"#0f3460","search_fg":"#fff","tree_bg":"#1a1a2e","tree_fg":"#e0e0e0","tree_hover":"#0f3460","tree_sel":"#e94560","tree_sel_fg":"#fff","ctrl_bg":"#16213e","accent":"#e94560","btn_fg":"#fff","btn_bg":"#0f3460","video_bg":"#000","status_bg":"#16213e","status_fg":"#888","group_fg":"#e94560","count_fg":"#888","channel_fg":"#e94560","state_fg":"#888","slider_groove":"#333","slider_handle":"#e94560","slider_sub":"#e94560",},"护眼绿": {"left_bg":"#1b2e1b","title_bg":"#2e4a2e","search_bg":"#3a5f3a","search_fg":"#c8e6c9","tree_bg":"#1b2e1b","tree_fg":"#c8e6c9","tree_hover":"#2e7d32","tree_sel":"#66bb6a","tree_sel_fg":"#000","ctrl_bg":"#2e4a2e","accent":"#66bb6a","btn_fg":"#fff","btn_bg":"#2e7d32","video_bg":"#000","status_bg":"#2e4a2e","status_fg":"#a5d6a7","group_fg":"#66bb6a","count_fg":"#a5d6a7","channel_fg":"#66bb6a","state_fg":"#a5d6a7","slider_groove":"#333","slider_handle":"#66bb6a","slider_sub":"#66bb6a",},"暖橙": {"left_bg":"#2e1a0e","title_bg":"#4e2a0e","search_bg":"#6d3a1a","search_fg":"#ffe0b2","tree_bg":"#2e1a0e","tree_fg":"#ffe0b2","tree_hover":"#e65100","tree_sel":"#ff9800","tree_sel_fg":"#000","ctrl_bg":"#4e2a0e","accent":"#ff9800","btn_fg":"#fff","btn_bg":"#e65100","video_bg":"#000","status_bg":"#4e2a0e","status_fg":"#ffcc80","group_fg":"#ff9800","count_fg":"#ffcc80","channel_fg":"#ff9800","state_fg":"#ffcc80","slider_groove":"#333","slider_handle":"#ff9800","slider_sub":"#ff9800",},"紫罗兰": {"left_bg":"#1a0e2e","title_bg":"#2a1a4e","search_bg":"#3a2a6d","search_fg":"#e1bee7","tree_bg":"#1a0e2e","tree_fg":"#e1bee7","tree_hover":"#6a1b9a","tree_sel":"#ab47bc","tree_sel_fg":"#fff","ctrl_bg":"#2a1a4e","accent":"#ab47bc","btn_fg":"#fff","btn_bg":"#6a1b9a","video_bg":"#000","status_bg":"#2a1a4e","status_fg":"#ce93d8","group_fg":"#ab47bc","count_fg":"#ce93d8","channel_fg":"#ab47bc","state_fg":"#ce93d8","slider_groove":"#333","slider_handle":"#ab47bc","slider_sub":"#ab47bc",},}==================== mpv 播放器封装 ====================class MpvPlayer:"""封装 python-mpv,嵌入到 QWidget"""def __init__(self, wid):    import mpv    locale.setlocale(locale.LC_NUMERIC, "C")  # mpv 需要    self.player = mpv.MPV(        wid=str(int(wid)),        vo="gpu",        hwdec="auto",        keep_open="yes",        input_default_bindings=False,        input_vo_keyboard=False,        osc=False,        log_handler=lambda *a: None,    )    self.player.volume = 80    self.current_urls = []    self.current_url_idx = 0    self.current_name = ""    self._playing = Falsedef play(self, urls, name=""):    self.current_urls = list(urls)    self.current_url_idx = 0    self.current_name = name    self._play_url(urls[0])def play_file(self, path):    """播放本地文件"""    self.current_urls = [path]    self.current_url_idx = 0    self.current_name = os.path.basename(path)    self._play_url(path)def _play_url(self, url):    try:        self.player.play(url)        self._playing = True    except Exception as e:        print(f"mpv play error: {e}")        self._playing = Falsedef try_next_source(self):    self.current_url_idx += 1    if self.current_url_idx < len(self.current_urls):        self._play_url(self.current_urls[self.current_url_idx])        return True    return Falsedef stop(self):    try:        self.player.command("stop")    except Exception:        pass    self._playing = Falsedef pause(self):    try:        self.player.cycle("pause")    except Exception:        pass@propertydef is_paused(self):    try:        return self.player.pause    except Exception:        return False@propertydef is_idle(self):    try:        return self.player.idle_active    except Exception:        return Truedef set_volume(self, vol):    try:        self.player.volume = max(0min(100, vol))    except Exception:        passdef get_volume(self):    try:        return int(self.player.volume or 80)    except Exception:        return 80def toggle_mute(self):    try:        self.player.cycle("mute")    except Exception:        pass@propertydef is_muted(self):    try:        return self.player.mute    except Exception:        return Falsedef screenshot(self, path):    try:        self.player.screenshot_to_file(path, "video")        return True    except Exception:        return Falsedef get_state_text(self):    try:        if self.player.idle_active:            return "就绪"        if self.player.pause:            return "已暂停"        if self.player.core_idle:            return "缓冲中..."        return "播放中"    except Exception:        return "就绪"def is_error(self):    """检测是否播放失败"""    try:        return self.player.idle_active and self._playing    except Exception:        return Falsedef terminate(self):    try:        self.player.terminate()    except Exception:        pass==================== 主窗口 ====================class TVPlayerLite(QMainWindow):def init(self):super().init()self.setWindowTitle("TV Player Lite v1.2 | Python学在坚持")self.setMinimumSize(960600)self.resize(1200720)self.mpv = Noneself.current_channel = ""self.current_urls = []self.current_url_idx = 0self.favorites = set()self.channels = dict(CHANNELS)self.theme_name = "经典白"self._click_cooldown = False  # 防双击卡顿self._load_config()self._build()self._apply_theme(self.theme_name)self._init_mpv()self._start_monitor()def _build(self):    central = QWidget()    self.setCentralWidget(central)    root = QHBoxLayout(central)    root.setContentsMargins(0000)    root.setSpacing(0)    self.splitter = QSplitter(Qt.Horizontal)    # ---- 左侧 ----    self.left_widget = QWidget()    self.left_widget.setMaximumWidth(270)    self.left_widget.setMinimumWidth(180)    ll = QVBoxLayout(self.left_widget)    ll.setContentsMargins(0000)    ll.setSpacing(0)    self.lbl_title = QLabel("  TV Player Lite")    self.lbl_title.setFixedHeight(40)    ll.addWidget(self.lbl_title)    # 工具行:主题+导入+导出+本地视频    tool_row = QHBoxLayout()    tool_row.setContentsMargins(6464)    self.cb_theme = QComboBox()    self.cb_theme.addItems(list(THEMES.keys()))    self.cb_theme.setCurrentText(self.theme_name)    self.cb_theme.currentTextChanged.connect(self._on_theme_change)    tool_row.addWidget(self.cb_theme, 1)    btn_imp = QPushButton("导入")    btn_imp.setFixedWidth(36)    btn_imp.setToolTip("导入频道JSON")    btn_imp.clicked.connect(self._import_channels)    tool_row.addWidget(btn_imp)    btn_exp = QPushButton("导出")    btn_exp.setFixedWidth(36)    btn_exp.setToolTip("导出频道JSON")    btn_exp.clicked.connect(self._export_channels)    tool_row.addWidget(btn_exp)    ll.addLayout(tool_row)    # 搜索    self.search_box = QLineEdit()    self.search_box.setPlaceholderText("  搜索频道...")    self.search_box.textChanged.connect(self._filter)    ll.addWidget(self.search_box)    # 频道树    self.tree = QTreeWidget()    self.tree.setHeaderHidden(True)    self.tree.itemClicked.connect(self._on_click)    self.tree.setContextMenuPolicy(Qt.CustomContextMenu)    self.tree.customContextMenuRequested.connect(self._ctx_menu)    ll.addWidget(self.tree, 1)    self.lbl_count = QLabel("")    ll.addWidget(self.lbl_count)    # 本地视频按钮    btn_local = QPushButton("📂 打开本地视频")    btn_local.setStyleSheet("margin:4px 6px;padding:8px;font-size:12px;")    btn_local.clicked.connect(self._open_local)    ll.addWidget(btn_local)    self.btn_local = btn_local    self._fill_tree()    self.splitter.addWidget(self.left_widget)    # ---- 右侧 ----    right = QWidget()    rl = QVBoxLayout(right)    rl.setContentsMargins(0000)    rl.setSpacing(0)    # 视频区域(mpv 渲染到这个 QFrame)    self.video_frame = QFrame()    self.video_frame.setMinimumSize(480320)    rl.addWidget(self.video_frame, 1)    # 控制栏    self.ctrl_bar = QWidget()    self.ctrl_bar.setFixedHeight(50)    cl = QHBoxLayout(self.ctrl_bar)    cl.setContentsMargins(100100)    cl.setSpacing(8)    self.btn_play = QPushButton("▶")    self.btn_play.setFixedSize(3636)    self.btn_play.clicked.connect(self._toggle_play)    cl.addWidget(self.btn_play)    self.btn_stop = QPushButton("⏹")    self.btn_stop.setFixedSize(3636)    self.btn_stop.clicked.connect(self._stop)    cl.addWidget(self.btn_stop)    cl.addSpacing(10)    self.btn_mute = QPushButton("🔊")    self.btn_mute.setFixedSize(3030)    self.btn_mute.clicked.connect(self._toggle_mute)    cl.addWidget(self.btn_mute)    self.sl_vol = QSlider(Qt.Horizontal)    self.sl_vol.setRange(0100)    self.sl_vol.setValue(80)    self.sl_vol.setMaximumWidth(120)    self.sl_vol.valueChanged.connect(self._vol_change)    cl.addWidget(self.sl_vol)    self.lbl_vol = QLabel("80%")    self.lbl_vol.setMinimumWidth(35)    cl.addWidget(self.lbl_vol)    cl.addSpacing(10)    self.btn_snap = QPushButton("截图")    self.btn_snap.clicked.connect(self._snapshot)    cl.addWidget(self.btn_snap)    self.btn_full = QPushButton("全屏")    self.btn_full.clicked.connect(self._toggle_fs)    cl.addWidget(self.btn_full)    cl.addStretch()    self.lbl_ch = QLabel("未选择频道")    cl.addWidget(self.lbl_ch)    self.lbl_state = QLabel("就绪")    cl.addWidget(self.lbl_state)    rl.addWidget(self.ctrl_bar)    self.splitter.addWidget(right)    self.splitter.setStretchFactor(00)    self.splitter.setStretchFactor(11)    root.addWidget(self.splitter)    # 快捷键    QShortcut(QKeySequence("F11"), selfself._toggle_fs)    QShortcut(QKeySequence("Escape"), selflambdaself.showNormal() if self.isFullScreen() else None)    QShortcut(QKeySequence("Space"), selfself._toggle_play)    QShortcut(QKeySequence("Ctrl+O"), selfself._open_local)    self.statusBar().showMessage("公众号: Python学在坚持 | 微信: ysp2338084 | 作者: 杨少平")# ---- mpv 初始化 ----def _init_mpv(self):    try:        wid = int(self.video_frame.winId())        self.mpv = MpvPlayer(wid)    except OSError as e:        err = str(e)        if "libmpv" in err or "DLL" in err or "cannot" in err.lower():            QMessageBox.critical(self"缺少 mpv 库",                "未找到 libmpv 动态库。\n\n"                "Windows 安装步骤:\n"                "1. 下载 mpv-dev: https://sourceforge.net/projects/mpv-player-windows/files/libmpv/\n"                "2. 解压后将 libmpv-2.dll 复制到本脚本同目录\n"                "3. 或复制到 C:\\Windows\\System32\\\n\n"                "然后重新运行程序。")        else:            QMessageBox.critical(self"mpv 初始化失败"f"错误: {e}")    except ImportError:        QMessageBox.critical(self"缺少 python-mpv",            "请安装: pip install python-mpv\n\n"            "同时需要 libmpv 动态库(见上方说明)")    except Exception as e:        QMessageBox.critical(self"mpv 初始化失败"f"错误: {e}")# ---- 频道树 ----def _fill_tree(self):    self.tree.clear()    total = 0    if self.favorites:        fg = QTreeWidgetItem(self.tree, ["⭐ 收藏频道"])        for g, chs in self.channels.items():            for ch in chs:                if ch["name"in self.favorites:                    it = QTreeWidgetItem(fg, [ch["name"]])                    it.setData(0, Qt.UserRole, ch["urls"])                    it.setData(0, Qt.UserRole + 1, ch["name"])        fg.setExpanded(True)    for g, chs in self.channels.items():        gi = QTreeWidgetItem(self.tree, [g])        f = gi.font(0); f.setBold(True); gi.setFont(0, f)        for ch in chs:            it = QTreeWidgetItem(gi, [ch["name"]])            it.setData(0, Qt.UserRole, ch["urls"])            it.setData(0, Qt.UserRole + 1, ch["name"])            total += 1        gi.setExpanded(True)    self.lbl_count.setText(f"  共 {total} 个频道")def _filter(self, kw):    kw = kw.lower()    for i in range(self.tree.topLevelItemCount()):        g = self.tree.topLevelItem(i)        vis = 0        for j in range(g.childCount()):            c = g.child(j)            m = kw in c.text(0).lower()            c.setHidden(not m)            if m: vis += 1        g.setHidden(vis == 0 and kw != "")def _ctx_menu(self, pos):    it = self.tree.itemAt(pos)    if not it or not it.data(0, Qt.UserRole): return    name = it.data(0, Qt.UserRole + 1)    menu = QMenu()    if name in self.favorites:        menu.addAction("取消收藏"lambdaself._toggle_fav(name))    else:        menu.addAction("⭐ 收藏"lambdaself._toggle_fav(name))    menu.exec_(self.tree.viewport().mapToGlobal(pos))def _toggle_fav(self, name):    if name in self.favorites: self.favorites.discard(name)    elseself.favorites.add(name)    self._save_config(); self._fill_tree(); self._apply_theme(self.theme_name)# ---- 播放(防双击卡顿) ----def _on_click(self, item, col):    urls = item.data(0, Qt.UserRole)    name = item.data(0, Qt.UserRole + 1)    if not urls: return    # 防双击:500ms 冷却    if self._click_cooldown: return    self._click_cooldown = True    QTimer.singleShot(500lambdasetattr(self'_click_cooldown'False))    self.current_channel = name    self.current_urls = list(urls)    self.current_url_idx = 0    self.lbl_ch.setText(name)    self.lbl_state.setText("加载中...")    if self.mpv:        self.mpv.play(urls, name)        self.btn_play.setText("⏸")    self._save_config()def _open_local(self):    """打开本地视频文件"""    path, _ = QFileDialog.getOpenFileName(        self"打开本地视频""",        "视频文件 (*.mp4 *.avi *.mkv *.mov *.flv *.wmv *.ts *.m3u8);;所有文件 (*)"    )    if not path: return    self.current_channel = os.path.basename(path)    self.current_urls = [path]    self.current_url_idx = 0    self.lbl_ch.setText(self.current_channel)    self.lbl_state.setText("加载中...")    if self.mpv:        self.mpv.play_file(path)        self.btn_play.setText("⏸")def _toggle_play(self):    if not self.mpv: return    self.mpv.pause()    if self.mpv.is_paused:        self.btn_play.setText("▶")    else:        self.btn_play.setText("⏸")def _stop(self):    if self.mpv:        self.mpv.stop()        self.btn_play.setText("▶")        self.lbl_state.setText("已停止")def _vol_change(self, v):    self.lbl_vol.setText(f"{v}%")    if self.mpv: self.mpv.set_volume(v)    self.btn_mute.setText("🔇" if v == 0 else "🔊")def _toggle_mute(self):    if not self.mpv: return    self.mpv.toggle_mute()    self.btn_mute.setText("🔇" if self.mpv.is_muted else "🔊")def _snapshot(self):    if not self.mpv: return    d = os.path.join(os.path.dirname(os.path.abspath(__file__)), "screenshots")    os.makedirs(d, exist_ok=True)    ts = datetime.now().strftime("%Y%m%d_%H%M%S")    ch = self.current_channel.replace(" ""_").replace("/""_"or "snap"    p = os.path.join(d, f"{ch}_{ts}.png")    if self.mpv.screenshot(p):        self.lbl_state.setText(f"已截图: {os.path.basename(p)}")        self.statusBar().showMessage(f"截图: {p}"5000)    else:        self.lbl_state.setText("截图失败")def _toggle_fs(self):    if self.isFullScreen(): self.showNormal()    elseself.showFullScreen()# ---- 导入导出 ----def _export_channels(self):    p, _ = QFileDialog.getSaveFileName(self"导出频道""channels.json""JSON (*.json)")    if p:        with open(p, "w", encoding="utf-8"as f:            json.dump(self.channels, f, ensure_ascii=False, indent=2)        total = sum(len(v) for v in self.channels.values())        QMessageBox.information(self"成功"f"已导出 {total} 个频道到:\n{p}")def _import_channels(self):    p, _ = QFileDialog.getOpenFileName(self"导入频道""""JSON (*.json)")    if not p: return    try:        with open(p, "r", encoding="utf-8"as f:            data = json.load(f)        if not isinstance(data, dict):            raise ValueError("顶层应为字典")        count = 0        for g, chs in data.items():            if not isinstance(chs, list): continue            for ch in chs:                if "name" in ch and "urls" in ch: count += 1        if count == 0raise ValueError("未找到有效频道")        self.channels = data        self._fill_tree(); self._apply_theme(self.theme_name)        QMessageBox.information(self"成功"f"已导入 {count} 个频道")    except Exception as e:        QMessageBox.warning(self"导入失败"f"格式错误: {e}")# ---- 主题 ----def _on_theme_change(self, name):    self.theme_name = name    self._apply_theme(name)    self._save_config()def _apply_theme(self, name):    t = THEMES.get(name, THEMES["经典白"])    self.left_widget.setStyleSheet(f"background:{t['left_bg']};")    self.lbl_title.setStyleSheet(f"color:{t['tree_fg']};font-size:15px;font-weight:bold;padding:8px;background:{t['title_bg']};")    self.search_box.setStyleSheet(f"padding:7px;margin:4px 6px;border:1px solid {t['slider_groove']};border-radius:4px;background:{t['search_bg']};color:{t['search_fg']};")    self.tree.setStyleSheet(f"""        QTreeWidget {{ background:{t['tree_bg']}; color:{t['tree_fg']}; border:none; font-size:13px; }}        QTreeWidget::item {{ padding:5px 8px; }}        QTreeWidget::item:hover {{ background:{t['tree_hover']}; }}        QTreeWidget::item:selected {{ background:{t['tree_sel']}; color:{t['tree_sel_fg']}; }}        QTreeWidget::branch {{ background:{t['tree_bg']}; }}    """)    self.lbl_count.setStyleSheet(f"color:{t['count_fg']};font-size:11px;padding:4px 6px;background:{t['left_bg']};")    self.btn_local.setStyleSheet(f"margin:4px 6px;padding:8px;font-size:12px;background:{t['btn_bg']};color:{t['btn_fg']};border:none;border-radius:4px;")    self.ctrl_bar.setStyleSheet(f"background:{t['ctrl_bg']};")    self.btn_play.setStyleSheet(f"background:{t['accent']};color:{t['btn_fg']};border:none;border-radius:18px;font-size:16px;")    self.btn_stop.setStyleSheet(f"background:{t['slider_groove']};color:{t['btn_fg']};border:none;border-radius:18px;font-size:14px;")    self.btn_mute.setStyleSheet(f"background:transparent;color:{t['tree_fg']};border:none;font-size:16px;")    self.sl_vol.setStyleSheet(f"""        QSlider::groove:horizontal {{ background:{t['slider_groove']}; height:4px; border-radius:2px; }}        QSlider::handle:horizontal {{ background:{t['slider_handle']}; width:14px; height:14px; margin:-5px 0; border-radius:7px; }}        QSlider::sub-page:horizontal {{ background:{t['slider_sub']}; border-radius:2px; }}    """)    self.lbl_vol.setStyleSheet(f"color:{t['state_fg']};font-size:11px;")    for b in [self.btn_snap, self.btn_full]:        b.setStyleSheet(f"background:{t['btn_bg']};color:{t['btn_fg']};border:none;border-radius:4px;padding:6px 12px;font-size:12px;")    self.lbl_ch.setStyleSheet(f"color:{t['channel_fg']};font-size:13px;font-weight:bold;")    self.lbl_state.setStyleSheet(f"color:{t['state_fg']};font-size:11px;margin-left:8px;")    self.video_frame.setStyleSheet(f"background:{t['video_bg']};")    self.statusBar().setStyleSheet(f"background:{t['status_bg']};color:{t['status_fg']};")    for i in range(self.tree.topLevelItemCount()):        self.tree.topLevelItem(i).setForeground(0, QColor(t["group_fg"]))# ---- 状态监控+自动切源 ----def _start_monitor(self):    self._err_count = 0    self.mon = QTimer()    self.mon.timeout.connect(self._check)    self.mon.start(3000)def _check(self):    if not self.mpv: return    state = self.mpv.get_state_text()    if state == "播放中":        self.lbl_state.setText("▶ 播放中")        self.lbl_state.setStyleSheet("color:#4caf50;font-size:11px;margin-left:8px;")        self._err_count = 0    elif state == "缓冲中...":        self.lbl_state.setText("⏳ 缓冲中...")        self.lbl_state.setStyleSheet("color:#ff9800;font-size:11px;margin-left:8px;")    elif state == "已暂停":        self.lbl_state.setText("⏸ 已暂停")    elif self.mpv.is_error():        self._err_count += 1        if self._err_count >= 2:            if self.mpv.try_next_source():                idx = self.mpv.current_url_idx                total = len(self.mpv.current_urls)                self.lbl_state.setText(f"⚠ 切换备用源 {idx+1}/{total}...")                self.lbl_state.setStyleSheet("color:#ff9800;font-size:11px;margin-left:8px;")                self._err_count = 0            else:                self.lbl_state.setText("❌ 所有源不可用")                self.lbl_state.setStyleSheet("color:#e53935;font-size:11px;margin-left:8px;")# ---- 配置 ----def _load_config(self):    if os.path.exists(CONFIG_FILE):        try:            with open(CONFIG_FILE, "r", encoding="utf-8"as f:                c = json.load(f)            self.current_channel = c.get("last_channel""")            self.favorites = set(c.get("favorites", []))            self.theme_name = c.get("theme""经典白")            g = c.get("geometry")            if g: self.resize(g.get("w"1200), g.get("h"720))            custom = c.get("custom_channels")            if custom and isinstance(custom, dict):                self.channels = custom        except Exception:            passdef _save_config(self):    c = {        "last_channel"self.current_channel,        "favorites"list(self.favorites),        "theme"self.theme_name,        "geometry": {"w"self.width(), "h"self.height()},    }    if self.channels != CHANNELS:        c["custom_channels"] = self.channels    try:        with open(CONFIG_FILE, "w", encoding="utf-8"as f:            json.dump(c, f, ensure_ascii=False, indent=2)    except Exception:        passdef closeEvent(self, e):    self._save_config()    if self.mpv:        self.mpv.terminate()    e.accept()==================== 启动 ====================if name == "main":app = QApplication(sys.argv)app.setFont(QFont("Microsoft YaHei"10))win = TVPlayerLite()win.show()sys.exit(app.exec_())

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-07-04 08:11:50 HTTP/2.0 GET : https://f.mffb.com.cn/a/489034.html
  2. 运行时间 : 0.316777s [ 吞吐率:3.16req/s ] 内存消耗:4,944.82kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=0ca49bf3e99917e5b88cd91d1a655313
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000350s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000652s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000307s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000266s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000506s ]
  6. SELECT * FROM `set` [ RunTime:0.000196s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000511s ]
  8. SELECT * FROM `article` WHERE `id` = 489034 LIMIT 1 [ RunTime:0.009238s ]
  9. UPDATE `article` SET `lasttime` = 1783123910 WHERE `id` = 489034 [ RunTime:0.009712s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000902s ]
  11. SELECT * FROM `article` WHERE `id` < 489034 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000459s ]
  12. SELECT * FROM `article` WHERE `id` > 489034 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.010179s ]
  13. SELECT * FROM `article` WHERE `id` < 489034 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.009424s ]
  14. SELECT * FROM `article` WHERE `id` < 489034 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.011633s ]
  15. SELECT * FROM `article` WHERE `id` < 489034 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.196220s ]
0.318336s