Skip to content
Snippets Groups Projects
run 6.8 KiB
Newer Older
don's avatar
don committed
#!/usr/bin/python3
import magic,os,pickle,re,subprocess

basedir = '/home/pi/kiosk'
remote = 'Kiosks:Kiosks/Test2'
statusfile  = os.path.join(basedir, 'state', 'statusfile')
contentbase = os.path.join(basedir, 'content')
slidebase   = os.path.join(basedir, 'slides')
tmpdir      = os.path.join(basedir, 'tmp')
loopfile    = os.path.join(basedir, 'state', 'loop')
looptmp     = os.path.join(basedir, 'state', 'loop.tmp')
confcsv     = os.path.join(basedir, 'conf.csv')
rclonebase  = ['/usr/bin/rclone', 'sync']
synccommand = rclonebase + [remote, contentbase]
don's avatar
don committed

# File formats that "feh" can display directly with no processing
directformats = [
  'JPEG image',
  'PNG image',
  'GIF image',
  'Netpbm image',
]

# Interpreter line for the loop script
loopscript = '#!/bin/bash\n'

don's avatar
don committed
# 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(contentdir, filename):
  srcpath = os.path.join(contentbase, contentdir, filename)
don's avatar
don committed
  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
def tmpmove(contentdir, moveall):
don's avatar
don committed
  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, contentdir, f)
don's avatar
don committed
      subprocess.call(['/bin/mv', '-f', srcpath, dstpath])
      filelist += [f]
    else:
      os.unlink(srcpath)
  return(filelist)

# Synchronize with cloud storage
subprocess.call(synccommand)

# 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:
  filestatus = {}

# Slide files to preserve
keepcontent = {}
keepslides = {}

def processfile(contentdir, f):
  print('processfile start', contentdir, f)
  slidedir = os.path.join(slidebase, contentdir)
  contentpath = os.path.join(contentbase, contentdir)
  filepath = os.path.join(contentpath, f)
  sb = os.stat(filepath)
  filetype = ms.file(filepath)
  filetag = os.path.join(contentdir, f)
  # Active file, save status record
  keepcontent[filetag] = True
  if filetag in filestatus:
    # Status record exists
    # Check size and modification time
    thisstatus = filestatus[filetag]
    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)
      # mark resulting slide files to save them
      for i in thisstatus['f']:
        keepslides[os.path.join(contentdir, i)] = True
      return
    else:
      # something has changed
      print('changed', f)
      thisstatus['s'] = sb.st_size
      thisstatus['m'] = sb.st_mtime
      thisstatus['t'] = filetype
  else:
    # new file, no status record yet, so make one
    print('new', f)
    filestatus[filetag] = {}
    filestatus[filetag]['s'] = sb.st_size
    filestatus[filetag]['m'] = sb.st_mtime
    filestatus[filetag]['t'] = filetype

  # Figure out some useful paths
  tmppath  = os.path.join(tmpdir,   f)
  destpath = os.path.join(slidedir, f)
  direct = False
  for d in directformats:
    if d in filetype:
       direct = True
  if direct:
    # files that can be displayed directly just need to be copied into place
    print('direct', filepath)
    tmpcopy(contentdir, f)
    subprocess.call(['/bin/cp', '-p', '-f', filepath, destpath])
    keepslides[filetag] = True
  elif 'PDF document' in filetype:
    tmpcopy(contentdir, f)
    subprocess.call(['/home/pi/kiosk/bin/file-pdf'])
  elif 'PostScript document' in filetype:
    tmpcopy(contentdir, f)
    subprocess.call(['/home/pi/kiosk/bin/file-ps'])
  elif 'PowerPoint' in filetype or 'Microsoft Word' in filetype or 'Zip archive data' in filetype:
    tmpcopy(contentdir, f)
    subprocess.call(['/home/pi/kiosk/bin/file-office'])
  elif 'ASCII text' in filetype:
    tmpcopy(contentdir, f)
    subprocess.call(['/home/pi/kiosk/bin/file-text'])
  else:
    print('unknown', filepath)
    print(filetype)
  filelist = tmpmove(contentdir, direct)
  filestatus[filetag]['f'] = filelist
  for i in filelist:
    keepslides[os.path.join(contentdir, i)] = True
  print('processfile done', contentdir, f)


# Process all files in one directory into one set of slides
def processdir(contentdir):
  slidedir = os.path.join(slidebase, contentdir)
  contentpath = os.path.join(contentbase, contentdir)
  # Make the slide directory, in case it is new
  try:
    os.mkdir(slidedir)
  except:
    pass
don's avatar
don committed

  # Get list of content files
  files = os.listdir(os.path.join(contentbase, contentdir))
  files.sort()

  # Examine all content files
  for f in files:
    processfile(contentdir, f)

contentdirs = ['.']
# 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
confdata = open(confcsv).readlines()
for confline in confdata[1:]:
  # Ignore comments
  if confline[0] != '#':
    fields = confline.split(',')
    action    = fields[0].strip().lower()
    parameter = fields[1].strip()
    pathname  = fields[2].strip()
    if action == 'slides':
      loopscript += "cd '%s'\n" % pathname
      loopscript += "feh --cycle-once -FZ -D %s -R %s\n" % (parameter, parameter)
      loopscript += "cd ..\n"
      contentdirs += [pathname]
      processdir(pathname)
      uppath  = fields[3].strip()
      if uppath != '-':
        upsrc = os.path.join(slidebase, pathname)
        updst = os.path.join(remote, uppath)
        doupload = rclonebase + [upsrc, updst]
        subprocess.call(doupload)
    if action == 'video':
      loopscript += "omxplayer -b '../content/%s'\n" % pathname

loopout = open(looptmp, 'w')
loopout.write(loopscript)
loopout.close()
subprocess.call(['/bin/chmod', 'u+x', looptmp])
subprocess.call(['/bin/mv', '-f', looptmp, loopfile])

print('keepslides', keepslides)
  
don's avatar
don committed
# final cleanup, delete obsolete slides
# Get list of slide files
for d in contentdirs:
  slidedir = os.path.join(slidebase, d)
  files = os.listdir(slidedir)
  files.sort()
  for f in files:
    if os.path.join(d, f) not in keepslides:
      try:
        os.unlink(os.path.join(slidedir, f))
        print('unlink slide', f)
      except:
        pass
don's avatar
don committed

# 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()