Commit 54b45e10 authored by birk's avatar birk
Browse files

Refactoring of caching media files - half way done.

parent 34ff2df9
......@@ -255,7 +255,7 @@ class ApiClient(object):
return c
async def get_media_entries(self, path_, limit_=None, meta_data_white_list_=None):
async def get_media_entries(self, path_, limit_=None, meta_data_white_list_=None, preload_media_=False):
"""
Requests media entries based on a complete api-path.
"""
......@@ -273,7 +273,9 @@ class ApiClient(object):
for i in roa['collection']['relations'].items():
if i[1]['name'] == 'Media-Entry':
path_ = i[1]['href']
task = asyncio.ensure_future(self.get_media_entry(path_, meta_data_white_list_=meta_data_white_list_))
task = asyncio.ensure_future(self.get_media_entry(path_,
meta_data_white_list_=meta_data_white_list_,
preload_media_=preload_media_))
tasks.append(task)
if tasks.__len__() >= limit:
ready = True
......@@ -290,7 +292,7 @@ class ApiClient(object):
return media_entries
async def get_media_entry(self, path_=None, id_=None, meta_data_white_list_=None):
async def get_media_entry(self, path_=None, id_=None, meta_data_white_list_=None, preload_media_=False):
cr = ApiClient.complete(path_, id_, 'media-entry')
j = await self.send_request(cr.path)
if j:
......@@ -308,9 +310,9 @@ class ApiClient(object):
if 'media-file' in roa['relations']:
mf = await self.get_media_file(roa['relations']['media-file']['href'])
if mf:
m.set_media_file(mf)
# TODO: Decide whether or not to cache the media file
await self.cache_media_file(m)
m.set_file_data(mf)
if preload_media_:
await self.cache_media_file(m)
else:
# a media entry without media files is invalid
print('No MediaFile for MediaEntry {}'.format(m.id))
......@@ -415,16 +417,8 @@ class ApiClient(object):
return None
async def cache_media_file(self, media_entry_):
bytes = await self.send_request(media_entry_.get_media_url)
if media_entry_.is_image:
with tempfile.NamedTemporaryFile(suffix='.jpg') as cache_file:
cache_file.write(bytes)
media_entry_.set_image(pyglet.image.load(cache_file.name))
elif media_entry_.is_video:
t = tempfile.NamedTemporaryFile(suffix='.mp4')
t.write(bytes)
media_entry_.set_video(t)
return None
# TODO: this is not asynched yet
file = MediaFile(media_entry_)
async def get_preview(self, path):
j = await self.send_request(path)
......
......@@ -3,6 +3,7 @@ import random
from tempfile import NamedTemporaryFile
import pyglet
import requests
from content.apidata import ApiData
from system.config import Config
......@@ -10,6 +11,11 @@ from system.config import Config
class MediaEntryData(ApiData):
IMAGE = 'image'
VIDEO = 'video'
AUDIO = 'audio'
DOCUMENT = 'document'
# static shortcuts for the return values of self.orientation
LANDSCAPE = -1
SQUARE = 0
......@@ -40,7 +46,8 @@ class MediaEntryData(ApiData):
self.uuid = None
self.is_published = None
self.responsible_user_id = None
self.media_file = None
self.file_data = None
self.file = None
self.image = None
self.video = None
if Config().dev_mode:
......@@ -48,8 +55,15 @@ class MediaEntryData(ApiData):
else:
self.duration = random.randint(30, 40)
def set_media_file(self, media_file_):
self.media_file = media_file_
def set_file_data(self, file_data_):
self.file_data = file_data_
def set_file(self, file_):
self.file = file_
self.file.push_handlers(on_cached=self.on_file_cached)
def on_file_cached(self, file_):
pass
def parse_data(self, json_:dict):
self.uuid = json_['id']
......@@ -60,25 +74,25 @@ class MediaEntryData(ApiData):
@property
def media_type(self):
if self.media_file and self.media_file.media_type:
return self.media_file.media_type
if self.file_data and self.file_data.media_type:
return self.file_data.media_type
return None
@property
def is_image(self):
return self.media_type == 'image'
return self.media_type == MediaEntryData.IMAGE
@property
def is_video(self):
return self.media_type == 'video'
return self.media_type == MediaEntryData.VIDEO
@property
def is_audio(self):
return self.media_type == 'audio'
return self.media_type == MediaEntryData.AUDIO
@property
def is_document(self):
return self.media_type == 'document'
return self.media_type == MediaEntryData.DOCUMENT
@property
def orientation(self):
......@@ -87,41 +101,38 @@ class MediaEntryData(ApiData):
"""
w = 0
h = 0
if self.media_file and self.media_file.get_preview():
if self.file_data and self.file_data.get_preview():
# TODO: Preview width and height are not trustworthy ...
w = self.media_file.get_preview().width
h = self.media_file.get_preview().height
w = self.file_data.get_preview().width
h = self.file_data.get_preview().height
return MediaEntryData.get_orientation(w, h)
@property
def get_media_url(self):
def file_url(self):
# TODO: Decide which data stream should be shown.
if self.media_file and len(self.media_file.previews) > 0:
return self.media_file.get_preview().data_stream
if self.file_data and len(self.file_data.previews) > 0:
return self.file_data.get_preview().data_stream
return None
@property
def get_media_filename(self):
if self.media_file and len(self.media_file.previews) > 0:
return self.media_file.get_preview().filename
return None
@property
def media_file_id(self):
if self.media_file and len(self.media_file.previews) > 0:
return self.media_file.get_preview().media_file_id
return None
# @property
# def media_file_id(self):
# if self.file_data and len(self.file_data.previews) > 0:
# return self.file_data.get_preview().media_file_id
# return None
@property
def media_width_height(self):
if self.media_file and len(self.media_file.previews) > 0:
return self.media_file.get_preview().width_height
def width_height(self):
# Check first the actual size of the loaded file and then as a fallback the values from the API.
if self.file and self.file.width and self.file.height:
return self.file.width, self.file.height
elif self.file_data and len(self.file_data.previews) > 0:
return self.file_data.get_preview().width_height
return None, None
def set_image(self, image_:pyglet.image.AbstractImage):
self.image = image_
if self.media_file and self.media_file.get_preview():
self.media_file.get_preview().set_size(self.image.width, self.image.height)
if self.file_data and self.file_data.get_preview():
self.file_data.get_preview().set_size(self.image.width, self.image.height)
def set_video(self, video_:NamedTemporaryFile):
self.video = video_
......@@ -133,14 +144,113 @@ class MediaEntryData(ApiData):
self._source = None
def set_media_size(self, width_, height_):
if self.media_file and self.media_file.get_preview():
self.media_file.get_preview().set_size(width_, height_)
if self.file_data and self.file_data.get_preview():
self.file_data.get_preview().set_size(width_, height_)
def __str__(self):
s = 'MediaEntryData {}'.format(self.uuid)
return s
class MediaFile(pyglet.event.EventDispatcher):
__SUFFIXES = {MediaEntryData.IMAGE: '.jpg', MediaEntryData.VIDEO: '.mp4', MediaEntryData.AUDIO: '.mp3', MediaEntryData.DOCUMENT: '.jpg'}
def __init__(self, entry_:MediaEntryData):
super(MediaFile, self).__init__()
self.__entry = entry_
self.__temp = None
self.__image_source = None
self.__video_source = None
self.__video_player = None
entry_.set_file(self)
def cache(self):
if self.__temp:
# TODO: clean up
pass
self.__temp = NamedTemporaryFile(suffix=self.__suffix, delete=False)
w = None
h = None
d = None
response = requests.get('{}{}'.format(Config().server, self.__entry.file_url), stream=True, auth=Config().api_auth)
for block in response.iter_content(1024):
self.__temp.write(block)
if self.__entry.media_type == MediaEntryData.IMAGE:
self.__image_source = pyglet.image.load(self.__temp.name)
w, h = self.__image_source.width, self.__image_source.height
self.dispatch_event('on_cached', self)
def play(self):
if not self.__temp:
self.cache()
if self.__entry.media_type == MediaEntryData.IMAGE:
pass
elif self.__entry.media_type == MediaEntryData.VIDEO:
if not self.__video_source:
self.__video_source = pyglet.media.load(self.__temp.name)
if not self.__video_player:
self.__video_player = self.__video_source.play()
self.__video_player.eos_action = self.__video_player.EOS_PAUSE
@self.__video_player.event
def on_eos():
self.on_video_end(self)
def stop(self):
# TODO: Stop the video
pass
def delete(self):
# TODO: Delete the file
pass
@property
def width(self):
if self.__image_source:
return self.__image_source.width
# TODO: Do I have to do the same for video?
return None
@property
def height(self):
if self.__image_source:
return self.__image_source.height
# TODO: Do I have to do the same for video?
return None
@property
def texture(self):
if self.__entry.media_type == MediaEntryData.IMAGE:
if not self.__image_source:
return None
return self.__image_source.get_texture()
elif self.__entry.media_type == MediaEntryData.VIDEO:
if not self.__video_player:
return None
return self.__video_player.get_texture()
return None
@property
def player(self):
if self.__entry.media_type == MediaEntryData.VIDEO:
if not self.__video_player:
self.play()
return self.__video_player
return None
def on_video_end(self):
pass
@property
def __suffix(self):
if self.__entry.media_type in MediaFile.__SUFFIXES:
return MediaFile.__SUFFIXES[self.__entry.media_type]
return None
MediaFile.register_event_type('on_cached')
class MediaFileData():
def __init__(self, server_:str, json_:dict):
self.id = json_['id']
......@@ -157,7 +267,7 @@ class MediaFileData():
def add_preview(self, preview_):
# TODO: Remove once the API delivers this for MediaFile.
if self.media_type is None or self.media_type is 'image':
if self.media_type is None or self.media_type is MediaEntryData.IMAGE:
self.media_type = preview_.media_type
self.previews.append(preview_)
......@@ -180,13 +290,13 @@ class MediaFileData():
document_extensions = ['.pdf', '.doc', '.docx', '.txt', '.ai']
filename, file_extension = os.path.splitext(self.filename)
if file_extension.lower() in image_extensions:
return 'image'
return MediaEntryData.IMAGE
elif file_extension.lower() in video_extensions:
return 'video'
return MediaEntryData.VIDEO
elif file_extension.lower() in sound_extensions:
return 'sound'
return MediaEntryData.SOUND
elif file_extension.lower() in document_extensions:
return 'document'
return MediaEntryData.DOCUMENT
print('#### unrecognized file extension: {} ####'.format(file_extension))
return None
......
......@@ -38,7 +38,7 @@ class Program(EventDispatcher):
self.__index = None
self._playlist = []
playlist = []
for m in await self._api.get_media_entries(self.start_url, limit, self._meta_data_white_list):
for m in await self._api.get_media_entries(self.start_url, limit, self._meta_data_white_list, preload_media_):
# only use images and videos
if m.is_image or m.is_video:
playlist.append(m)
......
......@@ -4,6 +4,7 @@ import pyglet
import requests
import sys
from content.mediaentry import MediaFile
from system.config import Config
......@@ -20,13 +21,13 @@ class MediaDisplay(pyglet.event.EventDispatcher):
self.index = index_
self.screen = screen_
self.texture = None
self.player = None # for videos
# self.player = None # for videos
self.area = None
self.visible = False
def find_position(self):
def define_area(self):
# Looks for a suitable position on the screen
w, h = self.media_entry.media_width_height
w, h = self.media_entry.width_height
if w and h:
# First init the are with the original size ...
self.area = Area(0, 0, w, h)
......@@ -36,20 +37,18 @@ class MediaDisplay(pyglet.event.EventDispatcher):
def show(self):
# Called when the content appears on the screen.
self.find_position()
if self.media_entry.is_image:
self.texture = self.media_entry.image.get_texture()
elif self.media_entry.is_video:
print('video {}'.format(self.media_entry.video.name))
self.player = pyglet.media.load(self.media_entry.video.name).play()
self.texture = self.player.get_texture()
# self.player.eos_action = self.player.EOS_PAUSE
# @self.player.event
# def on_eos():
# self.on_video_end(self)
if not self.media_entry.file or not self.media_entry.file.texture:
file = MediaFile(self.media_entry)
file.cache()
self.media_entry.file.play()
if self.media_entry.is_video:
@self.media_entry.file.player.event
def on_eos():
self.on_video_end(self)
self.texture = self.media_entry.file.texture
pyglet.clock.schedule_once(
self.on_timer_end, self.media_entry.duration)
self.set_width_height(self.area.width, self.area.height)
self.define_area()
self.visible = True
self.dispatch_event('on_play', self)
......@@ -59,12 +58,12 @@ class MediaDisplay(pyglet.event.EventDispatcher):
if self.texture:
self.texture.blit(a.x, a.y, 0, a.width, a.height)
elif self.media_entry.is_video:
if self.player:
if self.texture:
self.texture.blit(a.x, a.y, 0, a.width, a.height)
def on_video_end(self):
print('on video end ...')
self.player.stop()
# TODO: Do I need this here?
def on_timer_end(self, seconds_):
# timer used for still images
......@@ -72,26 +71,19 @@ class MediaDisplay(pyglet.event.EventDispatcher):
def on_content_end(self):
# print('content end %s' % self.screen.index)
if self.media_entry.video and type(self.media_entry.video) is tempfile.NamedTemporaryFile:
self.media_entry.video.close()
self.media_entry.video = None
# TODO: stop video and clear cache file
# if self.media_entry.video and type(self.media_entry.video) is tempfile.NamedTemporaryFile:
# self.media_entry.video.close()
# self.media_entry.video = None
self.dispatch_event('on_end', self, self.screen)
def hide(self):
# Called when the content disappears from the screen.
if self.media_entry.is_video:
self.player.pause()
self.player.pop_handlers()
# if self.media_entry.is_video:
# self.player.pause()
# self.player.pop_handlers()
self.visible = False
def set_width_height(self, width_, height_):
if self.media_entry.is_image:
self.texture.width = width_
self.texture.height = height_
elif self.media_entry.is_video:
self.player.width = width_
self.player.height = height_
MediaDisplay.register_event_type('on_play')
MediaDisplay.register_event_type('on_end')
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment