乒乓球口袋教练 HarmonyOS 学习应用(02):本地视频资源与播放器入口
一、课程 id 与视频文件不能直接拼接课程标题会随着教学内容调整rawfile 文件名却受构建产物约束。让页面直接从标题推导文件名会把改名和资源替换变成隐性风险。当前资源表使用 courseId 连接课程和视频资产播放器只接收已经解析过的资源信息。export interface CourseVideoAsset { courseId: string; fileName: string; url: string; } // Fill in your own video hosting base URL. Example: // https://your-domain.example.com/tabletennisballLearn/videos export const VIDEO_DOWNLOAD_BASE_URL: string ; interface CourseVideoFile { courseId: string; fileName: string; } const COURSE_VIDEO_FILES: CourseVideoFile[] [ { courseId: skill_penhold_grip, fileName: skill_penhold_grip.mp4 }, { courseId: skill_shakehand_grip, fileName: skill_penhold_grip.mp4 }, { courseId: skill_forehand_drive, fileName: skill_forehand_drive.mp4 }, { courseId: skill_backhand_block, fileName: skill_backhand_block.mp4 }, { courseId: skill_short_serve, fileName: skill_short_serve.mp4 }, { courseId: skill_forehand_loop, fileName: skill_forehand_loop.mp4 }, { courseId: skill_footwork_two_point, fileName: skill_footwork_two_point.mp4 }, { courseId: skill_receive_push, fileName: skill_receive_push.mp4 }, { courseId: skill_backhand_flick, fileName: skill_backhand_flick.mp4 }, { courseId: skill_forehand_flick, fileName: skill_forehand_flick.mp4 }, { courseId: skill_push_long, fileName: skill_push_long.mp4 }, { courseId: skill_sidespin_serve, fileName: skill_sidespin_serve.mp4 }, { courseId: skill_counter_loop, fileName: skill_counter_loop.mp4 }, { courseId: tactic_third_ball, fileName: tactic_third_ball.mp4 }, { courseId: tactic_placement, fileName: tactic_placement.mp4 },二、播放器入口如何先确认资源存在CourseVideoService 先查找资源条目再确认打包资源或已下载文件是否存在。详情页根据这个结果决定能否显示播放或下载入口播放器不负责猜测文件路径。这样资源缺失的原因能在进入媒体组件前被解释而不是把黑屏当作播放失败。事实唯一来源页面职责正常结果模型或服务的回读渲染可观察状态边界结果明确的缺项或失败分支说明原因并保留入口重进结果持久化或模型再次解析不复用旧页面变量export class CourseVideoService { static canDownload(courseId: string): boolean { const asset: CourseVideoAsset | undefined findCourseVideoAsset(courseId); return asset ! undefined asset.url.length 0; } static getDownloadFileName(courseId: string): string { const asset: CourseVideoAsset | undefined findCourseVideoAsset(courseId); return asset?.fileName ?? ; } static getDownloadedVideoPath(context: common.UIAbilityContext, courseId: string): string { const asset: CourseVideoAsset | undefined findCourseVideoAsset(courseId); if (asset undefined) { return ; } return CourseVideoService.videoDir(context) / asset.fileName; } static getDownloadedVideoUri(context: common.UIAbilityContext, courseId: string): string { const path: string CourseVideoService.getDownloadedVideoPath(context, courseId); if (path.length 0 || !CourseVideoService.exists(path)) { return ; } return file:// path; } static hasDownloadedVideo(context: common.UIAbilityContext, courseId: string): boolean { const path: string CourseVideoService.getDownloadedVideoPath(context, courseId); return path.length 0 CourseVideoService.exists(path); } static managedDownloads(context: common.UIAbilityContext): DownloadVideoItem[] { const result: DownloadVideoItem[] []; for (const courseId of MANAGED_COURSE_IDS) { const course: Course | undefined findCourse(courseId); const asset: CourseVideoAsset | undefined findCourseVideoAsset(courseId); if (course ! undefined asset ! undefined) { const path: string CourseVideoService.getDownloadedVideoPath(context, courseId); const item: DownloadVideoItem new DownloadVideoItem(); item.courseId courseId; item.title course.title; item.fileName asset.fileName; item.path path;三、下载状态为什么不能只存在按钮里下载完成也不能只写一个“已下载”布尔值。服务需要检查目标文件是否真实存在下载页、详情页和播放器复用这个判断。重启后重新读取文件状态才能避免详情页显示已下载而下载列表为空的错觉。} return TXT_NO_VIDEO_PLACEHOLDER; } private openPreferredVideo(c: Course): void { if (this.hasPlayableVideo(c)) { const params: PlayerParams { courseId: c.id, startSec: 0 }; router.pushUrl({ url: Routes.PLAYER, params }); return; } AppToast.show(TXT_NO_VIDEO); } private downloadActionText(): string { if (this.savingVideo) { return TXT_DOWNLOADING_LOCAL; } return this.downloadedVideo ? TXT_DOWNLOADED_LOCAL : TXT_DOWNLOAD_LOCAL; } private refreshDownloadState(c: Course): void { const ctx: common.UIAbilityContext getContext(this) as common.UIAbilityContext; this.downloadedVideo CourseVideoService.hasDownloadedVideo(ctx, c.id); } private confirmSaveVideo(c: Course): void { if (this.savingVideo || this.downloadedVideo) { return; } promptAction.showDialog({ title: TXT_DOWNLOAD_CONFIRM_TITLE, message: TXT_DOWNLOAD_CONFIRM_MSG, buttons: [ { text: TXT_DOWNLOAD_ONLY_LOCAL, color: this.palette.textSecondary }, { text: TXT_DOWNLOAD_TO_GALLERY, color: this.palette.primary } ] }).then((result: promptAction.ShowDialogSuccessResponse) {四、播放失败怎样留在可解释的页面异常时要区分三种情况课程不存在、资源表没有映射、文件没有落地。它们都不应生成空地址或默认跳到其他课程页面应保留返回或重新下载的操作。把三种问题混成一个“播放失败”会让维护者无法定位是模型还是文件出了问题。风险不能采用的做法当前链路的处理数据不存在用默认对象继续渲染停在可解释的空或失败状态页面重进复用上一次内存字段重新查询模型或服务重复动作追加同一业务事实通过稳定 id 或进入标记收敛AppSizes, AppText, ColorPalette, LightPalette, PlaceholderImage, AppToast, PlayerParams, Course, findCourse, nextCourse, prevCourse, Routes, ProgressService, formatDuration, courseCover, courseVideoSrc, CourseVideoService } from library1; const SPEEDS: number[] [0.5, 1.0, 1.25, 1.5, 2.0]; Component export struct FullscreenPlayerPage { StorageProp(palette) palette: ColorPalette LightPalette; State course: Course | null null; State playing: boolean true; State currentSec: number 0; State speedIdx: number 1; State showControl: boolean true; State videoReady: boolean false; State videoError: boolean false; State realDuration: number 0; State resolvedVideoSrc: ResourceStr ; State hasResolvedVideo: boolean false; private videoController: VideoController new VideoController(); // 占位模式计时器无视频文件时使用 private timerId: number -1; private hideTimer: number -1; aboutToAppear(): void { const p: PlayerParams router.getParams() as PlayerParams;五、本地视频的回读动作验收依次覆盖已映射课程、没有资源映射的课程和下载后重新进入。对已映射课程观察标题、媒体入口和控制层对不可用课程确认入口被解释性地收敛对下载后重进确认列表与详情得到同一个文件状态。验收阶段操作应观察到的结果建立事实完成一次与主题直接对应的动作服务或模型能回读该事实页面回读进入目标页或结果页标题、数据和状态相互一致重进验证返回、重启或切换模块后再次进入结果不依赖上一页残留六、取舍与后续维护当前实现把业务事实集中到模型与服务页面只负责呈现和触发操作。这样做会多出类型、查询和回读代码却能避免同一份统计或记录在多个页面分叉。需要扩展新课程、新资源或新题目时应先补齐稳定标识与服务合同再补页面入口。ArkTS 的响应式状态机制可参考 HarmonyOS ArkTS 状态管理文档。本文的代码和验收围绕同一个原则可见结果必须能回到对应模型、服务或持久化事实不以邻近页面和控件文字代替主题结果。七、围绕事实来源继续演进乒乓球口袋教练 HarmonyOS 学习应用02本地视频资源与播放器入口 关注的不是给页面再增加一层显示逻辑而是让“课程 id 与视频文件不能直接拼接”始终能回到唯一的业务事实。模型字段、服务返回值和页面展示应当保持单向关系事实先发生页面随后回读页面不保存第二份能够被误认为真实数据的临时副本。处理“播放器入口如何先确认资源存在”时新增条件必须先归属到课程模型、资源服务、日志事实或测验结果之一。把条件散落在多个组件里短期看起来省事后续就会出现入口不同、重进不同、统计不同的问题。把规则放回稳定入口页面只根据结果改变可见状态能让同一结论在不同入口被复查。“下载状态为什么不能只存在按钮里”说明当前实现选择了页面重建后重新查询而不是把对象和统计快照长期塞进路由参数。读取会增加少量成本却避免课程改名、资源替换、日志编辑或题库切换后继续显示旧内容。学习应用的结果需要能解释不能只追求一次渲染恰好看起来正确。异常分支也应保留语义异常时要区分三种情况课程不存在、资源表没有映射、文件没有落地。它们都不应生成空地址或默认跳到其他课程页面应保留返回或重新下载的操作。把三种问题混成一个“播放失败”会让维护者无法定位是模型还是文件出了问题。 这要求页面区分数据不存在、动作未完成和资源不可用并提供返回选择、补充输入、重新下载或再次答题等对应操作不能把不同原因压缩成同一条模糊的成功或失败提示。最后执行“本地视频的回读动作”时至少观察一次主题动作、一次目标页回读和一次重进或切换后的回读。三次观察都指向相同模型或服务结果才说明实现链路没有依赖旧页面残留。 最后执行“本地视频的回读动作”时至少观察一次主题动作、一次目标页回读和一次重进或切换后的回读。三次观察都指向相同模型或服务结果才说明实现链路没有依赖旧页面残留。面向维护的检查可以沿三个问题展开输入对象的稳定标识是什么哪个服务或模型决定结果页面如何把该结果显示出来。课程 id 先映射到受管理的视频资源再由详情和播放器统一判断可播放性避免资源缺失时仍创建空播放器。 这三个问题分别对应数据合同、调用边界和可见反馈只要其中一层被绕过新增入口就可能产生和原入口不同的事实。对失败路径也要保留同样的追踪能力。先让服务返回清晰的不可用、缺项或未完成状态再由页面提供与原因匹配的操作。这样日志、课程、资源和题目发生变化时回归测试能够看到具体的分歧点而不是只能面对一张没有信息的空白页面。这种组织不会承诺所有后续需求都零成本。新增字段需要同步更新模型、服务与验收但它把变更范围限制在可枚举的位置避免把同一判断复制到首页、详情、个人页和结果页后再分别修补。

