inital commit

(rough sketch of idea at this point)
This commit is contained in:
Tim Caldwell
2017-08-17 23:41:36 +12:00
commit 9bdb02a3e9
3 changed files with 715 additions and 0 deletions

275
data_centre.py Normal file
View File

@@ -0,0 +1,275 @@
import json
import logging
from collections import OrderedDict
import os
from random import randint
#TODO : standise the paths to things, use constants for file names, standise the naming convention of files
import time
PATH_TO_BROWSER = 'C:/TestFolderStucture'
PATH_TO_DATA_OBJECTS = 'C:/Users/Tim/PycharmProjects/videoLooper/'
EMPTY_BANK = dict(name='',location='',length=-1,start=-1,end=-1)
logger = logging.getLogger('myapp')
hdlr = logging.FileHandler('C:/Users/Tim/PycharmProjects/videoLooper/myapp.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
class data(object):
def __init__(self):
self._open_folders = []
self._browser_list = []
def rewrite_browser_list(self):
self._browser_list = generate_browser_list(PATH_TO_BROWSER, 0, self._open_folders)
def get_browser_data_for_display(self):
if not self._browser_list:
self.rewrite_browser_list()
browser_list_for_display = []
for index , dir in enumerate(self._browser_list):
browser_list_for_display.append(([dir['name'],dir['bank']],index))
#logger.info(browser_list_for_display)
return browser_list_for_display
def update_open_folders(self, folder_name):
if folder_name not in self._open_folders:
self._open_folders.append(folder_name)
else:
self._open_folders.remove(folder_name)
def get_all_looper_data_for_display():
memory_bank = read_json('DisplayData.json')
loop_data = []
for index, bank in enumerate(memory_bank):
length = convert_int_to_string_for_display(bank["length"])
start = convert_int_to_string_for_display(bank["start"])
end = convert_int_to_string_for_display(bank["end"])
loop_data.append(([str(index),bank["name"],length,start,end],index))
return loop_data
def is_file_in_memory_bank(file_name, memory_bank=[]):
if not memory_bank:
memory_bank = read_json('DisplayData.json')
for index, bank in enumerate(memory_bank):
if(file_name == bank['name']):
return True , index
return False, ''
def generate_browser_list(current_path, current_level, open_folder_list):
global results
results = []
add_folder_to_browser_list(current_path, current_level,open_folder_list)
memory_bank = read_json('DisplayData.json')
for browser_line in results:
is_file, file_name = extract_file_type_and_name_from_browser_format(browser_line['name'])
if is_file:
is_banked, bank_number = is_file_in_memory_bank(file_name, memory_bank)
if is_banked:
browser_line['bank'] = str(bank_number)
return results
def add_folder_to_browser_list(current_path, current_level,open_folder_list):
root, dirs, files = next(os.walk(current_path))
indent = ' ' * 4 * (current_level)
for folder in dirs:
is_open, char = check_folder_state(folder,open_folder_list)
#print('{}{}{}'.format(indent, folder, char))
results.append(dict(name='{}{}{}'.format(indent, folder, char), bank='x'))
if (is_open):
next_path = '{}/{}'.format(root, folder)
next_level = current_level + 1
add_folder_to_browser_list(next_path, next_level,open_folder_list)
for f in files:
if (os.path.splitext(f)[1] in ['.mp4']):
#print('{}{}'.format(indent, f))
results.append(dict(name='{}{}'.format(indent, f), bank='-'))
def check_folder_state(folder_name,open_folder_list):
if (folder_name in open_folder_list):
return True, '/'
else:
return False, '|'
def extract_file_type_and_name_from_browser_format(dir_name):
#logger.info('the name we got was {}'.format(dir_name))
if(dir_name.endswith('|') or dir_name.endswith('/')):
return False , dir_name.lstrip()[:-1]
else:
return True , dir_name.lstrip()
def get_length_for_file(location):
#TODO: will have omx.player get length of file probs..
pass
def create_new_bank_mapping_in_first_open(file_name):
memory_bank = read_json('DisplayData.json')
for index , bank in enumerate(memory_bank):
if(not bank['name']):
create_new_bank_mapping(index,file_name,memory_bank)
return True
return False
def create_new_bank_mapping(bank_number,file_name,memory_bank=[]):
has_location , location = get_path_for_file(file_name)
length = get_length_for_file(location)
new_bank = dict(name=file_name, location=location, length=-1, start=-1, end=-1)
update_a_banks_data(bank_number, new_bank, memory_bank)
def get_path_for_file(file_name):
for root, dirs, files in os.walk(PATH_TO_BROWSER):
if file_name in files:
print root
return True, '{}/{}'.format(root,file_name)
else:
return False, ''
def update_a_banks_data(bank_number, bank_info, memory_bank=[]):
if not memory_bank:
memory_bank = read_json('DisplayData.json')
memory_bank[bank_number] = bank_info
update_json('DisplayData.json', memory_bank)
def clear_all_banks():
memory_bank = read_json('DisplayData.json')
for index , bank in enumerate(memory_bank):
memory_bank[index] = EMPTY_BANK
update_json('DisplayData.json', memory_bank)
def read_json(file_name):
with open(PATH_TO_DATA_OBJECTS + file_name) as data_file:
data = json.load(data_file)
return data
def update_json(file_name,data):
with open('{}{}'.format(PATH_TO_DATA_OBJECTS, file_name), 'w') as data_file:
json.dump(data, data_file)
def get_all_settings_data_for_display():
settings = read_json('Settings.json')
display_settings = []
for index, setting in enumerate(settings):
display_settings.append(([setting['name'],setting['value']],index))
return display_settings
def get_a_banks_data(bank_number):
memory_bank = read_json('DisplayData.json')
return memory_bank[bank_number]
def switch_settings(setting_name):
settings = read_json('Settings.json')
for index, setting in enumerate(settings):
if setting['name'] == setting_name:
setting = cycle_setting_value(setting)
update_json('Settings.json',settings)
def cycle_setting_value(setting):
if setting['name'] == 'PLAYBACK_MODE':
if setting['value'] == 'LOOPER':
setting['value'] = 'PLAYLIST'
elif setting['value'] == 'PLAYLIST':
setting['value'] = 'RANDOM'
else:
setting['value'] = 'LOOPER'
elif setting['name'] == 'SYNC_LENGTHS':
if setting['value'] == 'ON':
setting['value'] = 'OFF'
else:
setting['value'] = 'ON'
elif setting['name'] == 'RAND_START':
if setting['value'] == 'ON':
setting['value'] = 'OFF'
else:
setting['value'] = 'ON'
elif setting['name'] == 'VIDEO_OUTPUT':
if setting['value'] == 'HDMI':
setting['value'] = 'COMPOSITE'
else:
setting['value'] = 'HDMI'
return setting
def get_next_context():
next_bank_number = read_json('next_bank_number.json')
memory_bank = read_json('DisplayData.json')
next_bank_details = memory_bank[next_bank_number]
start_value = next_bank_details['start']
end_value = next_bank_details['end']
length = next_bank_details['length']
use_rand_start, use_sync_length, sync_length, playback_mode = get_context_options_from_settings()
set_next_bank_number_from_playback_mode(playback_mode,next_bank_number)
if use_rand_start and use_sync_length:
start_value = randint(0, length - sync_length)
end_value = start_value + sync_length
elif use_rand_start and not use_sync_length:
start_value = randint(0, end_value)
elif not use_rand_start and use_sync_length:
end_value = min(length, start_value + sync_length)
context = dict(location=next_bank_details['location'],start=start_value,end=end_value, bank_number=next_bank_number)
def get_context_options_from_settings():
settings = read_json('Settings.json')
use_sync_length = False
sync_length = 0
use_rand_start = False
playback_mode = ''
for index, setting in enumerate(settings):
if setting['name'] == 'SYNC_LENGTHS' and setting['value'] == 'ON':
use_sync_length = True
elif setting['name'] == 'SYNC_LENGTHS_TO':
sync_length = setting['value']
elif setting['name'] == 'RAND_START' and setting['value'] == 'ON':
use_rand_start = True
elif setting['name'] == 'PLAYBACK_MODE':
playback_mode = setting['value']
return use_rand_start , use_sync_length , sync_length , playback_mode
def set_next_bank_number_from_playback_mode(playback_mode, current_bank_number):
next_bank_number = 0
if playback_mode == 'LOOPER':
next_bank_number = current_bank_number
elif playback_mode == 'RANDOM':
#TODO: actually find which banks have value and only use those
next_bank_number = randint(0,14)
elif playback_mode == 'PLAYLIST':
#TODO: implement some playlist objects and logic at some point
next_bank_number = current_bank_number
update_json('next_bank_number.json',next_bank_number)
def convert_int_to_string_for_display(time_in_seconds):
if time_in_seconds < 0:
return ''
elif time_in_seconds >= 6000:
return '99:99'
else:
return time.strftime("%M:%S", time.gmtime(time_in_seconds))

312
display_centre.py Normal file
View File

@@ -0,0 +1,312 @@
import logging
import sys
import traceback
import time
import math
from asciimatics.effects import RandomNoise
from asciimatics.event import KeyboardEvent
from asciimatics.exceptions import ResizeScreenError, NextScene
from asciimatics.scene import Scene
from asciimatics.screen import Screen
from asciimatics.widgets import Frame, Layout, Divider, Button, ListBox, Widget, MultiColumnListBox, PopUpDialog, Text, \
Label
import data_centre
import video_centre
VIDEO_DISPLAY_TEXT = 'NOW [{}] {} NEXT [{}] {}'
VIDEO_DISPLAY_BANNER_LIST = ['[','-','-','-','-','-','-','-','-','-','-',']']
VIDEO_DISPLAY_BANNER_TEXT = '{} {} {}'
logger = logging.getLogger('myapp')
hdlr = logging.FileHandler('C:/Users/Tim/PycharmProjects/videoLooper/myapp.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
class Display(Frame):
def __init__(self, switch, screen,driver, on_load=None):
super(Display, self).__init__(screen,
screen.height,
screen.width,
on_load=on_load,
title="r_e_c_u_r"
)
self._last_frame = 0
self.popup_message = None
self.video_driver = driver
self.my_frame_update_count = 40
layout_top = Layout([1,1,1])
self.add_layout(layout_top)
self.browser_button = Button("BROWSER", self.do_nothing())
self.browser_button.disabled = True
#self.browser_button.is_tab_stop = False
if switch[0]:
self.browser_button.custom_colour = "focus_button"
self.looper_button = Button("LOOPER", self.do_nothing())
self.looper_button.disabled = True
#self.looper_button.is_tab_stop = False
if switch[1]:
self.looper_button.custom_colour = "focus_button"
self.settings_button = Button("SETTINGS", self.do_nothing())
self.settings_button.disabled = True
#self.advance_button.is_tab_stop = False
if switch[2]:
self.settings_button.custom_colour = "focus_button"
layout_top.add_widget(Divider(), 0)
layout_top.add_widget(Divider(), 1)
layout_top.add_widget(Divider(), 2)
layout_top.add_widget(self.browser_button, 0)
layout_top.add_widget(self.looper_button, 1)
layout_top.add_widget(self.settings_button, 2)
layout_body = Layout([100], fill_frame=False)
self.add_layout(layout_body)
layout_body.add_widget(Divider())
video_display_text, video_banner_text = self.get_text_for_video_display()
self.player_info_label = Label(video_display_text)
self.player_info_banner = Label(video_banner_text)
layout_body.add_widget(self.player_info_banner)
layout_body.add_widget(self.player_info_label)
layout_body.add_widget(Divider())
self.fix()
def do_nothing(self):
return
def get_text_for_video_display(self):
now_bank, now_status, next_bank, next_status, duration, video_length = self.video_driver.get_info_for_video_display()
banner = create_video_display_banner(duration,video_length)
time_been = data_centre.convert_int_to_string_for_display(duration)
time_left = data_centre.convert_int_to_string_for_display(video_length - duration)
logger.info(VIDEO_DISPLAY_BANNER_TEXT.format(time_been,banner,time_left))
return VIDEO_DISPLAY_BANNER_TEXT.format(time_been,banner,time_left),VIDEO_DISPLAY_TEXT.format(now_bank , now_status, next_bank, next_status, duration)
def _update(self, frame_no):
if frame_no - self._last_frame >= self.frame_update_count or self._last_frame == 0:
self._last_frame = frame_no
video_display_text, video_banner_text = self.get_text_for_video_display()
self.player_info_label._text = video_display_text
self.player_info_banner._text = video_banner_text
super(Display, self)._update(frame_no)
@property
def frame_update_count(self):
# Refresh once every 1 seconds by default.
return 20
def get_focus_on_list(self, list):
#return self._layouts[self._focus]
return list.options[list.value][0][0]
def process_event(self, event):
if isinstance(event, KeyboardEvent):
if event.key_code in [ord('n'), ord('N')]:
raise NextScene
return super(Display, self).process_event(event)
class Browser(Display):
def __init__(self, screen, data, driver):
super(Browser, self).__init__([1,0,0],screen,driver, on_load=self._reload_list)
self._data_object = data
layout = Layout([100], fill_frame=True)
self.add_layout(layout)
self._browser_data_view = ScrollingMultiColumnListBox(
Widget.FILL_FRAME,
[50,10],
self._data_object.get_browser_data_for_display(),
titles=['path','bank']
)
layout.add_widget(self._browser_data_view)
self.fix()
def process_event(self, event):
numberMapping = [ord('q'),ord('w'),ord('e'),ord('r'),ord('t'),ord('y'),ord('u'),ord('i'),ord('o'),ord('p') ]
if isinstance(event, KeyboardEvent):
if event.key_code in numberMapping:
focus = self.get_focus_on_list(self._browser_data_view)
is_file, name = data_centre.extract_file_type_and_name_from_browser_format(focus)
if(is_file):
bank_number = numberMapping.index(event.key_code)
data_centre.create_new_bank_mapping(bank_number,name)
self._data_object.rewrite_browser_list()
self._reload_list(self._browser_data_view.value)
if event.key_code in [ord('m')]:
focus = self.get_focus_on_list(self._browser_data_view)
is_file , name = data_centre.extract_file_type_and_name_from_browser_format(focus)
if(is_file):
data_centre.create_new_bank_mapping_in_first_open(name)
self._data_object.rewrite_browser_list()
self._reload_list(self._browser_data_view.value)
else:
self._data_object.update_open_folders(name)
self._data_object.rewrite_browser_list()
self._reload_list(self._browser_data_view.value)
if event.key_code in [ord('c')]:
#self.popup_message = PopUpDialog(self._screen, 'Deleting all banks',['|'])
#self._scene.add_effect()
data_centre.clear_all_banks()
#time.sleep(1)
#self._scene.remove_effect(popup_message)
self._data_object.rewrite_browser_list()
self._reload_list(self._browser_data_view.value)
return super(Browser, self).process_event(event)
# Now pass on to lower levels for normal handling of the event.
def _update(self, frame_no):
logger.info('the BROWSER frame number is {}'.format(frame_no))
super(Browser, self)._update(frame_no)
def _reload_list(self, new_value=None):
self._browser_data_view.options = self._data_object.get_browser_data_for_display()
self._browser_data_view.value = new_value
class Looper(Display):
def __init__(self, screen, data,driver):
super(Looper, self).__init__([0, 1, 0],screen,driver,on_load=self._reload_list,)
self._data_object = data
layout = Layout([100], fill_frame=True)
self.add_layout(layout)
self._bank_data_view = MultiColumnListBox(
Widget.FILL_FRAME,
[10,35,10,10,10],
data_centre.get_all_looper_data_for_display(),
titles=['bank','name','length','start','end'])
layout.add_widget(self._bank_data_view)
self.fix()
def process_event(self, event):
# if isinstance(event, KeyboardEvent):
# if event.key_code in [ord('q'), ord('Q'), Screen.ctrl("c")]:
# raise StopApplication("User quit")
# elif event.key_code in [ord("r"), ord("R")]:
# self._reverse = not self._reverse
# elif event.key_code == ord("<"):
# self._sort = max(0, self._sort - 1)
# elif event.key_code == ord(">"):
# self._sort = min(7, self._sort + 1)
#self._last_frame = 0
# Now pass on to lower levels for normal handling of the event.
return super(Looper, self).process_event(event)
def _reload_list(self, new_value=None):
self._bank_data_view.options = data_centre.get_all_looper_data_for_display()
self._bank_data_view.value = new_value
class Settings(Display):
def __init__(self, screen, data,driver):
super(Settings, self).__init__([0, 0, 1], screen,driver,on_load=self._reload_list)
self._data_object = data
layout = Layout([100], fill_frame=True)
self.add_layout(layout)
self._settings_data_view = MultiColumnListBox(
Widget.FILL_FRAME,
[30, 30],
data_centre.get_all_settings_data_for_display(),
titles=['setting', 'value'])
layout.add_widget(self._settings_data_view)
self.fix()
def _reload_list(self,new_value=None):
self._settings_data_view.options = data_centre.get_all_settings_data_for_display()
self._settings_data_view.value = new_value
def process_event(self, event):
if isinstance(event, KeyboardEvent):
if event.key_code in [ord('m')]:
focus = self.get_focus_on_list(self._settings_data_view)
data_centre.switch_settings(focus)
self._reload_list(self._settings_data_view.value)
return super(Settings, self).process_event(event)
class ScrollingMultiColumnListBox(MultiColumnListBox):
def __init__(self, height, columns, options, titles):
super(ScrollingMultiColumnListBox, self).__init__(height, columns, options, titles)
def process_event(self, event):
if isinstance(event, KeyboardEvent):
if len(self._options) > 0 and event.key_code == Screen.KEY_UP and self._line == 0:
self._line = len(self._options)
elif len(self._options) > 0 and event.key_code == Screen.KEY_DOWN and self._line == len(self._options) - 1:
self._line = -1
super(ScrollingMultiColumnListBox,self).process_event(event)
def inspect_browser_focus(dir_name):
if(dir_name.endswith('|') or dir_name.endswith('/')):
return False , dir_name.lstrip()[:-1]
else:
return True , dir_name.lstrip()
def create_video_display_banner(duration,video_length):
banner_list = ['[','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-',']']
max = len(banner_list) - 1
if duration <= 0:
banner_list[0] = '<'
elif duration >= video_length:
banner_list[max] = '>'
else:
marker = int(math.floor(float(duration)/float(video_length)*(max-1))+1)
banner_list[marker] = '*'
return ''.join(banner_list)
def demo(screen):
scenes = [Scene([Browser(screen, data,video_driver)], -1),
Scene([Looper(screen, data,video_driver)], -1),
Scene([Settings(screen, data,video_driver)], -1)]
screen.play(scenes)
data = data_centre.data()
video_driver = video_centre.video_driver()
last_scene = None
while True:
try:
Screen.wrapper(demo, catch_interrupt=True)
sys.exit(0)
except ResizeScreenError as e:
last_scene = e.scene
except Exception as e:
logger.error(traceback.format_exc())
logger.error(str(e))
# Logs the error appropriately.

128
video_centre.py Normal file
View File

@@ -0,0 +1,128 @@
import time
import data_centre
class video_driver(object):
def __init__(self):
self.last_player = video_player()
self.current_player = video_player()
self.next_player = video_player()
self.manual_next = False
#self.video_driver.begin_playing()
def begin_playing(self):
#TODO:von startup set up the intital data_structures with a preloaded intial clip in bank 0
first_context = data_centre.get_next_context()
self.current_player.load_content(first_context)
self.wait_for_first_load()
def wait_for_first_load(self):
if self.current_player.is_loaded:
self.play_video()
else:
#need to wait here then
time.sleep(1)
self.wait_for_first_load()
def switch_players(self):
self.last_player = self.current_player
self.current_player = self.next_player
self.next_player.set_to_default()
def play_video(self):
self.current_player.play_content()
self.last_player.exit()
next_context = data_centre.get_next_context()
self.next_player.load_content(next_context)
self.wait_for_next_cycle()
def wait_for_next_cycle(self):
if(self.current_player.is_finished or self.manual_next):
self.manual_next = False
if self.next_player.is_loaded:
self.switch_players()
self.play_video()
else:
self.current_player.pause_content()
self.wait_for_next_load()
else:
#need to delay here and then
time.sleep(1)
self.wait_for_next_cycle()
def wait_for_next_load(self):
if(self.next_player.is_loaded):
self.switch_players()
self.play_video()
else:
#need to delay here, and then
time.sleep(1)
self.widget.after(self.delay, self._status_loop)
self.wait_for_next_load()
def get_info_for_video_display(self):
return self.current_player.bank_number, self.current_player.status, self.next_player.bank_number,\
self.next_player.status, self.current_player.duration, self.current_player.video_length
class video_player(object):
def __init__(self):
self.is_loaded = False
self.is_finished = False
self.status = 'UNASSIGNED'
self.bank_number = '-'
self.duration = 0
self.video_length = 10
def get_fake_duration(self):
self.duration = self.duration + 1
return self.duration
def play_content(self):
self.status = 'PLAYING'
time.sleep(1)
self.duration = 1
time.sleep(1)
self.duration = 2
time.sleep(1)
self.duration = 3
time.sleep(1)
self.duration = 4
time.sleep(1)
self.duration = 5
time.sleep(1)
self.duration = 6
time.sleep(1)
self.duration = 7
time.sleep(1)
self.duration = 8
time.sleep(1)
self.duration = 9
self.is_finished = True
pass
def load_content(self, context):
self.status = 'LOADING'
time.sleep(3)
self.status = 'LOADED'
self.is_loaded = True
#do the loading...
pass
def set_to_default(self):
self.is_finished = False
self.is_loaded = False
def exit(self):
pass
def pause_content(self):
self.status = 'PAUSED'