import markupsafe from odoo import fields, models, api from odoo.exceptions import ValidationError class VideoPlayback(models.Model): _name = 'yuthon.school.video' _description = '教学视频' _order = 'category_id, sequence, name' name = fields.Char( string='视频标题', required=True, help='如:Odoo 18 模块结构详解、Binary 字段用法等', ) sequence = fields.Integer( string='排序', default=10, help='同一目录下的视频排序序号', ) category_id = fields.Many2one( 'yuthon.school.category', string='学习目录', ondelete='restrict', help='该视频所属的学习目录分类', ) chapter_id = fields.Many2one( 'yuthon.school.chapter', string='所属章节', ondelete='restrict', help='该视频所属的教材章节', ) difficulty = fields.Selection( [('beginner', '入门'), ('intermediate', '进阶'), ('advanced', '高级')], string='难度等级', default='beginner', help='该视频的学习难度', ) duration = fields.Char( string='视频时长', help='如:15:30 表示 15 分钟 30 秒', ) video_file = fields.Binary( string='视频文件', attachment=True, required=True, help='上传本地教学视频(支持 MP4、WebM、OGG 等格式)', ) video_filename = fields.Char( string='文件名', help='视频文件的原始文件名', ) file_size_display = fields.Char( string='文件大小', compute='_compute_file_info', help='视频文件的大小', ) description = fields.Text( string='视频简介', help='该教学视频的内容概要', ) key_points = fields.Text( string='知识要点', help='该视频涵盖的关键知识点', ) active = fields.Boolean( string='启用', default=True, ) video_player = fields.Html( string='视频播放器', compute='_compute_video_player', sanitize=False, ) @api.depends('video_file', 'video_filename') def _compute_file_info(self): """计算文件大小,显示为可读格式""" for record in self: if record.video_file: encoded = record.video_file if isinstance(encoded, bytes): padding = 2 if encoded.endswith(b'==') else (1 if encoded.endswith(b'=') else 0) else: padding = 2 if encoded.endswith('==') else (1 if encoded.endswith('=') else 0) size = (len(encoded) * 3) // 4 - padding if size < 1024: record.file_size_display = f'{size} B' elif size < 1024 * 1024: record.file_size_display = f'{size / 1024:.1f} KB' elif size < 1024 * 1024 * 1024: record.file_size_display = f'{size / (1024 * 1024):.1f} MB' else: record.file_size_display = f'{size / (1024 * 1024 * 1024):.1f} GB' else: record.file_size_display = '0 B' @api.depends('video_file', 'video_filename') def _compute_video_player(self): """生成视频播放器HTML""" mime_map = { 'mp4': 'video/mp4', 'webm': 'video/webm', 'ogg': 'video/ogg', 'ogv': 'video/ogg', 'mov': 'video/quicktime', 'mkv': 'video/x-matroska', 'avi': 'video/x-msvideo', } for record in self: if record.video_file and record.id: mime_type = 'video/mp4' if record.video_filename: ext = record.video_filename.rsplit('.', 1)[-1].lower() if '.' in record.video_filename else '' mime_type = mime_map.get(ext, 'video/mp4') html = ( '
请上传视频文件并保存后查看播放。