karideo/back/src/api/data.py

219 lines
7.1 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
##
## @author Edouard DUPIN
##
## @copyright 2019, Edouard DUPIN, all right reserved
##
## @license MPL v2.0 (see license file)
##
import time
import json
import os
import sys
import datetime
import time, threading
import realog.debug as debug
from aiofiles import os as async_os
from pymediainfo import MediaInfo
from sanic import Sanic
from sanic import response
from sanic import views
from sanic import Blueprint
from sanic.exceptions import ServerError
from sanic.response import file_stream
from sanic_simple_swagger import swagger_blueprint, openapi_blueprint
from sanic_simple_swagger import doc
import tools
import data_interface
import data_global_elements
import hashlib
import shutil
tmp_value = 0
#curl -F 'file=@Totally_Spies.mp4;type=application/octet-stream' -H 'transfer-encoding:chunked' 127.0.0.1:15080/data -X POST -O; echo ;
def add(_app, _name_api):
elem_blueprint = Blueprint(_name_api)
"""
@elem_blueprint.get('/' + _name_api, strict_slashes=True)
@doc.summary("Show saisons")
@doc.description("Display a listing of the resource.")
@doc.produces(content_type='application/json')
async def list(request):
return response.json(data_global_elements.get_interface(_name_api).gets())
"""
class DataModelBdd:
id = int
size = int
sha512 = str
mime_type = str
original_name = [str, type(None)]
# creating time
create_date = str
data_global_elements.get_interface(_name_api).set_data_model(DataModelBdd)
@elem_blueprint.get('/' + _name_api + '/exist/<sha512:string>', strict_slashes=True)
@doc.summary("check resource existance")
@doc.description("simply check if the resource is already uploaded.")
@doc.produces(content_type='application/json')
async def check_existance(request, sha512):
value = data_global_elements.get_interface(_name_api).gets_where(select=[["==", "sha512", sha512]], filter=["id"])
if value != None:
return response.json({"found":True, "ids":value})
raise ServerError("No data found", status_code=404)
@elem_blueprint.post('/' + _name_api, strict_slashes=True, stream=True)
@doc.summary("send new file data")
@doc.description("Create a new data file (associated with his sha512.")
#@doc.consumes(DataModel, location='body')#, required=True)
@doc.response_success(status=201, description='If successful created')
async def create(_request):
debug.info("request streaming " + str(_request));
args_with_blank_values = _request.headers
debug.info("List arguments: " + str(args_with_blank_values));
async def streaming(_response):
#debug.info("streaming " + str(_response));
total_size = 0
temporary_file = os.path.join(_app.config['REST_TMP_DATA'], str(tmp_value) + ".tmp")
if not os.path.exists(_app.config['REST_TMP_DATA']):
os.makedirs(_app.config['REST_TMP_DATA'])
if not os.path.exists(_app.config['REST_MEDIA_DATA']):
os.makedirs(_app.config['REST_MEDIA_DATA'])
file_stream = open(temporary_file,"wb")
sha1 = hashlib.sha512()
while True:
#debug.warning("ploufffff " + str(dir(_request.stream)))
body = await _request.stream.read()
if body is None:
debug.warning("empty body");
break
total_size += len(body)
debug.verbose("body " + str(len(body)) + "/" + str(total_size))
file_stream.write(body)
sha1.update(body)
file_stream.close()
print("SHA512: " + str(sha1.hexdigest()))
new_data = {
"size": total_size,
"sha512": str(sha1.hexdigest()),
'original_name': _request.headers["filename"],
'mime_type': _request.headers["mime-type"],
'create_date': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
}
# TODO: Check if the element already exist ...
return_bdd = data_global_elements.get_interface(_name_api).post(new_data)
basic_data_path = os.path.join(_app.config['REST_MEDIA_DATA'], str(return_bdd["id"]))
if not os.path.exists(basic_data_path):
os.makedirs(basic_data_path)
destination_filename = os.path.join(basic_data_path, "video")
"""
if os.path.isfile(destination_filename) == True:
answer_data = {
"size": total_size,
"sha512": str(sha1.hexdigest()),
'filename': _request.headers["filename"],
'mime_type': _request.headers["mime-type"],
"already_exist": True,
}
await _response.write(json.dumps(answer_data, sort_keys=True, indent=4))
return
"""
# move the file
shutil.move(temporary_file, destination_filename)
# collect media info ...
media_info = MediaInfo.parse(destination_filename)
data_metafile = {
"sha512": str(sha1.hexdigest()),
"size": total_size,
'filename': _request.headers["filename"],
'mime_type': _request.headers["mime-type"],
'media_info': json.loads(media_info.to_json())
}
tools.file_write_data(os.path.join(basic_data_path, "meta.json"), json.dumps(data_metafile, sort_keys=True, indent=4))
await _response.write(json.dumps(return_bdd, sort_keys=True, indent=4))
return response.stream(streaming, content_type='application/json')
@elem_blueprint.get('/' + _name_api + '/<id:int>', strict_slashes=True)
@doc.summary("get a specific resource")
@doc.description("Get a resource with all the needed datas ... It permeit seek for video stream.")
@doc.produces(content_type='application/json')
async def retrive(request, id):
debug.warning("Request data media 2 : " + str(id));
"""
if id[-4:] == ".mp4":
id = id[:-4]
if id[-4:] == ".mkv":
id = id[:-4]
"""
filename = os.path.join(_app.config['REST_MEDIA_DATA'], str(id), "video")
value = data_global_elements.get_interface(_name_api).get(id)
debug.info("plouuuuuuf " + str(value))
headers = {
'Content-Type': value["mime_type"],
'Accept-Ranges': 'Accept-Ranges: bytes'
}
try:
with open(filename, 'rb') as fff:
range_start = None
range_end = None
fff.seek(0, 2)
file_length = fff.tell()
fff.seek(0)
try:
range_ = '0-' + str(file_length)
if 'range' in request.headers:
range_ = request.headers['range'].split('=')[1]
range_split = range_.split('-')
range_start = int(range_split[0])
fff.seek(range_start)
range_end = int(range_split[1])
except ValueError:
pass
if range_start and range_start != 0:
if not range_end:
range_end = file_length
read_length = range_end - range_start
else:
range_start = 0
read_length = file_length
range_end = file_length
fff.seek(range_start)
headers['Content-Length'] = read_length
headers['Content-Range'] = f'bytes {range_start}-{range_end-1}/{file_length}'
async def streaming_fn(response):
with open(filename, 'rb') as fff:
chunk_size = 8192
current_offset = range_start
while (current_offset < file_length):
chunk_start = current_offset
fff.seek(current_offset)
chunk_data = fff.read(min(chunk_size, file_length - current_offset))
current_offset += chunk_size
await response.write(chunk_data)
return response.stream(streaming_fn, headers=headers, status=206)
except FileNotFoundError:
return response.HTTPResponse(status=404)
_app.blueprint(elem_blueprint)