148 lines
5.5 KiB
Python
148 lines
5.5 KiB
Python
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}'
|
||
)
|
||
|