Skip to content
Snippets Groups Projects
run 6.52 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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, 'loop')
    looptmp     = os.path.join(basedir, 'loop.tmp')
    confcsv     = os.path.join(basedir, 'conf.csv')
    synccommand = ['/usr/bin/rclone', 'sync', 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 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:
        filepath = os.path.join(contentpath, f)
        sb = os.stat(filepath)
        filetype = ms.file(filepath)
        processfile = True
        filetag = os.path.join(contentdir, f)
        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)
            processfile = False
            # mark resulting slide files to save them
            for i in thisstatus['f']:
              keepslides[os.path.join(contentdir, i)] = True
          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
      
        # Active file, save status record
        keepcontent[filetag] = True
      
        # if this content file needs to be made into slide file(s)
        if processfile:
          # 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
    
    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)
        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()