{"id":1121,"date":"2026-01-25T01:58:08","date_gmt":"2026-01-25T08:58:08","guid":{"rendered":"https:\/\/strawhousepig.net\/wordpress\/?p=1121"},"modified":"2026-01-25T10:46:17","modified_gmt":"2026-01-25T17:46:17","slug":"batch-convert-heic-to-jpg","status":"publish","type":"post","link":"https:\/\/strawhousepig.net\/wordpress\/2026\/01\/25\/batch-convert-heic-to-jpg\/","title":{"rendered":"Batch convert HEIC to JPG"},"content":{"rendered":"\n<p>Decided to copy decades of digital photos from Photos (nee iPhoto) to NAS. If your client doesn&#8217;t decode and your server doesn&#8217;t transcode HEIC then you&#8217;ll need to convert HEIC image files. Unlike iPhoto, Photos makes the task of exporting the library in a sane manner with keywords intact unnecessarily impossible, or nearly so. Turns out some brave person created a Python script, <a href=\"https:\/\/github.com\/RhetTbull\/osxphotos\">osxphotos<\/a>, to accomplish that task. It&#8217;s certainly robust and installation failed for me using Mac Ports (likely due to the age of my system and plenty of outdated deps and other crud from unmaintained packages). But I digress. The instructions for &#8216;pip&#8217; worked no sweat.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>This particular script doesn&#8217;t rely on <code>osxphotos<\/code>, but it does use <code>exiftool<\/code> and <code>heif-dec<\/code> (which is provided by <code>libheif)<\/code>. Where I got <code>libheif<\/code> I don&#8217;t know. I initially was going to try <code>imagemagick<\/code> but that also would not install from Mac Ports (c&#8217;mon guys). Could be re-written to use <code>imagemagick<\/code> I&#8217;m sure, so long as it can decode HEIC.<\/p>\n\n\n\n<p>Read through and understand what this script does before running! You won&#8217;t want it to touch your Photos Library.photolibrary so be sure it is no where below your starting directory first. This script then finds all file paths ending in &#8220;.HEIC&#8221; which is the iPhone naming scheme. Perhaps <code>find...-iname<\/code> would be better. Next, check to see if a .jpg file with the same name in the same directory exists, if so suffix the new name, then convert the heic image as a jpg. Last step is to mv (move &amp; rename) the original into a directory created at pwd (present working directory), adding YYYYMMDD- from the file&#8217;s Create Date exif tag as a prefix so it will be possible to relocate them should that become something someone desires. My directory tree drothers for digital photos are Y&gt;M&gt;D&gt;files, which explains the preceding logic. As it is here this script writes a file in pwd with each command it would otherwise execute. Change $dryrun to, well, anything else to execute the conversion(s) instead.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\n# Are we dry running first? Or fsck it we'll do it live!\ndryrun=\"true\"\n\n# We are working here. Please be sure to cd to the proper dir as there is no prompt for assurance!\nhere=`pwd`\n\n# Quality level of jpg output. You can adjust from stdin, though there is no check against it.\nqlvl=90\nif &#91; $1 ]; then\nqlvl=$1\nfi\n\n# Originals HEIC image files will be moved here (prefixed with their 'Create Date').\norigs=$here\/heic-originals\nif &#91; ! -d $origs ]; then\nmkdir $here\/heic-originals\nfi\n\n# Start the loop. Find all files with 'HEIC' extension. Ignore the \"heic-originals\" dir.\nfor i in $(find $here -type f -iname \"*.HEIC\" ! -path \"$origs\/*\")\ndo\nfilename=$(basename $i)\n_ext=${filename##*.}\n_name=${filename%.*}\n_dir=$(dirname $i)\n# Name of new jpg file. Unless...\n_newf=$_dir\/$_name.jpg\n# If a jpg with the same name exists, I guess salt the new one with what happened.\nif &#91; -f $_newf ]; then\n_newf=$_dir\/$_name-heic2.jpg\nfi\n# Get Create Date to add to original filename when it is mv'd to Originals directory.\nodate=$(exiftool -s3 -d %Y%m%d -CreateDate $i)\n\n# Finally we can build our command.\n_com=\"heif-dec -q $qlvl $i $_newf &amp;&amp; mv $i $origs\/$odate-$_name.$_ext\"\n\n# For dry run sanity, maybe cd to a directory with only a few HEIC files to test.\nif &#91; $dryrun = \"true\" ]; then\necho \"$_com\" &gt;&gt; $here\/test-run.txt;\nelse\neval $_com\nfi\n\n# Done.\ndone\n\n<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Decided to copy decades of digital photos from Photos (nee iPhoto) to NAS. If your client doesn&#8217;t decode and your server doesn&#8217;t transcode HEIC then you&#8217;ll need to convert HEIC image files. Unlike iPhoto, Photos makes the task of exporting the library in a sane manner with keywords intact unnecessarily impossible, or nearly so. Turns [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[18,28,33,34],"class_list":["post-1121","post","type-post","status-publish","format-standard","hentry","category-code","tag-gnulinux","tag-os-x","tag-script","tag-shell-script"],"_links":{"self":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts\/1121","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/comments?post=1121"}],"version-history":[{"count":7,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts\/1121\/revisions"}],"predecessor-version":[{"id":1129,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/posts\/1121\/revisions\/1129"}],"wp:attachment":[{"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/media?parent=1121"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/categories?post=1121"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/strawhousepig.net\/wordpress\/wp-json\/wp\/v2\/tags?post=1121"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}