Intro
This is basically the same post as my previous post, Raspberry Pi photo frame using your pictures on your Google Drive. The main idea I am introducing in this treatment is better time separation of the photo triplets. Mostly for my own sake, I’ve re-named most of the relevant files and re-worked some as well in order to avoid name conflicts.
I find this treatment is pretty robust and can withstand a lot of errors and mistakes.
The files
The brains of the thing.
master3.sh
#!/bin/sh
# DrJ 1/2021
# call this from cron once a day to refesh random slideshow once a day
NUMFOLDERS=20
DEBUG=1
HOME=/home/pi
RANFILE=$HOME/random.list
REANFILE=$HOME/rean.list
DISPLAYFOLDER=$HOME/Pictures
DISPLAYFOLDERTMP=$HOME/Picturestmp
MSHOW=$HOME/mediashow
MSHOW2=$HOME/mediashowtmp2
SLEEPINTERVAL=3
STARTFOLDER=”MaryDocs/Pictures and videos”
echo “Starting master process at “`date`
cd $HOME
rm -rf $DISPLAYFOLDERTMP
mkdir $DISPLAYFOLDERTMP
#listing of all Google drive files starting from the picture root
# this takes a few minutes so we may want to skip for debugging
if [ “$1” = “skip” ]; then
if [ $DEBUG -eq 1 ]; then echo SKIP Listing all files from Google drive; fi
else
if [ $DEBUG -eq 1 ]; then echo Listing all files from Google drive; fi
rclone ls remote:”$STARTFOLDER” > files
# filter down to only jpegs, lose the docs folders
if [ $DEBUG -eq 1 ]; then echo Picking out the JPEGs; fi
egrep ‘\.[jJ][pP][eE]?[gG]$’ files |awk ‘{$1=””; print substr($0,2)}’|grep -i -v /docs/ > jpegs.list
fi
# throw NUMFOLDERS or so random numbers for picture selection, select triplets of photos by putting
# names into a file
if [ $DEBUG -eq 1 ]; then echo Generate random filename triplets; fi
./random-files3.pl -f $NUMFOLDERS -j jpegs.list -r $RANFILE
# copy over these 60 jpegs
if [ $DEBUG -eq 1 ]; then echo Copy over these random files; fi
cat $RANFILE|while read line; do
if [ $DEBUG -eq 1 ]; then echo filepath is $line; fi
rclone copy remote:”${STARTFOLDER}/$line” $DISPLAYFOLDERTMP
sleep $SLEEPINTERVAL
done
# do a re-analysis to push pictures further apart in time
if [ $DEBUG -eq 1 ]; then echo Re-analyzing pictures for their timestamps; fi
cd $DISPLAYFOLDERTMP; $HOME/reanalyze.pl
# copy over just the new pictures that we determined were needed
if [ $DEBUG -eq 1 ]; then echo Copy over the needed replacement files; fi
cat $REANFILE|while read line; do
if [ $DEBUG -eq 1 ]; then echo filepath is $line; fi
rclone copy remote:”${STARTFOLDER}/$line” $DISPLAYFOLDERTMP
sleep $SLEEPINTERVAL
done
# rotate pics as needed
if [ $DEBUG -eq 1 ]; then echo Rotate the pics which need it; fi
cd $DISPLAYFOLDERTMP; $HOME/rotate-as-needed.sh
cd ~
# kill any old slideshow
if [ $DEBUG -eq 1 ]; then echo Killing old fbi slideshow; fi
sudo pkill -9 -f fbi
pkill -9 -f m3.pl
# remove old pics
if [ $DEBUG -eq 1 ]; then echo Removing old pictures; fi
rm -rf $DISPLAYFOLDER
mv $DISPLAYFOLDERTMP $DISPLAYFOLDER
cp $MSHOW2 $MSHOW
#run looping fbi slideshow on these pictures
if [ $DEBUG -eq 1 ]; then echo Start fbi slideshow in background; fi
cd $DISPLAYFOLDER ; nohup ~/m3.pl >> ~/m3.log 2>&1 &
if [ $DEBUG -eq 1 ]; then echo “And now it is “`date`; fi
random-files3.pl
#!/usr/bin/perl
use Getopt::Std;
my %opt=();
getopts(“c:df:j:r:”,\%opt);
$nofolders = $opt{f} ? $opt{f} : 20;
$DEBUG = $opt{d} ? 1 : 0;
$cutoff = $opt{c} ? $opt{c} : 5;
$cutoffS = 60*$cutoff;
$jpegs = $opt{j} ? $opt{j} : “jpegs.list”;
$ranpicfile = $opt{r} ? $opt{r} : “jpegs-random.list”;
print “d,f,j,r: $opt{d}, $opt{f}, $opt{j}, $opt{r}\n” if $DEBUG;
$mshowt = “mediashowtmp”;
open(JPEGS,$jpegs) || die “Cannot open jpegs listing file $jpegs!!\n”;
@jpegs = ;
# remove newline character
$nopics = chomp @jpegs;
open(RAN,”> $ranpicfile”) || die “Cannot open random picture file $ranpicfile!!\n”;
for($i=0;$i<$nofolders;$i++) {
$t = int(rand($nopics-2));
print "random number is: $t\n" if $DEBUG;
# a lot of our pics follow this naming convention
# 20160831_090658.jpg
($date,$time) = $jpegs[$t] =~ /(\d{8})_(\d{6})/;
if ($date) {
print "date, time: $date $time\n" if $DEBUG;
# ensure neighboring picture is at least five minutes different in time
$iPO = $iP = $diff = 0;
($hr,$min,$sec) = $time =~ /(\d\d)(\d\d)(\d\d)/;
$secs = 3600*$hr + 60*$min + $sec;
print "Pre-pic logic\n";
while ($diff < $cutoffS) {
$iP++;
$priorPic = $jpegs[$t-$iP];
$Pdate = $Ptime = 0;
($Pdate,$Ptime) = $priorPic =~ /(\d{8})_(\d{6})/;
($Phr,$Pmin,$Psec) = $Ptime =~ /(\d\d)(\d\d)(\d\d)/;
$Psecs = 3600*$Phr + 60*$Pmin + $Psec;
print "hr,min,sec,Phr,Pmin,Psec: $hr,$min,$sec,$Phr,$Pmin,$Psec\n" if $DEBUG;
$diff = abs($secs - $Psecs);
print "diff: $diff\n" if $DEBUG;
# end our search if we happened upon different dates
$diff = 99999 if $Pdate ne $date;
}
# post-picture logic - same as pre-picture
print "Post-pic logic\n";
$diff = 0;
while ($diff < $cutoffS) {
$iPO++;
$postPic = $jpegs[$t+$iPO];
$Pdate = $Ptime = 0;
($Pdate,$Ptime) = $postPic =~ /(\d{8})_(\d{6})/;
($Phr,$Pmin,$Psec) = $Ptime =~ /(\d\d)(\d\d)(\d\d)/;
$Psecs = 3600*$Phr + 60*$Pmin + $Psec;
print "hr,min,sec,Phr,Pmin,Psec: $hr,$min,$sec,$Phr,$Pmin,$Psec\n" if $DEBUG;
$diff = abs($Psecs - $secs);
print "diff: $diff\n" if $DEBUG;
# end our search if we happened upon different dates
$diff = 99999 if $Pdate ne $date;
}
} else {
$iP = $iPO = 2;
}
$priorPic = $jpegs[$t-$iP];
$Pic = $jpegs[$t];
$postPic = $jpegs[$t+$iPO];
print RAN qq($priorPic
$Pic
$postPic
);
# this is how we'll preserve the order of the pictures. ls -1 often gives a different order!!
($p1) = $priorPic =~ /([^\/]+)$/;
($p2) = $Pic =~ /([^\/]+)$/;
($p3) = $postPic =~ /([^\/]+)$/;
print "p1 p2 p3: $p1 $p2 $p3" if $DEBUG;
$picsinorder .= $p1 . "\0" . $p2 . "\0" . $p3 . "\0";
}
close(RAN);
open(MS,">$mshowt”) || die “Cannot open mediashow file $mshowt!!\n”;
print MS $picsinorder;
close(MS);
print “pics in order: $picsinorder\n” if $DEBUG;
reanalyze.pl
#!/usr/bin/perl
use Getopt::Std;
my %opt=();
#
# assumption is that we are runnin this from a directory containing pictures
$tier1 = 100; $tier2 = 200; $tier3 = 300; # secs
$DEBUG = 1;
$HOME = “/home/pi”;
# pics are here
$pNames = “$HOME/reanpicnames”;
$ranfile = “$HOME/random.list”;
$reanfile = “$HOME/rean.list”;
$origfile = “$HOME/jpegs.list”;
$mshowt = “$HOME/mediashowtmp”;
$mshow2 = “$HOME/mediashowtmp2”;
open(REAN,”>$reanfile”) || die “Cannot open reanalyze file $reanfile!!\n”;
$ms = `cat $mshowt`;
print “Original media show: $ms\n” if $DEBUG;
@lines = split(‘\0’,$ms);
$Pdate = $Phr = $Pmin = $Psec = 0;
$diff = 9999;
for($i=0;$i<@lines;$i++){
$date = 0;
$secs = $ymd = 0;
$_ = $lines[$i];
$file = $_;
# ignore pictures with names like 20130820_180050.jpg
next if /\d{8}_\d{4}/;
open(ANAL,"$HOME/getinfo.py \"$file\"|") || die "Cannot open file: $file!!\n";
print "filename: $file\n" if $DEBUG;
while(){
#extract date and time from remaining pictures, if possible
# # DateTimeOriginal = 2018:08:18 20:16:47
# print STDERR “DATE: $_” if $DEBUG;
if (/date/i && $date++ < 1) {
print "date match in getinfo.pyoutput: $_" if $DEBUG;
($ymd,$hr,$min,$sec) = /(\d{4}:\d\d:\d\d) (\d\d):(\d\d):(\d\d)/;
$secs = 3600*$hr + 60*$min + $sec;
print "file,secs,ymd,i: $file,$secs,$ymd,$i\n" if $DEBUG;
$YMD[$i] = $ymd;
$SECS[$i] = $secs;
}
} # end loop over analysis of this pic
} # end loop over all files
# now go over that
$oldfolder = 0;
for($i=1;$i<@lines;$i++){
$folder = int($i/3) + 1;
next unless $folder != $oldfolder;
print "analyzing results. folder no. $folder\n" if $DEBUG;
# analyze pics in triplets
# center pic
$j = ($folder - 1)*3 + 1;
for ($o=-1;$o<2;$o+=2){
$k=$j+$o;
print "j,k,o: $j,$k,$o\n" if $DEBUG;
next unless $SECS[$j] > 0 && $YMD[$j] == $YMD[$k] && $YMD[$j] > 0;
print “We have non-0 dates we’re dealing with\n” if $DEBUG;
$file = $lines[$k];
chomp($file);
$diff = abs($SECS[$j] – $SECS[$k]);
print “diff: $diff\n” if $DEBUG;
next unless $diff < $tier3;
# the closer the files are together the more we push away
$bump = 1 if $diff < $tier3;
$bump = 2 if $diff < $tier2;
$bump = 3 if $diff < $tier1;
# get full filepath
$filepath = `grep \"$file\" $ranfile`;
chomp($filepath);
# now use that to search within the jpegs file listing
$prog = $o < 0 ? "head" : "tail";
$newfilepath = `grep -C$bump "$filepath" $origfile|$prog -1`;
($newfile) = $newfilepath =~ /([^\/]+)$/;
chomp($newfile);
print "file,filepath,newfile,newfilepath,bump: $file,$filepath,$newfile,$newfilepath,$bump\n" if $DEBUG;
print REAN $newfilepath;
# we'll get the new pictures over in a separate step to keep this more atomic
$ms =~ s/$file/$newfile/;
}
$oldfolder = $folder;
} # end loop over pics
# print out new mediashow pics in order
print "Printing new mediashow: $ms\n" if $DEBUG;
open(MS,">$mshow2″) || die “Cannot open mediashow $mshow2!!\n”;
print MS $ms;
close(MS)
rotate-as-needed.sh
#!/bin/sh
# DrJ 12/2020
# some of our downloaded files will be sideways, and fbi doesn’t auto-rotate them as far as I know
# assumption is that our current directory is the one where we want to alter files
ls -1|while read line; do
echo file is “$line”
o=`~/getinfo.py “$line”|grep -i orientation|awk ‘{print $NF}’`
echo orientation is $o
if [ “$o” -eq “6” ]; then
echo “90 clockwise is needed, o is $o”
# rotate and move it
~/rotate.py -90 “$line”
mv rot_”$line” “$line”
elif [ “$o” -eq “8” ]; then
echo “90 counterclock is needed, o is $o”
# rotate and move it
~/rotate.py 90 “$line”
mv rot_”$line” “$line”
elif [ “$o” -eq “3” ]; then
echo “180 rot is needed, o is $o”
# rotate and move it
~/rotate.py 180 “$line”
mv rot_”$line” “$line”
fi
done
rotate.py
#!/usr/bin/python3
# call with two arguments: degrees-to-rotate and filename
import PIL, os
import sys
from PIL import Image
# first do: pip3 install piexif
import piexif
degrees = int(sys.argv[1])
pic = sys.argv[2]
picture= Image.open(pic)
# see https://github.com/hMatoba/Piexif for piexif writeup
exif_dict = piexif.load(picture.info[“exif”])
exif_bytes = piexif.dump(exif_dict)
# both rotate and preserve EXIF data
picture.rotate(degrees,expand=True).save(“rot_” + pic,”jpeg”, exif=exif_bytes)
getinfo.py
#!/usr/bin/python3
import os,sys
from PIL import Image
from PIL.ExifTags import TAGS
for (tag,value) in Image.open(sys.argv[1])._getexif().items():
print (‘%s = %s’ % (TAGS.get(tag), value))
print (‘%s = %s’ % (TAGS.get(tag), value))
m3.pl
#!/usr/bin/perl
# show the pics ; rotate the screen as needed
# for now, assume the display is in a neutral
# orientation at the start
use Time::HiRes qw(usleep);
$DEBUG = 1;
$delay = 6; # seconds between pics
$mdelay = 200; # milliseconds
$mshow = “$ENV{HOME}/mediashow”;
$pNames = “$ENV{HOME}/pNames”;
# pics are here
$picsDir = “$ENV{HOME}/Pictures”;
chdir($picsDir);
$cn = `ls -1|wc -l`;
chomp($cn);
print “$cn files\n” if $DEBUG;
# throw up a first picture – all black. Trick to make black bckgrd permanent
system(“sudo fbi -a –noverbose -T 1 $ENV{HOME}/black.jpg”);
system(“sudo fbi -a –noverbose -T 1 $ENV{HOME}/black.jpg”);
sleep(1);
system(“sleep 2; sudo killall fbi”);
# start infinitely looping fbi slideshow
for (;;) {
# then start slide show
# shell echo cannot work with null character so we need to use a file to store it
#system(“cat $picNames|xargs -0 qiv -DfRsmi -d $delay \&”);
system(“sudo xargs -a $mshow -0 fbi -a –noverbose -1 -T 1 -t $delay “);
# fbi runs in background, then exits, so we need to monitor if it’s still alive
# wait appropriate estimated amount of time, then look aggressively for fbi
sleep($delay*($cn – 2));
for(;;) {
open(MON,”ps -ef|grep fbi|grep -v grep|”) || die “Cannot launch ps -ef!!\n”;
$match = ;
if ($match) {
print “got fbi match\n” if $DEBUG > 1;
} else {
print “no fbi match\n” if $DEBUG;
# fbi not found
last;
}
close(MON);
print “usleeping, noexist is $noexit\n” if $DEBUG > 1;
usleep($mdelay);
} # end loop testing if fbi has exited
} # close of infinite loop
crontab entries
@reboot sleep 20; ./m3.pl >> m3.log 2>&1
26 5 * * * ./master3.sh >> master.log 2>&1
And… that’s it!
Reminder
Don’t forget to make all these files executable. Something like:
$ chmod +x *.pl *.py *.sh
should do it.
My equipment
RPi 3 running Raspbian Lite
Pi Display (probably would also work with an HDMI display)
Pre-install
There are a few things you’ll need such as fbi, python3, pip, python Pillow and rclone. That’s basically described in my previous post so I won’t repeat it here.
Getting started
To see how badly things are going for you (hey, I like to be cautiously pessimistic) after you’ve created all these files and have installed rclone, do a
$ ./master3.sh
If you have your rclone file listing (which takes a long time) and want to focusing on debugging the rest of it, do a
$ ./master3.sh skip
Discussion
In this version of Raspberry Pi photo frame I’ve made more effort to force time separation between the randomly selected photos. I am afraid to overwrite what I have previously posted because that by itself is a complete solution and works quite well on its own. So this can be considered worthy of folks looking for a little more challenge to get slightly better results.
RPi lost Wifi
This could be a whole separate post. In the course of my hard work my RPi just would not acquire an IP address on wlan0.
Here’s a great command to see all the SSIDs it knows about:
$ sudo iwlist wlan0 scan > scan.log
Then you can inspect scan.log in an editor. Turns out the one SSID it needed wasn’t in the list. Turns out I had reserved a DHCP entry for it in my router. My router was simply not cooperating, it seems – the RPi wasn’t doing anything wrong. I was almost ready to re-install the whole thing and waste hours… My router is an older model Linksys WRT1200AC. I removed the DHCP reservation on the router, then did a
$ sudo service networking stop; sudo service networking start
on the RPi, and…all was good! Its assigned IP won’t change that often, I can always check the router to see what it is. The management software with the Linksys is quite good.
Conclusion
A more advanced treatment of photos is shown in this post than I have done previously. It is fairly robust and will withstand quite a few user errors in my experience. The end result will be an interesting display of your photos, randomly selected but in small groupings.
References and related
Please see this popular post Raspberry Pi photo frame using your pictures on your Google Drive for more details