大家好我是一个完全不懂桌面应用开发的小白。但我用3分钟时间在Trae这个AI编程工具里通过SOLO模式Agent Skills DeepSeek搞出了一个桌面应用。这篇文章我就手把手教你如何用AI编程工具让AI帮你写代码、做软件全程不需要你写一行代码 第一步搞清楚我们要用什么先说几个关键词其实很简单简单说你在Trae里打开SOLO模式装上合适的Skills用DeepSeek当大脑告诉AI你想做什么AI就帮你把代码写出来。️ 第二步3分钟极速教程真的只需要3分钟第1分钟安装Trae并配置DeepSeek去Trae官网下载安装包完全免费安装后打开点击右上角的AI侧边栏进入AI管理 → 模型设置添加自定义模型选择Provider为Novita或其他支持DeepSeek的服务商从模型下拉框选择 DeepSeek-V3-0324最新版代码能力超强粘贴你的API Key没有的话去Novita注册免费试用一般送额度搞定现在Trae的大脑已经换成DeepSeek了。第2分钟创建SOLO模式安装Skills在Trae中新建一个项目文件夹比如叫「my-app」点击左下角切换到 SOLO模式重要只有SOLO模式才能用Skills在对话中输入帮我创建一个Skill用于开发Electron桌面应用。Skill需要包含项目初始化、窗口配置、多页面路由、打包配置等规范。第3分钟让AI生成你的追剧神器在SOLO模式对话中输入以下指令用Electron帮我开发一个桌面应用名字叫「剧多多」。 功能要求 1. 点击按钮主窗口直接跳转到对应平台的官网 2. 窗口大小设置为1280x720 3. 应用图标放在桌面双击打开直接进入 4. 需要使用webview加载网页确保V#I#P视频可以正常播放保留登录态 请使用我刚刚创建的Electron开发Skill生成完整的项目代码。然后——坐等奇迹发生AI会开始思考、规划、生成代码。你只需要看到提示时点击「接受全部」就行。生成架构图生成代码import os import sys from pathlib import Path def _find_widevine_cdm(): chrome_app_roots [] for env_name in (PROGRAMFILES, PROGRAMFILES(X86), LOCALAPPDATA): base os.environ.get(env_name) candidates [] for root in chrome_app_roots: if not root.exists(): continue try: candidates.extend( list(root.glob(WidevineCdm/*/_platform_specific/win_x64/widevinecdm.dll)) ) candidates.extend( list(root.glob(*/WidevineCdm/*/_platform_specific/win_x64/widevinecdm.dll)) ) except OSError: continue if not candidates: return None def version_key(p: Path): version p.parents[2].name parts [] for x in version.split(.): try: parts.append(int(x)) except ValueError: parts.append(0) return tuple(parts) best max(candidates, keyversion_key) version_dir best.parents[2] return str(best), version_dir.name def _configure_qtwebengine_widevine(): found _find_widevine_cdm() if not found: return cdm_path, cdm_version found flags os.environ.get(QTWEBENGINE_CHROMIUM_FLAGS, ) if --widevine-cdm-path in flags: return extra ( f --widevine-cdm-path{cdm_path} f --widevine-cdm-version{cdm_version} --disable-featuresAutomationControlled --disable-direct-composition --disable-accelerated-video-decode ) os.environ[QTWEBENGINE_CHROMIUM_FLAGS] (flags extra).strip() _configure_qtwebengine_widevine() from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QComboBox, QLabel, QMessageBox, QProgressBar, QGridLayout, QFrame, QScrollArea, QStackedWidget) from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineCore import QWebEngineSettings, QWebEnginePage, QWebEngineScript, QWebEngineUrlRequestInterceptor from PyQt6.QtCore import QUrl, Qt, QSize, QStandardPaths, QTimer from PyQt6.QtGui import QIcon, QAction, QFont, QCursor, QDesktopServices import config try: from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput from PyQt6.QtMultimediaWidgets import QVideoWidget MULTIMEDIA_AVAILABLE True except Exception: QMediaPlayer None QAudioOutput None QVideoWidget None MULTIMEDIA_AVAILABLE False VIDEO_PLATFORMS [] class WebEngineView(QWebEngineView): def createWindow(self, _type): return self class VideoUrlInterceptor(QWebEngineUrlRequestInterceptor): def __init__(self, handler): super().__init__() self.handler handler def interceptRequest(self, info): url info.requestUrl().toString() if not url: return lower url.lower() if .m3u8 in lower or .mp4 in lower: self.handler(url) class v_i_pVideoPlayer(QMainWindow): def __init__(self): super().__init__() self.resize(1280, 900) self.setStyleSheet(background-color: #4DB6AC;) # 全局青色背景 self.h264_supported None self.pending_parse False self.last_video_url self.player_available MULTIMEDIA_AVAILABLE self.media_player None self.audio_output None self.video_widget None # 初始化界面 self.init_ui() # 加载默认首页 self.load_url(config.DEFAULT_HOME_URL) def init_ui(self): # 主窗口部件 central_widget QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) central_widget.setLayout(main_layout) # --- 1. 顶部 Header 区域 --- header_widget QWidget() header_widget.setStyleSheet(background-color: #00695C; color: white;) header_widget.setFixedHeight(50) header_layout QHBoxLayout() header_layout.setContentsMargins(20, 0, 20, 0) header_widget.setLayout(header_layout) title_label.setFont(QFont(Microsoft YaHei, 14, QFont.Weight.Bold)) title_label.setStyleSheet(color: white;) info_label.setFont(QFont(Microsoft YaHei, 10)) info_label.setStyleSheet(color: #E0F2F1;) header_layout.addWidget(title_label) header_layout.addStretch() header_layout.addWidget(info_label) main_layout.addWidget(header_widget) # --- 2. 控制/搜索区域 --- control_container QWidget() control_container.setStyleSheet(background-color: #4DB6AC;) # 青色背景 control_layout QHBoxLayout() control_layout.setContentsMargins(20, 15, 20, 15) control_layout.setSpacing(10) control_container.setLayout(control_layout) # 导航按钮组 (小一点) nav_layout QHBoxLayout() nav_layout.setSpacing(5) btn_style_nav QPushButton { background-color: rgba(255, 255, 255, 0.2); color: white; border: none; border-radius: 4px; font-weight: bold; } QPushButton:hover { background-color: rgba(255, 255, 255, 0.4); } self.btn_back QPushButton(←) self.btn_back.setFixedSize(30, 30) self.btn_back.setStyleSheet(btn_style_nav) self.btn_back.clicked.connect(self.navigate_back) self.btn_forward QPushButton(→) self.btn_forward.setFixedSize(30, 30) self.btn_forward.setStyleSheet(btn_style_nav) self.btn_forward.clicked.connect(self.navigate_forward) self.btn_refresh QPushButton(↻) self.btn_refresh.setFixedSize(30, 30) self.btn_refresh.setStyleSheet(btn_style_nav) self.btn_refresh.clicked.connect(self.reload_page) self.btn_home QPushButton(首页) self.btn_home.setFixedSize(50, 30) self.btn_home.setStyleSheet(btn_style_nav) self.btn_home.clicked.connect(lambda: self.load_url(config.DEFAULT_HOME_URL)) nav_layout.addWidget(self.btn_back) nav_layout.addWidget(self.btn_forward) nav_layout.addWidget(self.btn_refresh) nav_layout.addWidget(self.btn_home) label_addr.setAlignment(Qt.AlignmentFlag.AlignCenter) label_addr.setFixedSize(120, 40) label_addr.setStyleSheet( background-color: #2ECC71; color: white; border-radius: 4px; font-size: 12px; ) # 地址输入框 self.url_input QLineEdit() self.url_input.setPlaceholderText(在此输入或粘贴视频网址...) self.url_input.setFixedHeight(40) self.url_input.setStyleSheet( QLineEdit { border: 2px solid #2ECC71; border-radius: 4px; padding: 0 10px; font-size: 14px; background-color: white; } ) self.url_input.returnPressed.connect(self.load_from_input) # 线路选择 (稍微美化) self.combo_interface QComboBox() self.combo_interface.setFixedHeight(40) self.combo_interface.setMinimumWidth(120) self.combo_interface.setStyleSheet( QComboBox { border: 2px solid #2ECC71; border-radius: 4px; padding: 0 10px; background-color: white; } QComboBox::drop-down { border: none; } ) for item in config.v_i_p_INTERFACES: self.combo_interface.addItem(item[name], item[url]) # 立即播放按钮 self.btn_play QPushButton(立即播放) self.btn_play.setFixedSize(100, 40) self.btn_play.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.btn_play.setStyleSheet( QPushButton { background-color: #2ECC71; color: white; font-weight: bold; font-size: 14px; border-radius: 4px; border: none; } QPushButton:hover { background-color: #27ae60; } ) self.btn_play.clicked.connect(self.parse_video) # 用浏览器打开按钮 self.btn_open_browser QPushButton(浏览器播放) self.btn_open_browser.setFixedSize(100, 40) self.btn_open_browser.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.btn_open_browser.setStyleSheet( QPushButton { background-color: #3498DB; color: white; font-weight: bold; font-size: 14px; border-radius: 4px; border: none; } QPushButton:hover { background-color: #2980B9; } ) self.btn_open_browser.clicked.connect(self.open_in_system_browser) # 组装控制栏 control_layout.addLayout(nav_layout) control_layout.addSpacing(15) control_layout.addWidget(label_addr) control_layout.addWidget(self.url_input, 1) # 拉伸 control_layout.addWidget(self.combo_interface) control_layout.addWidget(self.btn_play) control_layout.addWidget(self.btn_open_browser) main_layout.addWidget(control_container) # --- 3. 提示文字 --- tip_widget QWidget() tip_widget.setStyleSheet(background-color: #4DB6AC;) tip_layout QVBoxLayout() tip_layout.setContentsMargins(0, 5, 0, 10) tip_widget.setLayout(tip_layout) tip_label.setAlignment(Qt.AlignmentFlag.AlignCenter) tip_label.setStyleSheet(color: white; font-size: 12px;) tip_layout.addWidget(tip_label) main_layout.addWidget(tip_widget) web_container QWidget() web_container.setStyleSheet(background-color: #4DB6AC; padding: 0px 20px;) web_layout QVBoxLayout() web_layout.setContentsMargins(0, 0, 0, 0) web_container.setLayout(web_layout) self.browser WebEngineView() self.browser.setStyleSheet(background-color: black;) if self.player_available: self.media_player QMediaPlayer() self.audio_output QAudioOutput() self.media_player.setAudioOutput(self.audio_output) self.video_widget QVideoWidget() self.media_player.setVideoOutput(self.video_widget) try: data_path QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation) profile_path os.path.join(data_path, v_i_p_video_player_profile) cache_path os.path.join(profile_path, cache) if not os.path.exists(profile_path): os.makedirs(profile_path, exist_okTrue) if not os.path.exists(cache_path): os.makedirs(cache_path, exist_okTrue) profile self.browser.page().profile() profile.setPersistentStoragePath(profile_path) profile.setCachePath(cache_path) self.video_interceptor VideoUrlInterceptor(self.on_video_url_detected) profile.setUrlRequestInterceptor(self.video_interceptor) print(fProfile storage path set to: {profile_path}) except Exception as e: print(fFailed to set profile path: {e}) script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentCreation) script.setWorldId(QWebEngineScript.ScriptWorldId.MainWorld) script.setRunsOnSubFrames(True) self.browser.page().scripts().insert(script) settings self.browser.settings() settings.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows, True) settings.setAttribute(QWebEngineSettings.WebAttribute.PlaybackRequiresUserGesture, False) settings.setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True) # 启用更多高级特性以提高兼容性 settings.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.Accelerated2dCanvasEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False) settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True) settings.setAttribute(QWebEngineSettings.WebAttribute.HyperlinkAuditingEnabled, False) self.browser.urlChanged.connect(self.update_url_bar) self.browser.loadProgress.connect(self.update_progress) self.browser.titleChanged.connect(self.update_title) self.browser.loadFinished.connect(self.on_load_finished) if self.player_available and self.video_widget is not None: self.view_stack QStackedWidget() self.view_stack.addWidget(self.browser) self.view_stack.addWidget(self.video_widget) web_layout.addWidget(self.view_stack) else: self.view_stack None web_layout.addWidget(self.browser) main_layout.addWidget(web_container, 1) # 浏览器占据主要空间 self.progress_bar QProgressBar() self.progress_bar.setFixedHeight(2) self.progress_bar.setTextVisible(False) self.progress_bar.setStyleSheet(QProgressBar { border: 0px; background-color: transparent; } QProgressBar::chunk { background-color: #2ECC71; }) main_layout.addWidget(self.progress_bar) # --- 5. 底部平台快捷栏 --- platform_widget QWidget() platform_widget.setStyleSheet(background-color: #4DB6AC;) platform_layout QVBoxLayout() platform_layout.setContentsMargins(20, 10, 20, 20) platform_widget.setLayout(platform_layout) bottom_tip.setAlignment(Qt.AlignmentFlag.AlignCenter) bottom_tip.setStyleSheet(color: white; font-size: 13px; margin-bottom: 10px;) platform_layout.addWidget(bottom_tip) # 图标布局 (单行显示) platforms_layout QHBoxLayout() platforms_layout.setSpacing(15) # 生成按钮 for platform in VIDEO_PLATFORMS: btn QPushButton(platform[name]) btn.setFixedHeight(50) btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) # 使用动态颜色样式 btn.setStyleSheet(f QPushButton {{ background-color: white; color: {platform[color]}; font-size: 16px; font-weight: bold; border-radius: 6px; border: 1px solid #ddd; }} QPushButton:hover {{ background-color: #f9f9f9; border: 2px solid {platform[color]}; }} ) # 使用闭包绑定URL btn.clicked.connect(lambda checked, urlplatform[url]: self.load_url(url)) platforms_layout.addWidget(btn) platform_layout.addLayout(platforms_layout) main_layout.addWidget(platform_widget) # --- 逻辑功能 --- def load_url(self, url_str): 加载URL if not url_str.startswith(http): url_str https:// url_str self.browser.load(QUrl(url_str)) def load_from_input(self): 从地址栏加载 url self.url_input.text().strip() if url: self.load_url(url) def parse_video(self): self.pending_parse True self.last_video_url if self.view_stack is not None: self.view_stack.setCurrentWidget(self.browser) self.browser.load(QUrl(final_url)) def open_in_system_browser(self): 在系统浏览器中打开当前页面 QDesktopServices.openUrl(self.browser.url()) def open_in_system_browser_url(self, url): QDesktopServices.openUrl(QUrl(url)) def on_video_url_detected(self, url): if not self.pending_parse: return if url self.last_video_url: return self.last_video_url url if self.player_available and self.media_player is not None: self.play_video_url(url) self.pending_parse False def play_video_url(self, url): if self.view_stack is not None and self.video_widget is not None: self.view_stack.setCurrentWidget(self.video_widget) self.media_player.setSource(QUrl(url)) self.media_player.play() def navigate_back(self): self.browser.back() def navigate_forward(self): self.browser.forward() def reload_page(self): self.browser.reload() def update_url_bar(self, qurl): 更新地址栏显示 self.url_input.setText(qurl.toString()) self.url_input.setCursorPosition(0) def update_progress(self, progress): 更新进度条 self.progress_bar.setValue(progress) if progress 100: self.progress_bar.hide() else: self.progress_bar.show() def update_title(self, title): 更新窗口标题 if __name__ __main__: app QApplication(sys.argv) window v_i_pVideoPlayer() window.show() sys.exit(app.exec())3、项目开发说明文档✨ 发生了什么AI到底做了什么你可能会好奇这几句话AI真的能做出一个完整的软件吗答案是能而且做得很好DeepSeek V3.1在代码生成和推理方面的能力非常强AIME 2025基准测试得分88.4%接近GPT-5的水平。加上Trae的Agent模式AI可以1、理解你的需求知道你要做一个聚合视频的桌面应用2、调用Skill规范按照Electron开发的最佳实践来组织代码3、生成完整项目包括主进程、渲染进程、package.json、打包配置等4、处理技术细节比如webview配置、跨域问题、登录态保持整个过程就像你请了一个24小时待命的程序员你说需求他写代码你只管验收 最终成果展示代码生成后在项目目录下运行bash npm start npm start你就会看到——「剧#多#多」桌面应用打开了如果你想打包成exe/dmg发给朋友只需要告诉AIAI会自动修改package.json添加打包配置。运行 npm run dist安装包就出来了 小白常见问题Q1我真的一点代码都不会能搞定吗A完全可以我本人就是例子。全程只需要打字AI写代码你负责确认就行。Q2DeepSeek要钱吗ADeepSeek本身是开源免费的但如果通过API调用服务商可能会收少量费用。不过一般注册都送额度做个这种小应用绰绰有余。Q3Skills到底是什么必须自己创建吗ASkill就是AI的「操作手册」告诉AI“做这类事情要按照这个流程”。你可以自己创建也可以用别人分享的。网上有很多现成的Skill可以直接导入。Q4生成的软件安全吗会不会有病毒A代码都在你的电脑上生成你可以查看每一行代码。 如果不放心可以让AI解释每段代码的作用。 进阶玩法还能做什么学会了这个套路你可以让AI帮你做只要你敢想AI就能做 最后说几句以前想做个软件得学编程、学框架、踩无数坑没几个月出不来。现在有了Trae DeepSeek Agent Skills普通人也能3分钟拥有自己的软件。我的「剧多多」已经打包好想体验的朋友可以私信我也可以按上面的教程自己做一个——自己做的用起来更香如果你按教程做出来了欢迎在评论区晒图有任何问题随时问我看到就回。#AI编程 #DeepSeek #Trae #零代码开发 #追剧神器 #个人项目 #效率工具
3分钟教你如何使用国产AI编程神器Trae的SOLO模式+Agent Skills+DeepSeek,零代码开发了一个超实用的爆款app(小白也能上手)
大家好我是一个完全不懂桌面应用开发的小白。但我用3分钟时间在Trae这个AI编程工具里通过SOLO模式Agent Skills DeepSeek搞出了一个桌面应用。这篇文章我就手把手教你如何用AI编程工具让AI帮你写代码、做软件全程不需要你写一行代码 第一步搞清楚我们要用什么先说几个关键词其实很简单简单说你在Trae里打开SOLO模式装上合适的Skills用DeepSeek当大脑告诉AI你想做什么AI就帮你把代码写出来。️ 第二步3分钟极速教程真的只需要3分钟第1分钟安装Trae并配置DeepSeek去Trae官网下载安装包完全免费安装后打开点击右上角的AI侧边栏进入AI管理 → 模型设置添加自定义模型选择Provider为Novita或其他支持DeepSeek的服务商从模型下拉框选择 DeepSeek-V3-0324最新版代码能力超强粘贴你的API Key没有的话去Novita注册免费试用一般送额度搞定现在Trae的大脑已经换成DeepSeek了。第2分钟创建SOLO模式安装Skills在Trae中新建一个项目文件夹比如叫「my-app」点击左下角切换到 SOLO模式重要只有SOLO模式才能用Skills在对话中输入帮我创建一个Skill用于开发Electron桌面应用。Skill需要包含项目初始化、窗口配置、多页面路由、打包配置等规范。第3分钟让AI生成你的追剧神器在SOLO模式对话中输入以下指令用Electron帮我开发一个桌面应用名字叫「剧多多」。 功能要求 1. 点击按钮主窗口直接跳转到对应平台的官网 2. 窗口大小设置为1280x720 3. 应用图标放在桌面双击打开直接进入 4. 需要使用webview加载网页确保V#I#P视频可以正常播放保留登录态 请使用我刚刚创建的Electron开发Skill生成完整的项目代码。然后——坐等奇迹发生AI会开始思考、规划、生成代码。你只需要看到提示时点击「接受全部」就行。生成架构图生成代码import os import sys from pathlib import Path def _find_widevine_cdm(): chrome_app_roots [] for env_name in (PROGRAMFILES, PROGRAMFILES(X86), LOCALAPPDATA): base os.environ.get(env_name) candidates [] for root in chrome_app_roots: if not root.exists(): continue try: candidates.extend( list(root.glob(WidevineCdm/*/_platform_specific/win_x64/widevinecdm.dll)) ) candidates.extend( list(root.glob(*/WidevineCdm/*/_platform_specific/win_x64/widevinecdm.dll)) ) except OSError: continue if not candidates: return None def version_key(p: Path): version p.parents[2].name parts [] for x in version.split(.): try: parts.append(int(x)) except ValueError: parts.append(0) return tuple(parts) best max(candidates, keyversion_key) version_dir best.parents[2] return str(best), version_dir.name def _configure_qtwebengine_widevine(): found _find_widevine_cdm() if not found: return cdm_path, cdm_version found flags os.environ.get(QTWEBENGINE_CHROMIUM_FLAGS, ) if --widevine-cdm-path in flags: return extra ( f --widevine-cdm-path{cdm_path} f --widevine-cdm-version{cdm_version} --disable-featuresAutomationControlled --disable-direct-composition --disable-accelerated-video-decode ) os.environ[QTWEBENGINE_CHROMIUM_FLAGS] (flags extra).strip() _configure_qtwebengine_widevine() from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QComboBox, QLabel, QMessageBox, QProgressBar, QGridLayout, QFrame, QScrollArea, QStackedWidget) from PyQt6.QtWebEngineWidgets import QWebEngineView from PyQt6.QtWebEngineCore import QWebEngineSettings, QWebEnginePage, QWebEngineScript, QWebEngineUrlRequestInterceptor from PyQt6.QtCore import QUrl, Qt, QSize, QStandardPaths, QTimer from PyQt6.QtGui import QIcon, QAction, QFont, QCursor, QDesktopServices import config try: from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput from PyQt6.QtMultimediaWidgets import QVideoWidget MULTIMEDIA_AVAILABLE True except Exception: QMediaPlayer None QAudioOutput None QVideoWidget None MULTIMEDIA_AVAILABLE False VIDEO_PLATFORMS [] class WebEngineView(QWebEngineView): def createWindow(self, _type): return self class VideoUrlInterceptor(QWebEngineUrlRequestInterceptor): def __init__(self, handler): super().__init__() self.handler handler def interceptRequest(self, info): url info.requestUrl().toString() if not url: return lower url.lower() if .m3u8 in lower or .mp4 in lower: self.handler(url) class v_i_pVideoPlayer(QMainWindow): def __init__(self): super().__init__() self.resize(1280, 900) self.setStyleSheet(background-color: #4DB6AC;) # 全局青色背景 self.h264_supported None self.pending_parse False self.last_video_url self.player_available MULTIMEDIA_AVAILABLE self.media_player None self.audio_output None self.video_widget None # 初始化界面 self.init_ui() # 加载默认首页 self.load_url(config.DEFAULT_HOME_URL) def init_ui(self): # 主窗口部件 central_widget QWidget() self.setCentralWidget(central_widget) # 主布局 main_layout QVBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) central_widget.setLayout(main_layout) # --- 1. 顶部 Header 区域 --- header_widget QWidget() header_widget.setStyleSheet(background-color: #00695C; color: white;) header_widget.setFixedHeight(50) header_layout QHBoxLayout() header_layout.setContentsMargins(20, 0, 20, 0) header_widget.setLayout(header_layout) title_label.setFont(QFont(Microsoft YaHei, 14, QFont.Weight.Bold)) title_label.setStyleSheet(color: white;) info_label.setFont(QFont(Microsoft YaHei, 10)) info_label.setStyleSheet(color: #E0F2F1;) header_layout.addWidget(title_label) header_layout.addStretch() header_layout.addWidget(info_label) main_layout.addWidget(header_widget) # --- 2. 控制/搜索区域 --- control_container QWidget() control_container.setStyleSheet(background-color: #4DB6AC;) # 青色背景 control_layout QHBoxLayout() control_layout.setContentsMargins(20, 15, 20, 15) control_layout.setSpacing(10) control_container.setLayout(control_layout) # 导航按钮组 (小一点) nav_layout QHBoxLayout() nav_layout.setSpacing(5) btn_style_nav QPushButton { background-color: rgba(255, 255, 255, 0.2); color: white; border: none; border-radius: 4px; font-weight: bold; } QPushButton:hover { background-color: rgba(255, 255, 255, 0.4); } self.btn_back QPushButton(←) self.btn_back.setFixedSize(30, 30) self.btn_back.setStyleSheet(btn_style_nav) self.btn_back.clicked.connect(self.navigate_back) self.btn_forward QPushButton(→) self.btn_forward.setFixedSize(30, 30) self.btn_forward.setStyleSheet(btn_style_nav) self.btn_forward.clicked.connect(self.navigate_forward) self.btn_refresh QPushButton(↻) self.btn_refresh.setFixedSize(30, 30) self.btn_refresh.setStyleSheet(btn_style_nav) self.btn_refresh.clicked.connect(self.reload_page) self.btn_home QPushButton(首页) self.btn_home.setFixedSize(50, 30) self.btn_home.setStyleSheet(btn_style_nav) self.btn_home.clicked.connect(lambda: self.load_url(config.DEFAULT_HOME_URL)) nav_layout.addWidget(self.btn_back) nav_layout.addWidget(self.btn_forward) nav_layout.addWidget(self.btn_refresh) nav_layout.addWidget(self.btn_home) label_addr.setAlignment(Qt.AlignmentFlag.AlignCenter) label_addr.setFixedSize(120, 40) label_addr.setStyleSheet( background-color: #2ECC71; color: white; border-radius: 4px; font-size: 12px; ) # 地址输入框 self.url_input QLineEdit() self.url_input.setPlaceholderText(在此输入或粘贴视频网址...) self.url_input.setFixedHeight(40) self.url_input.setStyleSheet( QLineEdit { border: 2px solid #2ECC71; border-radius: 4px; padding: 0 10px; font-size: 14px; background-color: white; } ) self.url_input.returnPressed.connect(self.load_from_input) # 线路选择 (稍微美化) self.combo_interface QComboBox() self.combo_interface.setFixedHeight(40) self.combo_interface.setMinimumWidth(120) self.combo_interface.setStyleSheet( QComboBox { border: 2px solid #2ECC71; border-radius: 4px; padding: 0 10px; background-color: white; } QComboBox::drop-down { border: none; } ) for item in config.v_i_p_INTERFACES: self.combo_interface.addItem(item[name], item[url]) # 立即播放按钮 self.btn_play QPushButton(立即播放) self.btn_play.setFixedSize(100, 40) self.btn_play.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.btn_play.setStyleSheet( QPushButton { background-color: #2ECC71; color: white; font-weight: bold; font-size: 14px; border-radius: 4px; border: none; } QPushButton:hover { background-color: #27ae60; } ) self.btn_play.clicked.connect(self.parse_video) # 用浏览器打开按钮 self.btn_open_browser QPushButton(浏览器播放) self.btn_open_browser.setFixedSize(100, 40) self.btn_open_browser.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) self.btn_open_browser.setStyleSheet( QPushButton { background-color: #3498DB; color: white; font-weight: bold; font-size: 14px; border-radius: 4px; border: none; } QPushButton:hover { background-color: #2980B9; } ) self.btn_open_browser.clicked.connect(self.open_in_system_browser) # 组装控制栏 control_layout.addLayout(nav_layout) control_layout.addSpacing(15) control_layout.addWidget(label_addr) control_layout.addWidget(self.url_input, 1) # 拉伸 control_layout.addWidget(self.combo_interface) control_layout.addWidget(self.btn_play) control_layout.addWidget(self.btn_open_browser) main_layout.addWidget(control_container) # --- 3. 提示文字 --- tip_widget QWidget() tip_widget.setStyleSheet(background-color: #4DB6AC;) tip_layout QVBoxLayout() tip_layout.setContentsMargins(0, 5, 0, 10) tip_widget.setLayout(tip_layout) tip_label.setAlignment(Qt.AlignmentFlag.AlignCenter) tip_label.setStyleSheet(color: white; font-size: 12px;) tip_layout.addWidget(tip_label) main_layout.addWidget(tip_widget) web_container QWidget() web_container.setStyleSheet(background-color: #4DB6AC; padding: 0px 20px;) web_layout QVBoxLayout() web_layout.setContentsMargins(0, 0, 0, 0) web_container.setLayout(web_layout) self.browser WebEngineView() self.browser.setStyleSheet(background-color: black;) if self.player_available: self.media_player QMediaPlayer() self.audio_output QAudioOutput() self.media_player.setAudioOutput(self.audio_output) self.video_widget QVideoWidget() self.media_player.setVideoOutput(self.video_widget) try: data_path QStandardPaths.writableLocation(QStandardPaths.StandardLocation.AppLocalDataLocation) profile_path os.path.join(data_path, v_i_p_video_player_profile) cache_path os.path.join(profile_path, cache) if not os.path.exists(profile_path): os.makedirs(profile_path, exist_okTrue) if not os.path.exists(cache_path): os.makedirs(cache_path, exist_okTrue) profile self.browser.page().profile() profile.setPersistentStoragePath(profile_path) profile.setCachePath(cache_path) self.video_interceptor VideoUrlInterceptor(self.on_video_url_detected) profile.setUrlRequestInterceptor(self.video_interceptor) print(fProfile storage path set to: {profile_path}) except Exception as e: print(fFailed to set profile path: {e}) script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentCreation) script.setWorldId(QWebEngineScript.ScriptWorldId.MainWorld) script.setRunsOnSubFrames(True) self.browser.page().scripts().insert(script) settings self.browser.settings() settings.setAttribute(QWebEngineSettings.WebAttribute.PluginsEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.LocalStorageEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows, True) settings.setAttribute(QWebEngineSettings.WebAttribute.PlaybackRequiresUserGesture, False) settings.setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.AllowRunningInsecureContent, True) # 启用更多高级特性以提高兼容性 settings.setAttribute(QWebEngineSettings.WebAttribute.DnsPrefetchEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.Accelerated2dCanvasEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.WebGLEnabled, True) settings.setAttribute(QWebEngineSettings.WebAttribute.XSSAuditingEnabled, False) settings.setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls, True) settings.setAttribute(QWebEngineSettings.WebAttribute.HyperlinkAuditingEnabled, False) self.browser.urlChanged.connect(self.update_url_bar) self.browser.loadProgress.connect(self.update_progress) self.browser.titleChanged.connect(self.update_title) self.browser.loadFinished.connect(self.on_load_finished) if self.player_available and self.video_widget is not None: self.view_stack QStackedWidget() self.view_stack.addWidget(self.browser) self.view_stack.addWidget(self.video_widget) web_layout.addWidget(self.view_stack) else: self.view_stack None web_layout.addWidget(self.browser) main_layout.addWidget(web_container, 1) # 浏览器占据主要空间 self.progress_bar QProgressBar() self.progress_bar.setFixedHeight(2) self.progress_bar.setTextVisible(False) self.progress_bar.setStyleSheet(QProgressBar { border: 0px; background-color: transparent; } QProgressBar::chunk { background-color: #2ECC71; }) main_layout.addWidget(self.progress_bar) # --- 5. 底部平台快捷栏 --- platform_widget QWidget() platform_widget.setStyleSheet(background-color: #4DB6AC;) platform_layout QVBoxLayout() platform_layout.setContentsMargins(20, 10, 20, 20) platform_widget.setLayout(platform_layout) bottom_tip.setAlignment(Qt.AlignmentFlag.AlignCenter) bottom_tip.setStyleSheet(color: white; font-size: 13px; margin-bottom: 10px;) platform_layout.addWidget(bottom_tip) # 图标布局 (单行显示) platforms_layout QHBoxLayout() platforms_layout.setSpacing(15) # 生成按钮 for platform in VIDEO_PLATFORMS: btn QPushButton(platform[name]) btn.setFixedHeight(50) btn.setCursor(QCursor(Qt.CursorShape.PointingHandCursor)) # 使用动态颜色样式 btn.setStyleSheet(f QPushButton {{ background-color: white; color: {platform[color]}; font-size: 16px; font-weight: bold; border-radius: 6px; border: 1px solid #ddd; }} QPushButton:hover {{ background-color: #f9f9f9; border: 2px solid {platform[color]}; }} ) # 使用闭包绑定URL btn.clicked.connect(lambda checked, urlplatform[url]: self.load_url(url)) platforms_layout.addWidget(btn) platform_layout.addLayout(platforms_layout) main_layout.addWidget(platform_widget) # --- 逻辑功能 --- def load_url(self, url_str): 加载URL if not url_str.startswith(http): url_str https:// url_str self.browser.load(QUrl(url_str)) def load_from_input(self): 从地址栏加载 url self.url_input.text().strip() if url: self.load_url(url) def parse_video(self): self.pending_parse True self.last_video_url if self.view_stack is not None: self.view_stack.setCurrentWidget(self.browser) self.browser.load(QUrl(final_url)) def open_in_system_browser(self): 在系统浏览器中打开当前页面 QDesktopServices.openUrl(self.browser.url()) def open_in_system_browser_url(self, url): QDesktopServices.openUrl(QUrl(url)) def on_video_url_detected(self, url): if not self.pending_parse: return if url self.last_video_url: return self.last_video_url url if self.player_available and self.media_player is not None: self.play_video_url(url) self.pending_parse False def play_video_url(self, url): if self.view_stack is not None and self.video_widget is not None: self.view_stack.setCurrentWidget(self.video_widget) self.media_player.setSource(QUrl(url)) self.media_player.play() def navigate_back(self): self.browser.back() def navigate_forward(self): self.browser.forward() def reload_page(self): self.browser.reload() def update_url_bar(self, qurl): 更新地址栏显示 self.url_input.setText(qurl.toString()) self.url_input.setCursorPosition(0) def update_progress(self, progress): 更新进度条 self.progress_bar.setValue(progress) if progress 100: self.progress_bar.hide() else: self.progress_bar.show() def update_title(self, title): 更新窗口标题 if __name__ __main__: app QApplication(sys.argv) window v_i_pVideoPlayer() window.show() sys.exit(app.exec())3、项目开发说明文档✨ 发生了什么AI到底做了什么你可能会好奇这几句话AI真的能做出一个完整的软件吗答案是能而且做得很好DeepSeek V3.1在代码生成和推理方面的能力非常强AIME 2025基准测试得分88.4%接近GPT-5的水平。加上Trae的Agent模式AI可以1、理解你的需求知道你要做一个聚合视频的桌面应用2、调用Skill规范按照Electron开发的最佳实践来组织代码3、生成完整项目包括主进程、渲染进程、package.json、打包配置等4、处理技术细节比如webview配置、跨域问题、登录态保持整个过程就像你请了一个24小时待命的程序员你说需求他写代码你只管验收 最终成果展示代码生成后在项目目录下运行bash npm start npm start你就会看到——「剧#多#多」桌面应用打开了如果你想打包成exe/dmg发给朋友只需要告诉AIAI会自动修改package.json添加打包配置。运行 npm run dist安装包就出来了 小白常见问题Q1我真的一点代码都不会能搞定吗A完全可以我本人就是例子。全程只需要打字AI写代码你负责确认就行。Q2DeepSeek要钱吗ADeepSeek本身是开源免费的但如果通过API调用服务商可能会收少量费用。不过一般注册都送额度做个这种小应用绰绰有余。Q3Skills到底是什么必须自己创建吗ASkill就是AI的「操作手册」告诉AI“做这类事情要按照这个流程”。你可以自己创建也可以用别人分享的。网上有很多现成的Skill可以直接导入。Q4生成的软件安全吗会不会有病毒A代码都在你的电脑上生成你可以查看每一行代码。 如果不放心可以让AI解释每段代码的作用。 进阶玩法还能做什么学会了这个套路你可以让AI帮你做只要你敢想AI就能做 最后说几句以前想做个软件得学编程、学框架、踩无数坑没几个月出不来。现在有了Trae DeepSeek Agent Skills普通人也能3分钟拥有自己的软件。我的「剧多多」已经打包好想体验的朋友可以私信我也可以按上面的教程自己做一个——自己做的用起来更香如果你按教程做出来了欢迎在评论区晒图有任何问题随时问我看到就回。#AI编程 #DeepSeek #Trae #零代码开发 #追剧神器 #个人项目 #效率工具