#!/usr/bin/env python # -*- coding: utf8 -*- __author__ = "Viktor Petersson" __copyright__ = "Copyright 2012, WireLoad Inc" __license__ = "Dual License: GPLv2 and Commercial License" __version__ = "0.1" __email__ = "vpetersson@wireload.net" import sqlite3, ConfigParser from sys import exit from requests import get from platform import machine from os import path, getenv, remove, makedirs from os import stat as os_stat from subprocess import Popen, call import html_templates from datetime import datetime from time import sleep, time import logging from glob import glob from stat import S_ISFIFO # Initiate logging logging.basicConfig(level=logging.INFO, filename='/tmp/screenly_viewer.log', format='%(asctime)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S') # Silence urllib info messages ('Starting new HTTP connection') # that are triggered by the remote url availability check in view_web requests_log = logging.getLogger("requests") requests_log.setLevel(logging.WARNING) logging.debug('Starting viewer.py') # Get config file config = ConfigParser.ConfigParser() conf_file = path.join(getenv('HOME'), '.screenly', 'screenly.conf') if not path.isfile(conf_file): logging.info('Config-file missing.') exit(1) else: logging.debug('Reading config-file...') config.read(conf_file) def time_lookup(): if nodetype == "standalone": return datetime.now() elif nodetype == "managed": return datetime.utcnow() def str_to_bol(string): if 'true' in string.lower(): return True else: return False class Scheduler(object): def __init__(self, *args, **kwargs): logging.debug('Scheduler init') self.update_playlist() def get_next_asset(self): logging.debug('get_next_asset') self.refresh_playlist() logging.debug('get_next_asset after refresh') if self.nassets == 0: return None idx = self.index self.index = (self.index + 1) % self.nassets logging.debug('get_next_asset counter %d returning asset %d of %d' % (self.counter, idx+1, self.nassets)) if shuffle_playlist and self.index == 0: self.counter += 1 return self.assets[idx] def refresh_playlist(self): logging.debug('refresh_playlist') dbisnewer = get("http://127.0.0.1:8080/dbisnewer/"+str(self.gentime)) logging.info('dbisnewer: code (%d), text: (%s)' % (dbisnewer.status_code, dbisnewer.text)) time_cur = time_lookup() logging.debug('refresh: counter: (%d) deadline (%s) timecur (%s)' % (self.counter, self.deadline, time_cur)) if dbisnewer.status_code == 200 and dbisnewer.text == "yes": self.update_playlist() elif shuffle_playlist and self.counter >= 5: self.update_playlist() elif self.deadline <= time_cur: self.update_playlist() def update_playlist(self): logging.debug('update_playlist') (self.assets, self.deadline) = generate_asset_list() self.nassets = len(self.assets) self.gentime = time() self.counter = 0 self.index = 0 logging.debug('update_playlist done, count %d, counter %d, index %d, deadline %s' % (self.nassets, self.counter, self.index, self.deadline)) def generate_asset_list(): logging.info('Generating asset-list...') conn = sqlite3.connect(database, detect_types=sqlite3.PARSE_DECLTYPES) c = conn.cursor() c.execute("SELECT asset_id, name, uri, md5, start_date, end_date, duration, mimetype FROM assets ORDER BY name") query = c.fetchall() playlist = [] time_cur = time_lookup() deadline = None for asset in query: asset_id = asset[0] name = asset[1].encode('ascii', 'ignore') uri = asset[2] md5 = asset[3] start_date = asset[4] end_date = asset[5] duration = asset[6] mimetype = asset[7] logging.debug('generate_asset_list: %s: start (%s) end (%s)' % (name, start_date, end_date)) if (start_date and end_date) and (start_date < time_cur and end_date > time_cur): playlist.append({"name" : name, "uri" : uri, "duration" : duration, "mimetype" : mimetype}) if (start_date and end_date) and (start_date < time_cur and end_date > time_cur): if deadline == None or end_date < deadline: deadline = end_date if (start_date and end_date) and (start_date > time_cur and end_date > start_date): if deadline == None or start_date < deadline: deadline = start_date logging.debug('generate_asset_list deadline: %s' % deadline) if shuffle_playlist: from random import shuffle shuffle(playlist) return (playlist, deadline) def load_browser(): logging.info('Loading browser...') browser_bin = "uzbl-browser" browser_resolution = resolution if show_splash: browser_load_url = "http://127.0.0.1:8080/splash_page" else: browser_load_url = black_page browser_args = [browser_bin, "--geometry=" + browser_resolution, "--uri=" + browser_load_url] browser = Popen(browser_args) logging.info('Browser loaded. Running as PID %d.' % browser.pid) if show_splash: # Show splash screen for 60 seconds. sleep(60) else: # Give browser some time to start (we have seen multiple uzbl running without this) sleep(10) return browser def get_fifo(): candidates = glob('/tmp/uzbl_fifo_*') for file in candidates: if S_ISFIFO(os_stat(file).st_mode): return file else: return None def disable_browser_status(): logging.debug('Disabled status-bar in browser') f = open(fifo, 'a') f.write('set show_status = 0\n') f.close() def view_image(image, name, duration): logging.debug('Displaying image %s for %s seconds.' % (image, duration)) url = html_templates.image_page(image, name) f = open(fifo, 'a') f.write('set uri = %s\n' % url) f.close() sleep(int(duration)) f = open(fifo, 'a') f.write('set uri = %s\n' % black_page) f.close() def view_video(video): arch = machine() ## For Raspberry Pi if arch == "armv6l": logging.debug('Displaying video %s. Detected Raspberry Pi. Using omxplayer.' % video) omxplayer = "omxplayer" omxplayer_args = [omxplayer, "-o", audio_output, "-w", str(video)] run = call(omxplayer_args, stdout=True) logging.debug(run) if run != 0: logging.debug("Unclean exit: " + str(run)) # Clean up after omxplayer omxplayer_logfile = path.join(getenv('HOME'), 'omxplayer.log') if path.isfile(omxplayer_logfile): remove(omxplayer_logfile) ## For x86 elif arch == "x86_64" or arch == "x86_32": logging.debug('Displaying video %s. Detected x86. Using mplayer.' % video) mplayer = "mplayer" run = call([mplayer, "-fs", "-nosound", str(video) ], stdout=False) if run != 0: logging.debug("Unclean exit: " + str(run)) def view_web(url, duration): # If local web page, check if the file exist. If remote, check if it is # available. if (html_folder in url and path.exists(url)): web_resource = 200 else: web_resource = get(url).status_code if web_resource == 200: logging.debug('Web content appears to be available. Proceeding.') logging.debug('Displaying url %s for %s seconds.' % (url, duration)) f = open(fifo, 'a') f.write('set uri = %s\n' % url) f.close() sleep(int(duration)) f = open(fifo, 'a') f.write('set uri = %s\n' % black_page) f.close() else: logging.debug('Received non-200 status (or file not found if local) from %s. Skipping.' % (url)) pass # Get config values configdir = path.join(getenv('HOME'), config.get('main', 'configdir')) database = path.join(getenv('HOME'), config.get('main', 'database')) nodetype = config.get('main', 'nodetype') show_splash = str_to_bol(config.get('viewer', 'show_splash')) audio_output = config.get('viewer', 'audio_output') shuffle_playlist = str_to_bol(config.get('viewer', 'shuffle_playlist')) try: resolution = config.get('viewer', 'resolution') except: resolution = '1920x1080' # Create folder to hold HTML-pages html_folder = '/tmp/screenly_html/' if not path.isdir(html_folder): makedirs(html_folder) # Set up HTML templates black_page = html_templates.black_page() # Fire up the browser run_browser = load_browser() logging.debug('Getting browser PID.') browser_pid = run_browser.pid logging.debug('Getting FIFO.') fifo = get_fifo() # Bring up the blank page (in case there are only videos). logging.debug('Loading blank page.') view_web(black_page, 1) logging.debug('Disable the browser status bar') disable_browser_status() scheduler = Scheduler() # Infinit loop. logging.debug('Entering infinite loop.') while True: asset = scheduler.get_next_asset() logging.debug('got asset'+str(asset)) if asset == None: # The playlist is empty, go to sleep. logging.info('Playlist is empty. Going to sleep.') sleep(5) else: logging.info('show asset %s' % asset["name"]) if "image" in asset["mimetype"]: view_image(asset["uri"], asset["name"], asset["duration"]) elif "video" in asset["mimetype"]: view_video(asset["uri"]) elif "web" in asset["mimetype"]: view_web(asset["uri"], asset["duration"]) else: print "Unknown MimeType, or MimeType missing"