shcool/school/models/video_playback.py
2026-08-03 12:03:31 +08:00

148 lines
5.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 = (
'<div style="text-align: center; padding: 15px; '
'background: #000; border-radius: 8px;">'
'<video controls preload="metadata" '
'style="width: 100%; max-width: 900px; border-radius: 4px;">'
f'<source src="/yuthon_school/video/{record.id}" type="{mime_type}">'
'<p style="color: #fff; padding: 20px;">'
'您的浏览器不支持视频播放。</p>'
'</video></div>'
)
record.video_player = markupsafe.Markup(html)
else:
record.video_player = markupsafe.Markup(
'<div style="text-align: center; color: #999; '
'padding: 30px; background: #f5f5f5; border-radius: 8px;">'
'<p>请上传视频文件并保存后查看播放。</p></div>'
)
@api.constrains('video_filename')
def _check_video_file(self):
"""验证上传的文件是否为视频格式"""
allowed_extensions = ['mp4', 'webm', 'ogg', 'ogv', 'mov', 'mkv', 'avi']
for record in self:
if record.video_filename:
ext = record.video_filename.rsplit('.', 1)[-1].lower() if '.' in record.video_filename else ''
if ext and ext not in allowed_extensions:
raise ValidationError(
f'请上传视频文件支持格式MP4、WebM、OGG、MOV、MKV、AVI当前文件格式为 .{ext}'
)