相关新闻

2026年TikTok算法突变,我砸了15万才总结出这3条跨境社媒避坑铁律(深度复盘)

2026年TikTok算法突变,我砸了15万才总结出这3条跨境社媒避坑铁律(深度复盘)

一、一场悄然来临的变革:当流量逻辑开始“转弯” 2025年末至2026年初,许多跨境社交媒体从业者隐约感觉到一丝异样。曾经屡试不爽的内容策略,互动数据开始波动;稳定增长的流量曲线,出现了难以解释的平缓甚至下滑。起初&…

2026/7/29 23:21:56 阅读更多 →
Windows系统Mesa3D图形驱动终极指南:免费开源OpenGL/Vulkan加速解决方案

Windows系统Mesa3D图形驱动终极指南:免费开源OpenGL/Vulkan加速解决方案

Windows系统Mesa3D图形驱动终极指南:免费开源OpenGL/Vulkan加速解决方案 【免费下载链接】mesa-dist-win Pre-built Mesa3D drivers for Windows 项目地址: https://gitcode.com/gh_mirrors/me/mesa-dist-win 想要在Windows系统上获得强大的开源图形驱动支持…

2026/7/29 23:20:56 阅读更多 →
性价比高的工商变更服务商

性价比高的工商变更服务商

今天下午,我刚从一个客户那回来。他微信上跟我说了一句话,让我差点想把手机摔了。“兄弟,我上个月找的那家代办,收费是便宜,880块全包。结果呢?我公司地址变更了三个月都没下来,税务局那边还多了…

