Skip to content

Instantly share code, notes, and snippets.

@triplingual
Last active February 14, 2024 03:18
Show Gist options
  • Save triplingual/a5aa4f15d8d255cbfd3f574d67f756bc to your computer and use it in GitHub Desktop.
Save triplingual/a5aa4f15d8d255cbfd3f574d67f756bc to your computer and use it in GitHub Desktop.
Script to transform Apple iTunes' .itc files into images (JPG or PNG)
#!/bin/bash
# Script to transform Apple iTunes' .itc files into images (JPG or PNG)
#
# This is a mod of the very useful script by Matthieu Riegler (@kyro38) at
# https://github.com/kyro38/MiscStuff/blob/master/Useless/itc/itcToImageV2.sh
#
# The mods are
# * to use `find` rather than `gfind` (I'm on a Mac and find` works just fine
# and I don't need to install `coreutils` just for this task)
# * to parameterize the source and destination directories for the .itc files
# * to add some communication with the user
# * to count the ITC files being processed in order to get a confirmation from the user
#
# My iTunes folder is usually nonstandard, I want to be able to run this on Album Artwork
# folders copied from others' machines (who don't want me hogging their machine for this
# effort), and I'm not consistent on my own machines whether I use the standard directory
# structure for iTunes.
#
# .itc files may be located in ~/Music/iTunes/Album Artwork unless you, like I,
# have customized iTunes' directory
#
# This script uses (/!\ needs ) ImageMagick's convert, hexdump, printf and dd.
#
# "Newer" (>2012?) iTunes ITC files may contain ARGB content, with as many as 3 sizes
# of the same image in one ITC file. Because of that, and depending on the size
# of your artwork directory, this script might be a little slow. One alternative
# is Simon Kennedy's Python module: http://www.sffjunkie.co.uk/python-itc.html
#
# Have a nice day.
# Test for inputs, use defaults if absent
if [ -z $1 ]; then
# absent any parameters, use defaults
AlbumArtwork="${HOME}/Music/iTunes/Album Artwork"
DestinationDir="$AlbumArtwork/Images"
echo
echo "Using default directories:"
echo " Source: " $AlbumArtwork
echo " Destination:" $DestinationDir
else
# Source dir
AlbumArtwork=$1
# Destination dir
DestinationDir=$2
echo
echo "Using these directories:"
echo " Source: " $AlbumArtwork
echo " Destination:" $DestinationDir
echo
fi
# Get count of ITC files for notice to script executor
FileCount=`find "$AlbumArtwork" -name '*.itc' | wc -l`
read -r -p "About to extract all art from
$AlbumArtwork
to
$DestinationDir.
There are $FileCount .itc files to be processed.
Continue (y/n)? " choice
case "$choice" in
y|Y )
echo "OK! Here we go."
;;
n|N )
echo "Abandoning script."
exit 1
;;
* )
echo "Invalid response, exiting."
exit 1
;;
esac
IFS=$'\n'
if [ ! -d "$DestinationDir" ]; then
mkdir "$DestinationDir"
echo "new Images dir"
fi
for file in `find "$AlbumArtwork" -name '*.itc'`; do
start=0x11C
exit=0;
i=1;
echo $file
while [ 1 ]; do
typeOffset=$(($start+0x30))
imageType=$(hexdump -n 4 -s $typeOffset -e '"0x"4/1 "%02x" "\n"' $file)
#If there is no next byte, jump to the next itc file.
if [[ -z $imageType ]]; then
break
fi
imageOffsetOffset=$(($start+8))
itemSize=$(hexdump -n 4 -s $start -e '"0x"4/1 "%02x" "\n"' $file)
imageOffset=$(hexdump -n 4 -s $imageOffsetOffset -e '"0x"4/1 "%02x" "\n"' $file)
imageStart=$(($start+$imageOffset))
imageSize=$(($itemSize-imageOffset))
imageWidth=$(hexdump -n 4 -s $(($start+56)) -e '"0x"4/1 "%02x" "\n"' $file)
imageWidth=$(printf "%d" $imageWidth)
imageHeight=$(hexdump -n 4 -s $(($start+60)) -e '"0x"4/1 "%02x" "\n"' $file)
imageHeight=$(printf "%d" $imageHeight)
dir=$(dirname "$file")
xbase=${file##*/} #file.etc
xpref=${xbase%.*} #file prefix
#echo $file
#echo itemsize $itemSize
#echo start $start
#echo imageOffset $imageOffset
#echo imageStart $imageStart
#echo imageSize $imageSize
#echo imageWidth $imageWidth
#echo imageHeight $imageHeight
if [[ $imageType -eq 0x504E4766 ]] || [[ $imageType -eq 0x0000000E ]] ; then
targetFile="$DestinationDir/$xpref-$i.png"
if [ ! -f "$targetFile" ]; then
echo PNG
dd skip=$imageStart count=$imageSize if="$file" of="$targetFile" bs=1 &> /dev/null
fi
elif [[ $imageType -eq 0x41524762 ]] ; then
targetFile="$DestinationDir/$xpref-$i.png"
if [ ! -f "$targetFile" ]; then
echo ARGB
dd skip=$imageStart count=$imageSize if="$file" of="$TMPDIR/test$i" bs=1 &> /dev/null
#Using a matrix to convert ARGB to RGBA since imagemagick does only support rgba input
convert -size $imageWidth"x"$imageHeight -depth 8 -color-matrix '0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0' rgba:"$TMPDIR/test$i" "$targetFile"
fi
elif [[ $imageType -eq 0x0000000D ]] ; then
targetFile="$DestinationDir/$xpref-$i.jpg"
if [ ! -f "$targetFile" ]; then
echo JPG
dd skip=$imageStart count=$imageSize if="$file" of="$targetFile" bs=1 &> /dev/null
fi
else
echo $imageType
exit=1
break;
fi
start=$(($start+$itemSize))
i=$(($i+1))
done
done
@Johnbo
Copy link

Johnbo commented Aug 31, 2022

I made no improvement at all, I simply added your lines 141-145 under 147 in order to always save as JPG if the imageType is not recognized. In other words: make JPG the default extension, even when it does not match with the hexdump.

@triplingual
Copy link
Author

Got it -- thank you for that!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment