78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
import base64
|
|
import os
|
|
import re
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
|
|
|
|
class VideoPlaybackController(http.Controller):
|
|
|
|
@http.route('/yuthon_school/video/<int:video_id>', type='http', auth='user', methods=['GET'])
|
|
def serve_video(self, video_id, **kwargs):
|
|
"""提供视频流服务,支持 Range 请求以实现视频进度拖拽"""
|
|
video = request.env['yuthon.school.video'].sudo().browse(video_id)
|
|
if not video.exists() or not video.video_file:
|
|
return request.not_found()
|
|
|
|
video_data = self._decode_video(video.video_file)
|
|
file_size = len(video_data)
|
|
|
|
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',
|
|
}
|
|
mime_type = 'video/mp4'
|
|
if video.video_filename:
|
|
ext = os.path.splitext(video.video_filename)[1].lower().lstrip('.')
|
|
mime_type = mime_map.get(ext, 'video/mp4')
|
|
|
|
# 处理 Range 请求(视频进度条拖拽需要)
|
|
range_header = request.httprequest.headers.get('Range')
|
|
if range_header:
|
|
range_match = re.search(r'bytes=(\d+)-(\d*)', range_header)
|
|
if range_match:
|
|
start = int(range_match.group(1))
|
|
end = int(range_match.group(2)) if range_match.group(2) else file_size - 1
|
|
|
|
if start >= file_size:
|
|
headers = [('Content-Range', f'bytes */{file_size}')]
|
|
return request.make_response(b'', headers, status=416)
|
|
|
|
end = min(end, file_size - 1)
|
|
chunk = video_data[start:end + 1]
|
|
|
|
headers = [
|
|
('Content-Type', mime_type),
|
|
('Content-Length', str(len(chunk))),
|
|
('Content-Range', f'bytes {start}-{end}/{file_size}'),
|
|
('Accept-Ranges', 'bytes'),
|
|
]
|
|
return request.make_response(chunk, headers, status=206)
|
|
|
|
# 返回完整视频内容(在线播放)
|
|
headers = [
|
|
('Content-Type', mime_type),
|
|
('Content-Length', str(file_size)),
|
|
('Accept-Ranges', 'bytes'),
|
|
]
|
|
return request.make_response(video_data, headers)
|
|
|
|
|
|
@staticmethod
|
|
def _decode_video(raw_file):
|
|
"""解码 Odoo 18 Binary 字段数据"""
|
|
if isinstance(raw_file, bytes):
|
|
try:
|
|
return base64.b64decode(raw_file, validate=True)
|
|
except Exception:
|
|
return raw_file
|
|
elif isinstance(raw_file, str):
|
|
return base64.b64decode(raw_file)
|
|
return b''
|