Commit 23fae6a5 authored by birk's avatar birk
Browse files

still refactoring caching and playback

parent 9e80658c
......@@ -115,40 +115,35 @@ class ApiClient(object):
return self.__active
async def send_request(self, path_,
retries=5,
retries=3,
interval=0.9,
back_off=3,
read_timeout=180,
back_off=1.5,
read_timeout=15.9,
http_status_codes_to_retry=HTTP_STATUS_CODES_TO_RETRY):
"""
This internal function handles all requests to the server and returns
either the entire JSON or None.
"""
back_off_interval = interval
raised_exc = None
attempt = 0
if self.debug:
print('send_request - {}'.format(path_))
if retries == -1: # -1 means retry indefinitely
attempt = -1
elif retries == 0: # Zero means don't retry
attempt = 1
else: # any other value means retry N times
attempt = retries + 1
self.__request_counter += 1
url = '{}{}'.format(self.__server, path_)
if self.__session.closed and self.debug:
print('Session closed!')
return None
while attempt != 0:
if raised_exc:
print('caught {} remaining tries {}, sleeping {} secs'.format(raised_exc, attempt, back_off_interval))
print('caught "{}" url:{} remaining tries {}, sleeping {} secs'.format(raised_exc,
url, attempt, back_off_interval))
await asyncio.sleep(back_off_interval)
# bump interval for the next possible attempt
back_off_interval = back_off_interval * back_off
......@@ -156,23 +151,20 @@ class ApiClient(object):
with aiohttp.Timeout(timeout=read_timeout):
async with getattr(self.__session, 'get')(url) as response:
if response.status == 200:
if response.content_type == 'application/json-roa+json':
try:
data = await response.json()
except json.JSONDecodeError as exc:
print('failed to decode response code:{} url:{} error:{} response:{}'.format(
response.status, url, exc,
response.reason)
)
raise aiohttp.errors.HttpProcessingError(
code=response.status, message=exc.msg)
else:
if self.debug:
print('... url:{} code:{} response:{}'.format(url, response.status, response.reason))
raised_exc = None
return data
elif response.content_type == 'image/jpeg' or response.content_type == 'video/mp4':
return await response.read()
try:
data = await response.json()
except json.JSONDecodeError as exc:
print('failed to decode response code:{} url:{} error:{} response:{}'.format(
response.status, url, exc,
response.reason)
)
raise aiohttp.errors.HttpProcessingError(
code=response.status, message=exc.msg)
else:
if self.debug:
print('... url:{} code:{} response:{}'.format(url, response.status, response.reason))
raised_exc = None
return data
elif response.status in http_status_codes_to_retry:
print('received invalid response code:{} url:{} response:{}'.format(
response.status, url, response.reason))
......@@ -209,9 +201,7 @@ class ApiClient(object):
else:
raised_exc = None
break
attempt -= 1
if raised_exc:
raise raised_exc
......
......@@ -53,7 +53,7 @@ class MediaEntryData(ApiData):
if Config().dev_mode:
self.duration = random.randint(3, 5)
else:
self.duration = random.randint(30, 40)
self.duration = random.randint(50, 80)
def set_file_data(self, file_data_):
self.file_data = file_data_
......@@ -94,17 +94,21 @@ class MediaEntryData(ApiData):
def is_document(self):
return self.media_type == MediaEntryData.DOCUMENT
@property
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
@property
def orientation(self):
"""
:return: MediaEntryData.LANDSCAPE, MediaEntryData.SQUARE, MediaEntryData.PORTRAIT
"""
w = 0
h = 0
if self.file_data and self.file_data.get_preview():
# TODO: Preview width and height are not trustworthy ...
w = self.file_data.get_preview().width
h = self.file_data.get_preview().height
w, h = self.width_height
return MediaEntryData.get_orientation(w, h)
@property
......@@ -114,39 +118,6 @@ class MediaEntryData(ApiData):
return self.file_data.get_preview().data_stream
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 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.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_
self._source = pyglet.media.load(self.video.name)
self.set_media_size(self._source.video_format.width,
self._source.video_format.height)
if not Config().dev_mode:
self.duration = self._source.duration
self._source = None
def set_media_size(self, 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
......@@ -159,89 +130,71 @@ class MediaFile(pyglet.event.EventDispatcher):
def __init__(self, entry_:MediaEntryData):
super(MediaFile, self).__init__()
self.__entry = entry_
self.__temp = None
self.__temp_file = None
self.__image_source = None
self.__video_source = None
self.__video_player = None
entry_.set_file(self)
self.__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
if self.__temp_file:
self.__temp_file.close()
self.__temp_file = NamedTemporaryFile(suffix=self.__suffix, delete=False)
response = None
attempts = 0
while not response or response.status_code != 200 and attempts < 3:
response = requests.get('{}{}'.format(Config().server, self.__entry.file_url), auth=Config().api_auth)
if response.status_code == 200:
self.__temp_file.write(response.content)
if self.__entry.is_image:
self.__image_source = pyglet.image.load(self.__temp_file.name)
elif self.__entry.is_video:
self.__video_source = pyglet.media.load(self.__temp_file.name)
self.dispatch_event('on_cached', self)
return None
else:
print('Problem caching {}'.format(self.__entry.file_url))
attempts += 1
print('Failed to cache file! {}'.format(self.__entry))
def delete(self):
# TODO: Delete the file
pass
if self.__temp_file:
self.__temp_file.close()
self.__temp_file = None
self.__image_source = None
self.__video_source = None
@property
def width(self):
if self.__image_source:
return self.__image_source.width
# TODO: Do I have to do the same for video?
elif self.__video_source and self.__video_source.video_format:
return self.__video_source.video_format.width
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?
elif self.__video_source and self.__video_source.video_format:
return self.__video_source.video_format.height
return None
@property
def texture(self):
if self.__entry.media_type == MediaEntryData.IMAGE:
if self.__entry.is_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
def source(self):
if self.__image_source:
return self.__image_source
elif self.__video_source:
return self.__video_source
return None
def on_video_end(self):
pass
@property
def __suffix(self):
if self.__entry.media_type in MediaFile.__SUFFIXES:
......@@ -294,7 +247,7 @@ class MediaFileData():
elif file_extension.lower() in video_extensions:
return MediaEntryData.VIDEO
elif file_extension.lower() in sound_extensions:
return MediaEntryData.SOUND
return MediaEntryData.AUDIO
elif file_extension.lower() in document_extensions:
return MediaEntryData.DOCUMENT
print('#### unrecognized file extension: {} ####'.format(file_extension))
......
import tempfile
import pyglet
import requests
import sys
from content.mediaentry import MediaFile
from system.config import Config
class MediaDisplay(pyglet.event.EventDispatcher):
......@@ -21,9 +18,13 @@ class MediaDisplay(pyglet.event.EventDispatcher):
self.index = index_
self.screen = screen_
self.texture = None
# self.player = None # for videos
self.player = None
if self.media_entry.is_video:
self.player = pyglet.media.Player() # for videos
@self.player.event
def on_eos():
self.on_video_end(self)
self.area = None
self.visible = False
def define_area(self):
# Looks for a suitable position on the screen
......@@ -37,55 +38,42 @@ class MediaDisplay(pyglet.event.EventDispatcher):
def show(self):
# Called when the content appears on the screen.
if not self.media_entry.file or not self.media_entry.file.texture:
if not self.media_entry.file:
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)
if not self.media_entry.file.source:
self.media_entry.file.cache()
if self.media_entry.is_image:
self.texture = self.media_entry.file.texture
elif self.media_entry.is_video:
self.player.queue(self.media_entry.file.source)
self.player.play()
pyglet.clock.schedule_once(self.on_timer_end, self.media_entry.duration)
self.define_area()
self.visible = True
self.dispatch_event('on_play', self)
self.dispatch_event('on_show', self)
def draw(self):
a = self.area
if self.media_entry.is_image:
if self.texture:
self.texture.blit(a.x, a.y, 0, a.width, a.height)
elif self.media_entry.is_video:
if self.texture:
self.texture.blit(a.x, a.y, 0, a.width, a.height)
if self.media_entry.is_video and self.player:
self.player.get_texture().blit(a.x, a.y, 0, a.width, a.height)
elif self.texture:
self.texture.blit(a.x, a.y, 0, a.width, a.height)
def on_video_end(self):
print('on video end ...')
# TODO: Do I need this here?
self.on_content_end()
def on_timer_end(self, seconds_):
# timer used for still images
# timer used for still images and to end videos
self.on_content_end()
def on_content_end(self):
# print('content end %s' % self.screen.index)
# 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()
self.visible = False
if self.player:
self.player.delete()
self.media_entry.file.delete()
MediaDisplay.register_event_type('on_play')
MediaDisplay.register_event_type('on_show')
MediaDisplay.register_event_type('on_end')
......
......@@ -175,6 +175,8 @@ class Screen(Window):
top = top - h - Screen.PADDING
def clear_media(self):
if Screen._content[self.__index]:
Screen._content[self.__index].hide()
Screen._content[self.__index] = None
@property
......@@ -265,8 +267,8 @@ class Screen(Window):
else:
if self.media:
self.media.draw()
if self.__caption:
self.__caption.draw()
if self.__caption:
self.__caption.draw()
def __str__(self):
return 'Screen {}'.format(self.index+1)
......
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