2026/7/29 23:20:56 阅读更多 →

最新新闻

华为MetaERP Oracle EBS‑R12 PA AutoAccounting(PA 自动会计)完整详解核心定位:AutoAccounting 是 PA 模块内置科目推导引擎(PA 业务层),

华为MetaERP Oracle EBS‑R12 PA AutoAccounting(PA 自动会计)完整详解核心定位:AutoAccounting 是 PA 模块内置科目推导引擎(PA 业务层),

Oracle EBS‑R12 PA AutoAccounting(PA 自动会计)完整详解核心定位:AutoAccounting 是 PA 模块内置科目推导引擎(PA 业务层),它不是最终会计凭证引擎;R12 架构下它输出默认科目 CCID&#xff0c…

2026/7/29 23:34:00 阅读更多 →
N_m3u8DL-RE终极指南:跨平台流媒体下载神器从零到精通

N_m3u8DL-RE终极指南:跨平台流媒体下载神器从零到精通

N_m3u8DL-RE终极指南:跨平台流媒体下载神器从零到精通 【免费下载链接】N_m3u8DL-RE Cross-Platform, modern and powerful stream downloader for MPD/M3U8/ISM. English/简体中文/繁體中文. 项目地址: https://gitcode.com/GitHub_Trending/nm3/N_m3u8DL-RE …

2026/7/29 23:32:59 阅读更多 →
Django-Vue3-Admin安全最佳实践:接口防护与数据加密策略

Django-Vue3-Admin安全最佳实践:接口防护与数据加密策略

Django-Vue3-Admin安全最佳实践:接口防护与数据加密策略 【免费下载链接】django-vue3-admin Django-Vue3-Admin is a comprehensive basic development platform based on the RBAC (Role-Based Access Control) model for permission control, with column-level …

2026/7/29 23:32:59 阅读更多 →
5个关键技术点优化GLM-5.2-colibri-int4-with-int8-mtp推理性能

5个关键技术点优化GLM-5.2-colibri-int4-with-int8-mtp推理性能

5个关键技术点优化GLM-5.2-colibri-int4-with-int8-mtp推理性能 【免费下载链接】GLM-5.2-colibri-int4-with-int8-mtp 项目地址: https://ai.gitcode.com/hf_mirrors/mateogrgic/GLM-5.2-colibri-int4-with-int8-mtp GLM-5.2-colibri-int4-with-int8-mtp是基于GLM-5.2…

2026/7/29 23:32:59 阅读更多 →
FireSharp认证管理:用户创建、密码重置与权限控制最佳实践

FireSharp认证管理:用户创建、密码重置与权限控制最佳实践

FireSharp认证管理:用户创建、密码重置与权限控制最佳实践 【免费下载链接】FireSharp An asynchronous cross-platform .Net library for Firebase 项目地址: https://gitcode.com/gh_mirrors/fi/FireSharp FireSharp是一个异步跨平台的.Net Firebase库&…

2026/7/29 23:32:59 阅读更多 →
【爱马仕】Hermes AI 智能工具快速搭建教程,Windows 整合包一键落地

【爱马仕】Hermes AI 智能工具快速搭建教程,Windows 整合包一键落地

Windows 轻量化部署 Hermes 智能体,本地桌面自动化工具搭建教程 文章核心亮点 内置全套运行依赖、免手动环境配置、全程图形化自动部署、适配本地办公自动化任务 资源下载地址:https://hm.ikidi.top/api/download/package/55?promoCodeIV644F14DA00 …

2026/7/29 23:32:59 阅读更多 →

日新闻

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:00:23 阅读更多 →
AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础 在上一期「AI编程系列」中,我们学习了如何构建一个基础的 AI 问答系统,通过简单的输入输出让模型回应问题。但现实世界中的 AI 应用往往需要处理更复杂的场景:…

2026/7/29 0:00:23 阅读更多 →
AI智能体开发实战:从工具调用到企业级部署

AI智能体开发实战:从工具调用到企业级部署

1. 从被动问答到主动执行:AI Agent的范式转变过去两年,大语言模型最显著的应用形态是聊天机器人——用户提问,AI回答。但真正的生产力革命发生在2023年下半年:当AI学会主动调用工具完成任务时,生产力工具的历史被彻底改…

2026/7/29 0:00:23 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