Newer
Older
#!/usr/bin/python3
import magic,os,pickle,re,subprocess
basedir = '/home/pi/kiosk'
contentbase = os.path.join(basedir, 'content')
slidebase = os.path.join(basedir, 'slides')
tmpdir = os.path.join(basedir, 'tmp')
statusfile = os.path.join(basedir, 'var', 'statusfile')
loopfile = os.path.join(basedir, 'var', 'loop')
waitfile = os.path.join(basedir, 'var', 'wait')
confcsv = os.path.join(basedir, 'var', 'conf.csv')
rclonebase = ['/usr/bin/rclone', 'sync']
synccommand = rclonebase + [remote, contentbase]
# Default parameter values; each value is given as a triple:
# [default, minimum, maximum]
defaults = {
'slide_rate' : [10, 2, 60],
'check_time' : [30, 1, 60],
}
# File formats that "feh" can display directly with no processing
directformats = [
'JPEG image',
'PNG image',
'GIF image',
'Netpbm image',
]
# Video file formats that "omxplayer" can display
videoformats = [
'ISO Media',
'AVI',
'DivX',
]
# Template for loop script
looptemplate = '''#!/bin/bash
cd %s
'''
# Template for wait script
waittemplate = '''#!/bin/bash
sleep %d
'''
# Keep track of history to avoid repeating work
filestatus = {}
# List of videos to play will get built here
videolist = []
# Actual parameter values will get filled in here
parameters = {}
# Set a parameter from the config file and the defaults
def setparam(parname):
# Set up parameters
v = -1
try:
v = int(parameters[parname])
except:
pass
if (v < defaults[parname][1]) or (v > defaults[parname][2]):
v = defaults[parname][0]
return(v)
# Empty the temporary working directory
def cleantmp():
try:
tmpfiles = os.listdir(tmpdir)
for f in tmpfiles:
fpath = os.path.join(tmpdir, f)
os.unlink(fpath)
except:
pass
# Copy a file into the temporary directory
def tmpcopy(filename):
srcpath = os.path.join(contentbase, filename)
dstpath = os.path.join(tmpdir, filename)
subprocess.call(['/bin/cp', '-p', '-f', srcpath, dstpath])
# Move PNG files from temporary to slide directory
# Returns a list of moved files, and cleans the temporary directory
tmpfiles = os.listdir(tmpdir)
tmpfiles.sort()
filelist = []
for f in tmpfiles:
srcpath = os.path.join(tmpdir, f)
if moveall or (f[-4:] == '.png'):
dstpath = os.path.join(slidebase, f)
subprocess.call(['/bin/mv', '-f', srcpath, dstpath])
filelist += [f]
else:
os.unlink(srcpath)
return(filelist)
# Write out a new version of an executable script file
def writescript(scriptpath, content):
tmppath = scriptpath + '.new'
fdout = open(tmppath, 'w')
fdout.write(content)
fdout.close()
subprocess.call(['/bin/chmod', 'u+x', tmppath])
subprocess.call(['/bin/mv', '-f', tmppath, scriptpath])
def processfile(f):
global videolist
print('processfile start', f)
filepath = os.path.join(contentbase, f)
sb = os.stat(filepath)
filetype = ms.file(filepath)
# Active file, save status record
keepcontent[f] = True
if f in filestatus:
# Status record exists
# Check size and modification time
if (thisstatus['s'] == sb.st_size) and (thisstatus['m'] == sb.st_mtime) and (thisstatus['t'] == filetype):
# exact match, do not need to process this file
print('exact', f)
# See if it is video or slides
if thisstatus['v']:
# Mark this video to get played
videolist += [f]
else:
# mark resulting slide files to save them
for i in thisstatus['f']:
keepslides[i] = True
return
else:
# something has changed
print('changed', f)
filestatus[f]['s'] = sb.st_size
filestatus[f]['m'] = sb.st_mtime
filestatus[f]['t'] = filetype
filestatus[f]['v'] = False
else:
# new file, no status record yet, so make one
print('new', f)
filestatus[f] = {}
filestatus[f]['s'] = sb.st_size
filestatus[f]['m'] = sb.st_mtime
filestatus[f]['t'] = filetype
filestatus[f]['v'] = False
destpath = os.path.join(slidebase, f)
# Check for images that can be displayed directly as slides
for ff in directformats:
if ff in filetype:
# Check for files that can be played as video clips
video = False
for ff in videoformats:
if ff in filetype:
video = True
if direct:
# files that can be displayed directly just need to be copied into place
print('direct', filepath)
subprocess.call(['/bin/cp', '-p', '-f', filepath, destpath])
subprocess.call(['/home/pi/kiosk/bin/file-pdf'])
elif 'PostScript document' in filetype:
subprocess.call(['/home/pi/kiosk/bin/file-ps'])
elif 'PowerPoint' in filetype or 'Microsoft Word' in filetype or 'Zip archive data' in filetype:
subprocess.call(['/home/pi/kiosk/bin/file-office'])
elif 'ASCII text' in filetype:
subprocess.call(['/home/pi/kiosk/bin/file-text'])
else:
print('unknown', filepath)
print(filetype)
print(f, filetype, direct, video)
filelist = tmpmove(direct)
filestatus[f]['f'] = filelist
keepslides[i] = True
filestatus[f]['v'] = video
print('processfile done', f)
# end of functions, main program starts here
# Start by synchronizing with cloud storage to get content
# Set up to read magic numbers and guess file types
ms = magic.open(magic.NONE)
ms.load()
# Empty temporary directory
cleantmp()
# Read previous file status, if it exists
try:
stf = open(statusfile, 'rb')
filestatus = pickle.load(stf)
stf.close()
except:
pass
# Slide files to preserve
keepcontent = {}
keepslides = {}
# Turn the configuration file from a Google Sheet to a CSV file
subprocess.call(['/home/pi/kiosk/bin/process-conf'])
# Run through the configuration file and record parameters
confdata = open(confcsv).readlines()
for confline in confdata[1:]:
fields = confline.split(',')
if len(fields) > 1:
parameter = fields[0].strip()
value = fields[1].strip()
parameters[parameter] = value
slide_rate = setparam('slide_rate')
check_time = setparam('check_time')
print('slide_rate', slide_rate)
print('check_time', check_time)
# Examine and process all content files
contentfiles = os.listdir(contentbase)
contentfiles.sort()
for f in contentfiles:
# Do not process config file as content
if f != 'conf.xlsx':
processfile(f)
# If requested, upload rendered slides for verification
try:
doupload = rclonebase + [slidebase, updst]
updst = os.path.join(remote, parameters['verify_folder'])
except:
pass
# Prepare the looping script
# Videos get run one at a time
for video in videolist:
loopscript += "omxplayer -b '../content/%s'\n" % video
# Write out the completed looping script
writescript(loopfile, loopscript)
# Prepare and write out the waiting script (convert minutes to seconds)
waitscript = waittemplate % (check_time * 60)
writescript(waitfile, waitscript)
print('keepslides', keepslides)
# final cleanup, delete obsolete slides
# Get list of slide files
files = os.listdir(slidebase)
files.sort()
for f in files:
if f not in keepslides:
try:
os.unlink(os.path.join(slidebase, f))
print('unlink slide', f)
except:
pass
# Delete status records for obsolete content
deletekeys = []
for s in filestatus.keys():
if s not in keepcontent:
deletekeys += [s]
for s in deletekeys:
del filestatus[s]
print('delete status', s)
# Write new status file
stf = open(statusfile, 'wb')
pickle.dump(filestatus, stf)
stf.close()