Hardware & Software updates…

Haven’t posted in a while, so going to touch a few different topics.

First off, an Apple Watch update. I ran my series 1 for four years. Upgraded to a Series 5 mainly for battery life. I was going on vacation, and didn’t want to worry about it running out of power while I was out and about for the day. I was happy with the 5. That was the last one that didn’t support blood oxygen monitoring. Didn’t worry too much about that since I already a fingertip blood oxygen and pulse reader. I ran that for four years also. I did not get the Series 9. Went for the Ultra 2. The Ultra 2 is a beast. It’s big but I really do appreciate the larger battery. I can run it all day, collect sleep data, and then just pop it on a charger for 10-15 minutes in the morning to get back to an 80% charge. I also like the larger display, especially with simple analog watch faces. Fully expect to run this for at least four years.

I got a M1 MacBook. My last one was coming up on a decade of use. It still runs, and is a viable laptop. I still use it, but the M1 is a whole new experience. I got it with extra RAM and large SSD, since I plan on using that as long as I ran the Intel based one. One of the reasons I like the MacBooks is that they are bloody tanks. The hardware is rock solid, and the OS is stable. The M1 runs Baldur’s Gate 3 just as well as my Windows 10 tower with an i9 CPU. There are times I’m tempted to make the M1 MacBook my primary system instead of my Windows tower system.

I’ve got a pair of 27″ 4K monitors, which the MacBook drives nicely. One is a direct USB C connection (which also supplies power), and the other is connected to a USB C dongle via HDMI. The dongle also where the Ethernet cable is connected. Also picked up a Mac centric keyboard and mouse. Both are Logi products. The keyboard is a MX Mechanical, with the number pad and the extra loud clicky goodness. The mouse is the MX Master S3 for the Mac. Really please with both. The mouse tracks reliably on the black surface of my desk more reliably than the Apple Magic Mouse. YMMV, but it works for me.

Yes, I’m still running Windows 10. Thankfully the system I built a few years ago doesn’t have a TPM. So I rarely get the annoying ‘you need to upgrade your hardware to install the OS I don’t want’ messages from Microsoft. I had to use a Windows 11 system for a few weeks and I did not enjoy the experience. I’ve collected a lot of data over the years, and it’s stored on my homebuilt tower system. When I do a search, that is where I want to focus, not the Interwebs. I certainly don’t want search results for products someone is paying Microsoft put at the top of the list. I figured out quickly how to remove the new search button that is located where the Start button used to be. That improved my experience right away. Another ‘feature’ I didn’t like was that I couldn’t remote desktop to the Windows 11 system using the Microsoft remote desktop app on my Mac. I had to remote desktop to my Windows 10 system, and then remote desktop from there to the Windows 11 system. Probably some sort of ‘security’ setting that Microsoft has buried somewhere. I didn’t take the time to figure it out. Just stopped using the Windows 11 system. The only actually useful feature I found was tabs in File Explorer. Something MacOS has for years.

My opinion on Windows 11 is that Microsoft is trying to take us back to the days of the Mainframe and terminals. They want all your data in their cloud, where they can generate a continuing revenue stream. Personally, I’m not fond of this concept. There are multiple good reasons for the distributed data and processor concept, not all of which are technical. I’m old enough to remember when desktop personal computers on a LAN killed the microcomputer industry, and why.

Sorting pictures

I’ve been backing up my iPhone photos to my Windows 10 desktop using Microsoft’s OneDrive.  The photos are on my local drive as well as the cloud.  The downside is that all the photos, movies, and screen shots are dumped into a single directory, and there are a lot of them.

I prefer to have them sorted by date into directories.  A directory for each year, with sub-directories for each month.  I could do this by hand, but that’s a pain, and I have to remember to do it on a regular basis.  Plus I’ll probably want to do the same for my wife’s iPhone photos.  So I decided to write a python script to scan the camera roll directory, and copy the files to a directory in my photo archive section.  The scrip will create directories if needed and skip files that are already in place.

That part was pretty straight forward. I used os.chdir() to get to source directory and os.listdir() to get the directory contents.  Don’t want to create month directories for directories, just files, so I used os.path.isfile() to filter out non-files, and then check the file extension.  I only want jpg, mov, png, and tiff files.  I use Camera+ most of the time, which produces tiff files instead of jpg files. The png files are screenshots.

I used os.stat() to get the create time, and found files I exported to the camera roll from Camera plus had a create time of when they were exported, not the time the photo was taken. (Once I started having Camera+ dump straight to the camera roll, I didn’t have this problem).

So, I dug a little deeper and found I could get an image created time stamp with a getImageDate() call.  Downside was this didn’t work for png, tiff, or mov files.   So I had to do some extra sorting, and wrote another function to use on just the jpg files. I called the open() function from the Image library, and extracted the exif data using the _getexif() function. This works most of the time, so when it fails I had it return a ‘?’ rather than the time stamp string.  Seeing that caused a fall back to the getImageDate() function.

This extra call to the Image library made the placement of the files more accurate.  I had the base time function return the month as a three character string and the year as a 4 characters string.  This required some basic string manipulation. Those two parameters were added to predetermined destination and passed to a function I had written for another project that checks if directory exits, and creates it if it does not.

Next was to have the program check to see if the file already existed in the correct destination directory.  If it did, don’t bother copying it again. This will make the incremental runs faster and save on unneeded file transfers.

Now I have a functional script that I can set up to automatically run once a week.  Once the files are archived off OneDrive, I can removed them there and on the iPhone is order to free up space.

Update: Source Code

The tricky part is getting the time stamps.  Here are the three routines that handle that.  Wordpress mucks with the spacing, but you should be able to figure it out.

def getImageDate(sPath,file):
    'Get the image created time stamp'
    img = Image.open(sPath + "\\" + file)
 
    tTime = "?"
    imgData = img._getexif()
 
    if 306 in imgData and imgData[306] < tTime: # 306 = DateTime
        tTime = imgData[306]
     if 36867 in imgData and imgData[36867] < tTime: # 36867 =                             DateTimeOriginal
         tTime = imgData[36867]
     if 36868 in imgData and imgData[36868] < tTime: # 36868 =         DateTimeDigitized
          tTime = imgData[36868]
      return tTime
def getTime2(file,sPath):
    'If the file is not a jpg, get the last modified time, which is close enough'
    os.chdir(sPath)
    (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)
    year = time.ctime(mtime)[-4:]
    month = time.ctime(mtime)[4:7]

return month, year

def getTime(sPath,dPath,file):
     'Most files are jpg files, so this is the default path'
      month = "Jan"
     year = "1956" #If we see this year, we know there is a problem
     months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]
     ext = file[-3:].lower()
     if ext == 'jpg':
          cTime = getImageDate(sPath,file)
           if cTime != '?':
                   year = cTime[0:4]
                    monthNum = cTime[5:7]
                    month = months[(int(monthNum)) - 1]
            else:
                       month,year = getTime2(file,sPath)
    else:
            month,year = getTime2(file,sPath)
    return month,year